一文讀懂CRNN+CTC文字識別

轉載自:https://zhuanlan.zhihu.com/p/43534801

文字識別也是圖像領域一個常見問題。然而,對於自然場景圖像,首先要定位圖像中的文字位置,然後才能進行識別。

所以一般來說,從自然場景圖片中進行文字識別,需要包括2個步驟:

  • 文字檢測:解決的問題是哪裏有文字,文字的範圍有多少
  • 文字識別:對定位好的文字區域進行識別,主要解決的問題是每個文字是什麼,將圖像中的文字區域進轉化爲字符信息。
圖1 文字識別的步驟

對於文字檢測不瞭解的讀者,請參考本專欄文章:

場景文字檢測—CTPN原理與實現zhuanlan.zhihu.com圖標

本文的重點是如何對已經定位好的文字區域圖片進行識別。

最簡單的文字識別基於單字符定位+分類,即定位單個文字區域後直接進行分類。

圖2 文字檢測定位文字圖像區域

基於RNN文字識別算法主要有兩個框架:


圖3 基於RNN文字識別2種基本算法框架
  1. CNN+RNN+CTC(CRNN+CTC)
  2. CNN+Seq2Seq+Attention

本文主要介紹第一種框架CRNN+CTC,對應代碼(tf1.15實現)如下,本文介紹的CRNN網絡結構都基於此代碼。另外該代碼已經支持不定長英文識別。

bai-shang/crnn_ctc_ocr_tfgithub.com圖標

CRNN基本網絡結構

圖4 CRNN網絡結構(此圖按照本文給出的github實現代碼畫的)

整個CRNN網絡可以分爲三個部分:

假設輸入圖像大小爲 [公式],注意提及圖像都是 [公式] 形式。

  • Convlutional Layers

這裏的卷積層就是一個普通的CNN網絡,用於提取輸入圖像的Convolutional feature maps,即將大小爲 [公式] 的圖像轉換爲 [公式] 大小的卷積特徵矩陣,網絡細節請參考本文給出的實現代碼。

  • Recurrent Layers

這裏的循環網絡層是一個深層雙向LSTM網絡,在卷積特徵的基礎上繼續提取文字序列特徵。對RNN不瞭解的讀者,建議參考:

完全解析RNN, Seq2Seq, Attention注意力機制zhuanlan.zhihu.com圖標

所謂深層RNN網絡,是指超過兩層的RNN網絡。對於單層雙向RNN網絡,結構如下:

圖5 單層雙向RNN網絡

而對於深層雙向RNN網絡,主要有2種不同的實現:

tf.nn.bidirectional_dynamic_rnn
圖6 深層雙向RNN網絡
tf.contrib.rnn.stack_bidirectional_dynamic_rnn
圖7 stack形深層雙向RNN網絡

在CRNN中顯然使用了第二種stack形深層雙向結構。

由於CNN輸出的Feature map是[公式]大小,所以對於RNN最大時間長度 [公式] (即有25個時間輸入,每個輸入 [公式] 列向量有 [公式] )。

  • Transcription Layers

將RNN輸出做softmax後,爲字符輸出。

關於代碼中輸入圖片大小的解釋:

在本文給出的實現中,爲了將特徵輸入到Recurrent Layers,做如下處理:

  • 首先會將圖像在固定長寬比的情況下縮放到 [公式] 大小( [公式] 代表任意寬度)
  • 然後經過CNN後變爲 [公式]
  • 針對LSTM設置 [公式] ,即可將特徵輸入LSTM。

所以在處理輸入圖像的時候,建議在保持長寬比的情況下將高縮放到 [公式],這樣能夠儘量不破壞圖像中的文本細節(當然也可以將輸入圖像縮放到固定寬度,但是這樣由於破壞文本的形狀,肯定會造成性能下降)。

考慮訓練Recurrent Layers時的一個問題:

圖8 感受野與RNN標籤的關係

