1. 程式人生 > >訊號為E時,如何讓語音識別脫“網”而出?

訊號為E時,如何讓語音識別脫“網”而出?

歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~

本文由騰訊教育雲發表於雲+社群專欄

一般沒有網路時,語音識別是這樣的

img

而同等環境下,嵌入式語音識別,是這樣的

不僅可以幫您邊說邊識、出口成章,有個性化名字的時候也難不倒它。

這就是嵌入式語音識別的魅力。

本文將從微信智聆的嵌入式語音識別引擎的實現和優化,

介紹嵌入式語音識別的技術選型。

01

語音識別,大體是這麼來的

語音識別,能讓機器“聽懂”人類的語音,把說話內容識別為對應文字。

開始於上世紀50年代

從最初的小詞量孤立識別系統

到如今的大詞量連續識別系統

語音識別系統的發展,效能得到顯著的提升,主要得利於以下幾個方面:

大資料時代的到來

深度神經網路在語音識別中的應用

GPU硬體的發展

因此,語音識別逐步走向實用化和產品化

img

語音輸入法,語音智慧助手,語音車載互動系統……

可以說,語音識別是人類征服人工智慧的前沿陣地,是目前機器翻譯、自然語言理解、人機互動等的奠基石。

然而,效能的提升基於服務端CPU/GPU高計算能力和大記憶體,沒有網路的時候將無法享受語音識別的便利

為了解決這個問題,微信智聆針對嵌入式語音識別進行研發。嵌入式語音識別,也稱為嵌入式LVCSR(或離線LVCSR,Large Vocabulary Continuous Speech Recognition),指全程執行在手機端的語音識別,而不依賴於服務端強大的計算能力。

在一些網路不穩的特殊場景(車載、境外等等),嵌入式語音識別可“曲線救國”。

那麼,實現一個嵌入式語音識別,存在哪些難點呢?

img

語音識別的基本流程

主流的語音識別演算法當中,包括聲學和語言兩大模型。聲學模型得利於近十年深度學習的發展,從GMM(高斯模型)到DNN(深度神經網路),再從DNN到LSTM RNN(迴圈神經網路),識別率不斷提升的同時,計算量也不斷地飛漲。而語言模型常用的n-gram演算法,階數越高效能越好,常用的模型多達數十G的記憶體。

所以綜合起來,嵌入式語音識別有以下幾個難點:

\1. 深度學習運算複雜,僅僅對模型進行裁剪效能損失大,需尋找挽回效能的方法;

\2. 裁剪模型不可避免,在模型訓練環節如何避免小模型訓練易陷入區域性最優的問題;

\3. 如何計算的更快,滿足嵌入式的CPU環境;

\4. 如何組織語言模型儲存,能在有限的記憶體下儲存更多的語言資訊。

本文將以語音識別的技術原理出發,淺談微信智聆嵌入式的實現技術。

內容將分為四個部分:

\1. 回顧語音識別的基本概念;

\2. 簡單介紹在速度和記憶體優化上我們做的部分工作,側重於工程應用實現;

\3. 說一說為了更好的效能我們做了哪些事,側重於演算法研究介紹;

\4. 我們進行實驗對比,最後我們進行總結。

02

語音識別的各個元件

img

語音識別“黑盒”

語音識別從輸入錄音輸出文字,黑盒子處理經過特徵提取、聲學模型、發音詞典、語言模型等流程,筆者認為可以把語音識別比作一臺計算機。

特徵提取相當於是路由器,作為領頭羊給後續環節提供源源不斷的資料來源。

聲學模型相當於語音識別的心臟——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)所示:

img

a:原始DNN模型的一層結構

img

(b)新DNN模型的兩層對應結構

利用SVD對聲學模型計算量優化大致分為3個步驟

(1)訓練初始DNN神經網路;

(2)對權重矩陣進行奇異值分解;

(3)對重構後的DNN模型重新訓練。

通過基於SVD的模型壓縮方法,我們可以在稍微降低模型效能的前提下,將聲學模型計算量減少30%。

3.哈夫曼優化語言模型記憶體

一般地,n-gram語言模型可以用一張有向圖儲存便於介紹儲存空間以及快速查詢,這張圖上的邊要儲存詞彙資訊。我們知道以漢語為例,不同詞語的出現頻率相差極大,如果所有詞彙的label id都用int型別儲存,那空間的利用率較為低下。

