Reconocimiento de emociones usando una red neuronal convolucional


Reconocer las emociones siempre ha sido un desafío emocionante para los científicos. Recientemente, estoy trabajando en un proyecto experimental SER (Reconocimiento de Emociones del Habla) para comprender el potencial de esta tecnología. Para esto seleccioné los repositorios más populares en Github y los convertí en la base de mi proyecto.

Antes de comenzar a comprender el proyecto, será bueno recordar qué tipo de cuellos de botella tiene SER.

Principales obstáculos


  • las emociones son subjetivas, incluso las personas las interpretan de manera diferente. Es difícil definir el concepto mismo de "emoción";
  • comentar sobre el audio es difícil. ¿Deberíamos marcar de alguna manera cada palabra individual, oración o toda la comunicación como un todo? ¿Un conjunto de qué tipo de emociones usar en reconocimiento?
  • Recopilar datos tampoco es fácil. Se pueden recopilar muchos datos de audio de películas y noticias. Sin embargo, ambas fuentes están "sesgadas" porque las noticias deben ser neutrales y se juegan las emociones de los actores. Es difícil encontrar una fuente "objetiva" de datos de audio.
  • los datos de marcado requieren grandes recursos humanos y de tiempo. A diferencia de dibujar cuadros en imágenes, se requiere personal especialmente capacitado para escuchar grabaciones de audio completas, analizarlas y proporcionar comentarios. Y luego muchas personas deben apreciar estos comentarios, porque las calificaciones son subjetivas.


Descripción del proyecto


Usar una red neuronal convolucional para reconocer emociones en grabaciones de audio. Y sí, el propietario del repositorio no se refirió a ninguna fuente.

Descripción de datos


Hay dos conjuntos de datos que se utilizaron en los repositorios RAVDESS y SAVEE, acabo de adaptar RAVDESS en mi modelo. Hay dos tipos de datos en el contexto RAVDESS: voz y canción.

Conjunto de datos RAVDESS (la base de datos audiovisuales de Ryerson de discurso y canción emocional) :

  • 12 actores y 12 actrices grabaron sus discursos y canciones en su actuación;
  • el actor # 18 no tiene canciones grabadas;
  • Las emociones disgusto (asco), neutral (neutral) y sorpresas (sorprendido) están ausentes en los datos de la "canción".

Desglose de emociones:


Cuadro de distribución de emociones:


Extracción de características


Cuando trabajamos con tareas de reconocimiento de voz, los coeficientes cepstrales (MFCC) son una tecnología avanzada, a pesar de que apareció en los años 80.

Cita del tutorial de MFCC :
Esta forma determina cuál es el sonido de salida. Si podemos identificar el formulario, nos dará una representación precisa del fonema sonado. La forma del tracto vocal se manifiesta en una envoltura de corto espectro, y el trabajo del MFCC es mostrar con precisión esta envoltura.


Forma de onda


Espectrograma

Usamos MFCC como una característica de entrada. Si está interesado en aprender más sobre lo que es MFCC, entonces este tutorial es para usted. La descarga de datos y su conversión al formato MFCC se puede hacer fácilmente usando el paquete de librosa Python.

Arquitectura de modelo predeterminada


El autor desarrolló un modelo CNN utilizando el paquete Keras, creando 7 capas: seis capas Con1D y una capa de densidad (Densa).

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) 

El autor comentó las capas 4 y 5 en la última versión (18 de septiembre de 2018) y el tamaño del archivo final de este modelo no se ajusta a la red proporcionada, por lo que no puedo lograr el mismo resultado en precisión: 72%.

El modelo simplemente se entrena con los parámetros batch_size=16 y batch_size=16 epochs=700 , sin ningún programa de entrenamiento, etc.

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

Aquí categorical_crossentropy es una función de las pérdidas, y la medida de la evaluación es la precisión.

Mi experimento


Análisis exploratorio de datos


En el conjunto de datos RAVDESS, cada actor muestra 8 emociones, pronunciando y cantando 2 oraciones, 2 veces cada una. Como resultado, se obtienen 4 ejemplos de cada emoción de cada actor, con la excepción de las emociones neutrales, el asco y la sorpresa mencionados anteriormente. Cada audio tiene una duración aproximada de 4 segundos, en el primer y último segundo suele ser silencio.

Ofertas tipicas :

Observación


Después de seleccionar un conjunto de datos de 1 actor y 1 actriz, y luego escuchar todos sus registros, me di cuenta de que los hombres y las mujeres expresan sus emociones de manera diferente. Por ejemplo:

  • la ira masculina (enojada) es más fuerte;
  • alegría de los hombres (feliz) y frustración (triste): una característica en los tonos de risa y llanto durante el "silencio";
  • alegría femenina (feliz), enojo (enojado) y frustración (triste) son más fuertes;
  • El asco femenino (asco) contiene el sonido del vómito.

Experimento de repetición


El autor eliminó las clases neutrales, disgustadas y sorprendidas para hacer el reconocimiento RAVDESS de 10 clases del conjunto de datos. Al intentar repetir la experiencia del autor, obtuve este resultado:



Sin embargo, descubrí que hay una fuga de datos cuando el conjunto de datos para la validación es idéntico al conjunto de datos de prueba. Por lo tanto, repetí la separación de los datos, aislando los conjuntos de datos de dos actores y dos actrices para que no fueran visibles durante la prueba:

  • los actores 1 a 20 se usan para los conjuntos Train / Valid en una proporción de 8: 2;
  • los actores 21 a 24 están aislados de las pruebas;
  • Parámetros del conjunto de trenes: (1248, 216, 1);
  • Parámetros de configuración válidos: (312, 216, 1);
  • Parámetros del conjunto de pruebas: (320, 216, 1) - (aislado).

