Deteksi Emosi Kontekstual dalam Percakapan Tekstual Menggunakan Jaringan Saraf Tiruan


Saat ini, berbicara dengan agen percakapan menjadi rutinitas sehari-hari, dan sangat penting bagi sistem dialog untuk menghasilkan respons semanusiawi mungkin. Sebagai salah satu aspek utama, perhatian utama harus diberikan untuk memberikan tanggapan yang sadar secara emosional kepada pengguna. Dalam artikel ini, kita akan menggambarkan arsitektur jaringan saraf berulang untuk deteksi emosi dalam percakapan tekstual , yang berpartisipasi dalam SemEval-2019 Tugas 3 "EmoContext" , yaitu, lokakarya tahunan tentang evaluasi semantik. Tujuan tugas adalah untuk mengklasifikasikan emosi (yaitu bahagia, sedih, marah, dan lain-lain) dalam kumpulan data percakapan 3 putaran.

Sisa artikel ini disusun sebagai berikut. Bagian 1 memberikan gambaran singkat tentang tugas EmoContext dan data yang disediakan. Bagian 2 dan 3 fokus pada teks pra-pemrosesan dan embeddings kata, akibatnya. Pada bagian 4, kami menggambarkan arsitektur model LSTM yang digunakan dalam pengiriman kami. Sebagai kesimpulan, kinerja akhir dari sistem kami dan kode sumber disajikan. Model ini diimplementasikan dalam Python menggunakan perpustakaan Keras.

1. Data Pelatihan


SemEval-2019 Tugas 3 "EmoContext" difokuskan pada deteksi emosi kontekstual dalam percakapan tekstual. Dalam EmoContext, diberi ucapan pengguna tekstual bersama dengan 2 putaran konteks dalam percakapan, kita harus mengklasifikasikan apakah emosi ucapan pengguna berikutnya adalah "bahagia", "sedih", "marah" atau "orang lain" (Tabel 1). Hanya ada dua peserta percakapan: orang anonim (Tuen-1 dan Turn-3) dan chatbot Ruuh berbasis AI (Turn-2). Untuk deskripsi terperinci, lihat ( Chatterjee et al., 2019 ).

Tabel 1. Contoh yang menunjukkan dataset EmoContext ( Chatterjee et al., 2019 )
Pengguna (Putar-1)Agen Percakapan (Turn-1)Pengguna (Turn-2)Kelas yang benar
Saya hanya memenuhi syarat untuk magang NabardWOOT! Itu berita bagus. Selamat!Saya mulai menangisSenang
Beraninya kau menampar anakkuJika Anda merusak mobil saya, saya akan melakukannya untuk Anda jugaCoba lakukan itu sekali sajaMarah
Aku lebih disakiti olehmuAnda tidak bersungguh-sungguh.katakan aku cinta akuSedih
Saya akan melakukan malam.Baiklah Buat saya tetap di dalam lingkaran.Tidak memberi WhatsApp no.Lainnya

Selama kompetisi, kami memiliki akses ke 30160 teks berlabel manusia yang disediakan oleh penyelenggara tugas, di mana masing-masing sekitar 5000 sampel dari kelas "angry", "sad", "happy" dan 15000 untuk kelas "others" (Tabel 2). Set Dev dan tes, yang juga disediakan oleh penyelenggara, berbeda dengan set kereta, memiliki distribusi kehidupan nyata, yaitu sekitar 4% untuk setiap kelas emosional dan sisanya untuk kelas "lain-lain". Data disediakan oleh Microsoft dan dapat ditemukan di grup LinkedIn resmi .

Tabel 2. Distribusi label kelas emosi dalam kumpulan data ( Chatterjee et al., 2019 ).
DatasetSenangSedihMarahLainnyaTotal
Melatih
14,07%
18.11%
18,26%
49,56%
30160
Dev
5,15%
4,54%
5,45%
84,86%
2755
Tes
5,16%
4,54%
5,41%
84,90%
5509
Jauh
33,33%
33,33%
33,33%
0%
900rb

Selain data ini, kami mengumpulkan 900k tweet bahasa Inggris untuk membuat dataset jauh 300k tweet untuk setiap emosi. Untuk membentuk dataset yang jauh, kami berdasarkan pada strategi Go et al. (2009), di mana kami hanya mengasosiasikan tweet dengan kehadiran kata-kata yang berhubungan dengan emosi seperti '#angry', '#annoyed', '#happy', '#sad,' #surprised ', dll. Daftar istilah kueri didasarkan pada istilah kueri dari SemEval-2018 AIT DISC ( Duppada et al., 2018 ).

Metrik kinerja utama EmoContext adalah skor F1 mikro-rata-rata untuk tiga kelas emosi, yaitu 'sedih', 'bahagia', dan 'marah'.

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. Teks Pra-Pemrosesan


