Zephyrnet-logo

Onderwerpmodellering: meerdere tags van onderzoeksartikelen voorspellen met behulp van de OneVsRest-strategie

Datum:

Afbeelding van OneVsRest-classificatie

Onlangs heb ik deelgenomen aan een NLP-hackathon - "Topic Modeling for Research Articles 2.0". Deze hackathon werd georganiseerd door de Analytics Vidhya platform als onderdeel van hun HackLive initiatief. De deelnemers werden begeleid door experts in een livesessie van 2 uur en kregen later een week de tijd om te strijden en het klassement te beklimmen.

Probleemstelling

Gezien de abstracts voor een reeks onderzoeksartikelen, is het de taak om de tags voor elk artikel in de testreeks te voorspellen.

trein- en testgegevens voor OneVsRest Classifier

De samenvattingen van het onderzoeksartikel zijn afkomstig uit de volgende 4 onderwerpen: informatica, wiskunde, natuurkunde, statistiek. Elk artikel kan mogelijk meerdere tags hebben onder 25 tags zoals Getaltheorie, Toepassingen, Kunstmatige Intelligentie, Astrofysica van sterrenstelsels, Informatietheorie, Materiaalkunde, Machine Learning et al. Inzendingen worden beoordeeld op micro F1-score tussen de voorspelde en waargenomen tags voor elk artikel in de testset.

voorbeeldinzending voor OneVsRest Classifier

Volledige probleemstelling en de dataset is beschikbaar hier..

Laten we zonder verder oponthoud aan de slag gaan met de code.

Gegevens laden en verkennen

Noodzakelijke bibliotheken importeren —

%matplotlib inline import numpy als np import panda's als pd import matplotlib.pyplot als plt import seaborn als sns #---------------------------- ------- van nltk.tokenize importeer word_tokenize van nltk.stem import PorterStemmer #------------------------------ ----- van sklearn.feature_extraction.text import CountVectorizer van sklearn.feature_extraction.text import TfidfVectorizer #---------------------------- ------- van sklearn import metrische gegevens van sklearn.metrics import nauwkeurigheid_score van sklearn.metrics import f1_score

Laad trein- en testgegevens uit .csv bestanden in Pandas DataFrame —

train_data = pd.read_csv('Train.csv') test_data = pd.read_csv('Test.csv')

Vorm van trein- en testgegevens —

print ("Treingrootte:", train_data.shape) print ("Testgrootte:", test_data.shape)

Output:

train OneVsRest-classificatie

Er zijn ~ 14k datapunten in de trein dataset en ~ 6k datapunten in de testset. Overzicht van trein- en testdatasets —

trein_data.info()

Output:

kolommen OneVsRest Classifier

test_data.info()

Output:

testgegevens voor OneVsRest Classifier

Zoals we kunnen zien aan de hand van de treingegevens, zijn er 31 kolommen - 1 kolom voor id, 1 kolom voor abstracte tekst, 4 kolommen voor onderwerpen, deze vormen allemaal onze functievariabelen en de volgende 25 kolommen zijn klasselabels die we hebben te 'leren' voor de voorspellingstaak.

topic_cols = ['Informatica', 'Wiskunde', 'Natuurkunde', 'Statistieken']
target_cols = ['Analyse van PDE's', 'Toepassingen', 'Kunstmatige intelligentie', 'Astrophysics of Galaxies', 'Computation and Language', 'Computer Vision and Pattern Recognition', 'Cosmology and Nongalactic Astrophysics', 'Data Structures and Algorithms' ', 'Differentiële meetkunde', 'Aarde en planetaire astrofysica', 'Fluid Dynamics', 'Informatietheorie', 'Instrumentatie en methoden voor astrofysica', 'Machine Learning', 'Materialenwetenschap', 'Methodologie', 'Getaltheorie' , 'Optimalisatie en controle', 'Representatietheorie', 'Robotica', 'Sociale en informatienetwerken', 'Statistiekentheorie', 'Sterk gecorreleerde elektronen', 'Supergeleiding', 'Systemen en controle']

Hoeveel datapunten hebben meer dan 1 tags?

my_list = [] for i in range(train_data.shape[0]): my_list.append(sum(train_data.iloc[i, 6:])) pd.Series(my_list).value_counts()

Output:

De meeste van onze onderzoeksartikelen hebben dus 1 of 2 tags.

Gegevens opschonen en voorverwerken voor OneVsRest Classifier

