一文读懂CRNN+CTC文字识别

转自:https://zhuanlan.zhihu.com/p/43534801

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

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

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

图1 文字识别的步骤

文字检测类似于目标检测,即用 box 标识出图像中所有文字位置。对于文字检测不了解的读者,请参考本专栏文章:

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

本文的重点是如何对已经定位好的文字区域图片进行识别。假设之前已经文字检测算法已经定位图中的“subway”区域(红框),接下来就是文字识别。

图2 文字检测定位文字图像区域

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

 

图3 基于RNN文字识别2种基本算法框架

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

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

bai-shang/crnn_ctc_ocr_tf​github.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 Networks​ftp.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输出) [公式] 矩阵中的某一个值 [公式] (注意 [公式] 与 [公式] 含义相同,都是在 [公式] 时 [公式] 的概率):

上式中的 [公式] 是通过递推计算的常数,任何时候都可以通过递推快速获得,那么即可快速计算梯度 [公式] ,之后梯度上升算法你懂的。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值