【云驻共创】探讨文字识别中的语言模型
今天和大家一起学习文字识别中的语言模型。这是2018年发表在ACMMM的一篇论文,名为《Attention and Language Ensemble for Scene Text Recognition with Convolutional Sequence Modeling》。
本文将会根据三个部分进行解读,分别是研究背景、算法模型的剖析、代码复现。
一、研究背景
1.什么是场景文本识别
场景文本识别的任务是识别自然产品图像中的一个文字信息。自然场景图片中包含了丰富的语义信息,能够用于基于内容的图片修复、自动驾驶、图片中的文字翻译等。由于受自然场景中文本多样性、背景的复杂性等影响因素影响,自然场景文本识别任务的难度远大于扫描文档的文字识别。并且具有重大的研究意义。
2.如何理解语言模型
首先举个例子,如果不基于语言先验去翻译下面这串拼音,很可能被错误的翻译成,你西安在赶什么。
所以语言模型的作用是根据当前语境的上下文推断当前句子的意思。对于语言序列w1,w2,…wn,语言模型就是计算该序列的概率。
3.为什么我们需要语言模型呢?
文本图像中包含两层信息:视觉纹理信息和语言信息。
由于单纯根据视觉纹理信息进行文字识别缺少了对上下文的字符语义信息的挖掘,时常会导致错误的文本识别结果。因此如何获得鲁棒的语言来提升识别性能,成为了近几年场景文本识别任务中比较欢迎的一个思路。
4.了解传统的语言模型
传统使用的语言模型是N-Gram的语言模型,它的计算方法如下:
- 当n=1时,表示当前的识别结果,依靠当前信息,不需要通过之前的时间布的信息。
- 当n=2时,即当前的w1需要针对前一个字符信息进行考虑,来推断当前的结果。
- 当n=3时,需要对前两个信息进行考虑。
为了获得更好的元件模型,通常我们n会取很大,但会导致一个缺点:设计复杂,计算量大。这就是为什么传统的方法在目前很少去使用的主要的原因。
如何去替代这个传统的语言模型呢
1.基于RNN的语言建模
比较经典的一个工作是发表在2016年CVPR的一篇基于RNN的一个语言模型的文章。首先通过CNN的视觉特征提取,对每个时间步引入注意力的操作,使得每个时间步关注一个区域的视觉信息。通过参考之前时间步的信息实现语言建模,从而预测当前时间步的字符。它的优势就是省去了N-Gram语言建模的复杂方式。
2.基于Transformer的语言模型
通过双向的语言模型建模,捕获更加鲁棒的语言信息。同时引用了迭代语言矫正的操作,进一步提升识别结果。
研究的主要贡献
- 提出了一个全部基于CNN的文本识别模型,解决了RNN梯度消失的问题。
- 提出新的联合考虑视觉信息和语言信息的方法,采用来自注意力模块和语言模块的多个loss同时监督,实现端到端训练。
- 在没有字典的情况下,该方法在SVT数据集上,词准确率达到了9%。
二、基于CNN的文本识别模型的剖析
前面介绍了语言模型是怎么做的,接下来就是算法具体的实现。首先了解一下该模型。
该模型摒弃了RNN结构,仅仅使用CNN来完成。在解码器中同时考虑了视觉信息和语言信息。
编解码器的具体结构如下:
通过5种结构的编码,得到Encorder信息,然后再输入解码器,也是Decorder这里面进行解码。如下图,通过语言解码和视觉解码,最后通过线性层来进行预测。
两种算法的具体实现如下:
总结一下,该算法的创新点如下:
- 摒弃了RNN,整个模型仅使用CNN搭建而成。
- 在解码器中同时考虑视觉信息和语言信息,通过对视觉和语言模型都加入监督实现视觉和语言的联合学习。
下图可以看出两个方法之间的对比
实验结果
最终实验结果在STV上只用了一个合成数据集就取得了83.9的识别结果。
三、代码复现
接下来按照实验结果来进行代码复现,复现思路分为三步,
- 原则是先实现,在调优已达到论文的要求
- 首先在GitHub上下载开源的代码,在开源代码基础上进行调优。经过不断试错,最终选择一个基于PyTorch框架的开源代码。
- 和Modelarts环境适配之后,经过一些参数调整,在单卡和八卡训练下达到精度要求。
首先了解Modelarts环境
Modelarts是一个即开即用的在线开发环境。Modelarts集成了Jupyter notebook,可为大家提供在线交互式开发调试工具。大家可以通过创建开发环境,自行开发调试训练模型,在在线环境为大家安装常用的机器学习引擎和软件库,实现即开即用。
然后模型部署的话,在Modelarts上训练好的模型,通过模型管理和服务部署功能,可以快速发布在线推理服务,实现高吞吐、低延时支持多模型的灰度发布。同时支持批量的推理任务,处理大数大批量数据推理时,高效分布式计算Modelarts提供了模型优化能力,使模型更好的匹配边缘设备与华为的HiLens结合,可将模型轻松部署到摄像头等端测试设备快速进行应用开发。
代码讲解
接下来通过六个部分对代码进行讲解。
1.Model主体:conv-ensemble-str/model/model.py
首先经过Encoder通过对输入图片进行编码处理之后,得到一个Encoder特征,再将Encoder特征和label输入到解码器中,然后就是loss的计算,train的设置。代码如下:
class Model(object):
def __init__(self, params, mode):
self.encoder = EncoderResnet(params, mode)
self.decoder = DecoderConv(params, mode, self.charset.num_charset)
def __call__(self, features, labels):
with tf.variable_scope('model'):
features, labels = self._preprocess(features, labels) # tf.logging.info('Preprocess data.')
encoder_output = self.encoder(features) # tf.logging.info('Create encoder.')
decoder_output = self.decoder(encoder_output, labels) # tf.logging.info('Create decoder.')
if self.mode == ModeKeys.TRAIN:
loss = self._compute_loss(decoder_output, labels) # tf.logging.info('Compute loss.')
train_op = self._build_train_op(loss)
predictions = self._create_predictions(decoder_output, features, labels) tf.logging.info('Create predictions.')
return predictions, loss, train_op
2.Encoder部分:conv-ensemble-str/model/encoder_resnet.py
首先它包括了很多个resnet blocks,它通过对特征进行卷积操作,多个叠加提升它侧身表达能力,这里用的是resnet_V2的结构。代码如下:
class EncoderResnet(object):
def __call__(self, features):
# conv1
with arg_scope([layers_lib.conv2d], activation_fn=None, normalizer_fn=None):
net = resnet_utils.conv2d_same(inputs, 16, 5, stride=2, scope='conv1')
# resnet blocks
blocks = []
for i in range(len(self.encoder_params['block_name'])):
block = resnet_v2.resnet_v2_block(scope=self.encoder_params['block_name'][i],
base_depth=self.encoder_params['base_depth'][i],num_units=self.encoder_params['num_units'][i], stride=self.encoder_params['stride'][i])
blocks.append(block)
net, _ = resnet_v2.resnet_v2(net, blocks, is_training=(self.mode == ModeKeys.TRAIN), global_pool=False, output_stride=2, include_root_block=False, scope='resnet')
return net
3.Decoder部分:conv-ensemble-str/model/decoder_conv.py
如下是参考配置,下方是Decoder的训练和Decoder测试的代码:
class DecoderConv(object):
def __init__(self, params, mode, num_charset):
self.params = params
self.params.update(DECODER_DEFUALT_PARAM)
self.mode = mode
self.num_charset = num_charset
self.max_sequence_length = self.params['dataset']['max_sequence_length']
def __call__(self, encoder_output, labels):
if self.mode == ModeKeys.TRAIN:
with tf.variable_scope("decoder"):
outputs = self.conv_decoder_train(encoder_output, labels)
return outputs
else:
outputs, _, __ = self.conv_decoder_infer(encoder_output)
return outputs
4.Decoder部分,卷积语言模型:conv-ensemble-str/model/decoder_conv.py
此部分为Decoder的语言模型和attention模型的具体操作
class ConvBlock(object):
def __call__(self, encoder_output, input_embed):
# 1D convolution
for layer_idx in range(self.params['cnn_layers']):
with tf.variable_scope("conv" + str(layer_idx)):
# language module ......
# shortcut and layer norm ......
# attention module ......
# shortcut and layer norm ......
next_layer = language_layer + attention_layer
language_logit, scores = self.create_logit(language_layer,...)
attention_logit, scores = self.create_logit(attention_layer,...)
return language_logit, attention_logit, scores
5.基于CNN的Language模块:conv-ensemble-str/model/decoder_conv.py
这是一维的卷积操作,具体实现是通过GLU进行语言的建模。
language_layer = self.conv1d_weightnorm(
inputs=language_layer,
out_dim=nout * 2,
kernel_size=kernal_width,
padding="VALID",
output_collection=output_collection)
# to avoid using future information
language_layer = language_layer[:, 0:-kernal_width + 1, :]
# add GLU
language_layer = self.gated_linear_units(language_layer, output_collection)
6.Attention模块:conv-ensemble-str/model/decoder_conv.py
def attention_score(self, dec_rep, encoder_output):
N, H, W, C = encoder_output.get_shape().as_list()
N = N or tf.shape(dec_rep)[0]
M = dec_rep.get_shape().as_list()[1] or tf.shape(dec_rep)[1]
encoder_reshape = tf.reshape(encoder_output, [N, H * W, C])
# N*(H*W)*C # N*M*C ** N*(H*W)*C --> N*M*(H*W)
att_score = tf.matmul(dec_rep, encoder_reshape, transpose_b=True) * tf.sqrt(1.0 / C)
att_score = tf.nn.softmax(att_score)
# N*M*(H*W) # N*M*(H*W) ** N*(H*W)*C --> N*M*C
att_out = tf.matmul(att_score, encoder_reshape)
att_score = tf.reshape(att_score, [N, M, H, W])
return att_out, att_score
最后对语言模型进行一个总结
语言模型的优势是语言模型能够帮助在视觉信息不充足的情况下,提升识别结果。但同时有两个问题:
- 第一个问题就是out of vocabulary问题,目前基于注意力的方法容易在训练集中没有出现过的词汇中识别错误,且精度和在测试过程中使用训练集中出现过的词汇的效果之间gap远大于基础分割的识别方法,因此如何获得一个鲁棒的语言模型是一种挑战。
- 第二个问题是,它的计算复杂度比较高。对于计算量问题,虽然目前Transformer应用于识别是一种趋势,且能够通过并行计算提升识别效率,但对于长文本的识别,其计算量增加明显(RNN为线性增加,Transformer为平方增加)。
后续是否能根据这种分割和Attention结合的训练思路,来提升识别结果,或许是一个解决思路。
Modelarts有近11G的数据集。有需要的可以扫码获取算法源代码。
本文整理自【内容共创系列】IT人加薪新思路,认证华为云签约作者,赢取500元稿酬和流量扶持!
查看活动详情:https://bbs.huaweicloud.com/blogs/293957
- 点赞
- 收藏
- 关注作者
评论(0)