版權聲明:本文為CSDN博主「南方朗郎」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sunshuai_coder/article/details/83658625
僅做筆記,未實驗
市面上語音識別技術原理已經有很多很多了,然而很多程序員兄弟們想研究的時候卻看的頭大,一堆的什么轉mfcc,然后獲取音素啥的,對於非專業音頻研究者或非科班出生的程序員來說,完全跟天書一樣。
最近在研究相關的實現,並且學習了keras和tensorflow等。用keras做了幾個項目之后,開始着手研究語音識別的功能,在網上下載了一下語音的訓練文件,已上傳到了百度雲盤:https://pan.baidu.com/s/1Au85kI_oeDjode2hWumUvQ
目錄如下,文件夾名就是里面的語音的標簽,語音由很多不同年齡性別的人發音收集而來

拿到一個語音文件之后需要先轉mfcc,這個操作很簡單,不需要什么高深的內功。用python寫一段函數專門用來獲取語音文件的fmcc值。
def get_wav_mfcc(wav_path): f = wave.open(wav_path,'rb') params = f.getparams() # print("params:",params) nchannels, sampwidth, framerate, nframes = params[:4] strData = f.readframes(nframes)#讀取音頻,字符串格式 waveData = np.fromstring(strData,dtype=np.int16)#將字符串轉化為int waveData = waveData*1.0/(max(abs(waveData)))#wave幅值歸一化 waveData = np.reshape(waveData,[nframes,nchannels]).T f.close() ### 對音頻數據進行長度大小的切割,保證每一個的長度都是一樣的 #【因為訓練文件全部是1秒鍾長度,16000幀的,所以這里需要把每個語音文件的長度處理成一樣的】 data = list(np.array(waveData[0])) # print(len(data)) while len(data)>16000: del data[len(waveData[0])-1] del data[0] # print(len(data)) while len(data)<16000: data.append(0) # print(len(data)) data=np.array(data) # 平方之后,開平方,取正數,值的范圍在 0-1 之間 data = data ** 2 data = data ** 0.5 return dat
參數為單個文件在磁盤的位置,mfcc是一堆的正數和負數組成的數組:
為了在訓練的時候避免損失函數應為負數導致輸出結果相差太大,需要把原始的mfcc全部轉為正數,直接平方后在開方就是正值了。

