【OCR技術系列之七】端到端不定長文字識別CRNN算法詳解


在以前的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層輸出的是序列中概率矩陣,那么\(p(\pi=--stta-t---e|x,S)=\prod_{t=1}^{T}y_{\pi_{t}}^{t}=(y_{-}^{1})\times(y_{-}^{2})\times(y_{s}^{3})\times(y_{t}^{4})\times(y_{t}^{5})\times(y_{a}^{6})\times(y_{-}^{7})\times(y_{t}^{8})\times(y_{-}^{9})\times(y_{-}^{10})\times(y_{-}^{11})\times(y_{e}^{12})\)

其中,\(y_{-}^{1}\)表示第一個序列輸出“-”的概率,那么對於輸出某條路徑\(\pi\)的概率為各個序列概率的乘積。所以要得到一個標簽可以有多個路徑來獲得,從直觀上理解就是,我們輸出一張文本圖像到網絡中,我們需要使得輸出為標簽L的概率最大化,由於路徑之間是互斥的,對於標注序列,其條件概率為所有映射到它的路徑概率之和:

其中\(\pi\in 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=-ln(\prod_{(x,z)\in S} p(l|x))=-\sum_{(x,z)\in S}lnp(l|x)\)

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM