1. 程式人生 > >教程:Connectionist Temporal Classification詳解補充

教程:Connectionist Temporal Classification詳解補充

感想

CTC的想法很難懂,尤其是對前向後向演算法不熟的人,然後再網上發現了一篇很好的教程,我把它翻譯了下來,裡面有大量的例子,專注於原理的講解,非常透徹,不像CTC的原論文,那樣全面冗長,希望也能幫助大家理解。我在五六月份找過其它中文資料,發現原理詳解的很少,我特地彌補一下中文這一塊的空缺。

介紹

Connectionist Temporal Classification (CTC)是一項技術,它是為RNN專門設計的頂層(top layer)。使得針對輸入序列的每一幀,網路能夠輸出一個標籤或者空白(blank)。CTC使得用一個RNN構建語音識別系統成為可能,這個和混合方法HMM+DNN不一樣。

語音識別問題

輸入是一個聲音片段x,然後分割成很多個幀:

X={x1,…,xT}

每一幀在時刻t,用xt表示,xt由G個聲譜的權重組成:

Xt=[xt1,…,xt,G]T

我們可以設計一個RNN對每一幀xt的輸出yt::

Y={y1,…,yT}

Yt是一個字母表的概率分佈:

Yt=[yt,1…yt,K]T

其中yt,k和lt代表在時刻t發音字母。

那麼,問題來了,我們怎樣把y解釋成一串字母。

首先,很直接,我們要找一個路徑

Π={π1…πT}

其中πt屬於[1,K]

設想一個人說了一個“a”,在說之前,之後有一個安靜的部分(silence),路徑π可以是:

aaaaaaaa

aaaaa___

____aaaa

__aaaaa_

但這個沒考慮到安靜期和發音的變化,下面這個例子所有對應 “a”的路徑:

aa______

aaa_____

_aaa____

___aaaa_

_aaaaaaa

上面的例子告訴我們,網路的輸出既不是y也不是π。而是一些叫做標籤的序列,用l表示:

L={l1,…,lS}

其中S<=T

對於上面的例子,所有那些路徑都是對應樣本標籤:l={a}.

所有下面的樣例對應l={h,e}:

hhheeee

__heeee

_hee___

hh__eee

_hh_eee

h__ee__

__h_ee_

注意空白(blanks)可以出現在he之前,之間,之後。

更復雜一點的例子是l={b,e,e}:

bbbeee_ee

_bb_ee__e

__bbbe_e_

但下面的例子對應l={b,e}

_b_eeeeee

bbb__eeee

_bb_eee__

這兩個例子顯示了空白在分開連續重複字母的重要性。

綜上,語音識別的輸出並不是字母表的分佈y或者路徑π,而是標籤l.CTC 是從y結合π來推出l的方法。

動態時間規整(Dynamic Time Warping)

Y的長度可能和l的長度不一樣,因此從y推理(inference)l實際上是動態時間規整問題。動態時間規整演算法使得我們可以用(x,l)對進行訓練,而不是(x,y)對,這是非常有價值的,因為我們在訓練前不需要預先分割y,並且把x和y對齊。

時間規整的意思是我們想要把yt對映到某個ls.由上述的例子我們其實是想要把yt   對映到要麼是ls,要麼是一個空白。因為要是我們這樣做了,我們就有機會識別後繼字母,比如coffee中“ff”,bee中的”ee“。

假設我們有一個“bee”的語音片段,最可能的路徑π是:

___bbeeee____

我們想要把它規整到l={b,e,e},好像我們可以建立下面的對映:

但是事實是我們沒有太多資訊去組織演算法把所有的e對映到l中的第一個e:

另一個問題是我們應該把這些空白(blanks)對映到π中的哪一個。

一個解決辦法就是我們把π對映到l’,我們在l之前,中間,後面都插入blank就構成了l’,對於上面的例子,我們就可以得到:

L’={,b,,e,,e,}

就有了下面的規整:

但有時π中的第一個字元不是空白,所以我們可以把第一幀對應到‘b’,而不是對應l’中的空白:

一個相似的情況是π中沒有對齊的空白,因此沒有幀可以對齊l’中的空白。所以這兩種情況都需要我們設計一個演算法計算π和l’的對映。

前向後向演算法

這裡我們推導用於計算P(l^{'}|x),很顯然他和P(l|x)等價,因為l^{'}在給定l的情況下構造是唯一的

1.前向演算法

上面的規整的例子說明\pi _{T}可以對映到要麼\iota ^{'}_{|l^{'}|},即\iota^{'}的空白,要麼\iota ^{'}_{|l^{'}-1|},根據\iota^{'}的構造法則,即l的最後一個元素。


可以寫成下面的形式:

在上面例子中,π1可以對映到要麼是\iota ^{'}_{1},即的第一個空白;要麼是\iota ^{'}_{2},l 的第一個元素。因此:

假設我們有l’={_,h,_,e,_}和一串序列y,因此有以下兩種情況:

緊接著我們用一個更通用的方法去對映\pi _{s}.大體上,我們可以這樣對映一個\pi _{t}到:

1.   \pi_{t-1} π_{t-1},用\iota ^{'}_{s(t-1)}表示

2.    緊挨著\iota ^{'}_{s(t-1)}的元素,用\iota ^{'}_{s(t-1)+1}表示,或者

3.    \iota ^{'}_{s(t-1)+2},如果\iota ^{'}_{s(t-1)}="_"

第三個情況是我們想跳過空白,即\iota ^{'}_{s(t-1)}="_".

一個例子:我們想要把π={h,h,h,h,e,e,e}對映到l’={_,h,_,e,_}

但是,當\iota ^{'}_{s(t-1)+2}=\iota ^{'}_{s(t-1)}的時候,上述第3種情況是不合理的。在這種情況下,我們不應該跳過空白。例如,當識別單詞bee的時候,l’={_,b ,_,e ,_,e ,_},在這種情況下,當我們跳過兩個e之間的空格,我們就會把兩個e錯誤地當成了一個e.在下面這個樣例中,即使我們把這幀(y5)聽起來更像一個e而不是空白,我們還是想要把它對映成空白,即\iota ^{'}_{5}="_",因此我們識別出了bee這個單詞。

綜上,我們可得:

2.後向演算法

和前向變數α(t ,s)相似,我們定義後向變數β(t ,s)

由於時間規整把πT對映到要麼是\iota ^{'}_{|l^{'}}|},,即用1的概率對齊空白,要麼\iota ^{'}_{|l^{'}-1|},即l集合中最後一個元素,概率為1,即:

和前向演算法的泛華規則相似,我們有:

搜尋空間

計算α(t ,s)的時候,我們需要α(t-1 ,s),α(t-1 ,s-1),α(t-1 ,s-2)。為了計算β(t,s),我們需要β(t+1,s),β(t+1,s+1),β(t+1,s+2)。其中的一些值為0,如圖:

每個圈代表搜尋空間的一個可能狀態,這些狀態以t和s的各自對齊,箭頭連線一個狀態和另一個狀態,當我們計算α(t ,s)的時候,我們不需要進入不可能的區域,即右上角的的連線狀態。

當我們計算β(t ,s)的時候,我們不可能進入左下角的狀態:

為了約束動態規劃演算法(dynamic programming algorithm),我們需要下列條件:

參考文獻

[1]. Connectionist Temporal Classification:A Tutorial with Gritty Details.

https://cxwangyi.wordpress.com/2015/10/07/connectionist-temporal-classification-the-gritty-details/