CRNN

CRNN详解:https://blog.csdn.net/bestrivern/article/details/91050960
https://www.cnblogs.com/skyfsm/p/10335717.html

1 概述

传统的OCR识别过程分为两步:单字切割和分类任务。现在更流行的是基于深度学习的端到端的文字识别,即我们不需要显式加入文字切割这个环节,而是将文字识别转化为序列学习问题,虽然输入的图像尺度不同,文本长度不同,但是经过DCNN和RNN后,在输出阶段经过一定的翻译后,就可以对整个文本图像进行识别,也就是说,文字的切割也被融入到深度学习中去了。

现今基于深度学习的端到端OCR技术有两大主流技术:CRNN OCR和attention OCR。其实这两大方法主要区别在于最后的输出层(翻译层),即怎么将网络学习到的序列特征信息转化为最终的识别结果。这两大主流技术在其特征学习阶段都采用了CNN+RNN的网络结构,CRNN OCR在对齐时采取的方式是CTC算法,而attention OCR采取的方式则是attention机制
CRNN_第1张图片

本文将介绍应用更为广泛的CRNN算法。CRNN 全称为 Convolutional Recurrent Neural Network,主要用于端到端地对不定长的文本序列进行识别,不用先对单个文字进行切割,而是将文本识别转化为时序依赖的序列学习问题,就是基于图像的序列识别。

二、CRNN网络结构

CRNN_第2张图片
网络结构包含三部分,从下到上依次为:

  • 卷积层,使用CNN,作用是从输入图像中提取特征序列;
  • 循环层,使用RNN,作用是预测从卷积层获取的特征序列的标签(真实值)分布;
  • 转录层,使用CTC,作用是把从循环层获取的标签分布通过去重整合等操作转换成最终的识别结果;

端到端OCR的难点在哪儿呢?在于怎么处理不定长序列对齐问题!CRNN OCR其实是借用了语音识别中解决不定长语音序列的思路。与语音识别问题类似,OCR可建模为时序依赖的词汇或者短语识别问题。基于**联结时序分类(Connectionist Temporal Classification, CTC)**训练RNN的算法,在语音识别领域显著超过传统语音识别算法。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,即 CNN 最后得到512个特征图,每个特征图的高度为1,宽度为W/4。
  • 接着针对LSTM,设置 T=(W/4) , D=512 ,即可将特征输入LSTM。
  • LSTM有256个隐藏节点,经过LSTM后变为长度为T × nclass的向量,再经过softmax处理,列向量每个元素代表对应的字符预测概率,最后再将这个T的预测结果去冗余合并成一个完整识别结果即可。
    CRNN_第3张图片

CRNN_第4张图片

2.1 CNN

卷积层的结构图
CRNN_第5张图片
这里有一个很精彩的改动,一共有四个最大池化层,但是最后两个池化层的窗口尺寸由 2x2 改为 1x2,也就是图片的高度减半了四次(除以),而宽度则只减半了两次(除以 ),这是因为文本图像多数都是高较小而宽较长,所以其feature map也是这种高小宽长的矩形形状,如果使用1×2的池化窗口可以尽量保证不丢失在宽度方向的信息,更适合英文字母识别(比如区分i和l)。

CRNN 还引入了BatchNormalization模块,加速模型收敛,缩短训练过程。

输入图像为灰度图像(单通道);高度为32,这是固定的,图片通过 CNN 后,高度就变为1,这点很重要;宽度为160,宽度也可以为其他的值,但需要统一,所以输入CNN的数据尺寸为 (channel, height, width)=(1, 32, 160)。
CNN的输出尺寸为 (512, 1, 40)。即 CNN 最后得到512个特征图,每个特征图的高度为1,宽度为40。

注意:最后的卷积层是一个2*2,s=1,p=0的卷积,此时也是相当于将feature map放缩为原来的1/2,所以整个CNN层将图像的h放缩为原来的1/32,所以最后CNN输出的featuremap的高度为1。

2.2.Map-to-Sequence

