代码
MindSpore系列分享(二)

MindSpore系列分享(二)

MindSpore系列分享(二)

本系列分享我们将一起理解MindSpore官网中展示的MNIST图片数据集分类项目。此项目中使用的是LeNet卷积神经网络、softmax输出、交叉熵损失函数以及动量优化方法。本次我们将分块讲解图片分类的工作原理。

1.LeNet卷积

首先了解一下卷积核数组在输入数组中的计算方式,虽然卷积层得名于卷积(convolution)运算,但我们通常在卷积层中使用更加直观的互相关(cross-correlation)运算。在二维卷积层中,一个二维输入数组和一个二维核(kernel)数组通过互相关运算输出一个二维数组。

图1:互相关运算

这里我们设置一个高和宽均为3的二维输入数组。我们可以将该数组形状记为3×3。核数组的高和宽分别是2。核数组在卷积计算中又称卷积核或过滤器(filter)。卷积核的高和宽决定卷积核窗口的形状,可以记为2×2。

在二维互相关运算过程中,卷积窗口将会从输入数组的最左上方开始计算,按照从左到右,从上到下的顺序,依次在输入数组区域内滑动,这里默认情况下卷积核移动的步幅是每次一行或一列。当卷积窗口滑动到输入数组的某一位置时,窗口中的输入子数组与核数组按元素相乘并求和,得到输出数组中相应位置的元素。二维互相关运算得出:

0×0+1×1+3×2+4×3=19

1×0+2×1+4×2+5×3=25

3×0+4×1+6×2+7×3=37

4×0+5×1+7×2+8×3=43

这就是卷积计算的过程,二维卷积层将输入数组和卷积核做互相关运算,并加上一个标量偏差来得到输出。卷积层的模型参数包括了卷积核和标量偏差。在训练模型的时候,通常我们需要先对卷积核随机初始化,然后再不断迭代卷积核和偏差。

实际上,卷积运算与互相关运算是类似的。想要得到卷积运算的输出,我们只需将核数组左右翻转并上下翻转,再与输入数组做互相关运算。所以,卷积运算和互相关运算虽然类似,但如果他们使用相同的核数组和输入数组,输出也并不一定会相同。如果卷积核数组元素在横轴和竖轴上都对称,那么卷积运算和互相关运算结果相同。

特征图(feature map)是二维卷积层输出的二维数组可以看作输入空间维度上某一级的表征。影响元素x的前向计算的所有可能输入区域叫做x的感受野(receptive field)。我们可以设置更深的卷积神经网络,那么输入数组通过每一层卷积后,高和宽会渐渐变小,特征图中单个元素的感受野就会变得更加广阔,从而捕捉输入上更大尺寸的特征。

1.1 填充

填充(padding)是表示在输入高和宽的输入数组两侧填充元素(通常是0元素)。如图2中,我们在输入数组的高和宽的两侧分别添加值为0的元素,每一边新增一层,使得输入高和宽从3变成5,并导致输出高和宽从2变成4。

图2:填充后互相关运算

一般卷积情况下,假设输入数组高和宽分别是nh、nw,卷积核数组的高和宽分别是kh、kw,如果在输入数组高的两侧一共填充ph行,在宽的两侧一共填充pw列,那么输出数组形状将会是

(nh-kh+ph+1)×(nw-kw+pw+1)

这就证明,输出数组的高和宽会分别增加ph和pw。所以,如果想要了解在构造网络时推测每个层的输出形状。我们可以设置ph=kh -1和pw=kw-1使得输入和输出具有相同的高和宽。

1.2 步幅

步幅是指核数组在输入数组在按照从左到右,从上到下移动过程中,每移动一次核数组跨过的元素行数或列数。目前我们看到的例子里,在高和宽两个方向上步幅均为1。我们也可以使用更大步幅。图3展示了在高上步幅为3、在宽上步幅为2的二维互相关运算。可以看到,输出第一列第二个元素时,卷积窗口向下滑动了3行,而在输出第一行第二个元素时卷积窗口向右滑动了2列。当卷积窗口在输入上再向右滑动2列时,由于输入元素无法填满窗口,无结果输出。图3中的阴影部分为输出元素及其计算所使用的输入和核数组元素。

