一文读懂CRNN+CTC文字识别

原文: https://zhuanlan.zhihu.com/p/43534801  推荐

文字识别也是图像领域一个常见问题。然而,对于自然场景图像,首先要定位图像中的文字位置,然后才能进行识别。

所以一般来说,从自然场景图片中进行文字识别,需要包括2个步骤:

  • 文字检测:解决的问题是哪里有文字,文字的范围有多少
  • 文字识别:对定位好的文字区域进行识别,主要解决的问题是每个文字是什么,将图像中的文字区域进转化为字符信息。

https://pic3.zhimg.com/80/v2-6e4468f2ae0101809d4bc681b979fdc2_hd.jpg图1 文字识别的步骤

对于文字检测不了解的读者,请参考本专栏文章:

场景文字检测—CTPN原理与实现zhuanlan.zhihu.com

本文的重点是如何对已经定位好的文字区域图片进行识别。

最简单的文字识别基于单字符定位+分类,即定位单个文字区域后直接进行分类。

https://pic4.zhimg.com/80/v2-b60d3033a91fc9476da6d9eb62837f37_hd.jpg图2 文字检测定位文字图像区域

基于RNN文字识别算法主要有两个框架:

 

https://pic2.zhimg.com/80/v2-43adade51e86ba6bf9719597080cf1b5_hd.jpg图3 基于RNN文字识别2种基本算法框架

  1. CNN+RNN+CTC(CRNN+CTC)
  2. CNN+Seq2Seq+Attention

本文主要介绍第一种框架CRNN+CTC,对应代码(Tensorflow实现)如下,本文介绍的CRNN网络结构都基于此代码。另外该代码已经支持不定英文识别。

bai-shang/crnn_ctc_ocr_tfgithub.com

CRNN基本网络结构

https://pic3.zhimg.com/80/v2-7ed5c65fe79dce49f006a9171cc1a80e_hd.jpg图4 CRNN网络结构(此图按照本文给出的github实现代码画的)

整个CRNN网络可以分为三个部分:

假设输入图像大小为  ,注意提及图像都是   形式。

  • Convlutional Layers

这里的卷积层就是一个普通的CNN网络,用于提取输入图像的Convolutional feature maps,即将大小为   的图像转换为   大小的卷积特征矩阵,网络细节请参考本文给出的实现代码。

  • Recurrent Layers

这里的循环网络层是一个深层双向LSTM网络,在卷积特征的基础上继续提取文字序列特征。对RNN不了解的读者,建议参考:

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

所谓深层RNN网络,是指超过两层的RNN网络。对于单层双向RNN网络,结构如下:

https://pic4.zhimg.com/80/v2-9f5125e0c99924d2febf25bafd019d6f_hd.jpg图5 单层双向RNN网络

而对于深层双向RNN网络,主要有2种不同的实现:

tf.nn.bidirectional_dynamic_rnn

https://pic3.zhimg.com/80/v2-c0132f0b748eb031c696dae3019a2d82_hd.jpg图6 深层双向RNN网络

tf.contrib.rnn.stack_bidirectional_dynamic_rnn

https://pic2.zhimg.com/80/v2-00861a152263cff8b94525d8b8945ee9_hd.jpg图7 stack形深层双向RNN网络

在CRNN中显然使用了第二种stack形深层双向结构。

由于CNN输出的Feature map是 大小,所以对于RNN最大时间长度   (即有25个时间输入,每个输入   列向量有   )。

  • Transcription Layers

将RNN输出做softmax后,为字符输出。

关于代码中输入图片大小的解释:

在本文给出的实现中,为了将特征输入到Recurrent Layers,做如下处理:

  • 首先会将图像在固定长宽比的情况下缩放到   大小(   代表任意宽度)
  • 然后经过CNN后变为 
  • 针对LSTM设置   ,即可将特征输入LSTM。

所以在处理输入图像的时候,建议在保持长宽比的情况下将高缩放到  ,这样能够尽量不破坏图像中的文本细节(当然也可以将输入图像缩放到固定宽度,但是这样由于破坏文本的形状,肯定会造成性能下降)。

考虑训练Recurrent Layers时的一个问题:

https://pic2.zhimg.com/80/v2-5803de0cd9eb4e20f6a722e02b196809_hd.jpg图8 感受野与RNN标签的关系