我们是不能直接把 CNN 得到的特征图送入 RNN 进行训练的,需要进行一些调整,根据特征图提取 RNN 需要的特征向量序列。
CRNN_第6张图片
现在需要从 CNN 模型产生的特征图中提取特征向量序列,每一个特征向量(如上图中的一个红色框)在特征图上按列从左到右生成,每一列包含512维特征,这意味着第 i 个特征向量是所有的特征图第 i 列像素的连接,这些特征向量就构成一个序列。

这些特征向量序列就作为循环层的输入,每个特征向量作为 RNN 在一个时间步(time step)的输入。

2.3.RNN

因为 RNN 有梯度消失的问题,不能获取更多上下文信息,所以 CRNN 中使用的是 LSTM,LSTM 的特殊设计允许它捕获长距离依赖。
LSTM 是单向的,它只使用过去的信息。然而,在基于图像的序列中,两个方向的上下文是相互有用且互补的。将两个LSTM,一个向前和一个向后组合到一个双向LSTM中。此外,可以堆叠多层双向LSTM,深层结构允许比浅层抽象更高层次的抽象。
这里采用的是两层各256单元的双向 LSTM 网络:
CRNN_第7张图片
通过上面一步,我们得到了40个特征向量,每个特征向量长度为512,在 LSTM 中一个时间步就传入一个特征向量进行分类,这里一共有40个时间步。
我们知道一个特征向量就相当于原图中的一个小矩形区域,RNN 的目标就是预测这个矩形区域为哪个字符,即根据输入的特征向量,进行预测,得到所有字符的softmax概率分布,这是一个长度为字符类别数的向量,作为CTC层的输入。
因为每个时间步都会有一个输入特征向量 x T x^T xT,输出一个所有字符的概率分布 y T y^T yT ,所以输出为 40 个长度为字符类别数的向量构成的后验概率矩阵。
CRNN_第8张图片
然后将这个后验概率矩阵传入转录层。

2.4、CTC loss

CRNN中需要解决的问题是图像文本长度是不定长的,所以会存在一个对齐解码的问题,所以RNN需要一个额外的搭档来解决这个问题,这个搭档就是著名的CTC解码。
CRNN采取的架构是CNN+RNN+CTC,cnn提取图像像素特征,rnn提取图像时序特征,而ctc归纳字符间的连接特性。

那么CTC有什么好处?因手写字符的随机性,人工可以标注字符出现的像素范围,但是太过麻烦,ctc可以告诉我们哪些像素范围对应的字符:
CRNN_第9张图片
我们知道,CRNN中RNN层输出的一个不定长的序列,比如原始图像宽度为W,可能其经过CNN和RNN后输出的序列个数为S,此时我们要将该序列翻译成最终的识别结果。RNN进行时序分类时,不可避免地会出现很多冗余信息,比如一个字母被连续识别两次,这就需要一套去冗余机制,但是简单地看到两个连续字母就去冗余的方法也有问题,比如cook,geek一类的词,所以CTC有一个blank机制来解决这个问题。这里举个例子来说明。
CRNN_第10张图片

如上图所示,我们要识别这个手写体图像,标签为“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":
CRNN_第11张图片
上面提到,RNN层输出的是序列中概率矩阵,那么
p ( π = − − s t t a − t − − − e ∣ x , S ) = ∏ t = 1 T y π t t = ( y − 1 ) × ( y − 2 ) × ( y s 3 ) × ( y t 4 ) × ( y t 5 ) × ( y a 6 ) × ( y − 7 ) × ( y t 8 ) × ( y − 9 ) × ( y − 10 ) × ( y − 11 ) × ( y e 12 ) \begin{array}{l} p(\pi=--s t t a-t---e \mid x, S)=\prod_{t=1}^{T} y_{\pi t}^{t}=\left(y_{-}^{1}\right) \times\left(y_{-}^{2}\right) \times\left(y_{s}^{3}\right) \times\left(y_{t}^{4}\right) \times\left(y_{t}^{5}\right) \times\left(y_{a}^{6}\right) \times\left(y_{-}^{7}\right) \times\left(y_{t}^{8}\right) \times\left(y_{-}^{9}\right) \times\left(y_{-}^{10}\right) \times\left(y_{-}^{11}\right) \times\left(y_{e}^{12}\right) \end{array} p(π=sttatex,S)=t=1Tyπtt=(y1)×(y2)×(ys3)×(yt4)×(yt5)×(ya6)×(y7)×(yt8)×(y9)×(y10)×(y11)×(ye12)