Sebelum tahap pelatihan apa pun, teks sudah diproses terlebih dahulu oleh alat teks Ekphrasis (Baziotis et al., 2017). Alat ini membantu untuk melakukan koreksi ejaan, normalisasi kata, segmentasi, dan memungkinkan untuk menentukan token mana yang harus dihilangkan, dinormalisasi atau dijelaskan dengan tag khusus. Kami menggunakan teknik berikut untuk tahap pra-pemrosesan.

  • URL, email, tanggal dan waktu, nama pengguna, persentase, mata uang, dan angka diganti dengan tag yang sesuai.
  • Istilah yang berulang, disensor, memanjang, dan bermodal diberi keterangan dengan tag yang sesuai.
  • Kata-kata yang memanjang secara otomatis diperbaiki berdasarkan pada corpus statistik kata bawaan.
  • Hashtag dan kontraksi membongkar (yaitu segmentasi kata) dilakukan berdasarkan built-in statistik kata corpus.
  • Kamus yang dibuat secara manual untuk mengganti istilah yang diekstrak dari teks digunakan untuk mengurangi beragam emosi.

Selain itu, Penekanan menyediakan tokenizer yang mampu mengidentifikasi sebagian besar emoji, emotikon, dan ekspresi rumit seperti kata yang disensor, ditekankan dan memanjang serta tanggal, waktu, mata uang, dan akronim.

