إحدى المهام الرئيسية لأنظمة الحوار ليست فقط توفير المعلومات التي يحتاجها المستخدم ، ولكن أيضًا لتوليد أكبر قدر ممكن من الإجابات البشرية. والاعتراف بمشاعر المحاور لم يعد مجرد ميزة رائعة ، بل هو ضرورة حيوية. في هذه المقالة ، سننظر في
بنية شبكة عصبية متكررة لتحديد المشاعر في المحادثات النصية ، والتي شاركت في
SemEval-2019 Task 3 "EmoContext" ، المسابقة السنوية في اللغويات الحاسوبية. كانت المهمة هي تصنيف المشاعر ("السعادة" و "الحزينة" و "الغاضبة" و "الآخرين") في محادثة من ثلاث ملاحظات شارك فيها روبوت دردشة وشخص.
في الجزء الأول من المقالة ، سننظر في المهمة المحددة في EmoContext والبيانات المقدمة من قبل المنظمين. في الجزأين الثاني والثالث ، نقوم بتحليل المعالجة الأولية للنص وطرق التمثيل المتجه للكلمات. في الجزء الرابع ، وصفنا بنية LSTM التي استخدمناها في المسابقة. الرمز مكتوب في بايثون باستخدام مكتبة Keras.
1. بيانات التدريب
تم تخصيص المسار "EmoContext" في SemEval-2019 لتعريف المشاعر في المحادثات النصية ، مع مراعاة سياق المراسلات. السياق في هذه الحالة هو عدة ملاحظات متتالية للمشاركين في الحوار. يوجد
مشاركان في المحادثة: مستخدم مجهول (يمتلك النسخة المتماثلة الأولى والثالثة)
وروبوت دردشة
Ruuh (يملك النسخة المتماثلة الثانية). استنادًا إلى ثلاث نسخ متماثلة ، من الضروري تحديد المشاعر التي واجهها المستخدم عند كتابة إجابة إلى chatbot (الجدول 1). في المجموع ، احتوى توصيف مجموعة البيانات على أربعة مشاعر: "سعيد" أو "حزين" أو "غاضب" أو "آخرون" (الجدول 1). ويرد وصف مفصل هنا: (
شاترجي وآخرون ، 2019 ).
جدول 1. أمثلة من مجموعة بيانات EmoContext ( Chatterjee et al.، 2019 )خلال المسابقة ، قدم المنظمون عدة مجموعات من البيانات. تتألف مجموعة البيانات التدريبية (القطار) من 30،160 نصًا تم تعليمها يدويًا. في هذه النصوص كان هناك ما يقرب من 5000 كائن ينتمي إلى الطبقات "السعيدة" و "الحزينة" و "الغاضبة" ، بالإضافة إلى 15000 نص من الفئة "الآخرين" (الجدول 2).
قدم المنظمون أيضًا مجموعات بيانات للتطوير (Dev) والاختبار (Test) ، والتي ، على عكس مجموعة البيانات التدريبية ، كان التوزيع حسب فئة المشاعر يتوافق مع الحياة الحقيقية: حوالي 4٪ لكل فئة من الفئات "سعيدة" و "حزينة" و " غاضب "، والباقي هو فئة" الآخرين ". البيانات المقدمة من Microsoft ، يمكنك تنزيلها في
المجموعة الرسمية على LinkedIn .
الجدول 2. توزيع ملصقات فئة العاطفة في مجموعة البيانات ( Chatterjee et al.، 2019 ).بالإضافة إلى هذه البيانات ، جمعنا 900 ألف رسالة باللغة الإنجليزية من Twitter لإنشاء مجموعة بيانات بعيدة (300 ألف تغريدة لكل عاطفة). عند إنشائه ، اتبعنا إستراتيجية Go et al. (2009) ، في إطار الرسائل التي ارتبطت ببساطة بوجود الكلمات المتعلقة بالمشاعر ، مثل #angry ، #annoyed ، # Happy ، #sad ، #surprised ، وما إلى ذلك. تستند قائمة المصطلحات إلى شروط من SemEval-2018 AIT DISC (
Duppada et al.، 2018 ).
مقياس الجودة الرئيسي في مسابقة EmoContext هو مقياس F1 المتوسط لفئات المشاعر الثلاثة ، أي بالنسبة للفئات "السعيدة" و "الحزينة" و "الغاضبة".
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. النص قبل المعالجة
قبل التدريب ، قمنا بمعالجة النصوص مسبقًا باستخدام أداة Ekphrasis (Baziotis et al. ، 2017). يساعد في تصحيح الهجاء وتطبيع الكلمات والمقطع وكذلك تحديد الرموز المميزة التي يجب إسقاطها أو تطبيعها أو تعليقها باستخدام علامات خاصة. في مرحلة ما قبل المعالجة ، قمنا بما يلي:
- تم استبدال عناوين URL والبريد والتاريخ والوقت والألقاب والنسب المئوية والعملات والأرقام بالعلامات المقابلة.
- تكرار المصطلحات الكبيرة والرقابة والمطولة التي رافقتنا بها علامات مناسبة.
- تم تصحيح الكلمات المطولة تلقائيًا.
بالإضافة إلى ذلك ، يحتوي Emphasis على رمز مميز يمكنه التعرف على معظم الرموز التعبيرية والرموز التعبيرية المعقدة ، وكذلك التواريخ والأوقات والعملات والمختصرات.
جدول 3. أمثلة على تجهيز النص مسبقا. 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. ناقلات تمثيل الكلمات
أصبح تمثيل المتجهات جزءًا لا يتجزأ من معظم الأساليب لإنشاء نظم البرمجة اللغوية العصبية باستخدام التعلم العميق. لتحديد أنسب نماذج لرسم الخرائط لناقلات الأمراض ، جربنا Word2Vec (
Mikolov et al. ، 2013 ) و GloVe (
Pennington et al.، 2014 ) و FastText (
Joulin et al.، 2017 ) ، وكذلك ناقلات
DataStories المدربة مسبقًا (
Baziotis et al. . ، 2017 ). يجد Word2Vec العلاقات بين الكلمات من خلال افتراض أن الكلمات ذات الدلالة موجودة في سياقات مماثلة. يحاول Word2Vec التنبؤ بالكلمة الهدف (بنية CBOW) أو السياق (بنية Skip-Gram) ، أي تقليل دالة الخسارة ، ويقوم GloV بحساب متجهات الكلمات ، مما يقلل من أبعاد المصفوفة المجاورة. يشبه منطق FastText منطق Word2Vec ، باستثناء أنه يستخدم جرامًا رمزيًا لبناء متجهات الكلمات ، ونتيجة لذلك ، يمكنه حل مشكلة الكلمات غير المعروفة.
بالنسبة لجميع النماذج المذكورة ، نستخدم معايير التدريب الافتراضية المقدمة من قبل المؤلفين. لقد قمنا بتدريب نموذج LSTM بسيط (خافت = 64) استنادًا إلى كل من هذه الموجهات التمثيلية ومقارنة كفاءة التصنيف باستخدام التحقق المتبادل. تم عرض أفضل نتيجة في مقاييس F1 بواسطة ناقلات DataStories المدربة مسبقًا.
لإثراء تعيين المتجهات المحددة بالتلوين العاطفي للكلمات ، قررنا صقل المتجهات باستخدام
مجموعة بيانات بعيد المسمى تلقائيًا (
Deriu et al.، 2017 ). استخدمنا مجموعة بيانات Distant لتدريب شبكة LSTM بسيطة لتصنيف الرسائل "الشريرة" و "الحزينة" و "السعيدة". تم تجميد طبقة التضمين أثناء التكرار الأول من التدريب من أجل تجنب حدوث تغييرات قوية في أوزان المتجهات ، وبالنسبة للتكرارات الخمسة التالية تم إذابة الطبقة. بعد التدريب ، تم حفظ المتجهات "المتأخرة" لاستخدامها لاحقًا في الشبكة العصبية ، وكذلك تم
مشاركتها .
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. بنية الشبكة العصبية
الشبكات العصبية المتكررة (RNNs) هي مجموعة من الشبكات العصبية التي تتخصص في معالجة سلسلة من الأحداث. على عكس الشبكات العصبية التقليدية ، تم تصميم RNNs للعمل مع تسلسل باستخدام الأرصدة الداخلية. لهذا ، يحتوي الرسم البياني الحسابي RNN على دورات تعكس تأثير المعلومات السابقة من تسلسل الأحداث على الحالية. تم تقديم الشبكات العصبية LSTM (ذاكرة طويلة المدى طويلة المدى) كامتداد
لشبكة RNN في عام 1997 (
Hochreiter and Schmidhuber، 1997 ). ترتبط خلايا تكرار LSTM لتجنب مشاكل الاندفاع والبهتان. LSTMs التقليدية فقط الحفاظ على المعلومات السابقة لأنها معالجة التسلسل في اتجاه واحد. تجمع LSTMs ثنائية الاتجاه التي تعمل في كلا الاتجاهين بين ناتج طبقتين LSTM مخفيتين تنقلان المعلومات في اتجاهين متعاكسين - واحدة في مجرى الزمن والأخرى مقابل - وبالتالي تستقبل في وقت واحد البيانات من الدول السابقة والمستقبلية (
Schuster و Paliwal ، 1997 ).
الشكل 1: نسخة مخفضة من العمارة. تستخدم الوحدة LSTM نفس الأوزان للمرحلتين الأولى والثالثة.يتم عرض تمثيل مبسط للنهج الموصوف في الشكل 1. تتكون بنية الشبكة العصبية من طبقة مدمجة ووحدتي LTSM ثنائية الاتجاه (خافتة = 64). الوحدة النمطية LTSM الأولى يحلل كلمات المستخدم الأول (أي النسخة المتماثلة الأولى والثالثة من المحادثة) ، الوحدة النمطية الثانية بتحليل كلمات المستخدم الثاني (النسخة المتماثلة الثانية). في المرحلة الأولى ، يتم إدخال كلمات كل مستخدم يستخدم تمثيلات متجهية سابقة التدريب في وحدة LTSM ثنائية الاتجاه المقابلة. ثم يتم دمج خرائط الميزات الثلاثة الناتجة في متجه ميزة مسطحة ، ثم يتم نقلها إلى طبقة مخفية متصلة بالكامل (خافت = 30) ، والتي تحلل التفاعلات بين الميزات المستخرجة. أخيرًا ، تتم معالجة هذه الخصائص في طبقة الإخراج باستخدام وظيفة تنشيط softmax لتحديد تسمية الفئة النهائية. لتقليل التداخل الزائد ، بعد إضافة طبقات تمثيل المتجه ، تمت إضافة طبقات التنظيم بضوضاء غوسية ، وأضيفت طبقات التسرب إلى كل وحدة LTSM (ع = 0.2) وطبقة مخفية متصلة بالكامل (ع = 0.1) (
Srivastava et al. ، 2014) ).
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. النتائج
في البحث عن الهيكل الأمثل ، قمنا بتجربة ليس فقط مع عدد الخلايا العصبية في الطبقات ، ووظائف التنشيط ومعلمات التنظيم ، ولكن أيضًا مع بنية الشبكة العصبية نفسها. يوصف هذا بمزيد من التفصيل في
العمل الأصلي .
أظهرت البنية الموضحة في القسم السابق أفضل النتائج عند التدريب على مجموعة بيانات Train والتحقق من صحتها على مجموعة بيانات Dev ، لذلك تم استخدامها في المرحلة الأخيرة من المسابقة. في مجموعة بيانات الاختبار الأخيرة ، أظهر النموذج مقياسًا متوسطًا دقيقًا لـ F1 يبلغ 72.59٪ ، وكانت النتيجة المحققة بين جميع المشاركين 79.59٪. ومع ذلك ، كانت النتيجة أعلى بكثير من القيمة الأساسية البالغة 58.68 ٪ التي حددها المنظمون.
الكود المصدري لنموذج الكلمات وتمثيل المتجهات متاح على جيثب.
النسخة الكاملة من المقال والعمل مع وصف المهمة موجودة على موقع ACL Anthology.
يمكن تنزيل
مجموعة بيانات التدريب من مجموعة LinkedIn الرسمية.
الاقتباس:
@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", }