Voordat u begint met het opschonen en voorbewerken van gegevens, is het een goed idee om eerst enkele willekeurige voorbeelden van trainingsgegevens af te drukken en te bekijken om een ​​overzicht te krijgen. Op basis van mijn observatie heb ik de onderstaande pijplijn gebouwd voor het opschonen en voorbewerken van de tekstgegevens:

De-contractie → Speciale tekens verwijderen → Stopwoorden verwijderen → Stemming

Eerst definiëren we enkele hulpfuncties die nodig zijn voor tekstverwerking.

De-contracting van de Engelse uitdrukkingen —

def decontracted(phrase): #specifieke zin = re.sub(r”zal niet”, “zal niet”, zin) zin = re.sub(r”kan niet”, “kan niet”, zin) # algemene zin = re.sub(r”n't”, “not”, zin) zin = re.sub(r”'re”, “zijn”, zin) zin = re.sub(r”'s”, “ is ”, zin) zin = re.sub(r”'d”, “zou”, zin) zin = re.sub(r”'ll”, “ zal”, zin) zin = re.sub(r”'t ”, “niet”, zin) zin = re.sub(r”'ve”, “hebben”, zin) zin = re.sub(r”'m”, “am”, zin) zin = re.sub( r"'em", "hen", frase) return frase

Declareer stopwoorden —
(Ik geef de voorkeur aan mijn eigen aangepaste set stopwoorden boven de ingebouwde. Het helpt me om de set stopwoorden gemakkelijk aan te passen, afhankelijk van het probleem)

stopwords = ['i', 'me', 'my', 'myself', 'wij', 'our', 'ours', 'ourselves', 'you', "you're", "you've" , "je zult", "je zou", 'jouw', 'van jou', 'jezelf', 'jezelf', 'hij', 'hem', 'zijn', 'zichzelf', 'zij', " zij is”, 'haar', 'haar', 'haarzelf', 'het', 'het is', 'haar', 'zichzelf', 'zij', 'hen', 'hun', 'hun', 'zichzelf' , 'wat', 'welke', 'wie', 'wie', 'dit', 'dat', 'dat zal', 'deze', 'die', 'ben', 'is', 'zijn' , 'was', 'waren', 'zijn', 'zijn', 'zijn', 'hebben', 'heeft', 'had', 'hebben', 'doen', 'doen', 'doen', ' doen', 'een', 'een', 'de', 'en', 'maar', 'als', 'of', 'omdat', 'als', 'tot', 'terwijl', 'van' , 'op', 'door', 'voor', 'met', 'over', 'tegen', 'tussen', 'in', 'door', 'tijdens', 'voor', 'na', ' boven', 'beneden', 'naar', 'van', 'omhoog', 'omlaag', 'in', 'uit', 'aan', 'uit', 'over', 'onder', 'opnieuw' , 'verder', 'dan', 'eenmaal', 'hier', 'daar', 'wanneer', 'waar', 'waarom', 'hoe', 'alle', 'elke', 'beide', ' elk', 'weinig', 'meer', 'de meeste', 'andere', 'sommige', 'zo', 'alleen', 'eigen', 'hetzelfde', 'dus', 'dan', 'te' , 'v ery', 's', 't', 'can', 'will', 'just', 'don', 'don't', 'should', 'should've', 'now', 'd' , 'll', 'm', 'o', 're', 've', 'y', 'ain', 'are', 'zijn niet', 'kon', 'kon niet', ' niet', 'niet', 'niet', 'niet', 'had', 'had niet', 'heeft', 'heeft niet', 'hebben', 'niet', ' is', 'is niet', 'ma', 'misschien', 'misschien niet', 'moet', 'moet niet', 'nodig', 'hoeft niet', 'shan', 'shan' t”, 'zou moeten', 'zou niet', 'was', 'was niet', 'weren', 'waren niet', 'won', 'zal niet', 'zou', 'zou' t"]

U kunt ook rechtstreeks stopwoorden importeren uit: word cloud API-

van wordcloud import WordCloud, STOPWORDS stopwords = set(list(STOPWORDS))

Stemmen met Porter stemmer —

def stam(zin): token_words = word_tokenize(zin) stem_sentence = [] voor woord in token_words: stemmer = PorterStemmer() stem_sentence.append(stemmer.stem(word)) stem_sentence.append(“ “) return “”.join( stam_zin)

Nu we alle functies hebben gedefinieerd, gaan we een tekstvoorverwerkingspijplijn schrijven —

