本文的主要内容来自一篇paper,题目为:MDig: Multi-digit Recognition using Convolutional Nerual Network on Mobile,文章内容并非对这篇paper的逐句翻译,如果您在阅读过程中有什么觉得不对、或者某些地方讲的不清楚,请参考原文: web.stanford.edu/class/cs231…

前言

将纸质文档转换为数字文档有着巨大的需求,因为数字文档更容易检索。经过多年的探索和研究,OCR(Optical Character Recognition,光学字符识别)技术日趋成熟,OCR技术在印刷、打印行业应用广泛,可以快速的将纸质资料转换为电子资料。而近些年来,卷积神经网络(CNN)快速发展,是最先进的图像识别技术,其应用范围不仅仅局限于转化文档,在人脸识别、号码识别、自动驾驶等领域得到广泛应用。

先前的研究表明,使用CNN,单个数字识别可以实现低于1%的错误率。对于多位数字识别,也有人进行门牌号码、车辆VIN(Vehicle Identification Number,车辆识别码)识别之类的研究。但是,据我们所知,在移动设备上使用CNN进行多位数字识别尚未得到很好的研究。

移动解决方案具有许多优点:便携、便宜且拥有便捷的交互界面。但是,移动平台有其自身的约束,例如实时响应速度、有限的内存资源。特别是,在移动设备上运行CNN是一个具有挑战性的问题,因为传统的CNN通常需要大量的内存。虽然在服务端有一个功能强大的服务器来处理和识别图像,可以大大减少客户端的计算,但该解决方案需要可靠的网络连接,存在额外的网络延迟。

因此,在大多数情况下,我们仍然优选基于客户端的解决方案。为了达到移动客户端的性能要求,我们从以下几个方面优化了系统:

  • 分割图像 为了减少识别过程中的计算量,对原始图像进行预处理,并分割出数字,输入给CNN的是图像分割块。
  • 简单的CNN架构 与那些为识别复杂的对象而设计的最先进的深度CNN相比,我们构建了一个神经元较少的浅层网络,并只针对手写数字识别进行训练。简单的CNN只需少量的内存,并能在移动设备上快速运行,实验结果表明它仍然可以达到不错的准确度 - 错误率低于1%。
  • 批量处理全连接层 批量化处理全连接层,更多的参数得到重用,局部缓存更有效。

测试结果表明,虽然使用了相对较浅的CNN,在MNIST数据集上的单个数字识别仍可以达到99.07%的Top 1精度。通过使用上述优化方法,我们可以在大约60ms内处理一个图像帧,提取32位数字。而批量化的全连接层将CNN运行时间再减少12%。

技术方案

流水线

如前面所述,移动应用程序受限于内存和低延迟要求,我们采用几种技术来克服这些限制。多位数字的识别过程包括:

  • 预处理 将图像预处理为灰度图像,并使用Canny边缘检测来定位数字、放大数字并将背景设置为全黑以减少噪点。
  • 分割 使用轮廓查找器分割数字块,并将其调整为28×28,以便于识别。此外,系统还基于数字的位置来计算哪些数字位属于同一个数。
  • 识别 使用CNN识别每个图像块中的数字。CNN在主机上训练,移动设备加载训练好的参数。程序在全连接层中批量处理多个图像,加速CNN计算。
预处理




图1:预处理和分割步骤中的输入和中间图像

用户拍摄写在浅色纸或纸板上的手写数字的照片。然而,在真实世界的灯光下,阴影和镜面高光使得数字分割困难,难以直接识别数字。例如,在图1(a)中,数字的颜色值接近阴影,因此对图像应用全局阈值不能有效的从背景中分割出数字。为了解决这一问题,我们首先在拍摄的图像上进行预处理。预处理步骤很有用,因为它可以消除纸张和光线带来的噪音且只放大数字。

在预处理中,图像上的Canny边缘特征计算结果被输入到轮廓查找器中,绘制出每个特征的边界框。边界框的结果如图1(b)所示。为了提高预处理步骤的速度,输入图像一开始就调整为640×480,并且对颜色值进行反向处理,将浅色背景转换为深色。

接下来,我们使用如下等式计算阈值,对边界框内的像素进行阈值处理:

阀值 = 均值 + 标准偏差 / 2
复制代码

为确保阈值与边距(margin)大小无关,均值和标准偏差值使用边界框内的所有像素来计算,而不是所有的图像像素。预处理后的图像如图1(c)所示。

分割数字块

