先前的文章《三個小白是如何在三個月內搭一個基於kaldi的嵌入式在線語音識別系統的 》說咱們花了不到三個月的時間搭了一個基於kaldi的嵌入式語音識別系統,不過它是基於傳統的GMM-HMM的,是給咱們練手用的,經過搭這個系統咱們累積了必定的語音識別領域的經驗,接下來咱們就要考慮作什麼形態的產品了。語音識別能夠分大詞彙量連續語音識別(Large Vocabulary Continuous Speech Recognition, LVCSR)和關鍵詞識別(Keyword Spotting, KWS)。LVCSR 要求很強的計算能力,這類方案主要在服務器上實現。KWS只要識別出關鍵詞便可,對算力要求不是很高,能夠在終端芯片上實現。因爲咱們公司的芯片主要用於終端產品上,算力不是很強,所以咱們就準備作關鍵詞識別。對於關鍵詞識別又可分爲幾種應用場景。一是音頻文獻中關鍵詞檢索,用於快速找到音頻文獻中須要的內容。二是語音喚醒詞識別,用於喚醒終端設備,讓其工做(不喚醒時設備處於睡眠狀態)。三是命令詞識別,用於語音命令控制的場景,終端設備收到某個命令詞後就執行相應的操做。好比智能家居場景中,當用戶說出「打開空調」被識別到後就把空調打開了。通過討論後咱們決定作中文命令詞識別,暫時把應用場景定在智能家居上,並定義了幾個命令詞,例如「打開空調」、「關閉空調」等。後面若是要作其餘場景,只要改變命令詞從新訓練模型便可,代碼部分是不須要改動的。html
先前的系統是基於GMM-HMM的,已out,咱們想用深度神經網絡(Deep Neural Networks,DNN)來作。kaldi中的DNN分爲nnet一、nnet二、nnet3三種。nnet1是由Karel寫的,使用的是DNN-HMM架構,這裏DNN說白了就是MLP(MultiLayer Perceptron,多層感知機)。nnet2和nnet3是由Daniel寫的,nnet2一樣使用的是DNN-HMM架構,nnet3還包含了其餘網絡架構,有CNN/RNN/LSTM等。nnet1沒有online decoder,nnet2和nnet3則有online decoder,比較下來咱們決定用nnet2。DNN-HMM是基於GMM-HMM的,是用DNN替代GMM,於是咱們前面的工做還能夠用得上,因此此次的工做主要分兩部分,一是模型訓練,二是nnet2 online decoder相關代碼的移植。上次負責模型訓練的同窗因爲忙其餘工做,此次模型訓練就由我來作。nnet2 online decoder代碼移植由另一個同窗負責。同時咱們在前處理中把VAD(Voice Activity Detection,語音活動檢測)加上,只把檢測到語音的部分送到後面模塊處理,這樣下降了功耗。web
此次我來弄模型訓練。因爲是新手,先得學習怎麼訓練模型,而後根據新的需求訓練出新的模型。通過半個多月的學習,大致上搞清楚了模型訓練的步驟。首先是數據準備,包括準備語料、字典和語言模型等。對於語料,能夠花錢買,也能夠本身錄,要將其分紅訓練集、測試集和交叉驗證集。字典表示一個詞是由哪些音素組成的。語言模型經過專業的工具(如srilm的ngram-count)生成。而後處理語料獲得scp/spk2utt/utt2spk等文件,處理字典、語言模型等獲得FST等文件。再就是作MFCC獲得每一幀的特徵向量,最後進行各個階段的訓練獲得相應的模型文件(final.mdl)。主要的階段有單音素訓練(mono)、三音素訓練(tri1)、LDA_MLLT訓練(tri2b)、SAT訓練(tri3b)、quick訓練(tri4b),每一步訓練都是基於上一步訓練解碼後對齊的結果。上面這幾步是GMM-HMM的訓練,若是要作深度神經網絡(DNN)的訓練,則還要把DNN訓練這步加上去。咱們此次作的是中文命令詞的識別,先定好命令詞,而後從thchs30裏找到這些詞的聲韻母的寫法,須要注意的是thchs30裏聲韻母的寫法跟一般拼音的寫法有些不同,再根據這些命令詞用工具把語言模型生成。咱們的語料是本身錄的,發動了周圍的同窗幫忙錄,有男聲和女聲。這些都準備好後先處理語料獲得scp等文件,再根據字典、語言模型等生成fst等文件,最後就開始各個階段的訓練了。先訓練傳統的GMM-HMM,不斷的調整參數,直至WER有一個不錯的值。GMM-HMM模型訓練好後我把模型load進咱們先前搭好的demo,實測下來效果還不錯。這說明GMM-HMM的模型訓練是OK的,接下來就要開始訓練DNN(nnet2)的模型了。服務器
我沒有馬上去訓練nnet2的模型,而是再去學習了下DNN的基礎知識(之前簡單學習過,一直沒用到,理解的不深,有些已經忘記了),重點關注了梯度降低法和網絡參數怎麼更新,並寫成了兩篇博客:《機器學習中梯度降低法原理及用其解決線性迴歸問題的C語言實現 》& 《kaldi中CD-DNN-HMM網絡參數更新公式手寫推導》。接下來就去看怎麼訓練nnet2的模型了。先到kaldi的官方網站上看訓練nnet2的相關內容,大體明白就開始基於咱們本身錄製的語料庫調試了。nnet2的訓練腳本較亂,一個腳本下有多個版本(nnet4a / nnet4b / nnet4c / nnet4d / nnet5c / nnet5d)。我剛開始不清楚孰優孰劣,把每一個都調通。在網上搜索調查了一下,kaldi的做者Daniel Povey在一個論壇裏說隱藏層用p-norm作激活函數的性能更好一些。因而決定用推薦的nnet4d(激活函數就是用的p-norm)來繼續訓練。通過屢次參數tuning後獲得了一個WER相對不錯的模型。網絡
在我訓練DNN模型的同時,負責代碼移植的同窗也在把nnet2 online decoder的相關代碼往咱們平臺上移植,套路跟我先前的同樣。同時kaldi也提供了一個應用程序(代碼見online2-wav-nnet2-latgen-faster.cc),對WAV文件作nnet2的online decoder。咱們先要把模型在這個應用程序上調通(一般kaldi代碼是沒有問題的,咱們在這個應用程序裏調通就說明模型訓練是沒有問題的,後面在咱們本身的平臺上去調試就有基準可參考了)。當咱們把模型放進應用程序裏運行,報了「Feature dimension is 113 but network expects 40」的錯。調查下來發現kaldi應用程序要求MFCC是13維的,且有i-vector的功能(100維的),這樣加起來就是113維的。而我訓練的nnet2模型是基於tri3b的(DNN-HMM要利用GMM-HMM的訓練解碼對齊結果,對齊的越好DNN模型的識別率就越高),13維MFCC+26維delta+1維pitch,共40維,因此模型輸入是40維的。討論後爲了下降複雜度,咱們決定先把應用程序中的i-vector功能給去掉,同時我基於單音素的模型(13維MFCC)從新訓練nnet2模型。基於新的模型運行應用程序不報錯了,可是識別率很低。咱們一時沒有了方向,作了幾回嘗試仍是識別率很低。後來咱們開始比較個人訓練處理流程和應用程序裏的處理流程,發現我訓練時用了CMVN(之前作GMM-HMM訓練時就有),而應用程序代碼處理流程裏沒有。因而在代碼裏把CMVN的處理加上,再去運行應用程序,識別率顯著提高了。咱們長舒了一口氣,由於咱們知道這個問題被解決了,從而內心有底了。再把應用程序的機制移植到咱們平臺上,同時另一個同窗也幫忙把webRTC的VAD也移植進來,有語音纔會把那段語音日後面模塊送,這跟應用程序中讀WAV文件很相似,因此處理起來機制就很相似。用了兩三天就把包含VAD、前處理(ANS、AGC)和nnet2 online decoder的系統聯調好了。測試了一下,被訓練過的人說命令詞識別率大於90%,而未被訓練過的識別率大於80%。可是有個嚴重的問題,就是集外詞(out-of-vocabulary,OOV,就是命令詞之外的詞)都會被識別成一個集內詞(命令詞),即集外詞沒有被拒識。架構
針對這個問題,我查了些資料並靜下心來想了想,在當前架構下說出一個詞,只會以WFST中路徑最短的一個做爲識別結果輸出,因此纔會有集外詞被識別成了集內詞。咱們的系統目前只能識別那些指定的關鍵詞,可是還不具有關鍵詞識別系統的任何特色。我在前面的文章《語音識別中喚醒技術調研》 中曾總結過實現關鍵詞識別的三種方法,一是基於LVCSR來作,在終端芯片上不太可行。二是keyword/filler方法,說白了就是把一些垃圾詞也放進模型裏去訓練(大意以下圖),識別時說集外詞很大多是垃圾詞做爲識別結果而不輸出從而實現集外詞拒識,在終端芯片上可行。三是純深度學習方法(相對於3,1和2是傳統方法)。咱們的架構是DNN-HMM,雖然也用了深度神經網絡,但DNN是用來替代GMM的,本質上仍是一種傳統方法,因此我決定把keyword/filler方法用到咱們的系統上。先從thchs30裏找到幾百個集外詞(垃圾詞),而後根據這些詞錄製語料並放進模型裏訓練。用新生成的模型去測試,集外詞拒識率大幅提升,可是一些狀況下集外詞仍是被識別爲集內詞。例如關鍵詞是「深度科技」,如說成「深度科學」就有可能被識別成「深度科技」。針對這種狀況,我把相關的詞(經常使用的)都放進垃圾詞裏,如「深度科學」、「深度科普」、「深度科研」等,再去測試這些詞就不會被識別成集內詞了。再例如一些詞發音跟集內詞發音很類似,好比說「深度科器」會被識別成「深度科技」,我試了試百度的小度音箱,把喚醒詞「小度小度」說成「角度角度」或者「巧度巧度」,小度音箱依舊會被喚醒。市面上已大規模商用的產品都有這個現象,我也就沒管它。與此同時,我還在看一些集外詞拒識的相關論文,發現好多都結合用置信度(conference measure)來解決這個問題,其中中科院自動化所的一篇博士論文《語音識別中的置信度研究與應用》講的比較好。看後我明白了要想在傳統架構下把集外詞拒識問題解決好,一是要用上keyword/filler方法,二是要用上置信度。目前我是沒有能力根據論文去實現置信度的,也沒有找到開源的關於置信度的實現,因而在kaldi WFST lattice代碼裏想辦法。經過大量的集內詞和集外詞的測試我發現能夠用一些變量去作判斷,可是有可能集外詞拒識率提升了,集內詞識別率也降低了(用置信度也會有一樣的問題,這個度很難掌控。這塊內容也是挺難的,尤爲對我一個作工程的且作語音識別沒多久的來講) 。通過一段時間的努力後集內詞的識別率和集外詞的拒識率都有了一個至關的水準,但離商用還有一段距離,後面還有不少事情要作,好比加大語料(咱們目前只有一個幾十人的語料庫,沒有好幾百人而且覆蓋男女以及不一樣年齡段的語料庫是不能商用的),後面會愈來愈難!機器學習
立刻就2019年過去了。回首這一年,三分之二的時間都用來作語音識別了,全是摸索着向前走,有痛苦,也有歡樂,從最初的什麼都不懂到如今的懂一點。但願2020年本身在這個領域進步再大一點。函數
原文出處:https://www.cnblogs.com/talkaudiodev/p/12085248.html工具