歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~
一般沒有網絡時,語音識別是這樣的
▽
而同等環境下,嵌入式語音識別,是這樣的
▽
不僅可以幫您邊說邊識、出口成章,有個性化名字的時候也難不倒它。
這就是嵌入式語音識別的魅力。
本文將從微信智聆的嵌入式語音識別引擎的實現和優化,
介紹嵌入式語音識別的技術選型。
01
語音識別,大體是這麼來的
語音識別,能讓機器“聽懂”人類的語音,把說話內容識別爲對應文本。
開始於上世紀50年代
從最初的小詞量孤立識別系統
到如今的大詞量連續識別系統
語音識別系統的發展,性能得到顯著的提升,主要得利於以下幾個方面:
大數據時代的到來
深度神經網絡在語音識別中的應用
GPU硬件的發展
因此,語音識別逐步走向實用化和產品化
語音輸入法,語音智能助手,語音車載交互系統……
可以說,語音識別是人類征服人工智能的前沿陣地,是目前機器翻譯、自然語言理解、人機交互等的奠基石。
然而,性能的提升基於服務端CPU/GPU高計算能力和大內存,沒有網絡的時候將無法享受語音識別的便利。
爲了解決這個問題,微信智聆針對嵌入式語音識別進行研發。嵌入式語音識別,也稱爲嵌入式LVCSR(或離線LVCSR,Large Vocabulary Continuous Speech Recognition),指全程運行在手機端的語音識別,而不依賴於服務端強大的計算能力。
在一些網絡不穩的特殊場景(車載、境外等等),嵌入式語音識別可“曲線救國”。
那麼,實現一個嵌入式語音識別,存在哪些難點呢?
語音識別的基本流程
主流的語音識別算法當中,包括聲學和語言兩大模型。聲學模型得利於近十年深度學習的發展,從GMM(高斯模型)到DNN(深度神經網絡),再從DNN到LSTM RNN(循環神經網絡),識別率不斷提升的同時,計算量也不斷地飛漲。而語言模型常用的n-gram算法,階數越高性能越好,常用的模型多達數十G的內存。
所以綜合起來,嵌入式語音識別有以下幾個難點:
\1. 深度學習運算複雜,僅僅對模型進行裁剪性能損失大,需尋找挽回性能的方法;
\2. 裁剪模型不可避免,在模型訓練環節如何避免小模型訓練易陷入局部最優的問題;
\3. 如何計算的更快,滿足嵌入式的CPU環境;
\4. 如何組織語言模型存儲,能在有限的內存下存儲更多的語言信息。
本文將以語音識別的技術原理出發,淺談微信智聆嵌入式的實現技術。
內容將分爲四個部分:
\1. 回顧語音識別的基本概念;
\2. 簡單介紹在速度和內存優化上我們做的部分工作,側重於工程應用實現;
\3. 說一說爲了更好的性能我們做了哪些事,側重於算法研究介紹;
\4. 我們進行實驗對比,最後我們進行總結。
02
語音識別的各個組件
語音識別“黑盒”
語音識別從輸入錄音輸出文字,黑盒子處理經過特徵提取、聲學模型、發音詞典、語言模型等流程,筆者認爲可以把語音識別比作一臺計算機。
特徵提取相當於是路由器,作爲領頭羊給後續環節提供源源不斷的數據來源。
聲學模型相當於語音識別的心臟——CPU,他將最直接影響着識別的準確性能。
語言模型相當於語音識別的硬盤,大量的詞彙組合信息存儲於此。
發音詞典相當於內存條,能有效組織聲學模型與語言模型的關係。
除此之外,語音識別包含一個×××,他如同計算機的操作系統,有效地組織着各個環節。
接下來,我們基於每個“部件”簡介其基本概念,以便後續介紹如何在這些“部件”上對嵌入式ASR工作的展開。
1.特徵提取
音識別特徵提取包括預加重、分幀、加窗、FFT(Fast Fourier Transform)等一系列流程,常用的特徵有PLP、MFCC、FBANK等等。一般來說,語音識別把一秒語音分成100段(之間有互相重疊),而特徵提取能把每段語音數據轉化爲一個向量(常見的有39維MFCC特徵)。
爲了關聯上下文信息,特徵作爲聲學模型的輸入時,常將相鄰幀拼湊一起。比如以39維特徵爲例,前後各取5幀信息,那麼總共有11幀,輸入的向量維度爲11*39=429。一般地,語音識別的性能與取幀寬度是正相關的。
作爲語音識別的路由器,特徵提取環節的運算量並不大。然而其作爲聲學模型拓撲結構的輸入,間接影響着深度學習的運算量,是我們在嵌入式ASR中要考慮的問題。
2.幀率抖動
5s統計一次直播流視頻幀率,1min計算一次幀率方差,方差過大,視爲推流幀率抖動.
3.聲學模型(acoustic model)
聲學模型作爲語音識別的CPU,其重要性不言自喻。
一般地,它佔據着語音識別大部分的運算開銷,直接影響着語音識別系統的性能。傳統語音識別系統普遍基於GMM-HMM的聲學模型,其中GMM對語音聲學特徵的分佈進行建模,HMM則用於對語音信號的時序性進行建模。
2006年深度學習興起以後,深度神經網絡(DNN,Deep Neural Networks)被應用於聲學模型。
近十多年,聲學模型的上深度學習的發展一路高歌,各種CNN、RNN、TDNN的拓撲結構如雨後春筍一一冒出,關於深度學習在聲學模型的更多介紹見文。
對於嵌入式LVCSR來說,選擇合適的DNN拓撲結構,並用合理的優化在手機實現結構的運算,是聲學模型在其的核心訴求。
4.語言模型(language model)
語言模型,NLP從業者相對更爲熟悉。在語音識別裏,語言模型用來評估一個句子(即圖2的詞語序列)出現的概率高低。
在語言模型的實現算法中,最常見的爲n-gram模型(n-gram models),利用當前詞前面的n個詞來計算其概率,是一個上下文有關模型。幾年來,神經語言模型(Neural language models)使用詞彙Embedding來預測,也得到廣泛的發展與應用。
在嵌入式ASR中,由於計算資源要留予聲學模型,所以語言模型採用的依舊是n-gram的思想。那麼在有限的內存中,如何最大化存儲語言模型,是嵌入式ASR要解決的問題。
5.發音詞典
發音詞典,是語音識別的內存條。內存能將硬盤的數據讀入,並使用cpu進行運算。同樣的,發音詞典,能將語言模型的詞條序列轉化爲音素序列,並用聲學模型進行分數評估運算。
發音詞典是連接聲學模型和語言模型的橋樑,他的大小直接影響聲學模型和語言模型的發揮空間。
在嵌入式ASR中,發音詞典的大小,與語言模型的規模互相共鳴,所以要解決的問題可以與語言模型歸爲一談。
6.×××
×××,估計這個詞的來自英文decoder的直譯,筆者認爲更恰當的名字應稱爲識別器。之所以叫×××,還有另外一個比較形象的原因。以16bit語音數據爲例,計算機的存儲是一堆我們看不懂的short類型數字,如同密碼一般。語音識別能破解這些密碼,將明文展示在我們面前。
所以通俗來講,×××就是將語音識別各個流程串聯的代碼工程。一般雲端採用與WFST(帶權優有限狀態自動機)搭檔的靜態×××,可以更方便地綜合處理語音識別的各個環節。而嵌入式爲了節省語言模型的內存開支,採用特定的動態×××。
03
開始優化這些組件——速度和內存優化
爲了優化這些“部件”佔用的時間與內存,我們做了一系列工作:
neon計算優化,奇異值分解優化,哈夫曼編碼優化。
1.neon優化聲學模型計算
neon的計算優化,已是廣大工程師們的老生常談,機器學習相關的T族們更是耳熟能詳。在嵌入式ASR引擎中,我們對核心高頻運算的函數進行了neon優化,採用了彙編語言進行編寫,最終有效提高了25%的計算速度。
接下來,本文現以實現char類型向量乘的介紹優化的實現,分三版本來介紹:
A. 優化前的樸素版
B. neon c版
C. neon彙編版
首先,我們將要實現的函數是:
/**
* 實現兩個char類型向量乘
* start_a: 向量A
* start_b: 向量B
* cnt:向量元素個數
* result:向量乘返回存儲變量
*/
void vector_product_neon(const char * start_a, const char * start_b, int & result,
const int cnt);
A. 優化前樸素版
void vector_product_neon(const char * start_a, const char * start_b, int & result,
const int cnt) {
int res = 0;
for(int j = 0; j < cnt; j++) {
res += int(*start_a) * int(*start_b);
start_a++;
start_b++;
}
result = res;
}
B. neon c版
Neon寄存器能實現128位空間的並行運算,對於char類型的向量乘而言,兩兩相乘的結果在short類型範圍內,故可8個爲一組實現。以下代碼,8個元素一組,一次循環處理兩組。在我們的深度學習運算中,隱層的向量長度保證爲16倍數,實現代碼如下:
void vector_product_neon(const char * start_a, const char * start_b, int & result,
const int cnt) {
int res = 0;
int32x4_t neon_sum = vdupq_n_s32(0);
int8x8_t neon_vector1;
int8x8_t neon_vector2;
for(int j = 0; j < cnt / 16; j++) {
neon_vector1 = vld1_s8((char *)start_a);
neon_vector2 = vld1_s8((char *)start_b);
int16x8_t neon_tmp = vmull_s8(neon_vector1, neon_vector2);
start_a += 8;
start_b += 8;
neon_vector1 = vld1_s8((char *)start_a);
neon_vector2 = vld1_s8((char *)start_b);
neon_tmp = vmlal_s8(neon_tmp, neon_vector1, neon_vector2);
neon_sum = vaddw_s16(neon_sum, vget_low_s16(neon_tmp));
neon_sum = vaddw_s16(neon_sum, vget_high_s16(neon_tmp));
start_a += 8;
start_b += 8;
}
for(int j = 0; j < 4; j++)
res += vgetq_lane_s32(neon_sum, j);
result = res;
}
C. neon彙編版
彙編版本的neon代碼編寫與維護成本高,但速度比c版本更快。秉着精益求精的態度,我們實現了彙編代碼:
void vector_product_neon(const char * start_a, const char * start_b, int & result,
const int cnt) {
int res = 0;
asm volatile(
"vmov.s32 q2, #0" "\n\t"
"lsr %[cnt], %[cnt], #4" "\n\t"
".charloop:"
"vld1.s8 {d0}, [%[vec1]]!" "\n\t"
"vld1.s8 {d1}, [%[vec2]]!" "\n\t"
"vmull.s8 q1, d0, d1" "\n\t"
"vld1.s8 {d0}, [%[vec1]]!" "\n\t"
"vld1.s8 {d1}, [%[vec2]]!" "\n\t"
"vmlal.s8 q1, d0, d1" "\n\t"
"vaddw.s16 q2, q2, d2" "\n\t"
"vaddw.s16 q2, q2, d3" "\n\t"
"subs %[cnt], %[cnt], #1" "\n\t"
"bne .charloop" "\n\t"
"vadd.s32 d4, d4, d5" "\n\t"
"vmov.s32 r4, d4[0]" "\n\t"
"add %[sum], r4" "\n\t"
"vmov.s32 r4, d4[1]" "\n\t"
"add %[sum], r4" "\n\t"
: [sum]"+r"(res)
: [vec1]"r"(start_a),
[vec2]"r"(start_b),
[cnt]"r"(cnt)
: "r4", "cc", "memory"
);
result = res;
}
2.奇異值分解優化聲學模型運算量
爲了降低乘加運算的次數,我們決定利用奇異值分解來對DNN進行重構,通過裁剪掉最小的奇異值及其相對應的特徵向量,來達到減少乘加運算數量的目標。奇異值分解將任意矩陣Wm×n(不失一般性,假設m≤n)分解成3個矩陣相乘:Wm×n =Um×mΣm×mVm×n。
其中:Σm×m 爲對角矩陣,即Σm×m =diag(σ1,σ2,…,σm),它的對角元素即爲Wm×n的奇異值;Um×m 爲單位正交矩陣,其列向量爲與奇異值對應的特徵向量;Vm×n中的行向量是互相單位正交的,也是與奇異值對應的特徵向量。
下圖是我們以DNN模型其中一層網絡作爲例子,闡述我們在重構DNN中的模型轉化,其中原始DNN模型爲圖中上方子圖(a),新重構DNN模型在下方子圖(b)所示:
a:原始DNN模型的一層結構
(b)新DNN模型的兩層對應結構
利用SVD對聲學模型計算量優化大致分爲3個步驟
(1)訓練初始DNN神經網絡;
(2)對權重矩陣進行奇異值分解;
(3)對重構後的DNN模型重新訓練。
通過基於SVD的模型壓縮方法,我們可以在稍微降低模型性能的前提下,將聲學模型計算量減少30%。
3.哈夫曼優化語言模型內存
一般地,n-gram語言模型可以用一張有向圖存儲便於介紹存儲空間以及快速查詢,這張圖上的邊要存儲詞彙信息。我們知道以漢語爲例,不同詞語的出現頻率相差極大,如果所有詞彙的label id都用int類型存儲,那空間的利用率較爲低下。
以“我”“要”“吃飯”爲例,假設語言模型的詞彙頻率:我>要>吃飯,那麼我們可以構建圖3的哈夫曼樹,則四個字使用的編號碼分別爲:我(0),要(10),吃飯(110)
二叉哈夫曼
十六叉哈夫曼樹
然而,採用圖4的二叉樹數據結構,一次只能處理1bit效率較低,也不便於工程實現。所以在工程實現的時候,我們按4bits編碼爲單位,對詞彙進行分類存儲處理。
我們使用一棵16叉樹的哈夫曼樹結構,每層樹節點的編號總量是上一層的16倍。樹中的所有編號爲0的子節點用於儲存詞彙,越高頻的詞彙儲存於深度越低的節點位置。
通過哈夫曼優化,我們的引擎最終成功降低了25%的內存佔用,同時引擎是資源文件也得到50%左右的優化。
04
識別性能的優化
1.基於TDNN優化聲學模型
近幾年,TDNN(Time-Delay Neural Network,延時神經網絡)【5】的拓撲結構被應用於語音識別。事實上,該結構於1989年被提出,隨着近幾年技術的發展,重新進入了大家的視線。
DNN結構
DNN的拓撲網絡僅針對單一特徵時刻點建模。
TDNN結構
TDNN的隱層結構,對語音特徵多個時刻點進行抽象建模,擁有更強的建模能力。除此之外,TDNN結構的多時刻建模參數是共享的(圖中紅、綠、紫用的是同樣的拓撲矩陣傳播)。
所以,TDNN雖然在訓練的時候,比DNN需要更多的BP運算。而在語音識別時,由於參數共享的原因,隱層的計算結果可以複用,每一幀僅需對所有參數進行一次運算,大大節省了計算量。最後,我們基於TDNN結構,引擎在保持計算量一致的前提下,識別率提升了相對20%的準確率。
2.基於多任務訓練優化性能
採用多任務聯合訓練,能有效提高聲學訓練的魯棒性,避免過早陷入局部最優。在嵌入式的模型中,模型輸出目標比較少,訓練容易陷入局部最優。所以我們,同時用目標多的大模型聯合訓練,讓訓練的隱層結構更爲魯棒。
聲學模型多任務訓練
在訓練的時候,我們網絡同時擁有輸出1和輸出2兩個,多任務訓練時,逆向迭代需要殘差協調,我們採用以下公式分配殘差,其中λ權衡兩個模型的訓練權重:
最終我們採用多任務訓練優化性能,對語音識別率帶來了一定提升,接下來所有的性能提升我們將在下一章結實驗給出。
3.基於區分性訓練(Discriminative Training)性能優化
聲學模型區分性訓練是針對MLE訓練的不足而提出的。DT訓練通常定義一個目標函數(Objective Function),或者說是準則函數(Criterion Function),來近似一個與分類代價相關的度量。通過區分性訓練,我們可以從一定程度上弱化模型假設錯誤所帶來的影響。
同時,由於區分性訓練致力於優化與識別效果好壞相關的度量,因此也就爲提高識別器性能提供了更直接的途徑。形象的說,MLE訓練告訴模型“這是椅子,那是桌子”,而區分性訓練則告訴模型“這是桌子而不是椅子,那是椅子而不是桌子”。MLE訓練更重視調整模型參數以反映訓練數據的概率分佈,而區分性訓練則更重視調整模型之間的分類面,以更好的根據設定的準則對訓練數據進行分類。
DT的目標函數是這樣的:
對DT的目標函數用一次貝葉斯公司可以得到:
分子正是ML的目標函數;而分母則是所有文本(包括訓練文本和它的所有競爭者)產生訓練語音的概率的(按語言模型加權的)和。由於分母上要枚舉所有可能的文本並不現實,所以實際中,一般是用一個已有的ML訓練的語音系別系統對訓練語音做一次解碼,得到n-best list或lattice,用這裏面的文本來近似分母上的求和。n-best list或lattice中包含了訓練文本的足夠接近的競爭者。
4.基於互信息的新詞發現
對於語音識別系統來說,語言模型對結果影響至關重要;而對於語言模型來講,語言模型的詞典是關鍵。一個好的分詞詞典,對於得到魯棒的語言模型是至關重要的,如果才能選出合理正確的“詞”所組成的詞典,首先最關鍵的一步就是基於現有語料的新詞挖掘。
由於嵌入式系統性能有限,因此選擇合適大小的詞表,並對語言模型進行適當剪枝頭,可以壓縮安裝包大小、限制內存消耗、提高識別性能。壓縮詞表可以篩選高頻詞,並通過一定的模型來識別篩掉截斷詞,如“新功”、“嘉年”、“扛生”、“鵝卵”、“劉德”、“利亞”等半個高頻詞。
一個簡單而又有效的新詞發現和篩選方案可以採用互信息和左右信息熵的計算方法,計算二元的信息熵的分數由三個對應部分組成: 1)點間互信息:點間互信息越高,內部聚合程度越高; 2)兩個單詞片段信息熵 h_r_l 和 h_l_r 的最小值:這個數值越大,則意味着兩個單詞一起出現的可能性越小; 3)單詞左右信息熵的最小值:這個數值越大就表示着候選詞出現的語境越多,越有可能成詞因此,分數越高表示成詞的可能性越大。
計算完二元的信息熵後,可以依次計算三元、四元的信息熵,三元的新詞發現和篩選是將二元替換原有的兩個單字做爲一個單字繼續進行,候選集可以取左信息熵或者右信息熵爲0的候選集,四元、五元以此類推。 另外,語言模型直接關係到識別結果輸出,因此選與應用場景相對應的語料進行統計尤爲重要。
05
實驗對比
第二章節和第三章節,介紹了一些我們完成的工作,本章節將分爲兩部分。首先,我們通過實驗對比驗證工作的成果。其次,我們將引擎和行業競品進行對比。
工作成果驗證
目前總共有6個通用測試集,測試集大小分別爲1220、6917、4069、2977、2946、2500條語音。其中測試集1是手機錄製測試集,集2是命令類的錄音,集3是麥克風錄音涉及一般生活情景,4、5、6集都是線上實網數據,區別是 集4、5背景比較乾淨,集6背景帶噪。
測試集
DNN
TDNN
TDNN優化版
1
10.4
8
6.9
2
13.7
11.3
9.3
3
22.9
18.3
15.6
4
15.8
13.3
12
5
15.3
12.2
10.5
6
22.6
20.3
17.8
在模型選取對比,我們針對DNN、TDNN、以及TDNN優化版(優化內容爲第三章的2、3、4小結內容),總共設計出三個不同版本的嵌入式語音識別引擎進行對比。
三個版本的嵌入式語音識別引擎在6個通用測試集上的實驗結果如表中所示。表中的數字表示字錯誤率,即100個字裏面識別錯字的數量。總體來看,TDNN對識別率帶來了20%左右的提升,其他工作也帶來了10%左右的提升。
從語音識別的基本概念,到語音識別速度和內存優化的介紹,以及沉澱的一些算法研究、實驗結果驗證,本文大體講述了語音識別從原理到實踐的基本過程。歡迎同樣從事語音AI識別的小夥伴加入我們~
此文已由作者授權騰訊雲+社區發佈,更多原文請點擊
搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!