写点什么

OCR 性能优化:从神经网络到橡皮泥

发布于: 2021 年 05 月 27 日

摘要:在这个算力还可以的时代,我们的研究人员一方面致力于不断地去研究各中不同的场景中的的通用网络,一方面致力于优化神经网络的学习方式,这些都是在试图化减少 AI 需要的算力资源。


本文分享自华为云社区《OCR性能优化系列(二):从神经网络到橡皮泥》,原文作者:HW007 。


OCR 是指对图片中的印刷体文字进行识别,最近在做 OCR 模型的性能优化,用 CudaC 将基于 TensorFlow 编写的 OCR 网络重写了一遍,最终做到了 5 倍的性能提升。通过这次优化工作对 OCR 网络的通用网络结构和相关的优化方法有较深的认识,计划在此通过系列博文记录下来,也作为对自己最近工作的一个总结和学习笔记。在第一篇《OCR 性能优化系列(一):BiLSTM 网络结构概览》中,从动机的角度来推演下基于 Seq2Seq 结构的 OCR 网络是如何一步步搭建起来的。接下来我们讲从神经网络到橡皮泥。

1. 深扒 CNN:也谈机器学习的本质


现在,从 OCR 性能优化系列(一)中的图 1 左下角的输入开始,串一遍图一的流程。首先是输入 27 张待识别的文字片段图片,每张图片的大小为 32*132。这些图片会经过一个 CNN 网络进行编码,输出 32 个 27*384 的初步编码矩阵。如下图所示:



​值得注意的是,在这步操作中出现了维度次序的调整,即输入由 27*(32*132)变成了 27*(384),你可以理解为把尺寸为 32*132 的图片拉长拍扁成一行(1*4224),然后再进行降维为 1*384 了,类似于上文优化策略一中做计算量优化时把 1024 降到 128 的例子。怎么做这个从 27*4224 到 27*384 的降维呢?最简单粗暴的方法就是上面的 Y=AX+B 模型,直接给 27*4224 乘以一个 4224*384 的矩阵 A,矩阵 A 是通过喂数据训练得到的。很明显,对同样的 X,不同的 A 得到的 Y 也不一样,而从 4224 到 384 这个维度降得又有点狠。于是,学者们便祭出了群殴大法,一个 A 不行,那就上多个 A 吧。于是在这里便祭出了 32 个 A,这个 32 便是后面的 LSTM 网络的序列长度,与输入图片的宽度 132 成比例。当输入图片的宽度变成 260 时,就需要 64 个 A 了。


也许有人会问在 CNN 中看不到你说的 32 个 A 啊?的确,那只是我对 CNN 网络功能的抽象,重点在于让大家对 CNN 编码过程中维度的变化及其下一层 LSTM 网络的序列长度有个形象的认识,知道 LSTM 的序列长度其实是“降维器”的数量。如果你比较机智的话,你应该发现我连“降维”都说错了,因为如果把 32 个“降维器”输出的结果拼一起的话是 32*384=12288,远大于 4224,数据经过 CNN 网络后,维度不但没有减少,反而增加了!其实这个东西比较正式的叫法是“编码器”或“解码器”,“降维器”是我本文中为了形象点自创的,不管叫猫还是叫咪,我希望你能记住,他的本质就是那个系数矩阵 A 而已。


现在我们还是沿着 32 个 A 这个思路走下去,经过 CNN 网络后,从表面上看,对于每个文字图片来说,数据维度从 32*132 变成了 32*384,表面上看这个数据量增多了,但信息量并没有增加。就像我在这长篇累牍地介绍 OCR,文字是增多了,信息量还是 OCR 的原理,或许能提升点可读性或趣味性。CNN 网络是怎么做到废话连篇(只增加数据量不增加信息量)的呢?这里隐含着一个机器学习模型中的一个顶级套路,学名叫“参数共享”。一个简单的例子,如果有一天你的朋友很高兴地告诉你他有一个新发现,“买 1 个苹果要给 5 块钱,买 2 个苹果要给 10 块钱,买 3 个苹果要给 15 块钱...”,相信你一定会怀疑他的智商,直接说“买 n 个苹果要 5*n 块钱”不就好了么?废话的本质在于,不做抽象总结,直接疯狂的举例子,给一大堆冗余数据。不废话的本质就是总结出规律和经验,所谓的机器学习就是如这个买苹果的例子,你期望简单粗暴的给机器举出大量的例子,机器便能总结出其中的规律和经验。这个规律和经验对应到上述的 OCR 例子中,便是整个模型的网络结构和模型参数。在模型网络上,目前主流还是靠人类智能,如对图片场景就上 CNN 网络,对序列场景就上 RNN 网络等等。网络结构选不对的话,学习出来的模型的效果会不好。


