在之前的OCR任務中,識別過程分爲兩步:單字切割和分類任務。咱們通常都會講一連串文字的文本文件先利用投影法切割出單個字體,在送入CNN裏進行文字分類。可是此法已經有點過期了,如今更流行的是基於深度學習的端到端的文字識別,即咱們不須要顯式加入文字切割這個環節,而是將文字識別轉化爲序列學習問題,雖然輸入的圖像尺度不一樣,文本長度不一樣,可是通過DCNN和RNN後,在輸出階段通過必定的翻譯後,就能夠對整個文本圖像進行識別,也就是說,文字的切割也被融入到深度學習中去了。算法
現今基於深度學習的端到端OCR技術有兩大主流技術:CRNN OCR和attention OCR。其實這兩大方法主要區別在於最後的輸出層(翻譯層),即怎麼將網絡學習到的序列特徵信息轉化爲最終的識別結果。這兩大主流技術在其特徵學習階段都採用了CNN+RNN的網絡結構,CRNN OCR在對齊時採起的方式是CTC算法,而attention OCR採起的方式則是attention機制。本文將介紹應用更爲普遍的CRNN算法。網絡
網絡結構包含三部分,從下到上依次爲:架構
端到端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,作以下處理:測試
CRNN中須要解決的問題是圖像文本長度是不定長的,因此會存在一個對齊解碼的問題,因此RNN須要一個額外的搭檔來解決這個問題,這個搭檔就是著名的CTC解碼。
CRNN採起的架構是CNN+RNN+CTC,cnn提取圖像像素特徵,rnn提取圖像時序特徵,而ctc概括字符間的鏈接特性。字體
那麼CTC有什麼好處?因手寫字符的隨機性,人工能夠標註字符出現的像素範圍,可是太過麻煩,ctc能夠告訴咱們哪些像素範圍對應的字符:優化
咱們知道,CRNN中RNN層輸出的一個不定長的序列,好比原始圖像寬度爲W,可能其通過CNN和RNN後輸出的序列個數爲S,此時咱們要將該序列翻譯成最終的識別結果。RNN進行時序分類時,不可避免地會出現不少冗餘信息,好比一個字母被連續識別兩次,這就須要一套去冗餘機制,可是簡單地看到兩個連續字母就去冗餘的方法也有問題,好比cook,geek一類的詞,因此CTC有一個blank機制來解決這個問題。這裏舉個例子來講明。spa
如上圖所示,咱們要識別這個手寫體圖像,標籤爲「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優化得最小便可。