文字識別(六)--不定長文字識別CRNN演算法詳解

Eason.wxd發表於2019-02-18

轉自:https://www.cnblogs.com/skyfsm/p/10335717.html

在以前的OCR任務中,識別過程分為兩步:單字切割和分類任務。我們一般都會講一連串文字的文字檔案先利用投影法切割出單個字型,在送入CNN裡進行文字分類。但是此法已經有點過時了,現在更流行的是基於深度學習的端到端的文字識別,即我們不需要顯式加入文字切割這個環節,而是將文字識別轉化為序列學習問題,雖然輸入的影象尺度不同,文字長度不同,但是經過DCNN和RNN後,在輸出階段經過一定的翻譯後,就可以對整個文字影象進行識別,也就是說,文字的切割也被融入到深度學習中去了。

現今基於深度學習的端到端OCR技術有兩大主流技術:CRNN OCR和attention OCR。其實這兩大方法主要區別在於最後的輸出層(翻譯層),即怎麼將網路學習到的序列特徵資訊轉化為最終的識別結果。這兩大主流技術在其特徵學習階段都採用了CNN+RNN的網路結構,CRNN OCR在對齊時採取的方式是CTC演算法,而attention OCR採取的方式則是attention機制。本文將介紹應用更為廣泛的CRNN演算法。

網路結構包含三部分,從下到上依次為:

  1. 卷積層,使用CNN,作用是從輸入影象中提取特徵序列;
  2. 迴圈層,使用RNN,作用是預測從卷積層獲取的特徵序列的標籤(真實值)分佈;
  3. 轉錄層,使用CTC,作用是把從迴圈層獲取的標籤分佈通過去重整合等操作轉換成最終的識別結果;

端到端OCR的難點在哪兒呢?在於怎麼處理不定長序列對齊問題!CRNN OCR其實是借用了語音識別中解決不定長語音序列的思路。與語音識別問題類似,OCR可建模為時序依賴的詞彙或者短語識別問題。基於聯結時序分類(Connectionist Temporal Classification, CTC)訓練RNN的演算法,在語音識別領域顯著超過傳統語音識別演算法。一些學者嘗試把CTC損失函式借鑑到OCR識別中,CRNN 就是其中代表性演算法。CRNN演算法輸入100*32歸一化高度的詞條影象,基於7層CNN(普遍使用VGG16)提取特徵圖,把特徵圖按列切分(Map-to-Sequence),每一列的512維特徵,輸入到兩層各256單元的雙向LSTM進行分類。在訓練過程中,通過CTC損失函式的指導,實現字元位置與類標的近似軟對齊。

CRNN借鑑了語音識別中的LSTM+CTC的建模方法,不同點是輸入進LSTM的特徵,從語音領域的聲學特徵(MFCC等),替換為CNN網路提取的影象特徵向量。CRNN演算法最大的貢獻,是把CNN做影象特徵工程的潛力與LSTM做序列化識別的潛力,進行結合。它既提取了魯棒特徵,又通過序列識別避免了傳統演算法中難度極高的單字元切分與單字元識別,同時序列化識別也嵌入時序依賴(隱含利用語料)。在訓練階段,CRNN將訓練影象統一縮放100×32(w × h);在測試階段,針對字元拉伸導致識別率降低的問題,CRNN保持輸入影象尺寸比例,但是影象高度還是必須統一為32個畫素,卷積特徵圖的尺寸動態決定LSTM時序長度。這裡舉個例子

現在輸入有個影象,為了將特徵輸入到Recurrent Layers,做如下處理:

  • 首先會將影象縮放到 32×W×1 大小
  • 然後經過CNN後變為 1×(W/4)× 512
  • 接著針對LSTM,設定 T=(W/4) , D=512 ,即可將特徵輸入LSTM。
  • LSTM有256個隱藏節點,經過LSTM後變為長度為T × nclass的向量,再經過softmax處理,列向量每個元素代表對應的字元預測概率,最後再將這個T的預測結果去冗餘合併成一個完整識別結果即可。

CRNN中需要解決的問題是影象文字長度是不定長的,所以會存在一個對齊解碼的問題,所以RNN需要一個額外的搭檔來解決這個問題,這個搭檔就是著名的CTC解碼。
CRNN採取的架構是CNN+RNN+CTC,cnn提取影象畫素特徵,rnn提取影象時序特徵,而ctc歸納字元間的連線特性。