我們可以把每個音頻的mfcc值當做對應的特征向量,然后進行訓練,我這里為了測試速度,取了seven 和 stop 兩個語音類別來進行訓練和識別,每個大概2700多個文件。並且分別從兩個文件夾中剪切出來100個當做測試集,並每樣拿出5個當做后面的試驗集。
test1 中放置的是 100個 seven 語音,test2 中放置的是100個 stop 語音,trunk中放的是5個seven 和5個stop 語音。
如圖1開頭的都是seven , 2開頭的都是stop 。
訓練之前需要先讀取數據創建數據集和標簽集:
# 加載數據集 和 標簽[並返回標簽集的處理結果] def create_datasets(): wavs=[] labels=[] # labels 和 testlabels 這里面存的值都是對應標簽的下標,下標對應的名字在 labsInd 和 testlabsInd 中 testwavs=[] testlabels=[] labsInd=[] ## 訓練集標簽的名字 0:seven 1:stop testlabsInd=[] ## 測試集標簽的名字 0:seven 1:stop # 現在為了測試方便和快速直接寫死,后面需要改成自動掃描文件夾和標簽的形式 #加載seven訓練集 path="D:\\wav\\seven\\" files = os.listdir(path) for i in files: # print(i) waveData = get_wav_mfcc(path+i) # print(waveData) wavs.append(waveData) if ("seven" in labsInd)==False: labsInd.append("seven") labels.append(labsInd.index("seven")) #加載stop訓練集 path="D:\\wav\\stop\\" files = os.listdir(path) for i in files: # print(i) waveData = get_wav_mfcc(path+i) wavs.append(waveData) if ("stop" in labsInd)==False: labsInd.append("stop") labels.append(labsInd.index("stop")) #加載seven測試集 path="D:\\wav\\test1\\" files = os.listdir(path) for i in files: # print(i) waveData = get_wav_mfcc(path+i) testwavs.append(waveData) if ("seven" in testlabsInd)==False: testlabsInd.append("seven") testlabels.append(testlabsInd.index("seven")) #加載stop測試集 path="D:\\wav\\test2\\" files = os.listdir(path) for i in files: # print(i) waveData = get_wav_mfcc(path+i) testwavs.append(waveData) if ("stop" in testlabsInd)==False: testlabsInd.append("stop") testlabels.append(testlabsInd.index("stop")) wavs=np.array(wavs) labels=np.array(labels) testwavs=np.array(testwavs) testlabels=np.array(testlabels) return (wavs,labels),(testwavs,testlabels),(labsInd,testlabsInd)
拿到數據集之后就可以開始進行神經網絡的訓練了,keras提供了很多封裝好的可以直接使用的神經網絡,我們先建立神經網絡模型
# 構建一個4層的模型 model = Sequential() model.add(Dense(512, activation='relu',input_shape=(16000,))) # 音頻為16000幀的數據,這里的維度就是16000,激活函數直接用常用的relu model.add(Dense(256, activation='relu')) model.add(Dense(64, activation='relu')) model.add(Dense(2, activation='softmax')) # 因為只有兩個類別的語音,最后輸出應該就是2個分類的結果 # [編譯模型] 配置模型,損失函數采用交叉熵,優化采用Adadelta,將識別准確率作為模型評估 model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=['accuracy']) # validation_data為驗證集 model.fit(wavs, labels, batch_size=124, epochs=5, verbose=1, validation_data=(testwavs, testlabels)) ## 進行5輪訓練,每個批次124個 # 開始評估模型效果 # verbose=0為不輸出日志信息 score = model.evaluate(testwavs, testlabels, verbose=0) print('Test loss:', score[0]) print('Test accuracy:', score[1]) # 准確度
訓練之后的結果:

兩個類型的文件一個4500多個,我本機使用CPU訓練大概用時20多秒。
最后顯示結果准確率為0.9050.也就是90.5%的准確率,這里可以加大數據集的數量和調參來加大准確率。
最后保存模型到文件:
model.save('asr_model_weights.h5') # 保存訓練模型
1
保存之后會在文件夾中生成一個文件【95M】:
先在訓練的模型已經有了,我們開始使用trunk中的文件進行試驗:
先加載之前訓練的模型:
model = load_model('asr_model_weights.h5') # 加載訓練模型
1
然后獲得當前需要試驗的文件的mfcc。並且將數據封裝成和訓練時一樣的維度。並且使用模型的predict函數輸出結果:
wavs=[]
wavs.append(get_wav_mfcc("D:\\wav\\trunk\\2c.wav")) # 使用某一個文件
X=np.array(wavs)
print(X.shape)
result=model.predict(X[0:1])[0] # 識別出第一張圖的結果,多張圖的時候,把后面的[0] 去掉,返回的就是多張圖結果
print("識別結果",result)
1
2
3
4
5
6
結果輸出:
可以看出結果是一個2個數的數組,里面返回的對應類別相似度,也就是說哪一個下標的值最大,就跟那個下標對應的標簽最相似。
之前訓練的時候,標簽的集是:[seven , stop]
所以如圖下標1的值達到了89.9%的相似度。
# 因為在訓練的時候,標簽集的名字 為: 0:seven 1:stop 0 和 1 是下標
name = ["seven","stop"] # 創建一個跟訓練時一樣的標簽集
ind=0 # 結果中最大的一個數
for i in range(len(result)):
if result[i] > result[ind]:
ind=1
print("識別的語音結果是:",name[ind])
1
2
3
4
5
6
7
我們把試驗文件換成 1b.wav
wavs.append(get_wav_mfcc("D:\\wav\\trunk\\1b.wav"))
1
結果輸出:
本機的試驗的識別速度在2秒內。
本文相關的代碼已上傳github:https://github.com/BenShuai/kerasTfPoj/tree/master/kerasTfPoj/ASR
