基于CNN和MFCC的语音情感识别

网友投稿 1119 2022-05-29

语音情感识别的主要任务是将蕴含在语音中的情感信息提取出来并识别出其类别。目前对于情感的描述主要有两种方法。第一种是基于离散的情感划分,将人类日常生活中广泛使用的基本情感分为愤怒、开心、兴奋、悲伤、厌恶等;另一种是基于连续维度情感划分,主要通过不同的效价度和激活程度来对不同情感进行区分的。

那么作为一个分类任务,特征选择是最关键的一步。本文中使用的语音特征是梅尔倒谱系数,有关梅尔倒谱系数是什么和怎样提取的知识,可参阅文章《Python语音信号处理》。

本文在一定程度上参考了MITESHPUTHRANNEU/Speech-Emotion-Analyzer这个项目,下面开始介绍如何通过卷积神经网络进行语音情感分析。

神经网络结构

使用到的架构其实还是很简单的,如下

数据集

我使用到是CASIA的语音情感数据库。CASIA汉语情感语料库由中国科学院自动化所(Institute of Automation, Chinese Academy of Sciences)录制,共包括四个专业发音人,六种情绪生气(angry)、高兴(happy)、害怕(fear)、悲伤(sad)、惊讶(surprise)和中性(neutral),共9600句不同发音。其中300句是相同文本的,也即是说对相同的文本赋以不同的情感来阅读,这些语料可以用来对比分析不同情感状态下的声学及韵律表现;另外100句是不同文本的,这些文本从字面意思就可以看出其情感归属,便于录音人更准确地表现出情感。

但是完整的CASIA数据集是收费的,因此我只找到了1200句残缺数据集。我把我找到的数据集放在我的网盘上:https://pan.baidu.com/s/1EsRoKaF17Q_3s2t7OMNibQ。

特征提取

我使用librosa模块进行MFCC的提取,提取代码如下。

%matplotlib inline

import librosa

import matplotlib.pyplot as plt

import numpy as np

path=r'D:\NLP\dataset\语音情感\test.wav'

y,sr = librosa.load(path,sr=None)

def normalizeVoiceLen(y,normalizedLen):

nframes=len(y)

y = np.reshape(y,[nframes,1]).T

#归一化音频长度为2s,32000数据点