图3:设定步幅后互相关运算

一般来说,当高上步幅为sh,宽上步幅为sw时,输出形状为

⌊(nh−kh+ph+sh)/sh⌋×⌊(nw−kw+pw+sw)/sw⌋

如果设置ph=kh−1ph=kh−1和pw=kw−1pw=kw−1,那么输出形状可以表示为

⌊(nh+sh−1)/sh⌋×⌊(nw+sw−1)/sw⌋

更进一步,如果输入的高和宽能分别被高和宽上的步幅整除,那么输出形状将是(nh/sh)×(nw/sw)。

填充作用于输出数组的高和宽。这常用来使输出与输入具有相同的高和宽。

步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的1/n(n为大于1的整数)。

1.3 多输入通道

我们上面用到的输入和输出都是二维数组,但是真实的数据往往维度会更高。例如,彩**片数据集在高和宽的维度外还有RGB(红、绿、蓝)3个颜色通道。假设彩**像的高和宽分别是h和w(像素),那么它可以表示为3×h×w的多维数组。我们将大小为三的这一维称为通道(channel)维。

当输入数据含有多个通道时,我们需要构造一个输入通道与输入数据的通道数相同的卷积核,从而能够与含多通道的输入数据做互相关运算。假设输入数据的通道数为ci,那么卷积核的输入通道同样为ci。设卷积核窗口形状为kh×kw。当ci=1时。此时的卷积核是只包含一个形状为kh×kw的二维数组。当ci>1时,每一个输入通道各分配一个形状为kh×kw的核数组。Ci个数组与输入通道维上连结,得到形状为ci×kh×kw的卷积核。此时卷积运算是各个通道上对二维数组和卷积核的二维数组做互相关运算,再将ci个通道的二维输出相加,得到一个二维数组。

下面展示的是2个输入通道的二维互相关计算,二维输入数组与二维核数组做互相关运算,再按通道数相加得到输出。

图4:多输入通道互相关运算

(1×1+2×2+4×3+5×4)+(0×0+1×1+3×2+4×3)=56

1.4 多输出通道

通过上面多输入通道的介绍,我们发现对各个通道的结果做累加,无论输入通道数是多少,输出通道总是1。假设输入通道数为ci,想要得到输出通道数为co,我们可以为每个输出通道分别创建形状为ci×kh×kw的核数组。那么此时的卷积核形状为co×ci×kh×kw。在做互相关运算时,每个输出通道上的结果由卷积核在该通道上的核数组与整个输入数组计算得出。

1×1卷积层是卷积窗口形状为1×1的多通道卷积层。因为使用了最小窗口,所以1×1卷积是不具有识别高和宽维度上相邻元素构成的模式的功能。1×1卷积主要使用在通道维度上。如图5所示,输入数组形状是3×3×3,卷积核形状为2×3×1×1,我们得到的输出数组形状为2×3×3。输入与输出具有相同的高和宽。输出中的每个元素来自输入中高和宽上相同位置的元素在不同通道之间按权重累加。假设我们将通道维当做特征维,将高和宽维度上的元素当成数据样本,那么1×1卷积层的作用于全连接层等价。

图5:1×1卷积互相关运算

使用多通道可以拓展卷积层的模型参数,1×1卷积层通常是用来调整网络层之间的通道数,控制模型的复杂度。

2. LeNet池化和全连接

在实际图像中,我们要检测的物体不会出现在固定的位置,即使我们连续拍摄同一个物体也会出现偏移。这会导致同一个边缘对应的输出可能出现在卷积输出Y中的不同位置,从而对后面的模式识别造成困难。本片分享我们介绍池化(pooling)层,它的主要作用便是为了缓解卷积层对位置上的过度敏感性。

2.1 二维最大池化层和平均池化层

和卷积计算类似,池化层每一次对输入数组的一个固定形窗口中的元素计算输出。该窗口也称池化窗口。与卷积层的运算法则不同,池化层的法则是直接计算池化窗口元素中的最大值或者平均值。所以也分别叫做最大池化和平均池化。在二维最大池化层中,池化窗口从输入数组的左上角开始,按照从左到右,从上到下的顺序,在输入数组中滑动。当池化窗口滑动到某一个位置时,窗口中的输入子数组的最大值就是输出数组中相应位置的元素。