如上面的分析中,其实在结构上,我用 32 个 4224*384 的 A 矩阵是完全能满足上述的 CNN 在数据的输入和输出的维度尺寸上的要求的,但这样的话训练出来的模型效果会不好。因为从理论上,用 32 个 4224*384 的 A 矩阵的参数量是 32*4224*384=51904512。这便意味着在学习过程中,模型太自由了,很容易学坏。在上面买苹果的例子中,用越少的字就能陈述清楚这个事实的人的总结能力就越强,因为越多的文字除了引入冗余外,还有可能会引入一些毫不相关的信息对以后在使用这个规律的时候进行干扰,假设这个苹果的价格是用 10 个字就能说清楚,你用了 20 个字,那很有可能你其他的 10 个字是在描述比如下雨啊、时间点啊之类的无关信息。这就是大名鼎鼎的“奥坎姆剃刀法则”,不要把简单的东西复杂化!在机器学习领域主要应用于模型结构的设计和选择中,“参数共享”的是其常用的套路之一,简单说就是,如果 32 个 4224*384 的 A 矩阵参数量太大了,导致模型在学习中太自由了,那我们就加限制让模型不那么自由,一定要逼它在 10 个字内把买苹果的事情说清楚。方法很简单,比如说如果这 32 个 A 长得完全一模一样,那这里的参数个数就只有 4224*384 个而已,一下子就减少 32 倍,如果觉得减少 32 倍太狠了,那我稍微放松一点,不要求这 32 个 A 一模一样,只要求他们长得很像就好。


在此我称这个剃刀大法为“不逼模型一把就不知道模型有多优秀大法”。可能有人会提出异议,虽然我允许你用 20 个字来说清楚苹果的价格,并没有抹杀你主动性很强,追求极致,用 10 个字就说清楚了的可能性。如果上文中的只用一个 A 矩阵就能 Cover 住的话,那么我不管我给 32 个 A 还是 64 个 A,模型都应该学出的是一模一样的 A 才对。这种说法在理论上是对的,但对于目前来说却是不现实的。


讨论这个问题还得回到机器学习的本质上,所有模型都可以抽象表示为 Y=AX,其中 X 是模型输入,Y 是模型输出,A 是模型,注意在此段中的 A 不同于上文中的 A,其既包含了模型的结构和又包含了参数。模型的训练过程便是已知 X 和 Y,求解 A。上面的问题其实就是解出来的 A 是不是唯一的?首先我退一步,假设我们认为在现实世界中这个问题是存在规律的,也就是说这个 A 在现实世界中是存在且唯一的,就像物理定律一样,那我们的模型是否能在大量的训练数据中捕获到这个规律呢?


以物理上的质能方程 E=M*C^2 为例,在这个模型中,模型的结构是 E=M*C^2,模型的参数是光速 C。这个模型爱因斯坦提出的,可以说是人类智能的结晶。如果用现在 AI 大法来解决这个问题呢,分两种情况,一种是强 AI,一种是弱 AI,首先说最极端的弱 AI 的方法,也就是目前的主流的 AI 方法,大部分人工小部分机器智能,具体说就是人类根据自己的智慧发现 E 和 M 之间的关系满足 E=M*C^2 这样的模式,然后喂很多的 E 和 M 数据进去给机器,让机器把这个模型中的参数 C 学习出来,在这个例子中 C 的解是唯一的,而且只要喂给机器少量的 M 和 C,就能把 C 解出来了。显然智能部分的工作量主要在于爱因斯坦大神是如何去排除如时间、温度、湿度等等各种乱七八糟的因素,确定 E 就只和 M 有关,且满足 E=M*C^2。这部分工作在目前机器学习领域被叫模型的“特征选择”,所以很多机器学习的工程师会戏称自己为“特征工程师”。


与之相反,强 AI 的预期就应该是我们喂给机器一大堆的数据,如能量、质量、温度、体积、时间、速度等等,由机器来告诉我这里面的能量就只和质量相关,他们的关系是 E=M*C^2,并且这个常数 C 的值就是 3.0*10^8(m/s)。在这里,机器不但要把模型的结构学出来,还要把模型的参数学出来。要达到这个效果,首先要完成的第一步是,找到一种泛化的模型,经过加工后能描述世界上存在的所有模型结构,就像橡皮泥能被捏成各种各样的形状一样。这个橡皮泥在 AI 领域就是神经网络了,所以很多偏理论的 AI 书或者课程都喜欢在一开始给你科普下神经网络的描述能力,证明它就是 AI 领域的那块橡皮泥。既然橡皮泥有了,接下来就是怎么捏了,难点就在这里了,并不是生活中每个问题每个场景都能用一个个数学模型来完美表示的,就算这层成立,在这个模型结构被发现之前,没人知道这个模型是什么样子,那你让机器怎么帮你捏出这个你本身都不知道是什么形状的东西?唯一的办法就是,给机器喂很多的例子,说捏出来的东西要满足能走路、会飞等等。其实这个问题是没有唯一解的,机器可以给你捏出一只小鸟、也可以给你捏出一只小强。原因有二,其一在于你无法把所有可能的例子都喂给机器,总会有“黑天鹅”事件;其二在于喂的例子太多的话,对机器的算力要求也高,这也是为啥神经网络很早就提出来了,这几年才火起来的原因。