Volví a entrenar el modelo y aquí está el resultado:


Prueba de rendimiento


En el gráfico Train Valid Gross queda claro que no hay convergencia para las 10 clases seleccionadas. Por lo tanto, decidí reducir la complejidad del modelo y dejar solo las emociones masculinas. Aislé a dos actores en el conjunto de prueba y puse el resto en el tren / conjunto válido, relación 8: 2. Esto asegura que no haya desequilibrio en el conjunto de datos. Luego entrené los datos masculinos y femeninos por separado para realizar la prueba.

Conjunto de datos masculino

  • Conjunto de trenes: 640 muestras de los actores 1-10;
  • Conjunto válido: 160 muestras de los actores 1-10;
  • Conjunto de prueba: 160 muestras de actores 11-12.

Línea de referencia: hombres


Conjunto de datos femenino

  • Conjunto de trenes: 608 muestras de actrices 1-10;
  • Conjunto válido: 152 muestras de las actrices 1-10;
  • Conjunto de prueba: 160 muestras de actrices 11-12.

Línea de referencia: mujeres


Como puede ver, las matrices de error son diferentes.

Hombres: Angry y Happy son las principales clases predichas en el modelo, pero no son iguales.

Mujeres: desorden (triste) y alegría (feliz): clases básicamente predichas en el modelo; la ira y la alegría se confunden fácilmente.

Recordando las observaciones del Análisis de Datos de Inteligencia , sospecho que las mujeres Angry y Happy son similares al punto de confusión, porque su forma de expresión es simplemente levantar la voz.

Además de eso, tengo curiosidad de que si simplifico aún más el modelo, dejaré solo las clases Positivo, Neutral y Negativo. O solo Positivo y Negativo. En resumen, agrupé las emociones en 2 y 3 clases, respectivamente.

2 clases:

  • Positivo: alegría (feliz), calma (calma);
  • Negativo: ira, miedo (miedo), frustración (triste).

3 clases:

  • Positivo: alegría (feliz);
  • Neutral: calma (calma), neutral (neutral);
  • Negativo: ira, miedo (miedo), frustración (triste).

Antes de comenzar el experimento, configuré la arquitectura del modelo utilizando datos masculinos, haciendo un reconocimiento de 5 clases.

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

Agregué 2 capas de Conv1D, una capa de MaxPooling1D y 2 capas de BarchNormalization; También cambié el valor de deserción a 0.25. Finalmente, cambié el optimizador a SGD con una velocidad de aprendizaje de 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]) 

Para entrenar el modelo, apliqué una reducción en la "meseta de entrenamiento" y val_loss solo el mejor modelo con un valor mínimo de val_loss . Y aquí están los resultados para las diferentes clases objetivo.

Nuevo modelo de rendimiento


Hombres, 5 clases



Mujeres, grado 5

Hombres, grado 2


Hombres, 3 clases


Incremento (aumento)


Cuando fortalecí la arquitectura del modelo, el optimizador y la velocidad del entrenamiento, resultó que el modelo todavía no converge en el modo de entrenamiento. Sugerí que este es un problema de cantidad de datos, ya que solo tenemos 800 muestras. Esto me llevó a métodos para aumentar el audio, al final dupliqué los conjuntos de datos. Echemos un vistazo a estos métodos.

Hombres, 5 clases


Incremento Dinámico

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



Ajuste de tono

 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) 


Agregar ruido blanco

 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 


Es notable que el aumento aumenta en gran medida la precisión, hasta un 70 +% en el caso general. Especialmente en el caso de la adición de blanco, que aumenta la precisión al 87.19%; sin embargo, la precisión de la prueba y la medida F1 caen en más del 5%. Y luego tuve la idea de combinar varios métodos de aumento para un mejor resultado.

Combinando varios métodos


Ruido blanco + sesgo


Prueba de aumento en hombres


Hombres, grado 2


Ruido blanco + sesgo

Para todas las muestras


Ruido blanco + sesgo

Solo para muestras positivas, ya que el conjunto de 2 clases está desequilibrado (hacia muestras negativas).


Echada + Ruido Blanco
Para todas las muestras


Echada + Ruido Blanco

Solo para muestras positivas


Conclusión


Al final, pude experimentar solo con un conjunto de datos masculino. Re-dividí los datos para evitar el desequilibrio y, como consecuencia, la fuga de datos. Configuré el modelo para experimentar con voces masculinas, ya que quería simplificar el modelo tanto como sea posible para comenzar. También realicé pruebas usando diferentes métodos de aumento; La adición de ruido blanco y sesgo ha funcionado bien en datos no balanceados.

Conclusiones


  • las emociones son subjetivas y difíciles de arreglar;
  • es necesario determinar de antemano qué emociones son adecuadas para el proyecto;
  • No confíes siempre en el contenido con Github, incluso si tiene muchas estrellas;
  • intercambio de datos: tenlo en cuenta;
  • el análisis exploratorio de datos siempre da una buena idea, pero debe ser paciente cuando se trata de trabajar con datos de audio;
  • determine qué le dará a la entrada de su modelo: ¿una oración, un registro completo o una exclamación?
  • la falta de datos es un factor de éxito importante en SER, sin embargo, crear un buen conjunto de datos con emociones es una tarea compleja y costosa;
  • simplifique su modelo en caso de falta de datos.

Mejora adicional


  • Usé solo los primeros 3 segundos como entrada para reducir el tamaño general de los datos; el proyecto original usó 2.5 segundos. Me gustaría experimentar con grabaciones de tamaño completo;
  • puede preprocesar los datos: recortar el silencio, normalizar la longitud rellenando con ceros, etc.
  • pruebe redes neuronales recurrentes para esta tarea.

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


All Articles