Heutzutage wird das Gespräch mit Gesprächspartnern zur täglichen Routine, und es ist für Dialogsysteme von entscheidender Bedeutung, Antworten so menschlich wie möglich zu generieren. Als einer der Hauptaspekte sollte das Hauptaugenmerk darauf gelegt werden, den Benutzern emotional bewusste Antworten zu geben. In diesem Artikel beschreiben wir
die wiederkehrende neuronale Netzwerkarchitektur zur Emotionserkennung in
Textkonversationen , die an
SemEval-2019 Task 3 „EmoContext“ teilgenommen hat,
dh einem jährlichen Workshop zur semantischen Bewertung. Das Aufgabenziel besteht darin, Emotionen (d. H. Glücklich, traurig, wütend und andere) in einem 3-Runden-Konversationsdatensatz zu klassifizieren.
Der Rest des Artikels ist wie folgt organisiert. Abschnitt 1 gibt einen kurzen Überblick über die EmoContext-Aufgabe und die bereitgestellten Daten. Die Abschnitte 2 und 3 konzentrieren sich folglich auf die Textvorverarbeitung und die Worteinbettung. In Abschnitt 4 haben wir die Architektur des in unserer Einreichung verwendeten LSTM-Modells beschrieben. Abschließend werden die endgültige Leistung unseres Systems und der Quellcode vorgestellt. Das Modell wird in Python mithilfe der Keras-Bibliothek implementiert.
1. Trainingsdaten
Die SemEval-2019-Aufgabe 3 „EmoContext“ konzentriert sich auf die kontextbezogene Emotionserkennung in Textkonversationen. In EmoContext müssen wir bei einer textuellen Benutzeräußerung zusammen mit zwei Kontextumdrehungen in einer Konversation klassifizieren, ob die Emotion der nächsten Benutzeräußerung „glücklich“, „traurig“, „wütend“ oder „andere“ ist (Tabelle 1). Es gibt nur zwei Konversationsteilnehmer: eine anonyme Person (Tuen-1 und Turn-3) und den AI-basierten Chatbot
Ruuh (Turn-2). Für eine detaillierte Beschreibung siehe (
Chatterjee et al., 2019 ).
Tabelle 1. Beispiele für den EmoContext-Datensatz ( Chatterjee et al., 2019 )Während des Wettbewerbs hatten wir Zugang zu 30160 von Menschen gekennzeichneten Texten, die von Aufgabenorganisatoren bereitgestellt wurden, wobei jeweils etwa 5000 Proben aus der Klasse „wütend“, „traurig“, „glücklich“ und 15000 aus der Klasse „andere“ stammten (Tabelle 2). Entwicklungs- und Testsets, die im Gegensatz zu einem Zugset auch von den Organisatoren bereitgestellt wurden, weisen eine reale Verteilung auf, die für jede emotionale Klasse etwa 4% und für die Klasse „andere“ den Rest beträgt. Daten von Microsoft zur Verfügung gestellt und können in
der offiziellen LinkedIn-Gruppe gefunden werden .
Tabelle 2. Verteilung der Emotion Class Label in Datensätzen ( Chatterjee et al., 2019 ).Zusätzlich zu diesen Daten haben wir 900.000 englische Tweets gesammelt, um einen entfernten Datensatz von 300.000 Tweets für jede Emotion zu erstellen. Um den entfernten Datensatz zu bilden, basieren wir auf der Strategie von Go et al. (2009), in dem wir Tweets einfach mit dem Vorhandensein emotionsbezogener Wörter wie "#angry", "#annoyed", "#happy", "#sad", "#surprised" usw. verknüpfen. Die Liste der Abfragebegriffe basierte auf den Abfragebegriffen von SemEval-2018 AIT DISC (
Duppada et al., 2018 ).
Die wichtigste Leistungsmetrik von EmoContext ist ein mikro-durchschnittlicher F1-Wert für drei Emotionsklassen, dh "traurig", "glücklich" und "wütend".
def preprocessData(dataFilePath, mode): conversations = [] labels = [] with io.open(dataFilePath, encoding="utf8") as finput: finput.readline() for line in finput: line = line.strip().split('\t') for i in range(1, 4): line[i] = tokenize(line[i]) if mode == "train": labels.append(emotion2label[line[4]]) conv = line[1:4] conversations.append(conv) if mode == "train": return np.array(conversations), np.array(labels) else: return np.array(conversations) texts_train, labels_train = preprocessData('./starterkitdata/train.txt', mode="train") texts_dev, labels_dev = preprocessData('./starterkitdata/dev.txt', mode="train") texts_test, labels_test = preprocessData('./starterkitdata/test.txt', mode="train")
2. Textvorverarbeitung
Vor jeder Schulungsphase wurden die Texte mit dem Textwerkzeug Ekphrasis vorverarbeitet (Baziotis et al., 2017). Dieses Tool hilft bei der Rechtschreibkorrektur, Wortnormalisierung und Segmentierung und ermöglicht die Angabe, welche Token weggelassen, normalisiert oder mit speziellen Tags versehen werden sollen. Wir haben die folgenden Techniken für die Vorverarbeitungsphase verwendet.
- URLs, E-Mails, Datum und Uhrzeit, Benutzernamen, Prozentsatz, Währungen und Zahlen wurden durch die entsprechenden Tags ersetzt.
- Wiederholte, zensierte, verlängerte und großgeschriebene Begriffe wurden mit den entsprechenden Tags versehen.
- Längliche Wörter wurden automatisch basierend auf dem integrierten Wortstatistikkorpus korrigiert.
- Das Entpacken von Hashtags und Kontraktionen (dh Wortsegmentierung) wurde basierend auf dem integrierten Wortstatistikkorpus durchgeführt.
- Ein manuell erstelltes Wörterbuch zum Ersetzen von aus dem Text extrahierten Begriffen wurde verwendet, um eine Vielzahl von Emotionen zu reduzieren.
Darüber hinaus bietet Emphasis den Tokenizer, mit dem die meisten Emojis, Emoticons und komplizierten Ausdrücke wie zensierte, hervorgehobene und verlängerte Wörter sowie Datums-, Uhrzeit-, Währungs- und Akronyme identifiziert werden können.
Tabelle 3. Beispiele für die Textvorverarbeitung. from ekphrasis.classes.preprocessor import TextPreProcessor from ekphrasis.classes.tokenizer import SocialTokenizer from ekphrasis.dicts.emoticons import emoticons import numpy as np import re import io label2emotion = {0: "others", 1: "happy", 2: "sad", 3: "angry"} emotion2label = {"others": 0, "happy": 1, "sad": 2, "angry": 3} emoticons_additional = { '(^・^)': '<happy>', ':‑c': '<sad>', '=‑d': '<happy>', ":'‑)": '<happy>', ':‑d': '<laugh>', ':‑(': '<sad>', ';‑)': '<happy>', ':‑)': '<happy>', ':\\/': '<sad>', 'd=<': '<annoyed>', ':‑/': '<annoyed>', ';‑]': '<happy>', '(^ ^)': '<happy>', 'angru': 'angry', "d‑':": '<annoyed>', ":'‑(": '<sad>', ":‑[": '<annoyed>', '( ? )': '<happy>', 'x‑d': '<laugh>', } text_processor = TextPreProcessor(
3. Worteinbettungen
Worteinbettungen sind zu einem wesentlichen Bestandteil jedes Deep-Learning-Ansatzes für NLP-Systeme geworden. Um die am besten geeigneten Vektoren für die Aufgabe der Emotionserkennung zu bestimmen, probieren wir die Modelle Word2Vec (
Mikolov et al., 2013 ), GloVe (
Pennington et al., 2014 ) und FastText (
Joulin et al., 2017 ) sowie vorab trainierte DataStories aus
Wortvektoren (
Baziotis et al., 2017 ). Das Schlüsselkonzept von Word2Vec besteht darin, Wörter, die im Trainingskorpus gemeinsame Kontexte haben, in unmittelbarer Nähe im Vektorraum zu lokalisieren. Sowohl das Word2Vec- als auch das Glove-Modell lernen geometrische Codierungen von Wörtern aus ihren Co-Auftrittsinformationen, aber im Wesentlichen ist das erstere ein Vorhersagemodell und das letztere ein zählbasiertes Modell. Mit anderen Worten, während Word2Vec versucht, ein Zielwort (CBOW-Architektur) oder einen Kontext (Skip-Gramm-Architektur) vorherzusagen, d. H. Um die Verlustfunktion zu minimieren, berechnet GloVe Wortvektoren, die eine Dimensionsreduktion auf der Matrix der Koexistenzzählungen durchführen. FastText ist Word2Vec sehr ähnlich, mit der Ausnahme, dass es Zeichen-n-Gramm verwendet, um Wortvektoren zu lernen, sodass das Problem des Wortschatzes gelöst werden kann.
Für alle oben genannten Techniken haben wir die von den Autoren bereitgestellten Standard-Trainingswagen verwendet. Wir trainieren ein einfaches LSTM-Modell (dim = 64) basierend auf jeder dieser Einbettungen und vergleichen die Wirksamkeit mithilfe der Kreuzvalidierung. Dem Ergebnis zufolge zeigten vorab trainierte DataStories-Einbettungen die beste durchschnittliche F1-Punktzahl.
Um ausgewählte Worteinbettungen mit der emotionalen Polarität der Wörter anzureichern, ziehen wir in Betracht, eine entfernte Phrase vor dem Training durch eine Feinabstimmung der Einbettungen auf dem automatisch beschrifteten entfernten Datensatz durchzuführen. Die Bedeutung des Pre-Trainings wurde in (
Deriu et al., 201 7) gezeigt. Wir verwenden den entfernten Datensatz, um das einfache LSTM-Netzwerk zu trainieren, um wütende, traurige und glückliche Tweets zu klassifizieren. Die Einbettungsschicht wurde für die erste Trainingsepoche eingefroren, um signifikante Änderungen der Einbettungsgewichte zu vermeiden, und dann für die nächsten 5 Epochen nicht eingefroren. Nach der Trainingsphase wurden die fein abgestimmten Einbettungen für die weiteren Trainingsphasen gespeichert und
öffentlich zugänglich gemacht .
def getEmbeddings(file): embeddingsIndex = {} dim = 0 with io.open(file, encoding="utf8") as f: for line in f: values = line.split() word = values[0] embeddingVector = np.asarray(values[1:], dtype='float32') embeddingsIndex[word] = embeddingVector dim = len(embeddingVector) return embeddingsIndex, dim def getEmbeddingMatrix(wordIndex, embeddings, dim): embeddingMatrix = np.zeros((len(wordIndex) + 1, dim)) for word, i in wordIndex.items(): embeddingMatrix[i] = embeddings.get(word) return embeddingMatrix from keras.preprocessing.text import Tokenizer embeddings, dim = getEmbeddings('emosense.300d.txt') tokenizer = Tokenizer(filters='') tokenizer.fit_on_texts([' '.join(list(embeddings.keys()))]) wordIndex = tokenizer.word_index print("Found %s unique tokens." % len(wordIndex)) embeddings_matrix = getEmbeddingMatrix(wordIndex, embeddings, dim)
4. Neuronale Netzwerkarchitektur
Ein wiederkehrendes neuronales Netzwerk (RNN) ist eine Familie künstlicher neuronaler Netzwerke, die auf die Verarbeitung sequentieller Daten spezialisiert ist. Im Gegensatz zu herkömmlichen neuronalen Netzen sind RRNs so konzipiert, dass sie mit sequentiellen Daten umgehen, indem sie ihre internen Gewichte gemeinsam nutzen, um die Sequenz zu verarbeiten. Zu diesem Zweck enthält der Berechnungsgraph von RRNs Zyklen, die den Einfluss der vorherigen Informationen auf die vorliegende darstellen. Als Erweiterung von RNNs wurden 1997 Long Short-Term Memory Networks (LSTMs) eingeführt (
Hochreiter und Schmidhuber, 1997 ). In LSTMs werden wiederkehrende Zellen auf eine bestimmte Weise verbunden, um zu vermeiden, dass Gradientenprobleme verschwinden und explodieren. Herkömmliche LSTMs bewahren nur Informationen aus der Vergangenheit, da sie die Sequenz nur in eine Richtung verarbeiten. Bidirektionale LSTMs kombinieren die Ausgabe von zwei verborgenen LSTM-Schichten, die sich in entgegengesetzte Richtungen bewegen, wobei sich eine durch die Zeit vorwärts und eine andere durch die Zeit rückwärts bewegt, wodurch Informationen aus vergangenen und zukünftigen Zuständen gleichzeitig
erfasst werden können (
Schuster und Paliwal, 1997 ).
Abbildung 1: Die Architektur einer kleineren Version der vorgeschlagenen Architektur. Die LSTM-Einheit für die erste und die dritte Runde hat gemeinsame Gewichte.Eine allgemeine Übersicht über unseren Ansatz ist in Abbildung 1 dargestellt. Die vorgeschlagene Architektur des neuronalen Netzwerks besteht aus der Einbettungseinheit und zwei bidirektionalen LSTM-Einheiten (dim = 64). Die erstere LSTM-Einheit soll die Äußerung des ersten Benutzers analysieren (d. H. Die erste Runde und die dritte Runde des Gesprächs), und die letztere soll die Äußerung des zweiten Benutzers analysieren (dh die zweite Runde). Diese beiden Einheiten lernen nicht nur die Darstellung von Semantik- und Stimmungsmerkmalen, sondern auch, wie benutzerspezifische Konversationsmerkmale erfasst werden, wodurch Emotionen genauer klassifiziert werden können. Im ersten Schritt wird jede Benutzeräußerung unter Verwendung vorab trainierter Worteinbettungen in eine entsprechende bidirektionale LSTM-Einheit eingespeist. Als nächstes werden diese drei Merkmalskarten in einem abgeflachten Merkmalsvektor verkettet und dann an eine vollständig verbundene verborgene Schicht (dim = 30) weitergeleitet, die die Wechselwirkungen zwischen erhaltenen Vektoren analysiert. Schließlich durchlaufen diese Funktionen die Ausgabeebene mit der Softmax-Aktivierungsfunktion, um eine endgültige Klassenbezeichnung vorherzusagen. Um die Überanpassung zu verringern, wurden Regularisierungsschichten mit Gaußschem Rauschen nach der Einbettungsschicht hinzugefügt, Dropout-Schichten (
Srivastava et al., 2014 ) wurden an jeder LSTM-Einheit (p = 0,2) und vor der verborgenen vollständig verbundenen Schicht (p = 0,1) hinzugefügt.
from keras.layers import Input, Dense, Embedding, Concatenate, Activation, \ Dropout, LSTM, Bidirectional, GlobalMaxPooling1D, GaussianNoise from keras.models import Model def buildModel(embeddings_matrix, sequence_length, lstm_dim, hidden_layer_dim, num_classes, noise=0.1, dropout_lstm=0.2, dropout=0.2): turn1_input = Input(shape=(sequence_length,), dtype='int32') turn2_input = Input(shape=(sequence_length,), dtype='int32') turn3_input = Input(shape=(sequence_length,), dtype='int32') embedding_dim = embeddings_matrix.shape[1] embeddingLayer = Embedding(embeddings_matrix.shape[0], embedding_dim, weights=[embeddings_matrix], input_length=sequence_length, trainable=False) turn1_branch = embeddingLayer(turn1_input) turn2_branch = embeddingLayer(turn2_input) turn3_branch = embeddingLayer(turn3_input) turn1_branch = GaussianNoise(noise, input_shape=(None, sequence_length, embedding_dim))(turn1_branch) turn2_branch = GaussianNoise(noise, input_shape=(None, sequence_length, embedding_dim))(turn2_branch) turn3_branch = GaussianNoise(noise, input_shape=(None, sequence_length, embedding_dim))(turn3_branch) lstm1 = Bidirectional(LSTM(lstm_dim, dropout=dropout_lstm)) lstm2 = Bidirectional(LSTM(lstm_dim, dropout=dropout_lstm)) turn1_branch = lstm1(turn1_branch) turn2_branch = lstm2(turn2_branch) turn3_branch = lstm1(turn3_branch) x = Concatenate(axis=-1)([turn1_branch, turn2_branch, turn3_branch]) x = Dropout(dropout)(x) x = Dense(hidden_layer_dim, activation='relu')(x) output = Dense(num_classes, activation='softmax')(x) model = Model(inputs=[turn1_input, turn2_input, turn3_input], outputs=output) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc']) return model model = buildModel(embeddings_matrix, MAX_SEQUENCE_LENGTH, lstm_dim=64, hidden_layer_dim=30, num_classes=4)
5. Ergebnisse
Bei der Suche nach einer optimalen Architektur haben wir nicht nur mit der Anzahl der Zellen in Schichten, Aktivierungsfunktionen und Regularisierungsparametern experimentiert, sondern auch mit der Architektur des neuronalen Netzwerks. Die detaillierten Informationen zu diesem Satz finden Sie im
Originalpapier .
Das im vorherigen Abschnitt beschriebene Modell zeigte die besten Ergebnisse für den Entwicklungsdatensatz und wurde daher in der abschließenden Bewertungsphase des Wettbewerbs verwendet. Im endgültigen Testdatensatz wurde eine durchschnittliche F1-Punktzahl von 72,59% für emotionale Klassen erreicht, während die maximale Punktzahl unter allen Teilnehmern 79,59% betrug. Dies liegt jedoch weit über der offiziellen Basislinie, die von den Organisatoren der Aufgaben veröffentlicht wurde und bei 58,68% lag.
Der Quellcode des Modells und die Worteinbettungen sind bei GitHub verfügbar.
Die Vollversion des Artikels und
das Aufgabenbeschreibungspapier finden Sie unter ACL Anthology.
Der Trainingsdatensatz befindet sich in der offiziellen Wettbewerbsgruppe bei LinkedIn.
Zitat:
@inproceedings{smetanin-2019-emosense, title = "{E}mo{S}ense at {S}em{E}val-2019 Task 3: Bidirectional {LSTM} Network for Contextual Emotion Detection in Textual Conversations", author = "Smetanin, Sergey", booktitle = "Proceedings of the 13th International Workshop on Semantic Evaluation", year = "2019", address = "Minneapolis, Minnesota, USA", publisher = "Association for Computational Linguistics", url = "https://www.aclweb.org/anthology/S19-2034", pages = "210--214", }