那麼CTC有什麼好處?因手寫字元的隨機性,人工可以標註字元出現的畫素範圍,但是太過麻煩,ctc可以告訴我們哪些畫素範圍對應的字元:

我們知道,CRNN中RNN層輸出的一個不定長的序列,比如原始影象寬度為W,可能其經過CNN和RNN後輸出的序列個數為S,此時我們要將該序列翻譯成最終的識別結果。RNN進行時序分類時,不可避免地會出現很多冗餘資訊,比如一個字母被連續識別兩次,這就需要一套去冗餘機制,但是簡單地看到兩個連續字母就去冗餘的方法也有問題,比如cook,geek一類的詞,所以CTC有一個blank機制來解決這個問題。這裡舉個例子來說明。

如上圖所示,我們要識別這個手寫體影象,標籤為“ab”,經過CNN+RNN學習後輸出序列向量長度為5,即t0~t4,此時我們要將該序列翻譯為最後的識別結果。我們在翻譯時遇到的第一個難題就是,5個序列怎麼轉化為對應的兩個字母?重複的序列怎麼解決?剛好位於字與字之間的空白的序列怎麼對映?這些都是CTC需要解決的問題。

我們從肉眼可以看到,t0,t1,t2時刻都應對映為“a”,t3,t4時刻都應對映為“b”。如果我們將連續重複的字元合併成一個輸出的話,即“aaabb”將被合併成“ab”輸出。但是這樣子的合併機制是有問題的,比如我們的標籤影象時“aab”時,我們的序列輸出將可能會是“aaaaaaabb”,這樣子我們就沒辦法確定該文字應被識別為“aab”還是“ab”。CTC為了解決這種二義性,提出了插入blank機制,比如我們以“-”符號代表blank,則若標籤為“aaa-aaaabb”則將被對映為“aab”,而“aaaaaaabb”將被對映為“ab”。引入blank機制,我們就可以很好地處理了重複字元的問題了。

但我們還注意到,“aaa-aaaabb”可以對映為“aab”,同樣地,“aa-aaaaabb”也可以對映為“aab”,也就是說,存在多個不同的字元組合可以對映為“aab”,更總結地說,一個標籤存在一條或多條的路徑。比如下面“state”這個例子,也存在多條不同路徑對映為"state":

上面提到,RNN層輸出的是序列中概率矩陣,那麼

,其中,y1−表示第一個序列輸出“-”的概率,那麼對於輸出某條路徑ππ的概率為各個序列概率的乘積。所以要得到一個標籤可以有多個路徑來獲得,從直觀上理解就是,我們輸出一張文字影象到網路中,我們需要使得輸出為標籤L的概率最大化,由於路徑之間是互斥的,對於標註序列,其條件概率為所有對映到它的路徑概率之和:

其中π∈B−1(l)的意思是,所有可以合併成l的所有路徑集合。

這種通過對映B和所有候選路徑概率之和的方式使得CTC不需要對原始的輸入序列進行準確的切分,這使得RNN層輸出的序列長度>label長度的任務翻譯變得可能。CTC可以與任意的RNN模型,但是考慮到標註概率與整個輸入串有關,而不是僅與前面小視窗範圍的片段相關,因此雙向的RNN/LSTM模型更為適合。

ctc會計算loss ,從而找到最可能的畫素區域對應的字元。事實上,這裡loss的計算本質是對概率的歸納:

如上圖,對於最簡單的時序為2的(t0t1)的字元識別,可能的字元為“a”,“b”和“-”,顏色越深代表概率越高。我們如果採取最大概率路徑解碼的方法,一看就是“--”的概率最大,真實字元為空即“”的概率為0.6*0.6=0.36。

但是我們忽略了一點,真實字元為“a”的概率不只是”aa” 即0.4*0.4 , 事實上,“aa”, “a-“和“-a”都是代表“a”,所以,輸出“a”的概率為:

0.4*0.4 + 0.4 * 0.6 + 0.6*0.4 = 0.16+0.24+0.24 = 0.64

所以“a”的概率比空“”的概率高!可以看出,這個例子裡最大概率路徑和最大概率序列完全不同,所以CTC解碼通常不適合採用最大概率路徑的方法,而應該採用字首搜尋演算法解碼或者約束解碼演算法。

通過對概率的計算,就可以對之前的神經網路進行反向傳播更新。類似普通的分類,CTC的損失函式O定義為負的最大似然,為了計算方便,對似然取對數。

我們的訓練目標就是使得損失函式O優化得最小即可。

相關文章