如下图1中所示,池化的窗口形状是2×2的最大池化,默认步幅都为1。阴影部分为第一个输出元素和计算所使用的输入元素。输出数组的高和宽分别为2,其中的4个元素由取最大值运算max得出:

max(0,1,3,4)=4,

max(1,2,4,5)=5,

max(3,4,6,7)=7,

max(4,5,7,8)=8.

图6:2×2最大池化层图

平均池化层的计算方法与最大池化层类似,只是将每次求池化窗口中的元素最大值改为求平均值。池化窗口形状为p×q的池化层称为p×q池化层,其中的池化运算叫作p×q池化。

在最开始我们提到的物体边缘检测的例子。现在我们将卷积层的输出作为2×2最大池化的输入。设该卷积层输入是X、池化层输出为Y。无论是X[i,j]和X[i,j+1]值不同,还是X[i,j+1]和X[i,j+2]不同,池化层输出均有Y[i,j]=1。也就是说,使用2×2最大池化层时,只要卷积层识别的模式在高和宽上移动不超过一个元素,我们依然可以将它检测出来。

2.2 填充、步幅和多通道

和卷积层一样,池化层也可以在输入数组的高和宽两侧填充0元素。并可以设置窗口的移动步幅来改变输出数组形状。池化层填充和步幅与卷积层填充和步幅的工作机制一样。

池化层在处理多通道输入数组的时候,与卷积层的工作机制是不同的,卷积层中是将每个通道中的输入数组卷积运算后再相加,会导致输出通道变为1个。而池化层则是在每个出入通道中池化计算,但不将池化结果相加,可以保证输入通道数与输出通道数相同。

2.3 LeNet卷积网络

图7:LeNet卷积网络

如上图中所示,是LeNet卷积网络的整体流程图,整体包含8个网络层,下面我们将了解每一层的计算。

输入层:我们使用的数据集是MNIST数据集,该数据集中的样本数据都是规格为32×32的灰度图,我们以1个样本图片为例。那么我们输入的图片规格就是1×1×32×32,表示一个通道输入1个32×32的数组。

C1层:C1层中数组规格为6×1×28×28,从1×1×32×32卷积得到。首先需要6个批次的卷积数组,每一个批次中都有1个规格为5×5的卷积数组,卷积步幅默认为1。即卷积数组规格为6×1×5×5。

该卷积层共有6+1×5×5×6=156个参数,其中6个偏置参数。这一层网络**有6×1×28×28=4704个节点,每个节点和当前层5×5=25个节点相连,所以本层卷积层共有6×(1×28×28)×(1×5×5+1)=122304个全连接。

S2层:S2层的数组规格为6×1×14×14,从1×1×28×28卷积得到。使用的是2×2,步幅为1的最大池化操作,所以并不改变批次数,只是将每一个输入数组从28×28降到14×14的输出数组。

该池化层共有6×2=12个可训练参数,以及6×(1×14×14)×(2×2+1)=5880个全连接。

C3层:C3层的数组规格为16×1×10×10,从6×1×14×14卷积得到。输出通道数数改变,所以卷积数组需要16批卷积数组,每一批中有6个卷积核与输入通道对应,每一个卷积数组规格都是5×5,步幅为1。即卷积数组规格为16×6×5×5。

该卷积层共有16+1×5×5×16=2416个参数,其中16个偏置参数。这一层网络**有16×1×10×10=1600个节点,每个节点和当前层5×5=25个节点相连,所以本层卷积层共有16×(1×10×10)×(1×5×5+1)=41600个全连接。

S4层:S4层的数组规格为16×1×5×5,这一层池化与S2层池化设置相同。所以输出数组只改变每一个数组的规格,不改变数量。

该池化层共有16×2=32个可训练参数,以及16×(1×5×5)×(2×2+1)=2000个全连接。

C5层:C5层是规格为120×1的一维向量,那么需要将S4层数组转换成一维向量,输入的数组规格是1×(16×1×5×)=1×400。使用全连接层将1×400转为1×120的向量。在全连接层中,每一个节点计算处结果后,都需要再经过激活函数计算,得出的值为输出的值。