以“我”“要”“吃飯”為例,假設語言模型的詞彙頻率:我>要>吃飯,那麼我們可以構建圖3的哈夫曼樹,則四個字使用的編號碼分別為:我(0),要(10),吃飯(110)

img

二叉哈夫曼

img

十六叉哈夫曼樹

然而,採用圖4的二叉樹資料結構,一次只能處理1bit效率較低,也不便於工程實現。所以在工程實現的時候,我們按4bits編碼為單位,對詞彙進行分類儲存處理。

我們使用一棵16叉樹的哈夫曼樹結構,每層樹節點的編號總量是上一層的16倍。樹中的所有編號為0的子節點用於儲存詞彙,越高頻的詞彙儲存於深度越低的節點位置。

通過哈夫曼優化,我們的引擎最終成功降低了25%的記憶體佔用,同時引擎是資原始檔也得到50%左右的優化。

04

識別效能的優化

1.基於TDNN優化聲學模型

近幾年,TDNN(Time-Delay Neural Network,延時神經網路)【5】的拓撲結構被應用於語音識別。事實上,該結構於1989年被提出,隨著近幾年技術的發展,重新進入了大家的視線。

img

DNN結構

DNN的拓撲網路僅針對單一特徵時刻點建模。

img

TDNN結構

TDNN的隱層結構,對語音特徵多個時刻點進行抽象建模,擁有更強的建模能力。除此之外,TDNN結構的多時刻建模引數是共享的(圖中紅、綠、紫用的是同樣的拓撲矩陣傳播)。

所以,TDNN雖然在訓練的時候,比DNN需要更多的BP運算。而在語音識別時,由於引數共享的原因,隱層的計算結果可以複用,每一幀僅需對所有引數進行一次運算,大大節省了計算量。最後,我們基於TDNN結構,引擎在保持計算量一致的前提下,識別率提升了相對20%的準確率。

2.基於多工訓練優化效能

採用多工聯合訓練,能有效提高聲學訓練的魯棒性,避免過早陷入區域性最優。在嵌入式的模型中,模型輸出目標比較少,訓練容易陷入區域性最優。所以我們,同時用目標多的大模型聯合訓練,讓訓練的隱層結構更為魯棒。

img

聲學模型多工訓練

在訓練的時候,我們網路同時擁有輸出1和輸出2兩個,多工訓練時,逆向迭代需要殘差協調,我們採用以下公式分配殘差,其中λ權衡兩個模型的訓練權重:

img

最終我們採用多工訓練優化效能,對語音識別率帶來了一定提升,接下來所有的效能提升我們將在下一章結實驗給出。

3.基於區分性訓練(Discriminative Training)效能優化

聲學模型區分性訓練是針對MLE訓練的不足而提出的。DT訓練通常定義一個目標函式(Objective Function),或者說是準則函式(Criterion Function),來近似一個與分類代價相關的度量。通過區分性訓練,我們可以從一定程度上弱化模型假設錯誤所帶來的影響。

同時,由於區分性訓練致力於優化與識別效果好壞相關的度量,因此也就為提高識別器效能提供了更直接的途徑。形象的說,MLE訓練告訴模型“這是椅子,那是桌子”,而區分性訓練則告訴模型“這是桌子而不是椅子,那是椅子而不是桌子”。MLE訓練更重視調整模型引數以反映訓練資料的概率分佈,而區分性訓練則更重視調整模型之間的分類面,以更好的根據設定的準則對訓練資料進行分類。

DT的目標函式是這樣的:

img

對DT的目標函式用一次貝葉斯公司可以得到:

img

分子正是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%左右的提升。

img

從語音識別的基本概念,到語音識別速度和記憶體優化的介紹,以及沉澱的一些演算法研究、實驗結果驗證,本文大體講述了語音識別從原理到實踐的基本過程。歡迎同樣從事語音AI識別的小夥伴加入我們~

相關閱讀
【每日課程推薦】機器學習實戰!快速入門線上廣告業務及CTR相應知識

此文已由作者授權騰訊雲+社群釋出,更多原文請點選

搜尋關注公眾號「雲加社群」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社群