对于Recurrent Layers,如果使用常见的Softmax cross-entropy loss,则每一列输出都需要对应一个字符元素。那么训练时候每张样本图片都需要标记出每个字符在图片中的位置,再通过CNN感受野对齐到Feature map的每一列获取该列输出对应的Label才能进行训练,如图9。

在实际情况中,标记这种对齐样本非常困难(除了标记字符,还要标记每个字符的位置),工作量非常大。另外,由于每张样本的字符数量不同,字体样式不同,字体大小不同,导致每列输出并不一定能与每个字符一一对应。

当然这种问题同样存在于语音识别领域。例如有人说话快,有人说话慢,那么如何进行语音帧对齐,是一直以来困扰语音识别的巨大难题。

https://pic3.zhimg.com/80/v2-11fcc223d3288932fd15a1d2a26f2c26_hd.jpg图9

所以CTC提出一种对不需要对齐的Loss计算方法,用于训练网络,被广泛应用于文本行识别和语音识别中。

Connectionist Temporal Classification(CTC)详解

在分析过程中尽量保持和原文符号一致。

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

 

整个CRNN的流程如图11。先通过CNN提取文本图片的Feature map,然后将每一个channel作为   的时间序列输入到LSTM中。

https://pic2.zhimg.com/80/v2-6e2120edda0684a2a654d0627ad13591_hd.jpg图10 CRNN+CTC框架

为了说明问题,我们定义:

  • CNN Feature map

Feature map的每一列作为一个时间片输入到LSTM中。设Feature map大小为   (图11中     )。下文中的时间序列   都从   开始,即   

定义为:

 

其中   每一列   为:

 

  • LSTM

LSTM的每一个时间片后接softmax,输出   是一个后验概率矩阵,定义为:

 

其中,   的每一列   为:

 

其中   代表需要识别的字符集合长度。由于   是概率,所以服从概率假设:

对   每一列进行   操作,即可获得每一列输出字符的类别。

那么LSTM可以表示为:

 

其中   代表LSTM的参数。LSTM在输入和输出间做了如下变换:

 

https://pic1.zhimg.com/80/v2-7a20ee2f70e41a8662fe89fc8773ead0_hd.jpg图11

  • 空白blank符号

如果要进行   的26个英文字符识别,考虑到有的位置没有字符,定义插入blank的字符集合:

 

其中blank表示当前列对应的图像位置没有字符(下文以 符号表示blank)。

  • 关于  变换

定义变换   如下(原文是大写的   ,知乎没这个符号):

 

其中   是上述加入blank的长度为   的字符集合,经过   变换后得到原始   ,显然对于 的最大长度有   

举例说明,当   时:

     

对于字符间有blank符号的则不合并:

 

当获得LSTM输出 后进行 变换,即可获得输出结果。显然   变换不是单对单映射,例如对于不同的 都可获得英文单词state。同时   成立。

那么CTC怎么做?

对于LSTM给定输入   的情况下,输出为   的概率为:

 

其中   代表所有经过   变换后是   的路径   

其中,对于任意一条路径   有:

 

注意这里的   中的   ,下标   表示   路径的每一个时刻;而上面   的下标表示不同的路径。两个下标含义不同注意区分。

*注意上式   成立有条件,此项不做进一步讨论,有兴趣的读者请自行研究。

如对于   的路径   来说:

 

实际情况中一般手工设置   ,所以有非常多条   路径,即   非常大,无法逐条求和直接计算   。所以需要一种快速计算方法。

CTC的训练目标

https://pic3.zhimg.com/80/v2-aa0e26bbce5b7b45ce7b6c767b6584b2_hd.jpg图14

CTC的训练过程,本质上是通过梯度   调整LSTM的参数   ,使得对于输入样本为   时使得   取得最大。

例如下面图14的训练样本,目标都是使得   时的输出   变大。

https://pic2.zhimg.com/80/v2-faa81dec02bd2bb4b186808a3b7ac689_hd.jpg图14

CTC借用了HMM的“向前—向后”(forward-backward)算法来计算 

要计算   ,由于有blank的存在,定义路径   为在路径   每两个元素以及头尾插入blank。那么对于任意的   都有   (其中   )。如:

 

显然   ,其中   是路径的最大长度,如上述例子中   

定义所有经   变换后结果是   且在   时刻结果为  (记为  )的路径集合为   

求导:

 

注意上式中第二项与   无关,所以:

 

