[PYTHON] Mit LSTM + Embedding habe ich die Bedeutung der Vorverarbeitung in NLP erneut bekräftigt und gleichzeitig ein Modell zur Unterscheidung der Emotionen von Tweets erstellt.

Motivation

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.

Zusammenfassung der Daten

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. image.png [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.

Modellierungsstrategie

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. image.png

Vanila LSTM ist die grundlegendste (klassische) LSTM-Struktur und ein Modell mit den folgenden Merkmalen.

  1. Eine versteckte Schicht (LSTM Unit Layer)
  2. Eine dichte Schicht für die Ausgabe

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.

Importieren Sie zunächst die erforderlichen Bibliotheken und Daten
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.

SCHRITT 1: Vorverarbeitung zum Bereinigen von Tweets Version 1

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 **]

  1. Machen Sie alle Wörter niedriger
  2. Löschen Sie die URL
  3. Piktogramme codieren
  4. Ersetzen Sie unnötige Symbole für die Modellierung durch Leerzeichen "" - @usernames
  1. Tokenisieren Sie den Tweet und entfernen Sie die Stoppwörter und Satzzeichen
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))

Ergebnis nach der Reinigung: URLs und @Username wurden ordnungsgemäß entfernt.

image.png

Schritt 2: Codieren Sie die Zeichen

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.

  1. ** Hash (One Hot Encoding) jedes im Tweet enthaltenen Zeichens ** --Hashing bedeutet "jedem Zeichen eine Indexnummer zuweisen".
  1. ** Padding / Truncating auf gehashte Tweets anwenden ** ――Padding / Truncating bedeutet einfach "Vereinheitlichen der Anzahl der Elemente in der Liste".
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')
Ausführungsergebnis: Tweets werden Zeile für Zeile ordnungsgemäß gehasht.

image.png

SCHRITT 3: Modellierung

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))

Bestätigung der Richtigkeit der Verifizierungsdaten

Es war 78%. Diese Zahl ist genauso genau wie bei anderen Kaggle DL-Implementierungen.

image.png

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.

Verbesserung 1

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. ..

image.png

Verbesserungspunkt 2

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.

Ergebnisse nach Verbesserung

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. image.png

Planen Sie von nun an

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. image.png

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. image.png

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

Mit LSTM + Embedding habe ich die Bedeutung der Vorverarbeitung in NLP erneut bekräftigt und gleichzeitig ein Modell zur Unterscheidung der Emotionen von Tweets erstellt.
Ich habe einen Fehler beim Abrufen der Hierarchie mit MultiIndex von Pandas gemacht
Ich habe versucht, ein Modell mit dem Beispiel von Amazon SageMaker Autopilot zu erstellen
Ich habe versucht, mit dem Seq2Seq-Modell von TensorFlow so etwas wie einen Chatbot zu erstellen
Das Konzept der Referenz in Python brach für einen Moment zusammen, also experimentierte ich ein bisschen.
Die Geschichte der Schaffung eines "Geist- und Zeit-Chatrooms" exklusiv für Ingenieure im Unternehmen
Ich schrieb einen Test in "Ich habe versucht, die Wahrscheinlichkeit eines Bingospiels mit Python zu simulieren".
Messen Sie die Wichtigkeit von Features mit einem zufälligen Gesamtstrukturwerkzeug
Hinweise zum Einbetten der Skriptsprache in Bash-Skripte
Ich habe eine Funktion erstellt, um das Modell von DCGAN zu überprüfen
Analysieren Sie das Themenmodell, mit GensimPy3 Romanautor zu werden
Ich habe versucht, die Anzahl der im Inland infizierten Menschen der neuen Korona mit einem mathematischen Modell vorherzusagen
Die Geschichte des Erstellens eines Bots, der aktive Mitglieder in einem bestimmten Slack-Kanal mit Python anzeigt
Ein Anfänger, der seit 2 Monaten programmiert, versuchte, das reale BIP Japans in Zeitreihen mit dem SARIMA-Modell zu analysieren.
Verwenden Sie den von word2vec gelernten Vektor in der Einbettungsebene von LSTM
[Einführung in StyleGAN] Ich habe mit "The Life of a Man" ♬ gespielt
Ich suchte mit Deep Learning nach einer ähnlichen Karte von Hearthstone
Ich habe viele Dateien für die RDP-Verbindung mit Python erstellt
Die Geschichte, einen Standardtreiber für db mit Python zu erstellen.
Ich habe versucht, Jojo mit LSTM ein seltsames Zitat zu machen
Die Geschichte, wie ein Geschäft BOT (AI LINE BOT) nach Go To EAT in der Präfektur Chiba durchsucht (1)