Pengenalan emosi menggunakan jaringan saraf convolutional


Mengenali emosi selalu menjadi tantangan yang menarik bagi para ilmuwan. Baru-baru ini, saya telah mengerjakan proyek SER eksperimental (Pengenalan Emosi Pidato) untuk memahami potensi teknologi ini - untuk ini saya memilih repositori paling populer di Github dan menjadikannya dasar proyek saya.

Sebelum kita mulai memahami proyek ini, alangkah baiknya mengingat jenis kemacetan yang dimiliki SER.

Hambatan utama


  • emosi bersifat subjektif, bahkan orang menafsirkannya secara berbeda. Sulit untuk mendefinisikan konsep "emosi";
  • mengomentari audio sulit. Haruskah kita menandai setiap kata, kalimat, atau keseluruhan komunikasi secara keseluruhan sebagai satu kesatuan? Seperangkat emosi seperti apa yang digunakan dalam pengakuan?
  • mengumpulkan data juga tidak mudah. Banyak data audio dapat dikumpulkan dari film dan berita. Namun, kedua sumber itu “bias” karena berita harus netral, dan emosi para aktor dimainkan. Sulit untuk menemukan sumber data audio yang “obyektif”.
  • data markup membutuhkan sumber daya manusia dan waktu yang besar. Tidak seperti menggambar bingkai pada gambar, itu membutuhkan personel terlatih khusus untuk mendengarkan seluruh rekaman audio, menganalisisnya dan memberikan komentar. Dan kemudian komentar ini harus dihargai oleh banyak orang lain, karena peringkatnya subjektif.


Deskripsi Proyek


Menggunakan jaringan saraf convolutional untuk mengenali emosi dalam rekaman audio. Dan ya, pemilik repositori tidak merujuk ke sumber apa pun.

Deskripsi Data


Ada dua set data yang digunakan dalam repositori RAVDESS dan SAVEE, saya baru saja mengadaptasi RAVDESS dalam model saya. Ada dua jenis data dalam konteks RAVDESS: pidato dan lagu.

Dataset RAVDESS (The Ryerson Audio-Visual Database of Emotional Speech and Song) :

  • 12 aktor dan 12 aktris merekam pidato dan lagu mereka dalam penampilan mereka;
  • aktor # 18 tidak memiliki lagu yang direkam;
  • Emosi Jijik (jijik), Netral (netral) dan Kejutan (terkejut) tidak ada dalam data "lagu".

Rincian Emosi:


Bagan Distribusi Emosi:


Ekstraksi fitur


Ketika kami bekerja dengan tugas-tugas pengenalan ucapan, Koefisien Cepstral (MFCCs) adalah teknologi canggih, terlepas dari kenyataan bahwa itu muncul di tahun 80-an.

Kutipan dari Tutorial MFCC :
Bentuk ini menentukan apa suara output. Jika kita dapat menentukan bentuk, itu akan memberi kita representasi akurat dari fonem yang dibunyikan. Bentuk saluran suara memanifestasikan dirinya dalam amplop spektrum pendek, dan tugas MFCC adalah menampilkan amplop ini secara akurat.


Bentuk gelombang


Spektrogram

Kami menggunakan MFCC sebagai fitur input. Jika Anda tertarik untuk mempelajari lebih lanjut tentang apa itu MFCC, maka tutorial ini cocok untuk Anda. Mengunduh data dan mengonversinya ke format MFCC dapat dengan mudah dilakukan dengan menggunakan paket librosa Python.

Arsitektur Model Default


Penulis mengembangkan model CNN menggunakan paket Keras, membuat 7 lapisan - enam lapisan Con1D dan satu lapisan kepadatan (Padat).

