語音識別中的CTC演算法的基本原理解釋

語音識別中的CTC演算法的基本原理解釋

目前主流的語音識別都大致分為特徵提取,聲學模型,語音模型幾個部分。目前結合神經網路的端到端的聲學模型訓練方法主要CTC和基於Attention兩種。

本文主要介紹CTC演算法的基本概念,可能應用的領域,以及在結合神經網路進行CTC演算法的計算細節。

CTC演算法概念

CTC演算法全稱叫:Connectionist temporal classification。從字面上理解它是用來解決時序類數據的分類問題。

傳統的語音識別的聲學模型訓練,對於每一幀的數據,需要知道對應的label才能進行有效的訓練,在訓練數據之前需要做語音對齊的預處理。而語音對齊的過程本身就需要進行反覆多次的迭代,來確保對齊更準確,這本身就是一個比較耗時的工作。

圖1 「你好」發音的波形示意圖

如圖1所示,是「你好」這句話的聲音的波形示意圖, 每個紅色的框代表一幀數據,傳統的方法需要知道每一幀的數據是對應哪個發音音素。比如第1,2,3,4幀對應n的發音,第5,6,7幀對應i的音素,第8,9幀對應h的音素,第10,11幀對應a的音素,第12幀對應o的音素。(這裡暫且將每個字母作為一個發音音素)

與傳統的聲學模型訓練相比,採用CTC作為損失函數的聲學模型訓練,是一種完全端到端的聲學模型訓練,不需要預先對數據做對齊,只需要一個輸入序列和一個輸出序列即可以訓練。這樣就不需要對數據對齊和一一標註,並且CTC直接輸出序列預測的概率,不需要外部的後處理。

既然CTC的方法是關心一個輸入序列到一個輸出序列的結果,那麼它只會關心預測輸出的序列是否和真實的序列是否接近(相同),而不會關心預測輸出序列中每個結果在時間點上是否和輸入的序列正好對齊。

圖2 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) 
brace , 表示有 N 個訓練樣本, x 是輸入樣本, z 是對應的真實輸出的label。一個樣本的輸入是一個序列,輸出的label也是一個序列,輸入的序列長度大於輸出的序列長度。

對於其中一個樣本 (x,z)x=(x_1,x_2,x_3,...,x_T) 表示一個長度為T幀的數據,每一幀的數據是一個維度為m的向量,即每個 x_i in R^mx_i 可以理解為對於一段語音,每25ms作為一幀,其中第 i 幀的數據經過MFCC計算後得到的結果。

z=(z_1,z_2, z_3,...z_U) 表示這段樣本語音對應的正確的音素。比如,一段發音「你好」的聲音,經過MFCC計算後,得到特徵 x , 它的文本信息是「你好」,對應的音素信息是 z=[n,i,h,a,o] (這裡暫且將每個拼音的字母當做一個音素)。

特徵 x 在經過RNN的計算之後,在經過一個 softmax 層,得到音素的後驗概率 yy^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_kgeq0

這個過程可以看做是對輸入的特徵數據 x 做了變換 N_w : (R^m)^T 
ightarrow (R^n)^T ,其中 N_w 表示RNN的變換, w 表示RNN中的參數集合。

過程入下圖所示:

以一段「你好」的語音為例,經過MFCC特徵提取後產生了30幀,每幀含有12個特徵,即 x in R^{30	imes14} (這裡以14個音素為例,實際上音素有200個左右),矩陣里的每一列之和為1。後面的基於CTC-loss的訓練就是基於後驗概率 y 計算得到的。

路徑 pi 和B變換

在實際訓練中並不知道每一幀對應的音素,因此進行訓練比較困難。可以先考慮一種簡單的情況,已知每一幀的音素的標籤 zprime , 即訓練樣本為 xzprime ,其中 zprime 不再是簡單的 [n,i,h,a,o] 標籤,而是:

zprime = [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

在我們的例子中, zprime =[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]zprime 包含了每一幀的標籤。在這種情況下有:

p(zprime|x) = p(zprime| y = N_w(x)) = y^1_{zprime_1}y^2_{zprime_2}y^3_{zprime_3}....y^T_{zprime_T} (1)

該值即為後驗概率圖中用黑線圈起來的部分相乘。我們希望相乘的值越大越好,因此,數學規劃可以寫為:

min_w -log(y^1_{z^prime_1}.y^2_{z^prime_2}.y^3_{z^prime_3}...y^T_{z^prime_T}) (2)

subject to: y = N_w(x) (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} = $$egin{cases} -frac{y^1_{zprime_1}...y^{i-1}_{zprime_{i-1}}.y^{i+1}_{zprime_{i+1}}....y^T_{zprime_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 = zprime_i qquad and qquad t=i \ 0 qquad 其他end{cases}

也就是說,在每個時刻 t (對應矩陣的一列),目標只與 y^t_{zprime_t} 是相關的,在這個例子中是與被框起來的元素相關。

其中 N_w 可以看做是RNN模型,如果訓練數據的每一幀都標記了正確的音素,那麼訓練過程就很簡單了,但實際上這樣的標記過的數據非常稀少,而沒有逐幀標記的數據很多,CTC可以做到用未逐幀標記的數據做訓練。

首先定義幾個符號:

L=lbrace a, o, e, i, u, check{u},b,p,m,...
brace

表示所有音素的集合

pi= (pi_1, pi_2, pi_3, ..., pi_T), pi_i in L

表示一條由 L 中元素組成的長度為 T 的路徑,比如 zprime 就是一條路徑,以下為幾個路徑的例子:

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)

因此,如果有一條路徑 piB(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 
brace 中所有元素概率之和。 即:

max_w p(z|y=N_w(x)) = p(z|x)= sum_{B(pi)=z}p(pi|x) (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 
brace 。 可知所有 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 
brace

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} 
eq 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} 分別表示 qy^{14}_h 之前和之後的部分,同樣的,用 r_{1:13}r_{15:30} 分別表示 ry^{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}qr 這四條路徑的概率之和為:

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_{tprime=1}^t y^{tprime}_{pi_{tprime}}

該值可以理解為從初始到 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.音素個數 。類似的,定義 eta_t(s) , 遞推公式為:

eta_{14}(h)=(eta_{15}(h) + eta_{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{eta_{14}(h)}{y^{14}_h}

= frac{alpha_{14}(h)eta_{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{eta_{14}(h)}{y^{14}_h}}{partial y^{14}_h}

= frac{alpha_{14}(h)eta_{14}(h)}{{(y^{14}_h)}^2}

得到此值後,就可以根據反向傳播演算法進行訓練了。

可以看到,這裡總的計算量非常小,計算 alphaeta 的計算量均大約為 (2.T.音素個數) ,(加法乘法各一次),得到 alphaeta 之後,在計算對每個 y^t_k 的偏導值的計算量為 (3.T.音素個數) ,因此總計算量大約為 (7.T.音素個數) ,這是非常小的,便於計算。


推薦閱讀:

無線智能音響的新潮流
語音基礎知識
HMMs
語音交互文章集錦(長期更新)

TAG:深度學習DeepLearning | 語音識別 |