對於Recurrent Layers,如果使用常見的Softmax cross-entropy loss,則每一列輸出都需要對應一個字符元素。那麼訓練時候每張樣本圖片都需要標記出每個字符在圖片中的位置,再通過CNN感受野對齊到Feature map的每一列獲取該列輸出對應的Label才能進行訓練,如圖9。

在實際情況中,標記這種對齊樣本非常困難(除了標記字符,還要標記每個字符的位置),工作量非常大。另外,由於每張樣本的字符數量不同,字體樣式不同,字體大小不同,導致每列輸出並不一定能與每個字符一一對應。

當然這種問題同樣存在於語音識別領域。例如有人說話快,有人說話慢,那麼如何進行語音幀對齊,是一直以來困擾語音識別的巨大難題。

圖9

所以CTC提出一種對不需要對齊的Loss計算方法,用於訓練網絡,被廣泛應用於文本行識別和語音識別中。

Connectionist Temporal Classification(CTC)詳解

在分析過程中儘量保持和原文符號一致。

Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networksftp.idsia.ch

整個CRNN的流程如圖10。先通過CNN提取文本圖片的Feature map,然後將每一個channel作爲 [公式] 的時間序列輸入到LSTM中。

圖10 CRNN+CTC框架

爲了說明問題,我們定義:

  • CNN Feature map

Feature map的每一列作爲一個時間片輸入到LSTM中。設Feature map大小爲 [公式] (圖11中 [公式][公式] )。下文中的時間序列 [公式] 都從 [公式] 開始,即 [公式]

定義爲:

[公式]

其中 [公式] 每一列 [公式] 爲:

[公式]

  • LSTM

LSTM的每一個時間片後接softmax,輸出 [公式] 是一個後驗概率矩陣,定義爲:

[公式]

其中, [公式] 的每一列 [公式] 爲:

[公式]

其中 [公式] 代表需要識別的字符集合長度。由於 [公式] 是概率,所以服從概率假設:[公式]

[公式] 每一列進行 [公式] 操作,即可獲得每一列輸出字符的類別。

那麼LSTM可以表示爲:

[公式]

其中 [公式] 代表LSTM的參數。LSTM在輸入和輸出間做了如下變換:

[公式]

圖11
  • 空白blank符號

如果要進行 [公式] 的26個英文字符識別,考慮到有的位置沒有字符,定義插入blank的字符集合:

[公式]

其中blank表示當前列對應的圖像位置沒有字符(下文以[公式]符號表示blank)。

  • 關於[公式] 變換

定義變換 [公式] 如下(原文是大寫的 [公式] ,知乎沒這個符號):

[公式]

其中 [公式] 是上述加入blank的長度爲 [公式] 的字符集合,經過 [公式] 變換後得到原始 [公式] ,顯然對於[公式]的最大長度有 [公式]

舉例說明,當 [公式] 時:

[公式] [公式] [公式] [公式]

對於字符間有blank符號的則不合並:

[公式]

當獲得LSTM輸出[公式]後進行[公式]變換,即可獲得輸出結果。顯然 [公式] 變換不是單對單映射,例如對於不同的[公式]都可獲得英文單詞state。同時 [公式] 成立。

那麼CTC怎麼做?

對於LSTM給定輸入 [公式] 的情況下,輸出爲 [公式] 的概率爲:

[公式]

其中 [公式] 代表所有經過 [公式] 變換後是 [公式] 的路徑 [公式]

其中,對於任意一條路徑 [公式] 有:

[公式]

注意這裏的 [公式] 中的 [公式] ,下標 [公式] 表示 [公式] 路徑的每一個時刻;而上面 [公式] 的下標表示不同的路徑。兩個下標含義不同注意區分。

*注意上式 [公式] 成立有條件,此項不做進一步討論,有興趣的讀者請自行研究。

如對於 [公式] 的路徑 [公式] 來說:

[公式] [公式]

實際情況中一般手工設置 [公式] ,所以有非常多條 [公式] 路徑,即 [公式] 非常大,無法逐條求和直接計算 [公式] 。所以需要一種快速計算方法。

CTC的訓練目標

圖14