model = Sequential() model.add(Conv1D(256, 5,padding='same', input_shape=(216,1))) #1 model.add(Activation('relu')) model.add(Conv1D(128, 5,padding='same')) #2 model.add(Activation('relu')) model.add(Dropout(0.1)) model.add(MaxPooling1D(pool_size=(8))) model.add(Conv1D(128, 5,padding='same')) #3 model.add(Activation('relu')) #model.add(Conv1D(128, 5,padding='same')) #4 #model.add(Activation('relu')) #model.add(Conv1D(128, 5,padding='same')) #5 #model.add(Activation('relu')) #model.add(Dropout(0.2)) model.add(Conv1D(128, 5,padding='same')) #6 model.add(Activation('relu')) model.add(Flatten()) model.add(Dense(10)) #7 model.add(Activation('softmax')) opt = keras.optimizers.rmsprop(lr=0.00001, decay=1e-6) 

Penulis berkomentar pada layer 4 dan 5 dalam rilis terbaru (18 September 2018) dan ukuran file akhir dari model ini tidak sesuai dengan jaringan yang disediakan, jadi saya tidak dapat mencapai hasil yang sama dalam akurasi - 72%.

Model ini hanya dilatih dengan parameter batch_size=16 dan epochs=700 , tanpa jadwal pelatihan, dll.

 # Compile Model model.compile(loss='categorical_crossentropy', optimizer=opt,metrics=['accuracy']) # Fit Model cnnhistory=model.fit(x_traincnn, y_train, batch_size=16, epochs=700, validation_data=(x_testcnn, y_test)) 

Di sini categorical_crossentropy adalah fungsi dari kerugian, dan ukuran evaluasi adalah akurasi.

Eksperimen saya


Analisis data eksplorasi


Dalam dataset RAVDESS, setiap aktor menunjukkan 8 emosi, mengucapkan dan menyanyikan 2 kalimat, masing-masing 2 kali. Hasilnya, 4 contoh dari setiap emosi diperoleh dari masing-masing aktor, dengan pengecualian dari emosi netral yang disebutkan di atas, jijik, dan kejutan. Setiap audio berlangsung sekitar 4 detik, pada detik pertama dan terakhir paling sering diam.

Penawaran khas :

Pengamatan


Setelah saya memilih dataset dari 1 aktor dan 1 aktris, dan kemudian mendengarkan semua catatan mereka, saya menyadari bahwa pria dan wanita mengekspresikan emosi mereka secara berbeda. Sebagai contoh:

  • kemarahan laki-laki (Marah) lebih keras;
  • kegembiraan pria (Bahagia) dan frustrasi (Sedih) - sebuah fitur dalam nada tertawa dan menangis selama "keheningan";
  • sukacita perempuan (Bahagia), kemarahan (Marah) dan frustrasi (Sedih) lebih keras;
  • female disgust (Disgust) berisi suara muntah.

Pengulangan percobaan


Penulis menghapus kelas netral, jijik, dan terkejut untuk membuat pengakuan kelas RAVDESS 10-set data. Mencoba mengulangi pengalaman penulis, saya mendapatkan hasil ini:



Namun, saya menemukan bahwa ada kebocoran data ketika dataset untuk validasi identik dengan dataset pengujian. Oleh karena itu, saya mengulangi pemisahan data, mengisolasi kumpulan data dari dua aktor dan dua aktris sehingga mereka tidak terlihat selama tes:

  • aktor 1 hingga 20 digunakan untuk set Train / Valid dalam rasio 8: 2;
  • aktor 21 hingga 24 diisolasi dari pengujian;
  • Parameter Set Kereta: (1248, 216, 1);
  • Parameter Set Valid: (312, 216, 1);
  • Parameter Set Tes: (320, 216, 1) - (terisolasi).

Saya melatih ulang modelnya dan inilah hasilnya:


Tes kinerja


Dari grafik Train Valid Gross jelas bahwa tidak ada konvergensi untuk 10 kelas yang dipilih. Karena itu, saya memutuskan untuk mengurangi kompleksitas model dan hanya menyisakan emosi pria. Saya mengisolasi dua aktor di set tes, dan meletakkan sisanya di kereta / set valid, rasio 8: 2. Ini memastikan bahwa tidak ada ketidakseimbangan dalam dataset. Kemudian saya melatih data pria dan wanita secara terpisah untuk melakukan tes.

