Es gibt viele Zusammenhänge, und ich habe beschlossen, das Problem der Textklassifizierung zu lösen, in dem klassifiziert wird: "Ist die Antwort des Schülers eine Goldmedaille, eine Silbermedaille, eine Bronzemedaille oder außerhalb des Bereichs?"
In der Praxis hatte ich die Aufgabe der Verarbeitung natürlicher Sprache nicht so sehr gelöst, also beschloss ich, das in Kaggle aufregende Problem der "Sentiment Analayis Emotionsanalyse" zu lösen und es zu üben.
Durch viele Versuche und Irrtümer habe ich festgestellt, dass die Vorverarbeitung (Textverarbeitung) für Verarbeitungsaufgaben in natürlicher Sprache wichtig ist (mehr als für andere Aufgaben), daher werde ich sie in diesem Artikel zusammenfassen.
Studenten und Praktiker aus der ganzen Welt konkurrieren um ihre Fähigkeiten, und der Datensatz "Sentiment140-Datensatz mit 1,6 Millionen Tweets" wird auf der Website des Online-Datenanalysewettbewerbs "Kaggle" geteilt. Verwenden Sie diese Daten. Machen.
Dies sind nicht die im Wettbewerb verwendeten Daten, sondern ein Datensatz, der von Kaggle-Freiwilligen geteilt wird und sagt: "Bitte verwenden Sie ihn für emotionale Analysen." Mit 1,6 Millionen Tweets ist es ein ziemlich umfangreicher Datensatz. Unten finden Sie eine Tabelle mit einer Zusammenfassung der Daten. [Klicken Sie hier für den Datensatz]: https://www.kaggle.com/kazanova/sentiment140
Wenn eine Aufgabe einen Text (Tweet) erhält, ist dieser Tweet positiv? Es ist ein Problem der binären Klassifizierung, das vorhersagt, ob es negativ ist.
Wir haben die Methode "Fixieren der Modellarchitektur und Ändern der Vorverarbeitung auf verschiedene Weise" übernommen. (Eigentlich habe ich verschiedene Dinge ausprobiert.) In diesem Artikel werde ich der Klarheit Priorität einräumen und sie kurz mit dem folgenden Ablauf teilen.
Vanila LSTM ist die grundlegendste (klassische) LSTM-Struktur und ein Modell mit den folgenden Merkmalen.
Der Code (Keras Sequential API) lautet wie folgt.
model = Sequential()
model.add(LSTM(32, activation='tanh', input_shape=(n_steps, n_features)))
model.add(Dense(2,activation='softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer='adam',metrics = ['accuracy'])
[Referenz: https://machinelearningmastery.com/how-to-develop-lstm-models-for-time-series-forecasting/]
Im Folgenden werde ich über die tatsächliche Implementierung berichten.
from collections import defaultdict, Counter
import time
import re
import string
import pandas as pd
import nltk
from nltk.corpus import stopwords
from keras.preprocessing.sequence import pad_sequences
from keras.preprocessing.text import one_hot
from sklearn.model_selection import train_test_split
from keras.models import Sequential
from keras.layers import Embedding,LSTM, Dense
from keras.callbacks import EarlyStopping
df = pd.read_csv("twitter_sentiment_kaggle.csv", encoding="latin-1",header=0,
names=['sentiment','id','date','flag','user','text'], usecols=["id", "sentiment", "text"])
print(df.head(2))
Ich denke nicht, dass es notwendig ist, hier zu erklären, also werde ich als nächstes gehen.
Was bedeutet es, einen Tweet zu bereinigen? Mit anderen Worten, es ist die Aufgabe, "Buchstaben und Symbole zu entfernen, die das Lernen stören".
Für diese Aufgabe muss das Modell "Emotionen aus Tweets lesen (verknüpfen)", damit die Informationen "@Benutzername" und "URL" im Weg sind.
In NLP-Aufgaben wird eine solche "Arbeit zum Löschen von Symbolen und Zeichen, die das Modell nicht lernen soll" im Allgemeinen als "Vorverarbeitung" bezeichnet.
Nun, die Vorbehandlung, die ich dieses Mal zuerst versucht habe, ist unten aufgeführt. Es gibt nichts Besonderes und alle sind grundlegende Vorverarbeitungen.
[** Vorbehandlung durchgeführt **]
def clean_text(text_data):
#Definition von Codierung, regulärem Ausdruck, Stoppwort, Interpunktion--->Freiwillige definieren es im Netz, also leihen wir es uns aus.
URL_PATTERN = r"((http://)[^ ]*|(https://)[^ ]*|( www\.)[^ ]*)"
EMOJI_ENCODER_DICT = {':)': 'smile', ':-)': 'smile', ':))': 'smile', ';d': 'wink', ':-E': 'vampire', ':(': 'sad', ':-(': 'sad', ':-<': 'sad', ':P': 'raspberry', ':O': 'surprised',
':-@': 'shocked', ':@': 'shocked',':-$': 'confused', ':\\': 'annoyed', ':#': 'mute', ':X': 'mute', ':^)': 'smile', ':-&': 'confused',
'$_$': 'greedy','@@': 'eyeroll', ':-!': 'confused', ':-D': 'smile', ':-0': 'yell', 'O.o': 'confused','<(-_-)>': 'robot', 'd[-_-]b': 'dj',
":'-)": 'sadsmile', ';)': 'wink', ';-)': 'wink', 'O:-)': 'angel','O*-)': 'angel','(:-D': 'gossip', '=^.^=': 'cat'}
USER_NAME_PATTERN = r'@[^\s]+'
NON_ALPHA_PATTERN = r"[^A-Za-z0-9]"
SEQUENCE_DETECT_PATTERN = r"(.)\1\1+"
SEQUENCE_REPLACE_PATTERN = r"\1\1"
ENGLISH_STOPWORDS = stopwords.words('english')
PUNCTUATIONS = string.punctuation.split()
###############################Tweets vorverarbeiten########################################
clean_tweets = []
for each_tweet in text_data:
#Machen Sie alle Buchstaben niedriger
each_tweet = each_tweet.lower()
#URL löschen
each_tweet = re.sub(URL_PATTERN, "", each_tweet).strip()
#Lassen Sie 3 oder mehr aufeinanderfolgende Zeichen auf 2 weg
each_tweet = re.sub(SEQUENCE_DETECT_PATTERN, SEQUENCE_REPLACE_PATTERN, each_tweet)
#Piktogramme codieren
for key in EMOJI_ENCODER_DICT.keys():
each_tweet = each_tweet.replace(key, " EMOJI " + EMOJI_ENCODER_DICT[key])
###Löschen Sie verschiedene Symbole, die für die Modellierung nicht erforderlich sind###
# ”@Benutzernamen löschen “
each_tweet = re.sub(USER_NAME_PATTERN, "", each_tweet)
#Löschen Sie alle außer Zahlen und Buchstaben
each_tweet = re.sub(NON_ALPHA_PATTERN, " ", each_tweet)
###Tokenisieren Sie Tweets(Das Element ist eine Liste jedes Wortes,)Und entfernen Sie die Stoppwörter und Satzzeichen###
tokenizer = nltk.TweetTokenizer(preserve_case=False, strip_handles=True, reduce_len=True)
tweet_tokens = tokenizer.tokenize(each_tweet)
#Stoppwörter und Interpunktion entfernt
clean_tweet_sentence = ' '
for word in tweet_tokens: #Schau dir jedes Wort an
if (word not in ENGLISH_STOPWORDS and word not in PUNCTUATIONS):
clean_tweet_sentence += (word+' ')
clean_tweets.append(clean_tweet_sentence)
return clean_tweets
#########################################################################################
#Tweets aufräumen
t = time.time()
clean_tweets_list = clean_text(df["text"])
print(f'Die Tweets sind wunderschön geworden.')
print(f'Ausführungszeit des Codes: {round(time.time()-t)} seconds')
#Tweets aufgeräumt'clean_tweet'Hinzufügen zu einer neuen Spalte
df["clean_text"] = clean_tweets_list
#Ergebnisse anzeigen
print(df[["text", "clean_text"]].head(2))
Nach dem Bereinigen des Tweets in SCHRITT 1 oben verarbeiten wir den Tweet, um das Modell zu trainieren. Mit anderen Worten, jedes im Tweet enthaltene Zeichen (englisches Wort) wird durch eine Zahl dargestellt. Das Modell kann nur Zahlen erkennen, es ist also eine notwendige Aufgabe, nicht wahr?
Die spezifischen Arbeiten sind nachstehend zusammengefasst.
def encode_with_oneHot(text, total_vocab_freq, max_tweet_length):
#Eine Hot-Codierung und Auffüllung/Abschneiden durchführen
encoded_tweets_oneHot = []
for each_tweet in text:
each_encoded_tweet = one_hot(each_tweet, total_vocab_freq)
encoded_tweets_oneHot.append(each_encoded_tweet)
each_encoded_tweets_oneHot_pad = pad_sequences(encoded_tweets_oneHot, maxlen=max_tweet_length,
padding="post", truncating="post")
return each_encoded_tweets_oneHot_pad
###################################################################################################
###Codieren Sie saubere Tweets###
#Wissen Sie, wie oft ein Wort erscheint
vocab_dict = defaultdict(int)
for each_t in df["clean_text_after_others"]:
for w in each_t.split():
vocab_dict[w] += 1
total_vocab_freq = len(vocab_dict.keys())#Zählen der Gesamtzahl der Wörter
#Verstehe die Länge der Sätze
sentence_length_dict = defaultdict(int)
for i, each_t in enumerate(df["clean_text_after_others"]):
sentence_length_dict[i] = len(each_t.split())
max_tweet_length = max(sentence_length_dict.values())#Zähle den längsten Satz
#Lauf
t = time.time()
one_hot_texts = encode_with_oneHot(df["clean_text"], total_vocab_freq, max_tweet_length)
print(f'Eine heiße Codierung von Tweets ist vorbei')
print(f'Ausführungszeit des Codes: {round(time.time()-t)} seconds')
In STEP2 wurde der Tweet gehasht (quantifiziert = One Hot-Codierung), sodass er für die Modellierung bereit ist. Unten finden Sie die Code-Freigabe.
embedding_length = 32
model = Sequential()
model.add(Embedding(input_dim=total_vocab_freq+1, output_dim=embedding_length, input_length=max_tweet_length, mask_zero=True))
model.add(LSTM(units=32))
model.add(Dense(2,activation='softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer='adam',metrics = ['accuracy'])
print(model.summary())
Wie bereits in "Modellierungsstrategie" erwähnt, ist dies die klassische LSTM-Struktur "Vanilla LSTM".
Ich habe jedoch eine Einbettungsebene vor der LSTM-Ebene hinzugefügt. Der Grund ist, dass Hash-Tweets allein die semantische Bedeutung nicht lesen können.
Das Einbetten ist eine Funktion, die eine Matrix einer beliebigen Dimension mit dem Hash-Wort als Schlüssel zurückgibt. Jedes Element dieser Matrix erhält eine semantische Bedeutung.
Mit anderen Worten, was bedeutet das? Wörter können wie "König-Mann + Frau = Königin" berechnet werden. Da es wichtig ist, werde ich es wiederholen, aber da jedes Wort eine Matrixdarstellung (semantische Bedeutung) erhält, ist eine solche Operation möglich.
Das LSTM kann nun die Beziehungen zwischen Wörtern lernen.
In Bezug auf das Einbetten ist der Artikel von @ 9ryuuuuu sehr leicht zu verstehen. Bitte lesen Sie ihn. https://qiita.com/9ryuuuuu/items/e4ee171079ffa4b87424
Lassen Sie uns nun die Daten formen und mit dem Training des Modells beginnen.
#Datenformung
y = pd.get_dummies(df["sentiment"]).values
X_train, X_test, y_train, y_test = train_test_split(one_hot_texts, y, test_size = 0.2, random_state = 123, stratify=y)
print(X_train.shape,y_train.shape)
print(X_test.shape,y_test.shape)
#Fang an zu lernen
batch_size = 256
callback = [EarlyStopping(monitor='val_loss', patience=2, verbose=1)]
hist = model.fit(X_train, y_train, epochs=5, batch_size=batch_size, callbacks=callback, verbose=1, validation_split=0.1)
#Zeigen Sie die Genauigkeit für Validierungsdaten an
import numpy as np
print("Validation Accuracy:",round(np.mean(hist.history['val_accuracy']), 4))
Es war 78%. Diese Zahl ist genauso genau wie bei anderen Kaggle DL-Implementierungen.
Maschinelles Lernen, das tf-idf + n-Gramm als Eingabe verwendet, ist etwas weniger genau. Im Beobachtungsbereich sind es etwa 68 bis 78%.
Bei diesem Modell beträgt der Abweichungswert also etwa 55 bis 60. (Vermuten) [Referenz: https://www.kaggle.com/kazanova/sentiment140/notebooks]
Aktualisieren Sie von hier aus einfach die Vorverarbeitung und versuchen Sie erneut, sie zu implementieren, ohne die Architektur des Modells zu ändern.
Es gab zwei wesentliche Verbesserungen.
Die erste Verbesserung ist der Umgang mit "Stoppwörtern". Stoppwörter sind Wörter wie "nicht", "nein" und "oben", eine Gruppe von Wörtern, die in der Welt der Verarbeitung natürlicher Sprache üblicherweise gelöscht werden. Wie im folgenden Beispiel gezeigt, war die Bedeutung des Tweets aufgrund des Löschens von "Nein" sehr unterschiedlich, sodass ich zur Vorverarbeitung überging, ohne Stoppwörter zu löschen. ..
Die zweite Verbesserung betrifft "die Anzahl der Wörter im Tweet". Nach der Vorverarbeitung gibt es einen Tweet mit nur einem Wort, z. B. ist der Tweet nur "Wiedergabe", daher habe ich solche Daten gelöscht.
Die Genauigkeit hat sich verbessert. Das Ergebnis sind 82% der Validierungsdaten. Dies ist das Ergebnis der Top-DL-Implementierung (Abweichungswert 60 ~ 65?). Ich bin also glücklich.
Auf den Ebenen Bronze Master und Grand Master erreiche ich 88-92% der Ergebnisse Es gibt noch Raum für Verbesserungen für mich. Ich dachte, dass viele Großmeister es mit CNN + LSTM implementiert haben.
Für diese Aufgabe verfolge ich jedoch eine gründliche Vorverarbeitung, und ich denke, dass die Genauigkeit 90% erreichen wird.
Dies liegt daran, dass es noch viele Bereiche gibt, die durch Vorbehandlung verbessert werden müssen. Zum Beispiel die folgende Vorverarbeitung.
Aber das ist eigentlich ziemlich schwierig, nicht wahr? Ist es Englisch für jeden Tweet? Ich muss ein Urteil fällen, aber die Genauigkeit der Bibliothek, die das Urteil gefällt, ist so schlecht, dass es nutzlos ist.
Dies ist der Satz, der von der Funktion langdetect.detect ausgegeben wird, die als "nicht englisch" beurteilt wird, aber eindeutig einen Satz in englischer Sprache enthält.
Die Automatisierung ist also schwierig. Was soll ich gegen diese Vorverarbeitung tun? Steht aus. Vielleicht fällt es Kaggler auch schwer, niemand (meiner Beobachtung nach) hat diese Vorbehandlung durchgeführt.
Daher möchte ich die Überprüfung fortsetzen und den Artikel aktualisieren.
Vielen Dank für das bisherige Anschauen.
Recommended Posts