经过上面一段的探讨,希望你在此时能对机器学习的模型结构和模型参数能有一个比较直观的理解。知道如果靠人类智能设计出模型结构,再靠机器学习出参数的话,在模型结构准确的前提下,如上面的 E=M*C^2,我们只需要喂给机器很少的数据就好,甚至模型都能有很好的解析解!但毕竟爱因斯坦只有一个,所以更多的是我们这种平凡的人把大量的例子喂给机器,期望他能捏出我们自己都不知道的那个形状,更别提有解析解这么漂亮的性质了。对机器学习训练过程熟悉的同学应该知道,机器学习捏橡皮泥用的“随机梯度下降法”,通俗点说,就是先捏一小下,看捏出来的东西是否能满足你提的要求(即喂的训练数据),如果不满足再捏一小下,如此循环直至满足你的要求才停下来。由此可见,机器捏橡皮泥的动力在于捏出来的东西不满足你的要求。这就证明了机器是个很“懒”的东西,当他用 20 个字描述出苹果的价格规律后,就没动力去做用 10 个字来描述苹果价格的规律了。所以,当你自己都不知道你想让机器捏出什么东西时,最好就是不要给机器太大的自由,这样机器会给你捏出一个很复杂的东西,尽管能满足你的要求,也不会很好用,因为违反了剃刀法则。而在机器学习的模型中,参数的数量越多,往往意味着更大的自由度和更复杂的结构,出现“过拟合”现象。因此很多经典的网络结果中都会使用一些技巧来做“参数共享”,达到减少参数的目的,如 CNN 网络中使用的卷积的方式来做参数共享,LSTM 中引入了一个变化不那么剧烈的产量 C 矩阵来实现参数共享。

2. 牛刀小试:LSTM 等你扒


经过上面小节的探讨,相信你已经 get 到分析机器学习的一些套路了,最后以双向 LSTM 来练下手吧。



​如上图,首先看 LSTM 网络的输入和输出,最明显能看到的输入有 32 个 27*384 的紫色矩阵,输出是 32 个 27*256 的矩阵,其中 27*256 是由两个 27*128 拼凑而成,分别由前向 LSTM 和反向 LSTM 网络输出。为了简单,我们暂时只看前向 LSTM,这样的话其实输入就是 32 个 27*384 的矩阵,输出是 32 个 27*128 的矩阵。根据上文中分析的“降维器”套路,这里需要 32 个 384*128 的矩阵。在根据“参数共享”套路,真正的单个 LSTM 单元的结构如下图所示:



​由图可知,真正的 LSTM 单元中并不是一个简单的 384*128 的矩阵 A,而是把 LSTM 单元序列中的上一个单元的输出节点 H 拉下来和输入 X 拼在一起,形成一个 27*512 的输入,在乘以一个 512*512 的参数矩阵,再联合上一个序列输出的控制节点 C 对得到的数据进行加工,把 512 维降到 128 维,最后得到两个输出,一个是 27*128 的新输出节点 H,一个是 27*128 的新控制节点 C。这个新输出的 H 和 C 会被引入到下一个 LSTM 单元,对下一个 LSTM 单元的输出进行影响。


在这里,可以看到由于矩阵 C 和矩阵 H 的存在,即使 LSTM 序列 32 个单元中的那个 512*512 的参数矩阵是一模一样的,即便每个单元的输入 H 和 X 之间的关系都是不大一样的,但由于都是乘以一样的 512*512 矩阵得到的,所以尽管不一样,相互间应该还是比较像,因为他们遵循了一套规律(那个一模一样的 512*512 矩阵)。在这里我们可以看到 LSTM 是通过把上一个单元的输出 H 和输入 X 拼起来作为输入,同时引入控制矩阵 C 来实现剃刀大法,达到参数共享,简化模型的目的。这种网络结构的构造也使得当前序列单元的输出和上个序列单元的输出产生联系,适用于序列场景的建模,如 OCR、NLP、机器翻译和语音识别等等。


在这里我们还能看到,虽然神经网络是那块橡皮泥,但由于喂不了所有的情况的数据、机器算力不支持、或者我们想提高机器的学习速度,在目前的 AI 应用场景中,我们都是给根据实际应用场景精和自己的先验知识对网络结构进行精心地设计,再把这块由人类捏得差不多的橡皮泥交由机器来捏。因此我更喜欢称现在是个弱 AI 的时代,在这个算力还可以的时代,我们的研究人员一方面致力于不断地去研究各中不同的场景中的的通用网络,如用于图像的 CNN、用于序列 RNN、LSTM、GRU 等等,一方面致力于优化神经网络的学习方式,如基于 SDG 的各种优化变种算法和强化学习的训练方式等等,这些都是在试图化减少 AI 需要的算力资源。


相信在人类的努力下,强 AI 的时代会到来。


点击关注,第一时间了解华为云新鲜技术~

发布于: 2021 年 05 月 27 日阅读数: 28
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
OCR性能优化:从神经网络到橡皮泥