Dataset jantan

  • Train Set - 640 sampel dari aktor 1-10;
  • Set Berlaku - 160 sampel dari aktor 1-10;
  • Set Tes - 160 sampel dari aktor 11-12.

Baris referensi: laki-laki


Dataset wanita

  • Train Set - 608 sampel dari aktris 1-10;
  • Set Berlaku - 152 sampel dari aktris 1-10;
  • Set Tes - 160 sampel dari aktris 11-12.

Baris referensi: wanita


Seperti yang Anda lihat, matriks kesalahan berbeda.

Pria: Angry dan Happy adalah kelas prediksi utama dalam model, tetapi mereka tidak sama.

Wanita: gangguan (Sedih) dan sukacita (Bahagia) - pada dasarnya prediksi kelas dalam model; kemarahan dan sukacita mudah dikacaukan.

Mengingat pengamatan dari Analisis Data Intelijen , saya curiga bahwa Angry dan Happy wanita mirip dengan titik kebingungan, karena cara mereka berekspresi hanya untuk mengangkat suara mereka.

Selain itu, saya ingin tahu bahwa jika saya menyederhanakan model lebih jauh, hanya menyisakan kelas Positif, Netral, dan Negatif. Atau hanya Positif dan Negatif. Singkatnya, saya mengelompokkan emosi menjadi 2 dan 3 kelas, masing-masing.

2 kelas:

  • Positif: sukacita (Bahagia), tenang (Tenang);
  • Negatif: marah, takut (takut), frustrasi (sedih).

3 kelas:

  • Positif: sukacita (Happy);
  • Netral: tenang (Tenang), netral (Netral);
  • Negatif: marah, takut (takut), frustrasi (sedih).

Sebelum memulai percobaan, saya mengatur arsitektur model menggunakan data pria, membuat pengenalan 5 kelas.

 #   -  target_class = 5 #  model = Sequential() model.add(Conv1D(256, 8, padding='same',input_shape=(X_train.shape[1],1))) #1 model.add(Activation('relu')) model.add(Conv1D(256, 8, padding='same')) #2 model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Dropout(0.25)) model.add(MaxPooling1D(pool_size=(8))) model.add(Conv1D(128, 8, padding='same')) #3 model.add(Activation('relu')) model.add(Conv1D(128, 8, padding='same')) #4 model.add(Activation('relu')) model.add(Conv1D(128, 8, padding='same')) #5 model.add(Activation('relu')) model.add(Conv1D(128, 8, padding='same')) #6 model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Dropout(0.25)) model.add(MaxPooling1D(pool_size=(8))) model.add(Conv1D(64, 8, padding='same')) #7 model.add(Activation('relu')) model.add(Conv1D(64, 8, padding='same')) #8 model.add(Activation('relu')) model.add(Flatten()) model.add(Dense(target_class)) #9 model.add(Activation('softmax')) opt = keras.optimizers.SGD(lr=0.0001, momentum=0.0, decay=0.0, nesterov=False) 

Saya menambahkan 2 lapisan Conv1D, satu lapisan MaxPooling1D dan 2 lapisan BarchNormalisasi; Saya juga mengubah nilai putus sekolah menjadi 0,25. Akhirnya, saya mengubah pengoptimal menjadi SGD dengan kecepatan belajar 0,0001.

 lr_reduce = ReduceLROnPlateau(monitor='val_loss', factor=0.9, patience=20, min_lr=0.000001) mcp_save = ModelCheckpoint('model/baseline_2class_np.h5', save_best_only=True, monitor='val_loss', mode='min') cnnhistory=model.fit(x_traincnn, y_train, batch_size=16, epochs=700, validation_data=(x_testcnn, y_test), callbacks=[mcp_save, lr_reduce]) 

Untuk melatih model, saya menerapkan pengurangan dalam "dataran tinggi pembelajaran" dan hanya menyimpan model terbaik dengan nilai val_loss minimum. Dan inilah hasil untuk kelas target yang berbeda.

Performa model baru


Pria, 5 kelas



Wanita, Kelas 5

Pria, Kelas 2