def text_preprocessing(text): preprocessed_abstract = [] voor zin in tekst: verzonden = decontracted(zin) verzonden = re.sub('[^A-Za-z0–9]+', ' ', verzonden) verzonden = ' ' .join(e.lower() for e in sent.split() if e.lower() niet in stopwoorden) sent = stam(verzonden) preprocessed_abstract.append(sent.strip()) return preprocessed_abstract

Voorbewerken van de treingegevens abstracte tekst—

train_data['preprocessed_abstract'] = text_preprocessing(train_data['ABSTRACT'].values) train_data[['ABSTRACT', 'preprocessed_abstract']].head()

Output:

voorverwerkte gegevens OneVsRest Classifier

Evenzo, het voorbewerken van de testdataset -

test_data['preprocessed_abstract'] = text_preprocessing(test_data['ABSTRACT'].values) test_data[['ABSTRACT', 'preprocessed_abstract']].head()

Output:

Abstracte OneVsRest-classificatie

Nu hebben we de originele kolom 'ABSTRACT' langer nodig. U kunt deze kolom uit de datasets verwijderen.

Codering van tekstgegevens

Treingegevens opsplitsen in trein- en validatiegegevenssets —

X = train_data[['Informatica', 'Wiskunde', 'Natuurkunde', 'Statistieken', 'preprocessed_abstract']] y = train_data[target_cols] van sklearn.model_selection import train_test_split X_train, X_cv, y_train, y_split(X train_test , y, test_size = 0.25, random_state = 21) print(X_train.shape, y_train.shape) print(X_cv.shape, y_cv.shape)

Output:

codering van tekstgegevens

Zoals we kunnen zien, hebben we ~ 10500 datapunten in onze trainingsset en ~ 3500 datapunten in de validatieset.

TF-IDF vectorisatie van tekstgegevens

Woordenschat opbouwen -

combined_vocab = list(train_data['preprocessed_abstract']) + list(test_data['preprocessed_abstract'])

Ja, hier heb ik willens en wetens een zonde begaan! Ik heb de volledige trein- en testgegevens voor het opbouwen van woordenschat gebruikt om er een model op te trainen. Idealiter zou uw model de testgegevens niet moeten zien.

vectorizer = TfidfVectorizer (min_df = 5, max_df = 0.5, sublinear_tf = True, ngram_range = (1, 1)) vectorizer.fit (combined_vocab)
X_train_tfidf = vectorizer.transform(X_train['preprocessed_abstract']) X_cv_tfidf = vectorizer.transform(X_cv['preprocessed_abstract']) 
print(X_train_tfidf.shape, y_train.shape) print(X_cv_tfidf.shape, y_cv.shape)

Output:

td-if-uitvoer

Na TF-IDF-codering krijgen we 9136 kenmerken, die elk overeenkomen met een afzonderlijk woord in de woordenschat.

Enkele belangrijke dingen die u hier moet weten —

  • Ik kwam niet direct tot de conclusie dat ik voor TF-IDF-vectorisatie moest gaan. Ik heb verschillende methoden geprobeerd, zoals BOW, W2V met behulp van een vooraf getraind GloVe-model, enz. Onder hen bleek TF-IDF de best presterende te zijn, dus hier demonstreer ik alleen dit.
  • Het leek me niet op magische wijze dat ik met uni-grammen zou gaan. Ik probeerde bi-grammen, tri-grammen en zelfs vier gram; het model dat de unigrams gebruikte, leverde de beste prestaties van allemaal.

Het coderen van tekstgegevens is een lastig iets. Vooral in competities waar zelfs een verschil van 0.001 in de prestatiestatistiek je een aantal plaatsen achter op het klassement kan duwen. Men moet dus openstaan ​​voor het uitproberen van verschillende permutaties en combinaties in een rudimentair stadium.

Voordat we verder gaan met modelleren, stapelen we alle kenmerken (onderwerpkenmerken + TF-IDF-gecodeerde tekstkenmerken) op elkaar voor respectievelijk trein- en testdatasets.

van scipy.sparse import hstack X_train_data_tfidf = hstack((X_train[topic_cols], X_train_tfidf)) X_cv_data_tfidf = hstack((X_cv[topic_cols], X_cv_tfidf))

Classificatie met meerdere labels met behulp van OneVsRest Classifier

Tot nu toe waren we alleen bezig met het verfijnen en vectoriseren van de feature-variabelen. Zoals we weten, is dit een classificatieprobleem met meerdere labels en kan elk document tegelijkertijd een of meer vooraf gedefinieerde tags hebben. We zagen al dat meerdere datapunten 2 of 3 tags hebben.

