歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐干貨哦~
本文作者:羅冬日
目前主流的語音識別都大致分為特征提取,聲學模型,語音模型幾個部分。目前結合神經網絡的端到端的聲學模型訓練方法主要CTC和基於Attention兩種。
本文主要介紹CTC算法的基本概念,可能應用的領域,以及在結合神經網絡進行CTC算法的計算細節。
CTC算法概念
CTC算法全稱叫:Connectionist temporal classification。從字面上理解它是用來解決時序類數據的分類問題。
傳統的語音識別的聲學模型訓練,對於每一幀的數據,需要知道對應的label才能進行有效的訓練,在訓練數據之前需要做語音對齊的預處理。而語音對齊的過程本身就需要進行反復多次的迭代,來確保對齊更准確,這本身就是一個比較耗時的工作。
上圖是“你好”這句話的聲音的波形示意圖, 每個紅色的框代表一幀數據,傳統的方法需要知道每一幀的數據是對應哪個發音音素。比如第1,2,3,4幀對應n的發音,第5,6,7幀對應i的音素,第8,9幀對應h的音素,第10,11幀對應a的音素,第12幀對應o的音素。(這里暫且將每個字母作為一個發音音素)
與傳統的聲學模型訓練相比,采用CTC作為損失函數的聲學模型訓練,是一種完全端到端的聲學模型訓練,不需要預先對數據做對齊,只需要一個輸入序列和一個輸出序列即可以訓練。這樣就不需要對數據對齊和一一標注,並且CTC直接輸出序列預測的概率,不需要外部的后處理。
既然CTC的方法是關心一個輸入序列到一個輸出序列的結果,那么它只會關心預測輸出的序列是否和真實的序列是否接近(相同),而不會關心預測輸出序列中每個結果在時間點上是否和輸入的序列正好對齊。
CTC引入了blank(該幀沒有預測值),每個預測的分類對應的一整段語音中的一個spike(尖峰),其他不是尖峰的位置認為是blank。對於一段語音,CTC最后的輸出是spike(尖峰)的序列,並不關心每一個音素持續了多長時間。
如圖2所示,拿前面的nihao的發音為例,進過CTC預測的序列結果在時間上可能會稍微延遲於真實發音對應的時間點,其他時間點都會被標記會blank。
這種神經網絡+CTC的結構除了可以應用到語音識別的聲學模型訓練上以外,也可以用到任何一個輸入序列到一個輸出序列的訓練上(要求:輸入序列的長度大於輸出序列)。
比如,OCR識別也可以采用RNN+CTC的模型來做,將包含文字的圖片每一列的數據作為一個序列輸入給RNN+CTC模型,輸出是對應的漢字,因為要好多列才組成一個漢字,所以輸入的序列的長度遠大於輸出序列的長度。而且這種實現方式的OCR識別,也不需要事先准確的檢測到文字的位置,只要這個序列中包含這些文字就好了。
RNN+CTC模型的訓練
下面介紹在語音識別中,RNN+CTC模型的訓練詳細過程,到底RNN+CTC是如何不用事先對齊數據來訓練序列數據的。
首先,CTC是一種損失函數,它用來衡量輸入的序列數據經過神經網絡之后,和真實的輸出相差有多少。
比如輸入一個200幀的音頻數據,真實的輸出是長度為5的結果。 經過神經網絡處理之后,出來的還是序列長度是200的數據。比如有兩個人都說了一句nihao這句話,他們的真實輸出結果都是nihao這5個有序的音素,但是因為每個人的發音特點不一樣,比如,有的人說的快有的人說的慢,原始的音頻數據在經過神經網絡計算之后,第一個人得到的結果可能是:nnnniiiiii...hhhhhaaaaaooo(長度是200),第二個人說的話得到的結果可能是:niiiiii...hhhhhaaaaaooo(長度是200)。這兩種結果都是屬於正確的計算結果,可以想象,長度為200的數據,最后可以對應上nihao這個發音順序的結果是非常多的。CTC就是用在這種序列有多種可能性的情況下,計算和最后真實序列值的損失值的方法。
詳細描述如下:
訓練集合為\(S=\lbrace (x^1,z^1), (x^2, z^2), ...(x^N,z^N) \rbrace\), 表示有\(N\)個訓練樣本,\(x\)是輸入樣本,\(z\)是對應的真實輸出的label。一個樣本的輸入是一個序列,輸出的label也是一個序列,輸入的序列長度大於輸出的序列長度。
對於其中一個樣本\((x,z)\),\(x=(x_1,x_2,x_3,...,x_T)\)表示一個長度為T幀的數據,每一幀的數據是一個維度為m的向量,即每個\(x_i \in R^m\)。 \(x_i\)可以理解為對於一段語音,每25ms作為一幀,其中第\(i\)幀的數據經過MFCC計算后得到的結果。
\(z=(z_1,z_2, z_3,...z_U)\)表示這段樣本語音對應的正確的音素。比如,一段發音“你好”的聲音,經過MFCC計算后,得到特征\(x\), 它的文本信息是“你好”,對應的音素信息是\(z=[n,i,h,a,o]\)(這里暫且將每個拼音的字母當做一個音素)。
特征\(x\)在經過RNN的計算之后,在經過一個\(softmax\)層,得到音素的后驗概率\(y\)。 \(y^t_k(k=1, 2,3,...n,t=1,2,3,...,T)\)表示在\(t\)時刻,發音為音素\(k\)的概率,其中音素的種類個數一共\(n\)個, \(k\)表示第\(k\)個音素,在一幀的數據上所有的音素概率加起來為1。即:
\(\sum_{t-1}^{T}y^t_k=1, y^t_k\geq0\)
這個過程可以看做是對輸入的特征數據\(x\)做了變換\(N_w\):\((R^m)^T \rightarrow (R^n)^T\),其中\(N_w\)表示RNN的變換,\(w\)表示RNN中的參數集合。
過程入下圖所示:
以一段“你好”的語音為例,經過MFCC特征提取后產生了30幀,每幀含有12個特征,即\(x \in R^{30\times14}\)(這里以14個音素為例,實際上音素有200個左右),矩陣里的每一列之和為1。后面的基於CTC-loss的訓練就是基於后驗概率\(y\)計算得到的。
路徑π和B變換
在實際訓練中並不知道每一幀對應的音素,因此進行訓練比較困難。可以先考慮一種簡單的情況,已知每一幀的音素的標簽\(z\prime\), 即訓練樣本為\(x\)和\(z\prime\),其中\(z\prime\)不再是簡單的\([n,i,h,a,o]\)標簽,而是:
\(z\prime = [\underbrace{n,n,n,...,n}_{T_1},\underbrace{i,i,i,...i}_{T_2},\underbrace{h,h,h,...h}_{T_3},\underbrace{a,a,a,...,a}_{T_4},\underbrace{o,o,o,...,o}_{T_5}]\)
$ T_1+T_2+T_3+T_4+T_5 = T $
在我們的例子中, \(z\prime =[n,n,n,n,n,n,n,i,i,i,i,i,i,h,h,h,h,h,h,h,a,a,a,a,a,a,o,o,o,o,o,o,o]\), $z\prime \(包含了每一幀的標簽。在這種情況下有: \)p(z\prime|x) = p(z\prime| y = N_w(x)) = y^1_{z\prime_1}y^2_{z\prime_2}y^3_{z\prime_3}....y^T_{z\prime_T}$ \(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\) (1)
該值即為后驗概率圖中用黑線圈起來的部分相乘。我們希望相乘的值越大越好,因此,數學規划可以寫為:
\(min_w -log(y^1_{z^\prime_1}.y^2_{z^\prime_2}.y^3_{z^\prime_3}...y^T_{z^\prime_T})\) \(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\) (2)
subject to: $ y = N_w(x) $$\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad$ (3)
目標函數對於后驗概率矩陣\(y\)中的每個元素\(y^t_k\)的偏導數為:
$\frac{\partial - log(y^1_{z^\prime_1}.y^2_{z^\prime_2}.y^3_{z^\prime_3}...y^T_{z^\prime_T})}{\partial y^t_k} = $$\begin{cases} -\frac{y^1_{z\prime_1}...y^{i-1}{z\prime{i-1}}.y^{i+1}{z\prime{i+1}}....y^T_{z\prime_T}}{y^1_{z^\prime_1}.y^2_{z^\prime_2}.y^3_{z^\prime_3}...y^T_{z^\prime_T}} , \qquad if \qquad k = z\prime_i \qquad and \qquad t=i \ 0 \qquad 其他\end{cases}$
也就是說,在每個時刻\(t\)(對應矩陣的一列),目標只與\(y^t_{z\prime_t}\)是相關的,在這個例子中是與被框起來的元素相關。
其中\(N_w\)可以看做是RNN模型,如果訓練數據的每一幀都標記了正確的音素,那么訓練過程就很簡單了,但實際上這樣的標記過的數據非常稀少,而沒有逐幀標記的數據很多,CTC可以做到用未逐幀標記的數據做訓練。
首先定義幾個符號:
\(L=\lbrace a, o, e, i, u, \check{u},b,p,m,...\rbrace\)
表示所有音素的集合
\(\pi= (\pi_1, \pi_2, \pi_3, ..., \pi_T), \pi_i \in L\)
表示一條由\(L\)中元素組成的長度為\(T\)的路徑,比如\(z\prime\)就是一條路徑,以下為幾個路徑的例子:
\(\pi^1= (j,j,i,n,y,y,e,e,w,w,u,u,u,r,r,e,e,n,n,r,r,u,u,sh,sh,u,u,i,i)\)
\(\pi^2= (n,n,n,n,i,i,i,i,h,h,h,h,a,a,a,a,a,a,a,a,a,o,o,o,o,o,o,o,o,o)\)
\(\pi^3= (h,h,h,h,h,h,a,a,a,a,a,a,a,o,o,o,o,n,n,n,n,n,n,i,i,i,i,i,i,i)\)
\(\pi^4= (n,i,h,a,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o)\)
\(\pi^5= (n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,i,h,a,o)\)
\(\pi^6= (n,n,n,i,i,i,h,h,h,h,h,a,,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o)\)
這6條路徑中,\(\pi^1\)可以被認為是“今夜無人入睡”, \(\pi^2\)可以被認為是在說“你好”,\(\pi^3\)可以被認為是在說“好你”,\(\pi4,\pi5,\pi6\)都可以認為是在說“你好”。
定義B變換,表示簡單的壓縮,例如:
\(B(a,a,a,b,b,b,c,c,d) = (a,b,c,d)\)
以上6條路徑為例:
\(B(\pi^1) = (j,i,n,y,e,w,u,r,e,n,r,u,s,h,u,i)\)
\(B(\pi^2) = (n,i,h,a,)\)
\(B(\pi^3) = (h,a,o,n,i)\)
\(B(\pi^4) = (n,i,h,a,o)\)
\(B(\pi^5) = (n,i,h,a,o)\)
\(B(\pi^6) = (n,i,h,a,o)\)
因此,如果有一條路徑\(\pi\)有\(B(\pi)=(n,i,h,a,o)\),則可以認為\(\pi\)是在說“你好”。即使它是如\(\pi^4\)所示,有很多“o”的音素,而其他音素很少。路徑\(\pi = (\pi^1,\pi^2,...,\pi^T)\)的概率為它所經過的矩陣y上的元素相乘:
\(p(\pi|x) = p(\pi| y = N_w(x)) = p(\pi| y)=\prod_{t=1}^{T} y^t_{\pi_t}\)
因此在沒有對齊的情況下,目標函數應該為\(\lbrace \pi | B(\pi) = z \rbrace\)中所有元素概率之和。 即:
\(max_w p(z|y=N_w(x)) = p(z|x)= \sum_{B(\pi)=z}p(\pi|x)\) \(\qquad\qquad\qquad\) (4)
在T=30,音素為\([n,i,h,a,o]\)的情況下,共有\(C^5_{29}\approx120000\)條路徑可以被壓縮為\([n,i,h,a,o]\)。 路徑數目的計算公式為\(C^{音素個數}_{T-1}\),量級大約為\((T-1)^{音素個數}\)。一段30秒包含50個漢字的語音,其可能的路徑數目可以高達\(10^8\),顯然這么大的路徑數目是無法直接計算的。因此CTC方法中借用了HMM中的向前向后算法來計算。
訓練實施方法
CTC的訓練過程是通過$\frac {\partial p(z|x)}{\partial w} $調整w的值使得4中的目標值最大,而計算的過程如下:
因此,只要得到$\frac {\partial p(z|x)}{\partial y^t_k} \(,即可根據反向傳播,得到\)\frac {\partial p(z|x)}{\partial w} $。下面以“你好”為例,介紹該值的計算方法。
首先,根據前面的例子,找到所有可能被壓縮為\(z=[n,i,h,a,o]\)的路徑,記為\(\lbrace \pi|B(\pi) = z \rbrace\)。 可知所有\(\pi\)均有\([n,n,n,....,n,i,.....,i,h,.....h,a,....a,o,...,o]\)的形式,即目標函數只與后驗概率矩陣y中表示\(n,i,h,a,o\)的5行相關,因此為了簡便,我們將這5行提取出來,如下圖所示。
在每一個點上,路徑只能向右或者向下轉移,畫出兩條路徑,分別用q和r表示,這兩條路徑都經過\(y^{14}_h\)這點,表示這兩點路徑均在第14幀的時候在發“h”音。因為在目標函數4的連加項中,有的項與\(y^{14}_h\)無關,因此可以剔除這一部分,只留下與\(y^{14}_h\)有關的部分,記為\(\lbrace \pi|B(\pi) = z, \pi_{14}=h \rbrace\)
$\frac {\partial p(z|y)}{\partial y^{14}_h} $
= $\frac {\partial \sum_{B(\pi)=z}p(\pi|y)}{\partial y^{14}_h} $
= $\frac {\partial \sum_{B(\pi)=z}\prod_{t=1}^T y^t_{\pi_t}}{\partial y^{14}h} \( =\)\frac {\overbrace{\partial \sum{B(\pi)=z,\pi_{14}=h}\prod_{t=1}^T y^t_{\pi_t}}^{和y^{14}h有關的項} + \overbrace{\partial \sum{B(\pi)=z,\pi_{14} \neq h}\prod_{t=1}^T y^t_{\pi_t}}^{和y^{14}_h無關的項}}{\partial y^{14}_h}$
=\(\frac {\partial \sum_{B(\pi)=z,\pi_{14}=h}\prod_{t=1}^T y^t_{\pi_t}}{\partial y^{14}_h}\)
這里的q和r就是與\(y^{14}_h\)相關的兩條路徑。用\(q_{1:13}\)和\(q_{15:30}\)分別表示\(q\)在\(y^{14}_h\)之前和之后的部分,同樣的,用\(r_{1:13}\)和\(r_{15:30}\)分別表示\(r\)在\(y^{14}_h\)之前和之后的部分.。可以發現,\(q_{1:13} + h + r_{15:30}\)與\(r_{1:13} + h + q_{15:30}\)同樣也是兩條可行的路徑。\(q_{1:13} + h + r_{15:30}\)、\(r_{1:13} + h + q_{15:30}\)、\(q\) 、\(r\)這四條路徑的概率之和為:
\(\underbrace{y^1_{q1}..y^{13}_{q13}.y^{14}_{h}.y^{15}_{q15}....y^{30}_{q30}}_{路徑q的概率}\)
+\(\underbrace{y^1_{q1}..y^{13}_{q13}.y^{14}_{h}.y^{15}_{r15}....y^{30}_{r30}}_{路徑q_{1:14}+r_{14:30}的概率}\)
+\(\underbrace{y^1_{r1}..y^{13}_{r13}.y^{14}_{h}.y^{15}_{q15}....y^{30}_{q30}}_{路徑r_{1:14}+q_{14:30}的概率}\)
+\(\underbrace{y^1_{r1}..y^{13}_{r13}.y^{14}_{h}.y^{15}_{r15}....y^{30}_{r30}}_{路徑r的概率}\)
=\((y^1_{q1}....y^{13}_{q13} + y^1_{r1}.....y^{13}_{r13}).y^{14}_h.(y^{15}_{q15}....y^{30}_{q15}+y^{15}_{r15}....y^{30}_{r30})\)
可以發現,該值可以總結為:(前置項)\(.y^{14}_h.\)(后置項)。由此,對於所有的經過\(y^{14}_h\)的路徑,有:
\(\sum_{B(\pi)=z,\pi_{14}=h}\prod_{t=1}^T y^t_{\pi_t}= (前置項).y^{14}_h.\)(后置項)$
定義:
$\alpha_(14)(h)=(前置項).y^{14}h = \sum{B(\pi_{1:14})=[n,i,h] }\prod_{t\prime=1}^t y^{t\prime}{\pi{t\prime}} $
該值可以理解為從初始到\(y^{14}_h\)這一段里,所有正向路徑的概率之和。並且發現,\(\alpha_{14}(h)\)可以由\(\alpha_{13}(h)\)和\(\alpha_{13}(i)\)遞推得到,即:
\(\alpha_{14}(h) = (\alpha_{13}(h) + \alpha_{13}(i))y^{14}_h\)
該遞推公式的含義是,只是在\(t=13\)時發音是“h”或“i”,在\(t=14\)時才有可能發音是“h”。那么在\(t=14\)時刻發音是“h”的所有正向路徑概率\(\alpha_{14}(h)\)就等於在\(t=13\)時刻,發音為“h”的正向概率\(\alpha_{13}(h)\)加上發音為“i”的正向概率\(\alpha_{13}(i)\),再乘以當前音素被判斷為“h”的概率\(y^{14}_h\)。由此可知,每個\(\alpha_t(s)\)都可以由\(\alpha_{t-1}(s)\)和\(\alpha_{t-1}(s-1)\)兩個值得到。\(\alpha\)的遞推流程如下圖所示:
即每個值都由上一個時刻的一個或者兩個值得到,總計算量大約為\(2.T.音素個數\)。類似的,定義\(\beta_t(s)\), 遞推公式為:
\(\beta_{14}(h)=(\beta_{15}(h) + \beta_{15}(a))y^{14}_h\)
因此有:
\(\sum_{B(\pi)=z,\pi_{14}=h}\prod_{t=1}^T y^t_{\pi_t}= (前置項).y^{14}_h.\)(后置項)$
=\(\frac{\alpha_{14}(h)}{y^{14}_h}. y^{14}_h.\frac{\beta_{14}(h)}{y^{14}_h}\)
=\(\frac{\alpha_{14}(h)\beta_{14}(h)}{y^{14}_h}\)
然后:
$\frac {\partial p(z|y)}{\partial y^{14}_h} $
= \(\frac {\sum_{B(\pi)=z,\pi_{14}=h}\prod_{t=1}^T y^t_{\pi_t}}{\partial y^{14}_h}\)
= \(\frac {\frac{\alpha_{14}(h)}{y^{14}_h}. y^{14}_h.\frac{\beta_{14}(h)}{y^{14}_h}}{\partial y^{14}_h}\)
=\(\frac{\alpha_{14}(h)\beta_{14}(h)}{{(y^{14}_h)}^2}\)
得到此值后,就可以根據反向傳播算法進行訓練了。
可以看到,這里總的計算量非常小,計算\(\alpha\)和\(\beta\)的計算量均大約為\((2.T.音素個數)\),(加法乘法各一次),得到\(\alpha\)和\(\beta\)之后,在計算對每個\(y^t_k\)的偏導值的計算量為\((3.T.音素個數)\),因此總計算量大約為\((7.T.音素個數)\),這是非常小的,便於計算。
目前,深度學習的算法已經大規模應用於騰訊雲的語音識別產品中。騰訊雲擁有業內最先進的語音識別技術,基於海量的語音數據,積累了數十萬小時的標注語音數據,采用LSTM,CNN,LFMMI,CTC等多種建模技術,結合超大規模語料的語言模型,對標准普通話的識別效果超過了97%的准確率。騰訊雲的語音技術,應用涵蓋范圍廣泛,具備出色的語音識別、語音合成、關鍵詞檢索、靜音檢測、語速檢測、情緒識別等能力。並且針對游戲,娛樂,政務等幾十個垂直做特殊定制的語音識別方案,讓語音識別的效果更精准,更高效,全面滿足電話客服質檢、語音聽寫、實時語音識別和直播字幕等多種場景的應用。想試用相關產品嗎?請猛戳:cloud.tencent.com/product/asr
問答
語音識別API如何調用?
相關閱讀
智能機器人語音識別技術
python語音識別終極指南
tensorflow LSTM +CTC實現端到端OCR
此文已由作者授權騰訊雲+社區發布,原文鏈接:https://cloud.tencent.com/developer/article/1122128?fromSource=waitui