从0到1:神经网络实现图像识别(五)

归山深浅去,须尽丘壑美。

莫学武陵人,暂游桃源里。

--裴迪《送崔九》中唐

上一次,在

神经网络的基本结构

上,增加了隐藏层,得以提取非线性可分的高层特征,并介绍了一种过拟合的缓解方法,在MNIST的验证数据集上,得到>98%的预测准确率。现在,不借助深度学习框架,继续为这个模型引入卷积处理,使模型进化为卷积神经网络ConvolutionalNeuronNetwork),通过抽取愈丰富的高层特征,实现更精准的预测识别。

挑战

引入隐藏层,使模型通过抽取高层特征,具备了处理非线性可分样本的能力,也带来新的挑战:一个具有784维特征的样本,网络中每增加一层500个节点的隐藏层,模型需要引入 784 * 500 = 392,000 个权值w参数项和 500个偏置项b,对彩色或更大尺寸的输入图片,则需要引入更多参数,提高了模型的过拟合风险,也需要更大计算量,使更多层(更深)神经网络的训练成本上升,直至不再可行。

卷积神经网络(CNN),是应对这一挑战的有效方法。

结构

卷积神经网络(CNN)在模型中引入了新的结构:卷积层(Convolution Layer)和池化层(Pooling Layer)。

网络模型由各个处理层叠加构成,构成的模式如下:

输入层 -->{+ -->?} + -->+ -->输出层

一个或多个卷积层,可选搭配池化层,组成Conv-Pool 单元,多个Conv-Pool单元串联(如LeNet) , 或并联(如GoogLeNet)组合在一起,置于输入层和全连接层(FC Layer)之间,一起构成整个卷积神经网络。

下图例举的CNN结构,包含两组Conv_Pool单元。

输入层的图片数据不再是一阶向量(Vector),而是n阶张量(Tensor),一张RGB色彩模式的图片,可以看作深度(channel)为3的矩阵,每个深度上的矩阵元素,表达当前颜色通道下,不同位置的亮度。

与全连接层不同,卷积层与池化层相邻的节点,只有部分相互连接。输入数据经过一系列Conv-Pool单元,提取出高层特征,再由全连接层完成分类,最后通过softmax得到图像归属于各个类别的概率分布。

卷积层

Convolutional neural nets are based on the simple fact that a vision system needs to use the sameknowledge at all locations in the image. --Geoffrey Hinton 2018

卷积神经网络立足于一个简单的事实:视觉系统在图像的不同区域上,使用了相同的理解机制。

Geoffrey Hinton, at age 8, enjoying adventures with python.

权值共享

某一个特征在图片的不同位置是同等重要的,基于这个假设前提,卷积层用一套共享的权值参数 -- "过滤器(Filter)",来抽取图像所有通道的高层特征,将输入数据转换为下一层的输出,CNN中卷积过滤器也被称为“卷积核(Kernel)”。过滤器的个数也被称为卷积核的深度。

下图卷积计算的例子中,过滤器个数(Fdepth)为1,过滤器权值参数w有三个分量矩阵,分别与输入张量x的3个通道(channel)矩阵做运算。

在每个输入通道的矩阵上,依次用过滤器同尺寸(Fheight x Fwidth)的数据块(block), 与对应的w分量矩阵分别做内积运算,3个内积结果和偏置b相加,得到输出矩阵Out上对应位置的元素。

在输入矩阵上,从左到右,自上而下,依次移动选取数据块(block),完成上述运算,得到输出矩阵Out的全部元素。

步长(strides)

在输入矩阵上选取输入数据块(block)时,可以在宽(input_width)和高(input_height)两个方向上逐元素“顺序移动”,也可以用特定步长(strides>1)”跳跃移动“,压缩输出尺寸,换取计算速度提升。

为了确保所有输入元素不遗漏地参与计算,在处理之前,可能需要先对输入矩阵,做边缘填充(padding)。

边缘填充(padding)

边缘填充可以使用就近元素值,也可以像上面的例子一样,使用全零填充(zero-padding)。

是否需要填充,以及填充的尺寸padding,可以用下面的公式判断和推算:

例如:

当输入矩阵为7x7, 卷积核为3x3,步长stride=1, 输出尺寸为5 x 5 时,padding= 0, 不需要做填充。

当输入矩阵为3x3, 卷积核为2x2,步长stride=1, 输出尺寸为4 x 4 时,padding= 1,需要做一层边缘填充。

多个过滤器

为抽取更丰富的特征,一个卷积层往往有多个过滤器,即Fdepth > 1,此时,输出结果Out会有Fdepth个分量矩阵,或者说卷积层的输出深度为D = Fdepth