其中, y − 1 y_{-}^{1} y1表示第一个序列输出“-”的概率,那么对于输出某条路径 π π π的概率为各个序列概率的乘积。所以要得到一个标签可以有多个路径来获得,从直观上理解就是,我们输出一张文本图像到网络中,我们需要使得输出为标签L的概率最大化,由于路径之间是互斥的,对于标注序列,其条件概率为所有映射到它的路径概率之和:
p ( l ∣ x ) = ∑ π ∈ B − 1 ( l ) p ( π ∣ x ) p(l \mid x)=\sum_{\pi \in B^{-1}(l)} p(\pi \mid x) p(lx)=πB1(l)p(πx)
其中 π ∈ B − 1 ( l ) \pi \in B^{-1}(l) πB1(l) 的意思是, 所有可以合并成的所有路径集合。
这种通过映射B和所有候选路径概率之和的方式使得CTC不需要对原始的输入序列进行准确的切分,这使得RNN层输出的序列长度>label长度的任务翻译变得可能。CTC可以与任意的RNN模型,但是考虑到标注概率与整个输入串有关,而不是仅与前面小窗口范围的片段相关,因此双向的RNN/LSTM模型更为适合。
ctc会计算loss ,从而找到最可能的像素区域对应的字符。事实上,这里loss的计算本质是对概率的归纳:

通过对概率的计算,就可以对之前的神经网络进行反向传播更新。类似普通的分类,CTC的损失函数O定义为负的最大似然,为了计算方便,对似然取对数。
O = − ln ⁡ ( ∏ ( x , z ) ∈ S p ( l ∣ x ) ) = − ∑ ( x , z ) ∈ S ln ⁡ p ( l ∣ x ) O=-\ln \left(\prod_{(x, z) \in S} p(l \mid x)\right)=-\sum_{(x, z) \in S} \ln p(l \mid x) O=ln(x,z)Sp(lx)=(x,z)Slnp(lx)
我们的训练目标就是使得损失函数O优化得最小即可。

通过对损失函数的计算,就可以对之前的神经网络进行反向传播,神经网络的参数根据所使用的优化器进行更新,从而找到最可能的像素区域对应的字符。
这种通过映射变换和所有可能路径概率之和的方式使得 CTC 不需要对原始的输入字符序列进行准确的切分。

测试阶段,过程与训练阶段有所不同,我们用训练好的神经网络来识别新的文本图像。这时候我们事先不知道任何文本,如果我们像上面一样将每种可能文本的所有路径计算出来,对于很长的时间步和很长的字符序列来说,这个计算量是非常庞大的,这不是一个可行的方案。
我们知道 RNN 在每一个时间步的输出为所有字符类别的概率分布,即一个包含每个字符分数的向量,我们取其中最大概率的字符作为该时间步的输出字符,然后将所有时间步得到一个字符进行拼接得到一个序列路径,即最大概率路径,再根据上面介绍的合并序列方法得到最终的预测文本结果。
在输出阶段经过 CTC 的翻译,即将网络学习到的序列特征信息转化为最终的识别文本,就可以对整个文本图像进行识别。

2.5 总结