De meeste traditionele algoritmen voor machine learning zijn ontwikkeld voor classificatieproblemen met één label. Daarom transformeren veel benaderingen in de literatuur het multi-label probleem in meerdere single-label problemen, zodat de bestaande single-label algoritmen kunnen worden gebruikt.

Een dergelijke techniek is Een-tegen-de-rest (OvR) multiclass/multilabel classifier, ook bekend als één-tegen-alles. In OneVsRest Classifier passen we één classifier per klasse toe en dit is de meest gebruikte strategie voor classificatie met meerdere klassen/multilabels en is een redelijke standaardkeuze. Voor elke classificatie wordt de klasse vergeleken met alle andere klassen. Naast de rekenefficiëntie is een voordeel van deze benadering de interpreteerbaarheid. Aangezien elke klasse slechts door één en één classifier wordt vertegenwoordigd, is het mogelijk om kennis over de klasse te verwerven door de bijbehorende classifier te inspecteren.

Laten we zoeken naar de optimale hyperparameter 'C' —
('C' geeft de inverse van de regularisatiesterkte aan. Kleinere waarden specificeren een sterkere regularisatie).

van sklearn.multiclass import OneVsRestClassifier van sklearn.linear_model import LogisticRegression
C_range = [0.01, 0.1, 1, 10, 100] voor i in C_range: clf = OneVsRestClassifier(LogisticRegression(C = i, solver = 'sag')) clf.fit(X_train_data_tfidf, y_train) y_pred_train = clf.predict(X_train_data_tfidf ) y_pred_cv = clf.predict(X_cv_data_tfidf) f1_score_train = f1_score(y_train, y_pred_train, gemiddelde = 'micro') f1_score_cv = f1_score(y_cv, y_pred_cv, gemiddelde = 'micro': i print(“C: Score”) ”,f1_score_train, “CV-score:”, f1_score_cv) print(“- “*50)

Output:

We kunnen zien dat de hoogste validatiescore wordt behaald bij C = 10. Maar de trainingsscore is hier ook erg hoog, wat een beetje verwacht was.

Laten we de hyperparameter nog verder afstemmen -

van sklearn.multiclass import OneVsRestClassifier van sklearn.linear_model import LogisticRegressionC_range = [10, 20, 40, 70, 100] voor i in C_range: clf = OneVsRestClassifier(LogisticRegression(C = i, solver = 'sag')) clf.fit( X_train_data_tfidf, y_train) y_pred_train = clf.predict(X_train_data_tfidf) y_pred_cv = clf.predict(X_cv_data_tfidf) f1_score_train = f1_score(y_train, y_pred_score), gemiddelde__pred_score), gemiddelde__pred_score) “C:”, i, “Treinscore:”,f1_score_train, “CV-score:”, f1_score_cv) print(“- “*1)

Output:

Het model met C = 20 geeft de beste score op de validatieset. Dus als we verder gaan, nemen we C = 20.

Als je merkt dat we hier de standaard L2-straf voor regularisatie hebben gebruikt, omdat het model met L2 me het beste resultaat gaf van L1, L2 en elastische-netmenging.

De juiste drempels bepalen voor OneVsRest Classifier

De standaarddrempel in binaire classificatiealgoritmen is 0.5. Maar dit is misschien niet de beste drempel gezien de gegevens en de prestatiestatistieken die we willen maximaliseren. Zoals we weten, wordt de F1-score gegeven door -

Een goede drempel (voor elk afzonderlijk label) zou degene zijn die de F1-score maximaliseert.

def get_best_thresholds(true, pred): thresholds = [i/100 for i in range(100)] best_thresholds = [] for idx in range(25): f1_scores = [f1_score(true[:, idx], (pred[: , idx] > thresh) * 1) voor thresh in drempels] best_thresh = drempels[np.argmax(f1_scores)] best_thresholds.append(best_thresh) retourneer best_thresholds

In een notendop, wat de bovenstaande functie doet, is voor elk van de 25 klassenlabels, het berekent de F1-scores die overeenkomen met elk van de honderd drempels en selecteert vervolgens die drempel die de maximale F1-score voor het gegeven klasselabel retourneert.

Als de individuele F1-score hoog is, zal de microgemiddelde F1 ook hoog zijn. Laten we de drempels halen -

CLF = OneVsRestClassifier (logistische regressie (C = 20, oplosser = 'sag')) clf.fit (X_train_data_tfidf, y_train) y_pred_train_proba = clf.predict_proba (X_train_data_tfidf) y_pred_cv_proba = clf.predict_proba (X_cv_data_tfidf) best_thresholds = get_best_thresholds (y_cv.values, y_pred_cv_proba ) print(beste_drempels)