CTC的訓練過程,本質上是通過梯度 [公式] 調整LSTM的參數 [公式] ,使得對於輸入樣本爲 [公式] 時使得 [公式] 取得最大。

例如下面圖14的訓練樣本,目標都是使得 [公式] 時的輸出 [公式] 變大。

圖14

CTC借用了HMM的“向前—向後”(forward-backward)算法來計算 [公式]

要計算 [公式] ,由於有blank的存在,定義路徑 [公式] 爲在路徑 [公式] 每兩個元素以及頭尾插入blank。那麼對於任意的 [公式] 都有 [公式] (其中 [公式] )。如:

[公式] [公式]

顯然 [公式] ,其中 [公式] 是路徑的最大長度,如上述例子中 [公式]

定義所有經 [公式] 變換後結果是 [公式] 且在 [公式] 時刻結果爲 [公式](記爲[公式] )的路徑集合爲 [公式]

求導:

[公式]

注意上式中第二項與 [公式] 無關,所以:

[公式]

而上述 [公式] 就是恰好與概率 [公式] 相關的路徑,即 [公式] 時刻都經過 [公式] ([公式] )。

舉例說明,還是看上面的例子 [公式] (這裏的下標 [公式] 代表不同的路徑):

圖15

藍色路徑 [公式]

[公式] [公式]

紅色路徑 [公式]

[公式] [公式]

還有 [公式] 沒有畫出來。

[公式][公式] 時恰好都經過 [公式] (此處下標代表路徑 [公式][公式] 時刻的字符)。所有類似於 [公式] 經過 [公式] 變換後結果是 [公式] 且在 [公式] 的路徑集合表示爲 [公式]

觀察 [公式] 。記 [公式] 藍色爲 [公式][公式] 紅色路徑爲 [公式][公式] 可以表示:

[公式] [公式]

那麼 [公式] 可以表示爲:

[公式] [公式]

計算:

[公式]

爲了觀察規律,單獨計算 [公式]

[公式]

[公式]

[公式]

[公式]

[公式]

不妨令:

[公式]

[公式]

那麼[公式]可以表示爲:

[公式]

推廣一下,所有經過 [公式] 變換爲 [公式][公式] 的路徑(即 [公式] )可以寫成如下形式:

[公式]

進一步推廣,所有經過 [公式] 變換爲 [公式][公式] 的路徑(即 [公式] )也都可以寫作:

[公式]

所以,定義前向遞推概率和 [公式]

對於一個長度爲 [公式] 的路徑 [公式] ,其中 [公式] 代表該路徑前 [公式] 個字符, [公式] 代表後 [公式] 個字符。

[公式]

其中 [公式] 表示前 [公式] 個字符 [公式] 經過 [公式] 變換爲的 [公式] 的前半段子路徑。 [公式] 代表了 [公式] 時刻經過 [公式] 的路徑概率中 [公式] 概率之和,即前向遞推概率和。

由於當 [公式] 時路徑只能從blank或 [公式] 開始,所以 [公式] 有如下性質:

[公式] [公式] [公式]

如上面的例子中 [公式] , [公式] , [公式] 。對於所有 [公式] 路徑,當 [公式] 時只能從blank和 [公式] 字符開始。

圖16

圖16是 [公式] 時經過壓縮路徑後能夠變爲 [公式] 的所有路徑 [公式] 。觀察圖15會發現對於 [公式] 有如下遞推關係:

[公式]

也就是說,如果 [公式] 時刻是字符 [公式] ,那麼 [公式] 時刻只可能是字符 [公式] 三選一,否則經過 [公式] 變換後無法壓縮成 [公式]

那麼更一般的:

[公式]

同理,定義反向遞推概率和 [公式]

[公式]

其中 [公式] 表示後 [公式] 個字符 [公式] 經過 [公式] 變換爲的 [公式] 的後半段子路徑。 [公式] 代表了 [公式] 時刻經過 [公式] 的路徑概率中 [公式] 概率之和,即反向遞推概率和。

由於當 [公式] 時路徑只能以blank或 [公式] 結束,所以有如下性質:

[公式] [公式] [公式]