而上述   就是恰好与概率   相关的路径,即   时刻都经过   (  )

举例说明,还是看上面的例子   (这里的下标   代表不同的路径):

https://pic1.zhimg.com/80/v2-b35c7212d02f2c4847a6038a5ef9a200_hd.jpg图15

蓝色路径   

 

红色路径   

 

还有   没有画出来。

而   在   时恰好都经过   (此处下标代表路径   的   时刻的字符)。所有类似于   经过   变换后结果是   且在   的路径集合表示为   

观察   。记   蓝色为   ,   红色路径为   ,   可以表示:

 

那么   可以表示为:

 

计算:

 

为了观察规律,单独计算   

 

 

 

 

 

不妨令:

 

 

那么 可以表示为:

 

推广一下,所有经过   变换为   且   的路径(即   )可以写成如下形式:

 

进一步推广,所有经过   变换为   且   的路径(即   )也都可以写作:

 

所以,定义前向递推概率和   

对于一个长度为   的路径   ,其中   代表该路径前   个字符,   代表后   个字符。

 

其中   表示前   个字符   经过   变换为的   的前半段子路径。   代表了   时刻经过   的路径概率中   概率之和,即前向递推概率和。

由于当   时路径只能从blank或   开始,所以   有如下性质:

   

如上面的例子中   ,   ,   。对于所有   路径,当   时只能从blank和   字符开始。

https://pic2.zhimg.com/80/v2-2371b50895b935ecb8f3925a0462e5e5_hd.jpg图16

图16是   时经过压缩路径后能够变为   的所有路径   。观察图15会发现对于   有如下递推关系:

 

也就是说,如果   时刻是字符   ,那么   时刻只可能是字符   三选一,否则经过   变换后无法压缩成   

那么更一般的:

 

同理,定义反向递推概率和   

 

其中   表示后   个字符   经过   变换为的   的后半段子路径。   代表了   时刻经过   的路径概率中   概率之和,即反向递推概率和。

由于当   时路径只能以blank或   结束,所以有如下性质:

   

如上面的例子中   ,   ,   ,   。对于所有   路径,当   时只能以   (blank字符)或   字符结束。

观察图15会发现对于   有如下递推关系

 

与   同理,对于   有如下递推关系:

 

那么forward和backward相乘有:

 

或:

 

注意,   可以通过图16的关系对应,如   

对比   :

 

可以得到   与forward和backward递推公式之间的关系:

 

 

* 为什么有上式   成立呢?

回到图15,为了方便分析,假设只有   共4条在   时刻经过字符   且   变换为   的路径,即 :

 

那么此时(注意虽然表示路径用   加法,但是由于   和   两件独立事情同时发生,所以   路径的概率   是乘法):

 

则有:

https://pic2.zhimg.com/80/v2-ef2eaf1c36fe5af6a0e0e1a0f4cc4955_hd.jpg

训练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:https://github.com/SeanNaren/warp-ctc

CTC总结

CTC是一种Loss计算方法,用CTC代替Softmax Loss,训练样本无需对齐。CTC特点:

  • 引入blank字符,解决有些位置没有字符的问题
  • 通过递推,快速计算梯度

看到这里你也应该大致了解MFCC+CTC在语音识别中的应用了(图17来源)。

https://pic4.zhimg.com/80/v2-31abfbb4be0cc6995367ea956488c20b_hd.jpg图17 MFCC+CTC在语音识别中的应用

CRNN+CTC总结

这篇文章的核心,就是将CNN/LSTM/CTC三种方法结合:

  • 首先CNN提取图像卷积特征
  • 然后LSTM进一步提取图像卷积特征中的序列特征
  • 最后引入CTC解决训练时字符无法对齐的问题

即提供了一种end2end文字图片识别算法,也算是方向的简单入门。

特别说明

一般情况下对一张图像中的文字进行识别需要以下步骤

  1. 定位文稿中的图片,表格,文字区域,区分文字段落(版面分析)
  2. 进行文本行识别(识别)
  3. 使用NLP相关算法对文字识别结果进行矫正(后处理)

本文介绍的CRNN框架只是步骤2的一种识别算法,其他非本文内容。CTC你学会(fei)了么?

 

发布了62 篇原创文章 · 获赞 235 · 访问量 169万+

猜你喜欢

转载自blog.csdn.net/javastart/article/details/104061167