该连接层共有5×5×16=400个输入节点,参数个数为5×5×16×120+120=48120个,输出节点120个。

F6层:F6层是规格为84×1的一维向量,与C5层计算相同,也是通过全连接层计算得到。为什么要转成84个神经元向量呢,如下图中所示,是所有字符标准格式,规格为12×7.所以有84个像素点,然后使用F6层的向量与这些标准图计算相似度。

该连接层共有120个输入节点,参数个数为120×84+84=10164个,输出节点84个。

图8:字符标准图

输出层:该连接层共有84个输入节点,参数个数为84×10+10=850个,输出节点10个。

输出层使用Softmax函数做多分类,在Softmax用于多分类过程中,它将多个神经元的输出,映射到(0,1)区间中,可以看作是每一个类别的概率值,从而实现多分类。Softmax从字面上来看,可以分成Soft和max两部分。Softmax的核心是Soft,对于图片分类来说,一张图片或多或少都会包含其它类别的信息,我们更期待得到图片对于每个类别的概率值,可以简单理解为每一个类别的可信度;max就是最大值的意思,选择概率值最大的当作分类的类别。

下面给出Softmax函数的定义

图9:Softmax表达式

其中zi是第i个节点的输出值,C是输出节点的个数,即分类类别的个数。通过Softmax函数可以将多分类的输出值转换为范围在[0,1],并且总和为1的概率分布。当使用Softmax函数作为输出节点的激活函数的时候,一般使用交叉熵作为损失函数。模型有了损失函数后,就可以使用梯度下降的方法求解最优参数值。

3. 交叉熵损失函数

模型在项目中的职责是拟合数据的规则特性,拟合的程度我们引入损失函数表示,接下来需要定义损失函数(Loss)。损失函数是深度学习的训练目标,也叫目标函数,可以理解为神经网络的输出(Logits)和标签(Labels)之间的距离,是一个标量数据。

本次模型输出是⼀个图像类别这样的离散值。对于这样的离散值预测问题,我们可以使⽤诸如softmax回归在内的分类模型。和线性回归不同,softmax回归的输出单元从⼀个变成了多个,且引⼊了softmax运算 使输出更适合离散值的预测和训练。

图10:softmax全连接运算图

o1 = x1w11 + x2w21 + x3w31 + x4w41 + b1,

o2 = x1w12 + x2w22 + x3w32 + x4w42 + b2,

o3 = x1w13 + x2w23 + x3w33 + x4w43 + b3.

既然分类问题需要得到离散的预测输出,⼀个简单的办法是将输出值oi当作预测类别是i的置信 度,并将值最⼤的输出所对应的类作为预测输出,即输出argmaxioi。然而,直接使⽤输出层的输出有两个问题。⼀⽅⾯,由于输出层的输出值的范围不确定,我们难以直观上判断这些值的意义。另⼀⽅⾯,由于真实标签是离散值,这些离散值与不确定范围的输出值之间的误差难以衡量。

softmax运算符(softmax operator)解决了以上两个问题。它通过下式将输出值变换成值为正且 和为1的概率分布:

图11:softmax运算符计算图

容易看出yˆ1 + ˆy2 + ˆy3 = 1且0 ≤ yˆ1, yˆ2, yˆ3 ≤ 1,因此yˆ1, yˆ2, yˆ3是⼀个合法的概率分布。这时候,如 果yˆ2 = 0.8,不管yˆ1和yˆ3的值是多少,softmax运算不改变预测类别输出。

使⽤softmax运算后可以更⽅便地与离散标签计算误差。我们已经知道,softmax运算 将输出变换成⼀个合法的类别预测分布。

实际上,真实标签也可以⽤类别分布表达:对于样本i,我们构造向量y (i) ∈ R q ,使其第y (i)(样本i类别的离散数值)个元素为1,其余为0。这样我们的 训练⽬标可以设为使预测概率分布yˆ(i)尽可能接近真实的标签概率分布y(i)。