如上面的例子中 [公式] , [公式] , [公式] , [公式] 。對於所有 [公式] 路徑,當 [公式] 時只能以 [公式] (blank字符)或 [公式] 字符結束。

觀察圖15會發現對於 [公式] 有如下遞推關係

[公式]

[公式] 同理,對於 [公式] 有如下遞推關係:

[公式]

那麼forward和backward相乘有:

[公式]

或:

[公式]

注意, [公式] 可以通過圖16的關係對應,如 [公式][公式]

對比 [公式] :

[公式]

可以得到 [公式] 與forward和backward遞推公式之間的關係:

[公式]


* 爲什麼有上式 [公式] 成立呢?

回到圖15,爲了方便分析,假設只有 [公式] 共4條在 [公式] 時刻經過字符 [公式][公式] 變換爲 [公式] 的路徑,即 :

[公式]

那麼此時(注意雖然表示路徑用 [公式] 加法,但是由於 [公式][公式] 兩件獨立事情同時發生,所以 [公式] 路徑的概率 [公式] 是乘法):

[公式]

則有:

訓練CTC

對於LSTM,有訓練集合 [公式] ,其中 [公式] 是圖片經過CNN計算獲得的Feature map, [公式] 是圖片對應的OCR字符label(label裏面沒有blank字符)。

現在我們要做的事情就是:通過梯度[公式]調整LSTM的參數[公式],使得對於輸入樣本爲[公式]時有 [公式] 取得最大。所以如何計算梯度纔是核心。

單獨來看CTC輸入(即LSTM輸出) [公式] 矩陣中的某一個值 [公式] (注意 [公式][公式] 含義相同,都是在 [公式][公式] 的概率):

[公式]

上式中的 [公式] 是通過遞推計算的常數,任何時候都可以通過遞推快速獲得,那麼即可快速計算梯度 [公式] ,之後梯度上升算法你懂的。

CTC編程接口

在Tensorflow中官方實現了CTC接口:

tf.nn.ctc_loss(
    labels,
    inputs,
    sequence_length,
    preprocess_collapse_repeated=False,
    ctc_merge_repeated=True,
    ignore_longer_outputs_than_inputs=False,
    time_major=True
)

在Pytorch中需要使用針對框架編譯的warp-ctc:github.com/SeanNaren/wa

2020.4更新,目前Pytorch已經有CTC接口:

torch.nn.CTCLoss(blank=0,reduction='mean',zero_infinity=False)

CTC總結

CTC是一種Loss計算方法,用CTC代替Softmax Loss,訓練樣本無需對齊。CTC特點:

  • 引入blank字符,解決有些位置沒有字符的問題
  • 通過遞推,快速計算梯度

看到這裏你也應該大致瞭解MFCC+CTC在語音識別中的應用了(圖17來源)。

圖17 MFCC+CTC在語音識別中的應用

CRNN+CTC總結

這篇文章的核心,就是將CNN/LSTM/CTC三種方法結合:

  • 首先CNN提取圖像卷積特徵
  • 然後LSTM進一步提取圖像卷積特徵中的序列特徵
  • 最後引入CTC解決訓練時字符無法對齊的問題

即提供了一種end2end文字圖片識別算法,也算是方向的簡單入門。

特別說明

一般情況下對一張圖像中的文字進行識別需要以下步驟

  1. 定位文稿中的圖片,表格,文字區域,區分文字段落(版面分析)
  2. 進行文本行識別(識別)
  3. 使用NLP相關算法對文字識別結果進行矯正(後處理)

本文介紹的CRNN框架只是步驟2的一種識別算法,其他非本文內容。CTC你學會(fei)了麼?


想了解其他文字識別方法,請點這裏:

文字識別方法整理zhuanlan.zhihu.com圖標

求求你們點個贊吧!


本文章只是介紹ctc原理,不包含1v1輔導服務。個人工程問題切勿提問和私聊:

  • 如何做畢設
  • 如何識別中文
  • 領導要求識別身份證和發票
  • 在xxx數據集效果
  • 關於xxx代碼的xxx問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章