输入数据与过滤器后经过上述卷积运算后,在输出矩阵某个输出深度d上,位于第ij列的元素值为:

其中CH为输入数据通道(Channel),R和C分别为过滤器在高度和宽度上的尺寸。

运算结果经过ReLU激活函数,得到的当前卷积层在深度d上激活后的输出:

多个输出深度上的矩阵,共同构成当前卷积层输出a,同时也作为CNN下一层的输入数据。

CNN中的卷积,本质上是互相关(Cross-Correlation)运算,与数学上卷积的原始定义不同,两种运算用于CNN模型参数训练是等效的。

你可以进一步了解卷积和互相关,也可以跳过这一节,从池化层继续阅读,不影响对卷积神经网络的整体理解和实现。

再深一点:卷积和互相关

数学意义上的原始卷积运算,是通过两个函数f和g生成第三个函数S的运算,S表征函数f与经过翻转和平移的函数g的乘积的积分。

一阶连续形式:

回忆积分的几何意义,它是乘积函数S所围成的曲边梯形的面积(Ref:wiki)。

二阶离散形式:

对上一节的(式1),如果不考虑输入通道channel这个维度跟输出深度d,再拿掉偏置项b, 可以简化为(式1'):

比较原始卷积(Convolution)和互相关(Cross-correlation),发现仅仅是运算符号有差异,使得输入数据x,与过滤器权值矩阵w做内积运算时,x数据块(block)中元素,与w中的元素是逆序对应的,如果把w旋转180°作为过滤器,卷积运算就成了互相关运算。

这个差异,使原始卷积运算满足结合律(associative):

Fa*(Fb*G) = (Fa*Fb)*G

结合率,在需要连续多次原始卷积的场景下非常实用:你可以先把各个卷积核做卷积运算,得到新的叠加卷积核,再与输入数据做一次卷积得到结果;如果这些卷积核的权值参数都是已知常数,则可以更进一步:预先计算叠加卷积核并存储起来,成倍提高运算效率。

互相关运算不满足结合律,目前成熟的CNN模型,也不需要做多个过滤器直接叠加的互相关运算;互相关运算另有效果拔群的优化方法,实现部分会做具体介绍。

通常,互相关运算用于图像模式匹配(template matching ),原始卷积运算用于图像柔化处理(smoothing)。CNN模型训练的时候,参数不是预先固定的常量,而是通过反向传播误差损失,迭代优化得到的,所以前向传播时过滤器w无论翻转与否,对模型参数训练是等效的。

池化层

池化层改变输入张量中,各个分量矩阵的长和宽尺寸,通常仅缩小矩阵的大小,不改变三维矩阵的深度;池化层减少了全连接层中的节点个数,也通过降低整个神经网络的参数量,缓解了过拟合风险。

池化处理也称为降采样(sub-sampling),可以直观理解为,降低了图片的分辨率,同时保留图像的一部分特征。

池化层处理的做法,同样是采用固定尺寸的过滤器,在输入矩阵上依次移动,摘取对应block像素上的最大值(max-pooling)或平均值(mean-pooling),构成输出矩阵的元素,作为采样结果。

例如:图示的max-pooling处理,采用尺寸为 2x2 的过滤器,对所有深度上的节点矩阵,以步长(strides)2,在长和宽两个维度跳跃移动,摘取4像素block上的最大值作为采样结果,从而将节点矩阵尺寸压缩到原来的25%。

若size_i分别为池化层输入矩阵的尺寸,F为池化过滤器尺寸,S为步长Strides, 则可以推算输出矩阵的尺寸。

全连接层和Softmax层

经过卷积层和池化层处理,抽取得到更高层特征后,经过若干全连接层完成分类,最后由softmax层将分类结果转换为标准概率分布。

这部分的处理可参考之前的介绍,这里重点介绍CNN的学习算法。

学习算法

首先,统一本文中的标识符和特定运算符含义:

再来回顾全连接层对误差δ的反向传播。

第L层(全连接层)原始输出:

该层经过函数f激活后的输出:

第L-1层的原始输出误差 δ 可以由L层的输出误差反推得到:

参数的梯度,由该层的激活前输出误差和上一层激活后输出推算:

池化层和卷积层由于节点间是局部连接,误差传递不同于全连接层,下面分别来看。

池化层反向传播

池化层对本层输入数据,同样移动过滤器定位数据块,逐数据块(block)做获取最大值或均值的固定处理,只有步长(Strides)和过滤器尺寸(Filter_size)超参数,没有权值参数需要训练,只需要将误差 δ 反向传播至上一层。这个过程是下采样的逆处理,也称为上采样(up-sampling);最大池化层和平均池化层的上采样过程分别来看:

最大池化层(max-pooling),由于池化处理时,输出矩阵的每组数据块(block),只有max value元素被采样,所以每个数据块,max _value元素的梯度为1,其它元素不参与误差传递,梯度为0。

为了让最大池化层的误差 δ 回传时对号入座,在max-pooling处理时,输入数据每个深度上各个数据块max value的位置索引,需要在采样后保存下来,反向传播时直接复用。

平均池化层(mean-pooling),更简单些,由于池化处理时,输出矩阵的每个数据块(block),各元素的均值被采样,所以误差 δ 反向传播,只要等权重平均分配到误差矩阵各个数据块对应元素位置即可。

结合两种上采样处理,池化层对误差的反向传播可以表示为:

卷积层反向传播

步长strides为 1时 , 一个输入通道channel上,一个过滤器filter的卷积层误差δ传递 :

其中第一部分,结合cross-correlation的定义,对误差元素逐项求导可以观察到,上一层误差矩阵,是本层误差矩阵padding后,与旋转180度的filter做cross-correlation运算的结果:

说明:此处*表示互相关(cross-correlation)运算。

即:

展开为逐项累加形式:

δ 的数据块元素与过滤器w的元素,逆序对应再做内积运算 ,这是原始定义的卷积运算,与前向预测的互相关运算不同。

第二部分:

是激活函数f的导函数,两部分合在一起:

或Cross-correlation形式:

步长Strides 大于 1 时,前向卷积结果较Strides=1的情况,跳过了部分元素后构成卷积输出,所以先扩展误差矩阵,补全跳过部分的元素位置,这部分数据不在网络中传递,所以在增补位置填梯度0,问题就成了步长为1时的误差矩阵求解,仍然用(式3) 计算上一层误差。

输入通道Channel和过滤器Filter个数Depth都大于1时,把D层深度误差矩阵的每一层,逐个矩阵与该层的C个过滤器矩阵做卷积,得到C个上一层误差矩阵分量,所有D深度上的误差矩阵做同样运算,得到D组,每组C个,合计DxC个误差矩阵分量;在D这个维度上将各分量按元素相加,就合并成C个误差矩阵分量,它的维度规格和channel个通道的输入数据完全一致,这就是传导到上一层误差张量 δ 。

计算filter的权值参数梯度▽w

对第二部分,由于z是 由w作为过滤器,做cross-correlation运算得出的:

对运算逐项展开求导可以观察到,本层过滤器权值参数梯度,是以本层误差张量 δ 作为过滤器,和上一层激活后输出做cross-correlation运算的结果:

展开为逐项累加形式:

计算filter的偏置梯度▽b:

D个过滤器的卷积核,偏置项梯度同样有D个,顺位d的这个卷积核偏置项梯度,是该网络层误差张量δ 在d这个分量矩阵上各个元素的和:

以上,是CNN模型中,全连接层,以及新引入的卷积层、池化层反向传播的具体算法,也是支撑各大CNN模型的基础构件。

实现的考量

基于上述算法,可以从底层完整实现一个多层卷积神经网络;然而从工程实践考量,又迎来新的挑战:

首先,CNN较之前的多层神经网络,虽然通过权值共享和降采样处理,压缩了参数量和运算量,可是需要通过增加过滤器深度,来抽取更丰富的高层图像特征,加上新引入的多样迭代运算,使原始算法的训练成本高企。

另一方面,输入包含了多通道,使数据量倍增;反向传播过程中,除了需要缓存各层级权值参数,还需要保持反向传播的误差张量和max-pooling索引矩阵,内存开销制约mini-batch的容量上限,训练模型参数时,各方向上的更新梯度容易相互抵消,模型收敛缓慢。

下一次,将分享几种加速计算的方法,通过改进原始的池化和卷积算法,使正向和反向传播,大幅提速到工程可行级别。再为批次梯度下降优化算法,引入动量因子和自适应方法,显著提高优化效率,使模型快速收敛,将MNIST数据集上的识别准确率,进一步提高到99%以上。

(完)

索引

从0到1:神经网络实现图像识别

1.目标问题:MNIST手写数字识别数据集

3.第一个神经网络:从二分类到多分类

5.卷积神经网络:卷积与池化

(五) CNN卷积经网络(本篇)

参考

[1] 斯坦福大学 Unsupervised Feature Learning and Deep Learning tutorial :UFLDL Tutorial.

[2] 斯坦福大学 class CS231n:The Stanford CS class CS231n.

[3] Y. LeCun, L. Bottou, Y. Bengio and P. Haffner: Gradient-Based Learning Applied to Document Recognition, *Proceedings of the IEEE, 86(11):2278-2324, November 1998.

长按订阅,获得更新

可以通过“原文” 查看文中链接和Github源码。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181003G141TH00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券