我们可以像线性回归那样使⽤平⽅损失函数∥yˆ(i) − y (i)∥ 2/2。然而,想要预测分类结果正确,我们其实并不需要预测概率完全等于标签概率。我们只需要其中的一个预测值足够大,就足够我们分类使用。例如我们预测一个数字图片类别为“1”的预测值为0.6,预测为“7”和“9”的值为0.2,或者预测为“7”和的值为0.35,预测为“9”的值为0.05,两种情况下分类都是正确的,但是计算的损失值是不同的。改善上述问题的⼀个⽅法是使⽤更适合衡量两个概率分布差异的测量函数。其中,交叉熵(cross entropy)是⼀个常⽤的衡量⽅法。图像分类应用通常采用交叉熵损失(CrossEntropy)。

图12:交叉熵损失函数表达式

其中带下标的y(i)j是向量y(i)中⾮0即1的元素,需要注意将它与样本i类别的离散数值,即不带下标的y(i)区分。在上式中,我们知道向量y (i)中只有第y(i)个元素y(i) y(i)为1,其余全为0,于是H(y (i) , yˆ (i) ) = − log yˆ(i) y(i)。也就是说,交叉熵只关⼼对正确类别的预测概率,因为只要其值⾜ 够⼤,就可以确保分类结果正确。当然,遇到⼀个样本有多个标签时,例如图像⾥含有不⽌⼀个物体时,我们并不能做这⼀步简化。但即便对于这种情况,交叉熵同样只关⼼对图像中出现的物体类别的预测概率。假设训练数据集的样本数为n,交叉熵损失函数定义为

图13:简化交叉熵损失表达式

从另⼀个⻆度来看,最小化交叉熵损失函数等价于最⼤化训练数据集所有标签类别的联合预测概率。

4.动量法优化

损失函数的定义方便了样本的输出与标签之间误差的计算,也就是损失值,但仅仅是计算出了损失值是不够的,到这里模型还是一个“死”的,我们需要通过减小损失值的形式优化模型参数,优化器用于神经网络求解(训练)。由于神经网络参数规模庞大,无法直接求解,因而深度学习中采用随机梯度下降算法(SGD)及其改进算法进行求解。MindSpore封装了常见的优化器,如SGD、ADAM、Momemtum等等。本例采用Momentum优化器,通常需要设定两个参数,动量(moment)和权重衰减项(weight decay)。

本次体验项目中使用的是动量法。目标函数有关自变量的梯度代表了目标函数在自变量当前位置下降最快方向。因此,梯度下降也叫最陡下降。每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代方向仅仅取决于自变量当前位置,这可能会带来一些问题。

例如我们使用一个输入和输出分别为二维向量x=[x1,x2]⊤和标量的⽬标函数。

图14:二维目标函数

基于这个目标函数的梯度下降,演示使用学习率为0.4时自变量的迭代动向。

图15:目标函数自变量迭代动向

可以看到,目标函数在竖直方向比在水平方向的斜率的绝对值更大。因此给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要一个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而这样也会造成自变量在水平方向上朝最优解移动变慢。

动量法的提出就是解决梯度下降中的上述问题的。设置时间步t的自变量为Xt,随机梯度为gt,学习率为ηt。动量法创建速度变量v0,并将其元素初始化成0。在 时间步t > 0,动量法对每次迭代的步骤做如下修改:

vt ← γvt−1 + ηtgt

xt ← xt−1 − vt

其中,动量超参数γ满⾜0≤γ<1。当γ=0时,动量法等价于梯度下降。

5. 总结

本次分享内容首先归纳通常机器学习项目的组成部分,由数据集、模型、损失函数、优化器、模型预测这几部分组成。并且在每一个部分中,又有很多不同的方法。例如卷积模型有LeNet和ResNet等等。我们的需要做的是从分析数据集特性开始,然后在每一个部分中选择合适的方法构建出一个完整的项目,使用数据集训练、预测,最终保存符合我们要求的模型。

本系列图片分类项目分享已完结,也有每一块内容的单独解释分享,内容请见下方标题连接,下个系列将分享自然语言处理项目中的一些技术,敬请期待!

MindSpore图片分类之LeNet网络卷积原理

MindSpore图片分类之LeNet网络池化和全连接

MindSpore图片分类之代码实现

以上是个人的一些见解,理解有限,欢迎大家去论坛相关帖下指正讨论!