即使将图像尺寸调整为640×480,对于图像识别来说仍然太大。此外,用户可能想在同一页面上写多个数字,一次性找出每个数字是有用的。因此,分割步骤被引入进来,解决掉这两个问题。

我们分两步对图像进行分割,首先找到每个数的边界框,然后分割边界框内的每个数字位。在第一步中,我们使用轮廓查找器来定位每个数字位,并在每个数字位周围绘制边界框,然后通过计算和比较数字的位置,合并属于相同数的数字边界框。结果如图1(d)所示。在第二步中,我们使用空格从左到右扫描合并的边界框(每列之间的空列),分割出数字块。数字块的大小调整为28×28,所以它与CNN的输入大小兼容。分段的数字块如图1(e)所示。

使用CNN进行数字识别

进行数字分割之后,原始图像中的每个数字位依次缩放成28×28的图像块。图像块送入CNN进行识别。我们使用了自定义的CNN体系结构,由两个卷积层(C1和C3)、两个最大池化层(S2和S4)和两个全连接层(F5和F6)组成,如图2所示。F2的输出传给10路softmax层,它产生10个标签(即'0' - '9')上的概率分布。



图2:CNN架构

第一个卷积层(C1)用8个5×5大小的核过滤输入的28×28灰度图像,第二个卷积层(C3)使用16个大小为5×5×8的核过滤下采样后的14×14×8特征映射。两个卷积层都使用单位步幅。在S2和S4层处,使用2×2非重叠最大池化进行下采样。最后,两个全连接层F5和F6分别具有128和10个神经元。

整个神经网络的尺寸(例如卷积窗口大小、层数、内核数等)和LeNet-5接近,它是手写数字识别早期使用的CNN,但我们减少了一个全连接层。不过,我们使用了更简单但更受欢迎的组件来构建网络。例如,ReLU非线性函数用于卷积层和全连接层的输出,比sigmoid或双曲线更有效,在相同精度下训练得更快。

离线训练

我们使用Python构建和训练图2所示的CNN架构,使用MNIST作为训练数据集。使用MATLAB进行大小端格式转换后,每个输入图像是一个28×28的数字块,有着灰色背景和白色数字。我们计算图像均值,对每个图像减去均值,以形成最终的输入块。由于输入块是中心只有一个对象的单通道图像,我们没有对它执行任何数据扩充。为了加速训练过程并减少过拟合,我们在F5和F6之间插入了一个dropout层,丢弃率为0.5。初始学习速率设置为0.01,并批量处理100张图像,我们尝试了不同的学习率和正规化,发现在学习率5e-4、正规化率5e-3的时候,训练5个周期(epoch)后,模型可以达到99.07%的验证准确率。

移动端实现

鉴于相对较低的CPU性能和有限的内存资源,在移动平台上实施CNN具有一定的挑战。在这个项目中,,我们基于DeepBeliefSDK,一个面向移动平台的开源CNN框架,构建了CNN。DeepBeliefSDK使用高度优化的C++代码实现,卷积操作转换为通用矩阵-矩阵乘法(GEMM),支持好几种GEMM库。对于Android平台来说,它使用Eigen库实现了NEON优化的GEMM。

DeepBeliefSDK最初是为AlexNet而构建的,但框架的模块化允许我们大量重用代码。由于我们的CNN使用了和AlexNet相同的组件(比如卷积、全连接、ReLU、最大池化和softmax层),我们调用DeepBeliefSDK中的内部函数和类方法,手动构建网络。然后采用DeepBeliefSDK的标准文件格式保存网络,这样我们的的主应用程序可以调用DeepBeliefSDK库API使用该框架。由于我们的CNN相对较浅,网络参数(主要是32位浮点权重)没有进行压缩,而是将它们直接转储到二进制文件中,最终文件大小为426 kB。

参考实现

源码

项目源代码位于 github.com/jingpu/MDig 。

主图像处理流水线,包括预处理、分割和CNN使用C++实现,用Android NDK 10d 构建,应用程序UI使用Android SDK API 21和OpenCV java库。C++代码依赖于两个库,OpenCV和 我们修改过的DeepBeliefSDK。我们使用具有NEON优化的NDK构建带Eigen库的DeepBeliefSDK,关闭了Eigen库中的多线程优化,以便获得更一致的性能分析。

UI展示

请参看以下演示视频:

v.qq.com/x/page/n077…


本文到此结束,下一篇文章将说明如何build代码并运行,敬请关注!