if(nframes

res=normalizedLen-nframes

res_data=np.zeros([1,res],dtype=np.float32)

y = np.reshape(y,[nframes,1]).T

y=np.c_[y,res_data]

else:

y=y[:,0:normalizedLen]

return y[0]

def getNearestLen(framelength,sr):

framesize = framelength*sr

#找到与当前framesize最接近的2的正整数次方

nfftdict = {}

lists = [32,64,128,256,512,1024]

for i in lists:

nfftdict[i] = abs(framesize - i)

sortlist = sorted(nfftdict.items(), key=lambda x: x[1])#按与当前framesize差值升序排列

framesize = int(sortlist[0][0])#取最接近当前framesize的那个2的正整数次方值为新的framesize

return framesize

VOICE_LEN=32000

#获得N_FFT的长度

N_FFT=getNearestLen(0.25,sr)

#统一声音范围为前两秒

y=normalizeVoiceLen(y,VOICE_LEN)

print(y.shape)

#提取mfcc特征

mfcc_data=librosa.feature.mfcc(y=y, sr=sr,n_mfcc=13,n_fft=N_FFT,hop_length=int(N_FFT/4))

# 画出特征图,将MFCC可视化。转置矩阵,使得时域是水平的

plt.matshow(mfcc_data)

plt.title('MFCC')

上面代码的作用是加载声音,取声音的前两秒进行情感分析。getNearestLen()函数根据声音的采样率确定一个合适的语音帧长用于傅立叶变换。然后通过librosa.feature.mfcc()函数提取mfcc特征,并将其可视化。

下面的代码将数据集中的mfcc特征提取出来,并对每帧的mfcc取平均,将结果保存为文件。

#提取特征

import os

import pickle

counter=0

fileDirCASIA = r'D:\NLP\dataset\语音情感\CASIA database'

mfccs={}

mfccs['angry']=[]

mfccs['fear']=[]

mfccs['happy']=[]

mfccs['neutral']=[]

mfccs['sad']=[]

mfccs['surprise']=[]

mfccs['disgust']=[]

listdir=os.listdir(fileDirCASIA)

for persondir in listdir:

if(not r'.' in persondir):

emotionDirName=os.path.join(fileDirCASIA,persondir)

emotiondir=os.listdir(emotionDirName)

for ed in emotiondir:

if(not r'.' in ed):

filesDirName=os.path.join(emotionDirName,ed)

files=os.listdir(filesDirName)

for fileName in files:

if(fileName[-3:]=='wav'):

counter+=1

fn=os.path.join(filesDirName,fileName)

print(str(counter)+fn)

y,sr = librosa.load(fn,sr=None)

y=normalizeVoiceLen(y,VOICE_LEN)#归一化长度

mfcc_data=librosa.feature.mfcc(y=y, sr=sr,n_mfcc=13,n_fft=N_FFT,hop_length=int(N_FFT/4))

feature=np.mean(mfcc_data,axis=0)

mfccs[ed].append(feature.tolist())

with open('mfcc_feature_dict.pkl', 'wb') as f:

pickle.dump(mfccs, f)

数据预处理

代码如下:

%matplotlib inline

import pickle

import os

import librosa

import matplotlib.pyplot as plt

import numpy as np

from keras import layers

from keras import models

from keras import optimizers

from keras.utils import to_categorical

#读取特征

mfccs={}

with open('mfcc_feature_dict.pkl', 'rb') as f:

mfccs=pickle.load(f)

#设置标签

emotionDict={}

emotionDict['angry']=0

emotionDict['fear']=1

emotionDict['happy']=2

emotionDict['neutral']=3

emotionDict['sad']=4

emotionDict['surprise']=5

data=[]

labels=[]

data=data+mfccs['angry']

print(len(mfccs['angry']))

for i in range(len(mfccs['angry'])):

labels.append(0)

data=data+mfccs['fear']

print(len(mfccs['fear']))

for i in range(len(mfccs['fear'])):

labels.append(1)

print(len(mfccs['happy']))

data=data+mfccs['happy']

for i in range(len(mfccs['happy'])):

labels.append(2)

print(len(mfccs['neutral']))

data=data+mfccs['neutral']

for i in range(len(mfccs['neutral'])):

labels.append(3)

print(len(mfccs['sad']))

data=data+mfccs['sad']

for i in range(len(mfccs['sad'])):

labels.append(4)

print(len(mfccs['surprise']))

data=data+mfccs['surprise']

for i in range(len(mfccs['surprise'])):

labels.append(5)

print(len(data))

print(len(labels))

#设置数据维度

data=np.array(data)

data=data.reshape((data.shape[0],data.shape[1],1))

labels=np.array(labels)

labels=to_categorical(labels)

#数据标准化

DATA_MEAN=np.mean(data,axis=0)

DATA_STD=np.std(data,axis=0)

data-=DATA_MEAN

data/=DATA_STD

接下来保存好参数,模型预测的时候需要用到。

paraDict={}

paraDict['mean']=DATA_MEAN

paraDict['std']=DATA_STD

paraDict['emotion']=emotionDict

with open('mfcc_model_para_dict.pkl', 'wb') as f:

pickle.dump(paraDict, f)

最后是打乱数据集并划分训练数据和测试数据。

ratioTrain=0.8

numTrain=int(data.shape[0]*ratioTrain)

permutation = np.random.permutation(data.shape[0])

data = data[permutation,:]

labels = labels[permutation,:]

x_train=data[:numTrain]

x_val=data[numTrain:]

y_train=labels[:numTrain]

y_val=labels[numTrain:]

print(x_train.shape)

print(y_train.shape)

print(x_val.shape)

print(y_val.shape)

定义模型

使用keras定义模型,代码如下:

from keras.utils import plot_model

from keras import regularizers

model = models.Sequential()

model.add(layers.Conv1D(256,5,activation='relu',input_shape=(126,1)))

model.add(layers.Conv1D(128,5,padding='same',activation='relu',kernel_regularizer=regularizers.l2(0.001)))

model.add(layers.Dropout(0.2))

model.add(layers.MaxPooling1D(pool_size=(8)))

model.add(layers.Conv1D(128,5,activation='relu',padding='same',kernel_regularizer=regularizers.l2(0.001)))

model.add(layers.Dropout(0.2))

model.add(layers.Conv1D(128,5,activation='relu',padding='same',kernel_regularizer=regularizers.l2(0.001)))

model.add(layers.Dropout(0.2))

model.add(layers.Conv1D(128,5,padding='same',activation='relu',kernel_regularizer=regularizers.l2(0.001)))

model.add(layers.Dropout(0.2))

model.add(layers.MaxPooling1D(pool_size=(3)))

model.add(layers.Conv1D(256,5,padding='same',activation='relu',kernel_regularizer=regularizers.l2(0.001)))

model.add(layers.Dropout(0.2))

model.add(layers.Flatten())

model.add(layers.Dense(6,activation='softmax'))

plot_model(model,to_file='mfcc_model.png',show_shapes=True)

model.summary()

训练模型

编译并训练模型

opt = optimizers.rmsprop(lr=0.0001, decay=1e-6)

model.compile(loss='categorical_crossentropy', optimizer=opt,metrics=['accuracy'])

import keras

callbacks_list=[

keras.callbacks.EarlyStopping(

monitor='acc',

patience=50,

),

keras.callbacks.ModelCheckpoint(

filepath='speechmfcc_model_checkpoint.h5',

monitor='val_loss',

save_best_only=True

),

keras.callbacks.TensorBoard(

log_dir='speechmfcc_train_log'

)

]

history=model.fit(x_train, y_train,

batch_size=16,

epochs=200,

validation_data=(x_val, y_val),

callbacks=callbacks_list)

model.save('speech_mfcc_model.h5')

model.save_weights('speech_mfcc_model_weight.h5')

可视化训练结果:

plt.plot(history.history['loss'])

plt.plot(history.history['val_loss'])

plt.title('model loss')

plt.ylabel('loss')

plt.xlabel('epoch')

plt.legend(['train', 'test'], loc='upper left')

plt.show()

plt.plot(history.history['acc'])

plt.plot(history.history['val_acc'])

plt.title('model acc')

plt.ylabel('acc')

plt.xlabel('epoch')

基于CNN和MFCC的语音情感识别

plt.legend(['train', 'test'], loc='upper left')

plt.show()

从上图中可以发现,模型在训练了60轮后开始过拟合,此时训练精度达到70%,验证精度达到50%。最终训练到200轮后,训练精度达到95%。

测试

最后对训练好的模型进行测试。

#单元测试,载入模型

from keras.models import load_model

import pickle

model=load_model('speech_mfcc_model.h5')

paradict={}

with open('mfcc_model_para_dict.pkl', 'rb') as f:

paradict=pickle.load(f)

DATA_MEAN=paradict['mean']

DATA_STD=paradict['std']

emotionDict=paradict['emotion']

edr = dict([(i, t) for t, i in emotionDict.items()])

import librosa

filePath=r'record1.wav'

y,sr = librosa.load(filePath,sr=None)

y=normalizeVoiceLen(y,VOICE_LEN)#归一化长度

mfcc_data=librosa.feature.mfcc(y=y, sr=sr,n_mfcc=13,n_fft=N_FFT,hop_length=int(N_FFT/4))

feature=np.mean(mfcc_data,axis=0)

feature=feature.reshape((126,1))

feature-=DATA_MEAN

feature/=DATA_STD

feature=feature.reshape((1,126,1))

result=model.predict(feature)

index=np.argmax(result, axis=1)[0]

print(edr[index])

由于数据集太小的原因,效果也就那样。

机器学习 神经网络 语音通话

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【云驻共创】初探数通网络开放可编程
下一篇:一、QEMU环境配置
相关文章