Tabel 3. Contoh-contoh pra-pemrosesan teks.
Teks asliTeks pra-diproses
AKU MERASA ANDA ... Saya membobol jutaan keping <allcaps> saya merasa Anda </allcaps>. [Diulang] saya membobol jutaan keping
lelah dan aku juga merindukanmu :–(lelah dan aku juga merindukanmu <sad>
Anda harus liiiiii mendengarkan ini: www.youtube.com/watch?v=99myH1orbs4Anda harus mendengarkan <elongated> untuk ini: <url>
Apartemen saya yang mengurusnya. Sewa saya sekitar $ 650.apartemen saya mengurusnya. sewa saya sekitar <money>.

 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( # terms that will be normalized normalize=['url', 'email', 'percent', 'money', 'phone', 'user', 'time', 'url', 'date', 'number'], # terms that will be annotated annotate={"hashtag", "allcaps", "elongated", "repeated", 'emphasis', 'censored'}, fix_html=True, # fix HTML tokens # corpus from which the word statistics are going to be used # for word segmentation segmenter="twitter", # corpus from which the word statistics are going to be used # for spell correction corrector="twitter", unpack_hashtags=True, # perform word segmentation on hashtags unpack_contractions=True, # Unpack contractions (can't -> can not) spell_correct_elong=True, # spell correction for elongated words # select a tokenizer. You can use SocialTokenizer, or pass your own # the tokenizer, should take as input a string and return a list of tokens tokenizer=SocialTokenizer(lowercase=True).tokenize, # list of dictionaries, for replacing tokens extracted from the text, # with other expressions. You can pass more than one dictionaries. dicts=[emoticons, emoticons_additional] ) def tokenize(text): text = " ".join(text_processor.pre_process_doc(text)) return text 

3. Word Embeddings


Word embeddings telah menjadi bagian penting dari setiap pendekatan pembelajaran mendalam untuk sistem NLP. Untuk menentukan vektor yang paling cocok untuk tugas pendeteksian emosi, kami mencoba Word2Vec ( Mikolov et al., 2013 ), GloVe ( Pennington et al., 2014 ) dan model FastText ( Joulin et al., 2017 ) serta DataStories yang sudah dilatih sebelumnya. kata vektor ( Baziotis et al., 2017 ). Konsep kunci dari Word2Vec adalah untuk menemukan kata-kata, yang berbagi konteks umum dalam corpus pelatihan, dalam jarak dekat dalam ruang vektor. Baik model Word2Vec dan Glove mempelajari pengkodean geometris kata-kata dari informasi yang terjadi bersamaan, tetapi pada dasarnya yang pertama adalah model yang dapat diprediksi, dan yang kedua adalah model yang berdasarkan hitungan. Dengan kata lain, sementara Word2Vec mencoba untuk memprediksi kata target (arsitektur CBOW) atau konteks (arsitektur Lewati-gram), yaitu untuk meminimalkan fungsi kerugian, GloVe menghitung vektor kata yang melakukan pengurangan dimensionalitas pada matriks penghitungan kejadian bersama. FastText sangat mirip dengan Word2Vec kecuali untuk fakta bahwa ia menggunakan karakter n-gram untuk mempelajari vektor kata, sehingga ia dapat memecahkan masalah kosakata yang tidak tersedia.

Untuk semua teknik yang disebutkan di atas, kami menggunakan kereta bayi pelatihan standar yang disediakan oleh penulis. Kami melatih model LSTM sederhana (dim = 64) berdasarkan masing-masing embeddings ini dan membandingkan efektivitas menggunakan cross-validation. Menurut hasil, embeddings pra-pelatihan DataStories menunjukkan skor rata-rata F1 terbaik.

Untuk memperkaya embedding kata yang dipilih dengan polaritas emosional dari kata-kata tersebut, kami mempertimbangkan untuk melakukan frase pra-pelatihan yang jauh dengan menyelaraskan embeddings pada dataset jauh yang berlabel secara otomatis. Pentingnya menggunakan pra-pelatihan ditunjukkan dalam ( Deriu et al., 201 7). Kami menggunakan dataset jauh untuk melatih jaringan LSTM sederhana untuk mengklasifikasikan tweet yang marah, sedih, dan bahagia. Lapisan embeddings dibekukan untuk zaman pelatihan pertama untuk menghindari perubahan yang signifikan dalam bobot embeddings, dan kemudian dibekukan untuk 5 epos berikutnya. Setelah tahap pelatihan, embeddings yang telah disesuaikan disimpan untuk fase pelatihan selanjutnya dan tersedia untuk umum .

 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. Arsitektur Jaringan Saraf Tiruan


Jaringan saraf berulang (RNN) adalah keluarga jaringan saraf tiruan yang khusus menangani pemrosesan data sekuensial. Berbeda dengan jaringan saraf tradisional, RRN dirancang untuk menangani data sekuensial dengan berbagi bobot internal mereka yang memproses urutan. Untuk tujuan ini, grafik perhitungan RRN mencakup siklus, mewakili pengaruh informasi sebelumnya pada yang sekarang. Sebagai perpanjangan dari RNNs, jaringan Memori Jangka Pendek (LSTM) telah diperkenalkan pada tahun 1997 ( Hochreiter dan Schmidhuber, 1997 ). Dalam LSTM sel berulang dihubungkan dengan cara tertentu untuk menghindari menghilang dan meledaknya masalah gradien. LSTM tradisional hanya menyimpan informasi dari masa lalu karena mereka memproses urutan hanya dalam satu arah. LSTM dua arah menggabungkan output dari dua lapisan LSTM tersembunyi yang bergerak dalam arah yang berlawanan, di mana satu bergerak maju melalui waktu, dan yang lain bergerak mundur melalui waktu, sehingga memungkinkan untuk menangkap informasi dari kedua negara masa lalu dan masa depan secara bersamaan ( Schuster dan Paliwal, 1997 ).


Gambar 1: Arsitektur versi yang lebih kecil dari arsitektur yang diusulkan. Unit LSTM untuk belokan pertama dan belokan ketiga memiliki bobot yang dibagi.

Tinjauan tingkat tinggi dari pendekatan kami disediakan pada Gambar 1. Arsitektur yang diusulkan dari jaringan saraf terdiri dari unit embedding dan dua unit LSTM dua arah (redup = 64). Mantan unit LSTM dimaksudkan untuk menganalisis ucapan pengguna pertama (yaitu giliran pertama dan putaran ketiga dari percakapan), dan yang terakhir dimaksudkan untuk menganalisis ucapan pengguna kedua (yaitu giliran kedua). Kedua unit ini tidak hanya mempelajari representasi fitur semantik dan sentimen, tetapi juga cara menangkap fitur percakapan khusus pengguna, yang memungkinkan pengelompokan emosi secara lebih akurat. Pada langkah pertama, setiap ucapan pengguna dimasukkan ke dalam unit LSTM dua arah yang sesuai menggunakan embeddings kata yang sudah dilatih sebelumnya. Selanjutnya, ketiga peta fitur ini disatukan dalam vektor fitur yang rata dan kemudian diteruskan ke lapisan tersembunyi yang sepenuhnya terhubung (dim = 30), yang menganalisis interaksi antara vektor yang diperoleh. Akhirnya, fitur-fitur ini melanjutkan melalui lapisan output dengan fungsi aktivasi softmax untuk memprediksi label kelas akhir. Untuk mengurangi overfitting, lapisan regularisasi dengan noise Gaussian ditambahkan setelah lapisan embedding, lapisan dropout ( Srivastava et al., 2014 ) ditambahkan pada setiap unit LSTM (p = 0,2) dan sebelum lapisan yang terhubung sepenuhnya tersembunyi (p = 0,1).

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


Dalam proses mencari arsitektur yang optimal, kami bereksperimen tidak hanya dengan jumlah sel dalam lapisan, fungsi aktivasi dan parameter regularisasi tetapi juga dengan arsitektur jaringan saraf. Info terperinci tentang frasa ini dapat ditemukan di koran aslinya .

Model yang dijelaskan pada bagian sebelumnya menunjukkan skor terbaik pada dataset dev, sehingga digunakan pada tahap evaluasi akhir kompetisi. Pada dataset tes akhir, itu mencapai 72,59% skor rata-rata mikro F1 untuk kelas emosional, sedangkan skor maksimum di antara semua peserta adalah 79,59%. Namun, ini jauh di atas garis dasar resmi yang dirilis oleh pengatur tugas, yaitu 58,68%.

Kode sumber model dan embedding kata tersedia di GitHub.
Versi lengkap artikel dan makalah uraian tugas dapat ditemukan di ACL Anthology.
Dataset pelatihan terletak di grup kompetisi resmi di LinkedIn.

Kutipan:

 @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", } 

Source: https://habr.com/ru/post/id439850/


All Articles