Reconhecimento de emoções usando uma rede neural convolucional


Reconhecer emoções sempre foi um desafio emocionante para os cientistas. Recentemente, estou trabalhando em um projeto SER experimental (Reconhecimento de Emoção de Fala) para entender o potencial dessa tecnologia - para isso, selecionei os repositórios mais populares no Github e os fiz a base do meu projeto.

Antes de começarmos a entender o projeto, será bom lembrar que tipo de gargalos o SER possui.

Principais obstáculos


  • as emoções são subjetivas, até as pessoas as interpretam de maneira diferente. É difícil definir o próprio conceito de "emoção";
  • comentar sobre o áudio é difícil. De alguma forma, devemos marcar cada palavra, sentença ou toda a comunicação como um todo? Um conjunto de que tipo de emoções usar no reconhecimento?
  • coletar dados também não é fácil. Muitos dados de áudio podem ser coletados de filmes e notícias. No entanto, ambas as fontes são "tendenciosas" porque as notícias devem ser neutras e as emoções dos atores são interpretadas. É difícil encontrar uma fonte "objetiva" de dados de áudio.
  • dados de marcação requerem grandes recursos humanos e de tempo. Ao contrário de desenhar quadros em imagens, requer pessoal especialmente treinado para ouvir gravações de áudio inteiras, analisá-las e fornecer comentários. E esses comentários devem ser apreciados por muitas outras pessoas, porque as classificações são subjetivas.


Descrição do Projeto


Usando uma rede neural convolucional para reconhecer emoções em gravações de áudio. E sim, o proprietário do repositório não se referiu a nenhuma fonte.

Descrição dos dados


Existem dois conjuntos de dados que foram usados ​​nos repositórios RAVDESS e SAVEE. Acabei de adaptar o RAVDESS no meu modelo. Existem dois tipos de dados no contexto RAVDESS: fala e música.

Conjunto de dados RAVDESS (o banco de dados audiovisual da Ryerson de fala e música emocional) :

  • 12 atores e 12 atrizes gravaram seus discursos e músicas em sua performance;
  • o ator 18 não tem músicas gravadas;
  • emoções Nojo (nojo), Neutro (neutro) e Surpresas (surpresa) estão ausentes nos dados da "música".

Repartição das Emoções:


Gráfico de distribuição de emoções:


Extração de recursos


Quando trabalhamos com tarefas de reconhecimento de fala, os Coeficientes Cepstrais (MFCCs) são uma tecnologia avançada, apesar de aparecer nos anos 80.

Citação do Tutorial do MFCC :
Essa forma determina qual é o som de saída. Se conseguirmos identificar o formulário, ele nos dará uma representação precisa do fonema tocado. A forma do trato vocal se manifesta em um envelope de curto espectro, e o trabalho da MFCC é exibir com precisão esse envelope.


Forma de onda


Spectrogram

Usamos o MFCC como um recurso de entrada. Se você estiver interessado em aprender mais sobre o que é o MFCC, este tutorial é para você. O download de dados e a conversão para o formato MFCC pode ser feito facilmente usando o pacote librosa Python.

Arquitetura de modelo padrão


O autor desenvolveu um modelo CNN usando o pacote Keras, criando 7 camadas - seis camadas Con1D e uma camada de densidade (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) 

O autor comentou as camadas 4 e 5 na versão mais recente (18 de setembro de 2018) e o tamanho final do arquivo deste modelo não se encaixa na rede fornecida, portanto, não é possível obter o mesmo resultado com precisão - 72%.

O modelo é simplesmente treinado com os parâmetros batch_size=16 e epochs=700 , sem nenhuma programação de treinamento, 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)) 

Aqui a categorical_crossentropy é uma função das perdas e a medida da avaliação é a precisão.

Minha experiência


Análise exploratória de dados


No conjunto de dados RAVDESS, cada ator mostra 8 emoções, pronunciando e cantando 2 frases, 2 vezes cada. Como resultado, 4 exemplos de cada emoção são obtidos de cada ator, com exceção das emoções neutras, nojo e surpresa acima mencionados. Cada áudio dura aproximadamente 4 segundos; no primeiro e no último segundo, geralmente é o silêncio.

Ofertas típicas :

Observação


Depois de selecionar um conjunto de dados de 1 ator e 1 atriz, e depois ouvir todos os seus registros, percebi que homens e mulheres expressam suas emoções de maneiras diferentes. Por exemplo:

  • raiva masculina (irritada) é apenas mais alta;
  • alegria dos homens (feliz) e frustração (triste) - uma característica dos tons de riso e choro durante o "silêncio";
  • alegria feminina (feliz), raiva (irritada) e frustração (triste) são mais altas;
  • nojo feminino (nojo) contém o som de vômito.

Repetição da experiência


O autor removeu as classes neutra, repugnante e surpresa para fazer o reconhecimento RAVDESS de 10 classes do conjunto de dados. Tentando repetir a experiência do autor, obtive este resultado:



No entanto, descobri que há um vazamento de dados quando o conjunto de dados para validação é idêntico ao conjunto de dados de teste. Portanto, repeti a separação dos dados, isolando os conjuntos de dados de dois atores e duas atrizes para que eles não fiquem visíveis durante o teste:

  • os atores 1 a 20 são usados ​​para conjuntos Train / Valid na proporção 8: 2;
  • os atores 21 a 24 são isolados dos testes;
  • Parâmetros do conjunto de trens: (1248, 216, 1);
  • Parâmetros de conjunto válidos: (312, 216, 1);
  • Parâmetros do conjunto de teste: (320, 216, 1) - (isolado).

Treinei novamente o modelo e aqui está o resultado:


Teste de desempenho


No gráfico Bruto válido do trem, fica claro que não há convergência para as 10 classes selecionadas. Por isso, decidi reduzir a complexidade do modelo e deixar apenas as emoções masculinas. Eu isolei dois atores no conjunto de teste e coloquei o resto no conjunto comboio / válido, na proporção de 8: 2. Isso garante que não haja desequilíbrio no conjunto de dados. Depois, treinei os dados masculino e feminino separadamente para realizar o teste.

Conjunto de dados masculino

  • Conjunto de trem - 640 amostras dos atores 1-10;
  • Conjunto válido - 160 amostras dos atores 1-10;
  • Conjunto de teste - 160 amostras dos atores 11-12.

Linha de referência: men


Conjunto de dados feminino

  • Conjunto de trem - 608 amostras das atrizes 1-10;
  • Conjunto válido - 152 amostras das atrizes 1-10;
  • Conjunto de teste - 160 amostras das atrizes 11-12.

Linha de referência: mulheres


Como você pode ver, as matrizes de erro são diferentes.

Homens: zangado e feliz são as principais classes previstas no modelo, mas não são iguais.

Mulheres: desordem (triste) e alegria (feliz) - basicamente classes previstas no modelo; raiva e alegria são facilmente confundidas.

Lembrando as observações da Intelligence Data Analysis , suspeito que as mulheres Angry e Happy sejam semelhantes ao ponto de confusão, porque o modo de expressão delas é simplesmente elevar suas vozes.

Além disso, estou curioso para simplificar ainda mais o modelo, deixando apenas as classes Positiva, Neutra e Negativa. Ou apenas positivo e negativo. Em resumo, agrupei as emoções em 2 e 3 classes, respectivamente.

2 aulas:

  • Positivo: alegria (feliz), calmo (calmo);
  • Negativo: raiva, medo (com medo), frustração (triste).

3 classes:

  • Positivo: alegria (feliz);
  • Neutro: calmo (calmo), neutro (neutro);
  • Negativo: raiva, medo (com medo), frustração (triste).

Antes de iniciar o experimento, configurei a arquitetura do modelo usando dados masculinos, fazendo o reconhecimento em 5 classes.

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

Eu adicionei 2 camadas de Conv1D, uma camada de MaxPooling1D e 2 camadas de BarchNormalization; Eu também alterei o valor da desistência para 0,25. Por fim, mudei o otimizador para SGD com uma velocidade de aprendizado 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 treinar o modelo, apliquei uma redução no "platô de treinamento" e salvei apenas o melhor modelo com um valor mínimo de val_loss . E aqui estão os resultados para as diferentes classes de destino.

Desempenho do novo modelo


Homens, 5 classes



Feminino, 5ª Série

Masculino, 2ª Série


Homens, 3 classes


Aumento (aumento)


Quando reforcei a arquitetura do modelo, o otimizador e a velocidade do treinamento, verificou-se que o modelo ainda não converge no modo de treinamento. Sugeri que esse é um problema de quantidade de dados, pois temos apenas 800 amostras. Isso me levou a métodos para aumentar o áudio, no final, eu dobrei os conjuntos de dados. Vamos dar uma olhada nesses métodos.

Homens, 5 classes


Incremento dinâmico

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



Ajuste de inclinação

 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) 


Deslocamento

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


Adicionando ruído branco

 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 


É perceptível que o aumento aumenta muito a precisão, em até 70 +% no caso geral. Especialmente no caso da adição de branco, que aumenta a precisão para 87,19% - no entanto, a precisão do teste e a medida F1 caem em mais de 5%. E então tive a ideia de combinar vários métodos de aumento para obter um melhor resultado.

Combinando vários métodos


Ruído branco + viés


Teste de aumento em homens


Masculino, 2ª Série


Ruído branco + viés

Para todas as amostras


Ruído branco + viés

Somente para amostras positivas, uma vez que o conjunto de 2 classes é desequilibrado (para amostras negativas).


Passo + ruído branco
Para todas as amostras


Passo + ruído branco

Apenas para amostras positivas


Conclusão


No final, eu pude experimentar apenas um conjunto de dados masculino. Dividi os dados para evitar desequilíbrios e, consequentemente, vazamento de dados. Configurei o modelo para experimentar vozes masculinas, pois queria simplificá-lo o máximo possível para começar. Também conduzi testes usando diferentes métodos de aumento; a adição de ruído branco e viés funcionou bem em dados desequilibrados.

Conclusões


  • as emoções são subjetivas e difíceis de corrigir;
  • é necessário determinar com antecedência quais emoções são adequadas para o projeto;
  • Nem sempre confie no conteúdo do Github, mesmo que ele tenha muitas estrelas;
  • compartilhamento de dados - lembre-se disso;
  • a análise exploratória de dados sempre dá uma boa ideia, mas você precisa ser paciente quando se trata de trabalhar com dados de áudio;
  • Determine o que você dará à entrada do seu modelo: uma frase, um registro inteiro ou uma exclamação?
  • a falta de dados é um importante fator de sucesso no SER, no entanto, criar um bom conjunto de dados com emoções é uma tarefa complexa e cara;
  • simplifique seu modelo em caso de falta de dados.

Melhoria adicional


  • Usei apenas os primeiros 3 segundos como entrada para reduzir o tamanho total dos dados - o projeto original usou 2,5 segundos. Eu gostaria de experimentar gravações em tamanho real;
  • você pode pré-processar os dados: corte o silêncio, normalize o comprimento preenchendo com zeros, etc;
  • tente redes neurais recorrentes para esta tarefa.

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


All Articles