结课:应用实例--照片字符识别 (photo OCR)

结课:应用实例--照片字符识别 (photo OCR)

1. 问题描述与流水线pipeline

​ 照片OCR意指:照片光学字符识别(photo Optical Character Recognition)。

​ 随着数码摄影的日益流行以及近年来手机中拍照功能的逐渐成熟,我们现在很容易就会有一大堆从各地拍摄的数码照片。吸引众多开发人员的一个应用是如何让计算机更好地理解这些照片的内容,照片OCR技术主要解决的问题是让计算机读出照片中拍到的文字信息。

​ 用下图的照片举例,如果计算机能够读出照片中的文字,那么下次想再把这张照片找出来时,只需要输入照片中的文字LULAB's ANTIQUE MALL,然后计算机就自动地找出这张照片,这样可以省去大把时间翻阅相片集。

![1.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fi6o8vddfwj20ew095wlo.jpg)
关于照片中的文字识别流水线大体上有三个步骤: ​ 首先,给定某张图片,需要找出这张图片中哪里有文字信息(**Text Detection**)。
![2.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fi6o8w35omj206j042abo.jpg)
​ 接下来,重点关注这些文字区域,并且在这些区域中对文字内容进行识别。这也衍生出很多应用,如:假如能为盲人提供一种照相机,这种相机可以“看见”他们前面有什么东西,可以告诉他们面前的路牌上写的是什么字等,也有研究人员将照片OCR技术应用到汽车导航系统,使汽车能读出街道的标识自动导航至目的地。具体来讲又分为:先对文字区域的矩形轮廓进行字符切分(**character segmentation**),比如下图中的“ANTIQUEMALL”,我们会试着将其分割成独立的字符。
![3.jpg](http://wx4.sinaimg.cn/mw690/7b8d2108gy1fi6o8wq1eyj209z01lmxx.jpg)
​ 最后,在成功将字段分割为独立的字符后,运行一个分类器,输入这些可识别的字符(Character recognition),识别出第一个字母是一个A、第二个字母是一个N、第三个字母是一个T等等。
![4.jpg](http://wx4.sinaimg.cn/mw690/7b8d2108gy1fi6o8x4gobj209601kmx6.jpg)
​ 实际上,现有的很多照片OCR系统会进行更为复杂的处理,比如在最后会进行拼写校正。假如字符分割和分类系统告诉你它识别到的字是“C1eaning”,那么很多拼写修正系统会告诉你这可能是单词“Cleaning”。字符分类算法刚才把字母l识别成了数字1。

​ 像这样的一个系统我们把它称之为机器学习流水线(machine learning pipeline),归纳一下,下图表示的就是照片OCR较为完整的流水线,首先将现有的图片传给文字检测系统,以识别出文字,接着将字段分割为独立的字符,最后我们对单个的字母进行识别。

![5.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6o8xd6lej20hf01qwei.jpg)
​ 设计一个机器学习系统,其中你需要作出的最重要的决定就是要怎样组织好这个流水线,换句话说在这个照片OCR问题中,应该如何将这个问题分成一系列不同的模块,需要设计这个流程以及流水线中的每一个模块,这通常会影响到最终的算法的表现。如果你有一个工程师的团队在完成同样类似的任务,那么通常可以让不同的人来完成不同的模块,如可以假设文字检测这个模块需要大概1到5个人,字符分割部分需要另外1到5个人,字母识别部分还需要另外1到5个人,因此使用流水线的方式通常提供了一个很好的办法来将整个工作分给不同的组员去完成。在复杂的机器学习系统中流水线的概念应用的就十分广泛。

2. 滑动窗 (sliding windows)

6.jpg

​ 本节关注照片OCR流水线中的组件是如何工作的,具体来说我们将关注一种叫滑动窗(sliding windows)的分类器,滑动窗的第一个应用是文字检测(text detection)中。如下图所示的照片,我们想要找出图片中出现文字的区域。文字识别是计算机视觉中的一个非同寻常的问题,因为取决于想要找到的文字的长度这些长方形区域会呈现不同的宽高比。

![7.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fi6o8yl9p1j2082070n0p.jpg)

​ 我们先看一个简单的例子,下图为行人检测(pedestrian detection),这个问题的实现似乎比文字检测的问题更简单,原因是大部分的行人都比较相似。因此可以使用一个固定宽高比(矩形的高度和宽度的比值)的矩形来分离出你希望找到的行人,在行人监测的问题中不同矩形的宽高比都是一样的,但对文字检测的问题而言,高度和宽度的比值对不同行的文字是不同的。虽然在行人检测的问题中行人可能会与相机处于不同的距离位置,因此这些矩形的高度也取决于他们离相机的距离远近,但这个比值应该是一样的。

![8.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6o8zpkklj208g06yn01.jpg)
​ 以下为建立一个行人检测系统的具体步骤,假如说我们把宽高比标准化到82:36,接下来我们要做的就是到大街上去收集一大堆正负训练样本,下图左侧这些是82×36像素大小的有行人的图像样本,而;右侧样本里没有行人的图像样本。且一共展示了12个正样本(y=1)以及12个负样本(y=0)。
![9.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6o90rrwwj20fg088wha.jpg)
​ 在更典型的行人识别应用中我们需要有1000到10000个训练样本甚至更多,在得到如此大规模的训练样本后,我们要做的事是训练一个神经网络或别的学习算法,通过输入这些82×36像素的图像块,然后对y进行分类,将图像块分成"有行人"和"没有行人"两类。

​ 现在对于下面这张新的测试集图片,我们想来试试看在这张图片中找行人,我们要做的是首先对这个图像取一小块长方形(下图中红色框表示),比如这是一个82×36的图像块,我们将这个图像块通过我们训练得到的分类器来确定这个图像块中是不是有行人,如果分类器训练良好此时应该报告这个图像块y=0,因为没有行人。接下来我们把这个红色的长方形图片向右滑动一点点,继续得到一个新的图像块(下图中蓝色块),同样把它传入我们的分类器看看这里面有没有行人。当做完后我们再向右滑动一点窗口,然后同样地把图像块传入分类器。

![10.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6o92bhgjj20b9080adv.jpg)
​ 每次滑动窗口的大小是一个参数,通常被称为步长(step size),有时也称为步幅参数(stride parameter)。若每次移动一个像素那代表用的步长或者说步幅是1,这样通常表现得最好,但可能计算量比较大。因此通常使用4个像素作为步长值或者每次8个像素等等。图中的绿色叠加框为多次移动的结果,当水平的一行全部取完后,窗块向下移动一个步长继续这样的操作,最终这些图像块会全部放入训练好的分类器中运行。

​ 需要注意的是所选的矩形非常小,只能探测到某种尺寸的行人,接下来我们看看更大的图像块,因此我们选用更大一些的图像块,如下图所示的黑色方块,传入分类器运行。这里"用更大一些的图像块"意思是:用图中较大的图像块时,应先取出这个图像块,然后把这张图像块重新压缩到82×36的尺寸,即取一个大一点的图然后重新把大小调整到小的尺寸,或者说调整到可以传入所训练出来的分类器能识别行人应该使用的尺寸。

![11.jpg](http://wx4.sinaimg.cn/mw690/7b8d2108gy1fi6o942ehhj20cq08w42f.jpg)
​ 最后完成整个过程以后,算法就能检测出图像中是否出现行人。
![12.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6o95jdg1j20bc07xjwb.jpg)
​ 接下来,我们再看看文字识别的例子,和行人检测类似,我们也可以先收集一些带标签的训练集包括正样本和负样本,分别对应文字出现的区域,正样本表示图像中有文字(y=1),负样本表示没有文字图像(y=0)。接着通过收集到的训练集对分类器进行训练。
![13.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6o96b826j20h205nwfu.jpg)
​ 为了表达方便,我们用一个固定的比例滑动窗来运行矩形窗算法,最终我们得到的结果如下图(用白色的区域表示文字检测系统已经发现了文字),图中黑色的就表示在原图中,分类器在这一个区域没有找到任何文字,灰色的阴影部分表示分类器似乎发现了文字但并不十分确信,比较白亮的区域说明分类器预测这个区域有文字有比较大的概率。
![14.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6o979fmwj208509ggnh.jpg)
​ 此时并未完成文字检测,因为我们实际上想做的是在图像中有文字的各区域都画上矩形窗,故还需要完成一步!我们取出分类器的输出,接着输入到"展开器"(expansion operator)中,展开器的作用是取过这张图片,并将每一个白色的小点都扩展为一块白色的区域,如右下侧结果。其具体的做法是对于每一个像素我们考察一下它是不是在某个白色像素的范围之内,比如在左下侧图中某个白色像素点的五或十个像素范围中,此时我们将那幅图的相同像素设为白色。
![15.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fi6o97kwydj20gt08jdht.jpg)
​ 现在所有的白色小点都做了扩展,让它们都变大了,现在可以根据右下侧的这张图锁定那些连接部分,围绕着它们画个框。我们通过文字检测找到这些有文字的长方形,现在就能剪下这些图像区域应用流水线的后面步骤对文字进行识别。

​ 流水线的第二步是字符分割,那么对于上面这样的图像,我们应该怎样分割出图像中的单个字符呢?同样地,利用监督学习算法,通过找到的一些正样本和一些负样本训练分类器以决定图像中的两个字符之间是否有一条分界线。

![16.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6o97zkt3j20g005gjs3.jpg)
​ 训练好这个分类器以后,我们将一维的矩形窗分类器因沿着一条直线从左向右判别是否需要在矩形中间画条线。同样地需要画线时,分类器会输出y=1,不要画线分开时分类器输出y=0。
![17.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6o98plh5j20db024wfk.jpg)

4.jpg

​ 流水线的最后一步是字符分类(character classification),我们可以使用一种标准的监督学习算法,比如神经网络,通过输入分割好的单个字母图像,然后将图像按字母分类化为26个字母A到Z中的一个,如果算上数字字符的话也可以有36种字符。

3. 获取更多的数据:人工数据合成(artificial data synthesis)

​ “人工数据合成”(artificialdatasynthesis)一种很容易为学习算法获取大量的训练集数据的方法,人工数据合成的概念通常包含两种不同的变体,第一种是我们从无到有来创造新的数据,第二种是我们利用已经有的一小部分带标签的训练集来扩充为一个大的训练集。

![1.jpg](http://wx4.sinaimg.cn/mw690/7b8d2108gy1fi6yf0ykk9j20fc04yq3x.jpg)
​ 为了介绍人工数据合成的概念,我们使用之前用过的照片OCR流水线中的字母识别问题(如上),假设输入一个图像数据,我们想识别出是什么字母。假如我们从其他地方收集到一大堆带标记的数据(为了方便已经把这些图像都处理为灰度图像),如下图这些正方形长宽比的图像,现在的目标就是对任意一个图像块我们要能识别出图像中心的那个字符。对于蓝色正方形块中所圈的字母,我们会识别出是一个T、一个S和一个I等等。
![2.jpg](http://wx4.sinaimg.cn/mw690/7b8d2108gy1fi6yf18vvyj206u06vtab.jpg)
​ 上图都是我们的原始图像,我们怎样获得一个更大的训练集呢?其实计算机中通常都有一个很大的字体库(如下),所以如果想要获得更多的训练样本,其中一种方法是采集同一个字符的不同种字体,接着将这些字符加上不同的随机背景,如可以取字母C剪切后把它粘贴到一个随机背景前面,接着应用某个模糊操作,比如均匀等比例缩放、旋转操作等等,这样就很容易有一个关于字母C的训练样本,重复这样的动作,一段时间后可以的到下图右侧的数据。
![3.jpg](http://wx3.sinaimg.cn/mw690/7b8d2108gy1fi6yf22o5gj204w06bjrh.jpg)

4.jpg

​ 人工数据合成的第二种方法是使用已有的样本,我们选取一个真实的样本,然后添加别的数据来扩大训练集。下图是一个字母A来自于一个真实的图像而不是一个合成的图像,现在仅在图像上加了一些灰色的网格,这时要做的就是取出这个图像,接着进行人工扭曲、人工变形,这样就从一个图像A生成16种新的样本。用这种方法可以把一个很小的带标记的训练集扩大为更多的训练样本。
![5.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6yf3v6suj20e307ztbg.jpg)
​ 在引入变形的方法中有一点需要注意,即所选择要引入的干扰或变形要能代表我们可能会在测试集中看到的噪音源或干扰项,如字符识别这个例子,下图左侧这种扭曲的方法事实上是很合理的,因为像这种扭曲的图像A的情况我们确实有可能在测试集中会遇到。相对而言为数据集仅仅添加一些纯随机的噪声通常来讲是没什么用的,如下图右侧的这四幅图像中每一个图像的每一个像素都是加入了一些随机高斯噪声后的结果,而对每个像素点也就是像素点的亮度就只是加了一些,因此这是完全没有意义的。因此除非我们觉得会在测试集中遇到这些像素的噪声的出现,否则这些随机的噪声的添加是无意义的。
![6.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6yf4y72aj20bz02v3z7.jpg)
​ 但是人工数据合成的过程其实并没有什么技巧可言,有时候只能一遍遍地尝试然后观察效果,但在确定需要添加什么样的变形时,一定要考虑好所添加的这些额外的变形是有意义的,能让你所产生的训练样本至少在某种程度上具有一定的代表性。同往常一样在花费大量精力考虑如何产生大量人工训练样本之前,通常最好应该先保证已经有了一个低偏差的分类器,这样得到大量的数据才真的会起作用。且为了获取大量数据的时候,我们应该算一算需要花多少时间、精力来收集到10倍于我们现有数据量的数据。

​ 还有一种很好的办法帮助我们收集数据,我们称之为"众包"(crowd sourcing),现在已经有一些网站或者一些服务机构通过网络雇佣一些人替我们完成标记大量训练数据的工作,当然也缺乏一定可靠性。如 “亚马逊土耳其机器人”(Amazon Mechanical Turk)就是当前最流行的一个众包选择。

4. 上限分析(ceiling analysis)

​ 同开发一个机器学习系统最宝贵的是开发系统所花费的时间,我们需要尽量避免花费大量时间在某一个模块上,而该模块上付出的劳动都对最终系统的表现并没有太大的帮助。因此这里介绍一个帮助团队尽快搞清系统中的流水线中最值得投入模块的方法:上限分析(ceiling analysis)。

​ 我们继续使用上面关于照片OCR流水线的例子,每个方框都表示一个机器学习的组成部分,这需要一个小工程师团队来完成,但问题是应该如何分配有限的资源呢?也就是说这些模块中哪一个或者哪两个等是最值得花更多的精力去改善它的效果的?

![6.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6o8xd6lej20hf01qwei.jpg)
​ 这里假如我们用字符识别的准确度作为评判的量度,现在设给定一些测试样本图像,测试后发现整个系统的估计准确率为72%,换句话说对一些测试集图像并且对测试集中的每一幅图像都对其分别运行文字检测、字符分割、字符识别。

​ 上限分析的主要思想是,首先我们关注第一个模块:文字检测,我们要做的实际上是对每一个测试集样本都为它提供一个正确的文字检测结果,换句话说,我们要模拟如果是100%正确地检测出图片中的文字信息,最终的结果应该是什么样的,即只需要找到对应的图像然后人为地识别出测试集图像中出现文字的区域,然后要做的就是让这些绝对正确的结果传给下一个模块也就是传给字符分割模块。假设现在的系统的准确率提高到89%,然后我们继续进行接着执行流水线中的下一模块字符分割,同前面一样我们人工地给出正确的字符分割结果,然后看看这样做以后效果怎样变化,假如我们这样做以后整个系统准确率提高到90%,注意跟前面一样这里说的准确率是指整个系统的准确率。最后一个模块字符识别同样也是人工给出这一模块的正确标记,这样做以后我们应该理所当然得到100%准确率。

![7.jpg](http://wx4.sinaimg.cn/mw690/7b8d2108gy1fi6yf50ttzj208j03w74h.jpg)
​ 进行上限分析的一个好处是我们知道了对每一个模块进行改善,它们各自的上升空间是多大。所以如果我们拥有完美的文字检测模块那么整个系统的表现将会从准确率72%上升到89%,效果的增益是17%,这意味着在现有系统的基础上花费时间和精力改善文字检测模块的效果,那么系统的表现可能会提高17%。同样如果我们取得完美的字符分割模块,那么最终系统表现只提升了1%,这便提供了一个重要的信息即不管我们投入多大精力在字符分割上,系统效果的潜在上升空间也都是很小很小,所以我们应该不会让一个比较大的工程师团队花时间忙于字符分割模块。最后如果我们取得完美的字符识别模块,那么整个系统的表现将提高10%,所以最后这个模块对系统的性能有较大的提高。
![8.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6yf5ksc4j20c403rt92.jpg)
​ 我们再看一个复杂一点的例子,假如想对下面这张图像进行人脸识别,也即希望识别出照片里这个人是不是你的朋友,假如我们设计了如下的流水线,第一步要做的是图像预处理(preprocess),把背景去掉。下一步我们希望检测出人脸的位置,通常我们会运行一个滑动窗分类器在人脸周围画一个框,在检测到脸部以后,如果想要识别出这个人,那么眼睛是一个很重要的线索,此外我们需要运行另一个分类器来检测脸上其他重要的部位,比如分割出鼻子、嘴巴。这样找出了眼睛、鼻子、嘴巴后,把这些特征输入给某个逻辑回归的分类器,进而辨别出这个人是谁。
![9.jpg](http://wx1.sinaimg.cn/mw690/7b8d2108gy1fi6yf75s9jj20ds088wig.jpg)

10.jpg

​ 那么对上面这个流水线怎么进行上限分析呢?我们还是每次关注一个步骤,假如整个系统的准确率达到了85%,那么要做的第一件事情还是找到测试集,然后对前景和背景进行分割(如使用Photoshop手动把背景删掉),然后观察准确率提高多少,这个例子中准确率假设仅提高了0.1%,这时它是个很明显的信号,告诉我们并不值得花太多精力在预处理或者背景移除上。

接下来再遍历测试集给出正确的脸部识别图案,接下来还是依次运行眼睛、鼻子和嘴巴的分割,依次手动给出眼睛、鼻子和嘴巴的正确位置,最后给出最终的正确标记将准确率提高到100%。

![11.jpg](http://wx2.sinaimg.cn/mw690/7b8d2108gy1fi6yf81m9zj207c0570t1.jpg)
现在从上图中可以清楚地看到通过不同的步骤,系统的表现增加了多少,比如有了完美的脸部识别,整个系统的表现似乎提高了5.9%,说明了在脸部检测上多做点努力是有意义的,其次眼部分割系统表现提高4%,逻辑回归分类器提高大约3%。这样可以很清楚地指出了哪一个模块是最值得花精力去完善的,轻重较为明显。

​ 总结一下,流水线是非常常用却也很复杂的机器学习应用,当我们在开发某个机器学习应用的时候,一定不要花时间去做一些到头来没意义的事情,而上限分析在判断应该改进哪个模块上是个很有用的工具,当我们真的把精力花在那个模块上并且改进了它,就会让整个系统的表现有一个显著的提高。

5. 课程小节(Summary)

  • 监督学习(Supervised Learning)
    --线性回归(linear regression)、逻辑回归(logistic regression)、神经网络(NN)、支持向量机(SVMs)
  • 无监督学习(Unsupervised Learning)
    --K-均值(K-means)、主成分分析(PCA)、异常检测(anomaly detection)
  • 特殊应用/话题(Special application/topics)
    --推荐系统(recommender systems)、大数据机器学习(large scale ML)
  • 构建机器学习系统的建议(Advice on building a ML system)
    --偏差/方差(Bias/variance)、正则化(regularization)、评价算法(evaluation of learning algorithm)、学习曲线(learning curves)、误差分析(error analysis)、上限分析(ceiling analysis)

END

posted @ 2017-08-03 17:45  SrtFrmGNU  阅读(730)  评论(0编辑  收藏  举报