Pria, 3 kelas


Menambah (augmentasi)


Ketika saya memperkuat arsitektur model, optimizer dan kecepatan pelatihan, ternyata model tersebut masih tidak menyatu dalam mode pelatihan. Saya menyarankan bahwa ini adalah masalah kuantitas data, karena kami hanya memiliki 800 sampel. Ini mengarahkan saya ke metode peningkatan audio, pada akhirnya saya menggandakan dataset. Mari kita lihat metode-metode ini.

Pria, 5 kelas


Peningkatan Dinamis

 def dyn_change(data): """    """ dyn_change = np.random.uniform(low=1.5,high=3) return (data * dyn_change) 



Penyesuaian Pitch

 def pitch(data, sample_rate): """    """ bins_per_octave = 12 pitch_pm = 2 pitch_change = pitch_pm * 2*(np.random.uniform()) data = librosa.effects.pitch_shift(data.astype('float64'), sample_rate, n_steps=pitch_change, bins_per_octave=bins_per_octave) 


Offset

 def shift(data): """   """ s_range = int(np.random.uniform(low=-5, high = 5)*500) return np.roll(data, s_range) 


Menambahkan White Noise

 def noise(data): """    """ #     : https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.random.html noise_amp = 0.005*np.random.uniform()*np.amax(data) data = data.astype('float64') + noise_amp * np.random.normal(size=data.shape[0]) return data 


Terlihat bahwa augmentasi sangat meningkatkan akurasi, hingga 70+% dalam kasus umum. Terutama dalam kasus penambahan putih, yang meningkatkan akurasi hingga 87,19% - namun, akurasi tes dan ukuran F1 turun lebih dari 5%. Dan kemudian saya mendapat ide untuk menggabungkan beberapa metode augmentasi untuk hasil yang lebih baik.

Menggabungkan beberapa metode


Kebisingan putih + bias


Menguji augmentasi pada pria


Pria, Kelas 2


Kebisingan putih + bias

Untuk semua sampel


Kebisingan putih + bias

Hanya untuk sampel positif, karena set 2-kelas tidak seimbang (terhadap sampel negatif).


Pitch + White Noise
Untuk semua sampel


Pitch + White Noise

Hanya untuk sampel positif


Kesimpulan


Pada akhirnya, saya hanya bisa bereksperimen dengan dataset pria. Saya membagi ulang data untuk menghindari ketidakseimbangan dan, sebagai akibatnya, kebocoran data. Saya menyiapkan model untuk bereksperimen dengan suara laki-laki, karena saya ingin menyederhanakan model sebanyak mungkin untuk memulai. Saya juga melakukan tes menggunakan berbagai metode augmentasi; penambahan white noise dan bias telah bekerja dengan baik pada data yang tidak seimbang.

Kesimpulan


  • emosi bersifat subjektif dan sulit untuk diperbaiki;
  • perlu untuk menentukan terlebih dahulu emosi mana yang cocok untuk proyek;
  • Jangan selalu mempercayai konten dengan Github, meskipun memiliki banyak bintang;
  • berbagi data - ingatlah;
  • analisis data eksplorasi selalu memberikan ide yang bagus, tetapi Anda harus bersabar ketika harus bekerja dengan data audio;
  • Tentukan apa yang akan Anda berikan pada input model Anda: sebuah kalimat, seluruh catatan, atau tanda seru?
  • kurangnya data merupakan faktor keberhasilan penting dalam SER, namun, membuat dataset yang baik dengan emosi adalah tugas yang kompleks dan mahal;
  • sederhanakan model Anda jika tidak ada data.

Perbaikan lebih lanjut


  • Saya hanya menggunakan 3 detik pertama sebagai input untuk mengurangi ukuran data keseluruhan - proyek asli digunakan 2,5 detik. Saya ingin bereksperimen dengan rekaman ukuran penuh;
  • Anda dapat melakukan pra-proses data: memangkas keheningan, menormalkan panjang dengan mengisi dengan nol, dll;
  • coba jaringan saraf berulang untuk tugas ini.

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


All Articles