预测过程中,先使用标准的CNN网络提取文本图像的特征,再利用BLSTM将特征向量进行融合以提取字符序列的上下文特征,然后得到每列特征的概率分布,最后通过转录层(CTC)进行预测得到文本序列。
利用BLSTM和CTC学习到文本图像中的上下文关系,从而有效提升文本识别准确率,使得模型更加鲁棒。
在训练阶段,CRNN 将训练图像统一缩放为160×32(w × h);在测试阶段,针对字符拉伸会导致识别率降低的问题,CRNN 保持输入图像尺寸比例,但是图像高度还是必须统一为32个像素,卷积特征图的尺寸动态决定 LSTM 的时序长度(时间步长)。

CTC的核心思路主要分为以下几部分:
1.它扩展了RNN的输出层,在输出序列和最终标签之间增加了多对一的空间映射,并在此基础上定义了CTC Loss函数 ----- 多对一的情况
2.它借鉴了HMM(Hidden Markov Model)的Forward-Backward算法思路,利用动态规划算法有效地计算CTC Loss函数及其导数,从而解决了RNN端到端训练的问题 ----- 动态规划的路径计算
3.最后,结合CTC Decoding算法RNN可以有效地对序列数据进行端到端的预测 -----预测问题

CTC本质上是基于最大似然的损失函数,其中很重要的就是求通过路径的最大概率乘积,但是求解很复杂,因此借鉴了Forward-Backward算法思路。高效的计算是CTC区别其他损失函数的关键。(训练的时候我们输入的标签,如何通过标签找到全部的组合,首先我们现在输入的标签每个字母之间插入一个空白‘-’,所以我们组合的路径一定大于标签的路径的。通过下面的规则找到的路径就能转化为目标路径了。具体可参考:https://blog.csdn.net/weixin_37721058/article/details/99702801)

3 OCR训练过程中遇到的问题

下述问题主要来自PaddleOCR中整理的部分问题。

1、预处理部分,图片的长和宽为什么要处理成32的倍数?
以检测中的resnet骨干网络为例,图像输入网络之后,需要经过5次2倍降采样,共32倍,因此建议输入的图像尺寸为32的倍数。

2、基于深度学习的文字检测方法有哪几种?各有什么优缺点?
A:常用的基于深度学习的文字检测方法一般可以分为基于回归的、基于分割的两大类,当然还有一些将两者进行结合的方法。

(1)基于回归的方法分为box回归和像素值回归。a. 采用box回归的方法主要有CTPN、Textbox系列和EAST,这类算法对规则形状文本检测效果较好,但无法准确检测不规则形状文本。 b. 像素值回归的方法主要有CRAFT和SA-Text,这类算法能够检测弯曲文本且对小文本效果优秀但是实时性能不够。

(2)基于分割的算法,如PSENet,这类算法不受文本形状的限制,对各种形状的文本都能取得较好的效果,但是往往后处理比较复杂,导致耗时严重。目前也有一些算法专门针对这个问题进行改进,如DB,将二值化进行近似,使其可导,融入训练,从而获取更准确的边界,大大降低了后处理的耗时。

3、对于中文行文本识别,CTC和Attention哪种更优?
A:(1)从效果上来看,通用OCR场景CTC的识别效果优于Attention,因为带识别的字典中的字符比较多,常用中文汉字三千字以上,如果训练样本不足的情况下,对于这些字符的序列关系挖掘比较困难。中文场景下Attention模型的优势无法体现。而且Attention适合短语句识别,对长句子识别比较差。

(2)从训练和预测速度上,Attention的串行解码结构限制了预测速度,而CTC网络结构更高效,预测速度上更有优势。

4、简单的对于精度要求不高的OCR任务,数据集需要准备多少张呢?
A:(1)训练数据的数量和需要解决问题的复杂度有关系。难度越大,精度要求越高,则数据集需求越大,而且一般情况实际中的训练数据越多效果越好。

(2)对于精度要求不高的场景,检测任务和识别任务需要的数据量是不一样的。对于检测任务,500张图像可以保证基本的检测效果。对于识别任务,需要保证识别字典中每个字符出现在不同场景的行文本图像数目需要大于200张(举例,如果有字典中有5个字,每个字都需要出现在200张图片以上,那么最少要求的图像数量应该在200-1000张之间),这样可以保证基本的识别效果。

5、单张图上多语种并存识别(如单张图印刷体和手写文字并存),应该如何处理?
A:单张图像中存在多种类型文本的情况很常见,典型的以学生的试卷为代表,一张图像同时存在手写体和印刷体两种文本,这类情况下,可以尝试”1个检测模型+1个N分类模型+N个识别模型”的解决方案。 其中不同类型文本共用同一个检测模型,N分类模型指额外训练一个分类器,将检测到的文本进行分类,如手写+印刷的情况就是二分类,N种语言就是N分类,在识别的部分,针对每个类型的文本单独训练一个识别模型,如手写+印刷的场景,就需要训练一个手写体识别模型,一个印刷体识别模型,如果一个文本框的分类结果是手写体,那么就传给手写体识别模型进行识别,其他情况同理。

6、目前OCR普遍是二阶段,端到端的方案在业界落地情况如何?
A:端到端在文字分布密集的业务场景,效率会比较有保证,精度的话看自己业务数据积累情况,如果行级别的识别数据积累比较多的话two-stage会比较好。百度的落地场景,比如工业仪表识别、车牌识别都用到端到端解决方案。

7、支持空格的模型,标注数据的时候是不是要标注空格?中间几个空格都要标注出来么?
A:如果需要检测和识别模型,就需要在标注的时候把空格标注出来,而且在字典中增加空格对应的字符。标注过程中,如果中间几个空格标注一个就行。

8、训练文字识别模型,真实数据有30w,合成数据有500w,需要做样本均衡吗?
A:需要,一般需要保证一个batch中真实数据样本和合成数据样本的比例是1:1~1:3左右效果比较理想。如果合成数据过大,会过拟合到合成数据,预测效果往往不佳。还有一种启发性的尝试是可以先用大量合成数据训练一个base模型,然后再用真实数据微调,在一些简单场景效果也是会有提升的。

9、文本识别中LSTM和GRU如何选择?
A:从项目实践经验来看,序列模块采用LSTM的识别效果优于GRU,但是LSTM的计算量比GRU大一些,可以根据自己实际情况选择。

10、训练识别时,如何选择合适的网络输入shape?
A:一般高度采用32,最长宽度的选择,有两种方法:

(1)统计训练样本图像的宽高比分布。最大宽高比的选取考虑满足80%的训练样本。
(2)统计训练样本文字数目。最长字符数目的选取考虑满足80%的训练样本。然后中文字符长宽比近似认为是1,英文认为3:1,预估一个最长宽度。

11、如何识别文字比较长的文本?
A:在中文识别模型训练时,并不是采用直接将训练样本缩放到[3,32,320]进行训练,而是先等比例缩放图像,保证图像高度为32,宽度不足320的部分补0,宽高比大于10的样本直接丢弃。预测时,如果是单张图像预测,则按上述操作直接对图像缩放,不做宽度320的限制。如果是多张图预测,则采用batch方式预测,每个batch的宽度动态变换,采用这个batch中最长宽度。

12、PaddleOCR如何做到横排和竖排同时支持的?
A:合成了一批竖排文字,逆时针旋转90度后加入训练集与横排一起训练。预测时根据图片长宽比判断是否为竖排,若为竖排则将crop出的文本逆时针旋转90度后送入识别网络。

13、中文文本检测、文本识别构建训练集的话,大概需要多少数据量
A:检测需要的数据相对较少,在PaddleOCR模型的基础上进行Fine-tune,一般需要500张可达到不错的效果。 识别分英文和中文,一般英文场景需要几十万数据可达到不错的效果,中文则需要几百万甚至更多。

14、文本长度超过25,应该怎么处理?
A:默认训练时的文本可识别的最大长度为25,超过25的文本会被忽略不参与训练。如果您训练样本中的长文本较多,可以修改配置文件中的 max_text_length 字段,设置为更大的最长文本长度

你可能感兴趣的:(计算机视觉)