Output:

[0.45, 0.28, 0.19, 0.46, 0.24, 0.24, 0.24, 0.28, 0.22, 0.2, 0.22, 0.24, 0.24, 0.41, 0.32, 0.15, 0.21, 0.33, 0.33, 0.29, 0.16, 0.66, 0.33, 0.36, 0.4 ]

Zoals u kunt zien, hebben we voor elk klasselabel een afzonderlijke drempelwaarde verkregen. We gaan dezelfde waarden gebruiken in ons uiteindelijke OneVsRest Classifier-model. Voorspellingen doen met behulp van de bovenstaande drempels —

y_pred_cv = np.empty_like(y_pred_cv_proba)for i, thresh in enumerate(best_thresholds): y_pred_cv[:, i] = (y_pred_cv_proba[:, i] > thresh) * 1 print(f1_score(y_precv,score(y_precv_score(y_precv_score(y_precv) ))

Output:

0.7765116517811312

We zijn er dus in geslaagd om een ​​significant betere score te behalen met behulp van de variabele drempels.

Tot nu toe hebben we hyperparameterafstemming uitgevoerd op de validatieset en zijn we erin geslaagd om de optimale hyperparameter (C = 20) te verkrijgen. Ook hebben we de drempels aangepast en de juiste set drempels verkregen waarvoor de F1-score maximaal is.

Een voorspelling doen op de testgegevens met behulp van OneVsRest Classifier

Met behulp van de bovenstaande parameters gaan we verder met het bouwen van een volwaardig model voor de volledige trainingsgegevens en het maken van een voorspelling op de testgegevens.

# trein- en testgegevens X_tr = train_data[['Informatica', 'Wiskunde', 'Natuurkunde', 'Statistieken', 'preprocessed_abstract']] y_tr = train_data[target_cols] X_te = test_data[['Informatica', 'Wiskunde ', 'Natuurkunde', 'Statistieken', 'preprocessed_abstract']] # tekstgegevenscodering vectorizer.fit(combined_vocab) X_tr_tfidf = vectorizer.transform(X_tr['preprocessed_abstract']) X_te_tfidf = vectorizer.transform(X_te['spretractprocess'] ) # stacking X_tr_data_tfidf = hstack((X_tr[topic_cols], X_tr_tfidf)) X_te_data_tfidf = hstack((X_te[topic_cols], X_te_tfidf)) # modelleren en voorspelling doen met de beste drempelwaarden clf =(Cs 20Regression =) fit (X_tr_data_tfidf, y_tr) y_pred_tr_proba = clf.predict_proba (X_tr_data_tfidf) y_pred_te_proba = clf.predict_proba (X_te_data_tfidf) y_pred_te = np.empty_like (y_pred_te_proba) voor i, thresh in sommen (best_thresholds): y_pred_te [:, i] = (y_pred_te_proba [ :, i] > dorsen) * 1

Zodra we onze testvoorspellingen hebben ontvangen, voegen we ze toe aan de respectievelijke id's (zoals in het voorbeeldinzendingsbestand) en doen we een indiening in het aangewezen formaat.

ss = pd.read_csv('SampleSubmission.csv') ss[target_cols] = y_pred_te ss.to_csv('LR_tfidf10k_L2_C20.csv', index = False)

Het beste van deelname aan de hackathons is dat je kunt experimenteren met verschillende technieken, dus als je in de toekomst een soortgelijk probleem tegenkomt, heb je een goed begrip van wat werkt en wat niet. En ook leer je veel van andere deelnemers door actief deel te nemen aan de discussies.

U vindt de volledige code hier. op mijn GitHub-profiel.

Over de auteur

Pratik Nabriya is een ervaren dataprofessional die momenteel in dienst is bij een Analytics & AI-bedrijf in Noida. Hij is bedreven in Machine learning, Deep learning, NLP, Time-Series Analyse, SQL, Datamanipulatie & Visualisatie en is bekend met het werken in een Cloud omgeving. In zijn vrije tijd doet hij graag mee aan Hackathons en schrijft hij blogs & artikelen op het kruispunt van Data & Finance.

PlatoAi. Web3 opnieuw uitgevonden. Gegevensintelligentie versterkt.
Klik hier om toegang te krijgen.

Bron: https://www.analyticsvidhya.com/blog/2021/09/onevsrest-classifier-for-predicting-multiple-tags-of-research-articles/

spot_img

Laatste intelligentie

spot_img