最近考试和杂事太多,所以课程进度一直在耽误,现在手中事情稍微少一些了,立刻开始CNN的学习。这周中主要是介绍CNN的原理以及为什么CNN能在计算机视觉领域如此出众,我个人认为这周课程非常有意思。

边缘检测实例(Edge Detection Example)

卷积神经网络基本过程

假设有一个尺寸为(6×6×1)(6 \times 6 \times 1)的原始图像的灰度图片,有一个尺寸为(3×3×1)(3 \times 3 \times 1)的filter(有的literature中称之为kernal)

第一步:第一个区域的卷积计算,其过程为3×1+0×0+1×1+1×1+5×0+8×1+2×1+7×0+2×1=53 \times 1 + 0 \times 0 + 1 \times -1 + 1 \times 1 + 5 \times 0 + 8 \times -1 + 2 \times 1 + 7 \times 0 + 2 \times -1 = -5,相当于filter贴近了第一块区域与各个模块相乘后最终相加,同时将结果写在第一个上。

第二步:filter右移1格后继续进行卷积计算

……

第五步:filter下移1格后继续进行卷积计算

……

直至filter全部进行覆盖得到最终的结果

这样一个最基本的卷积过程就结束了,其实就是一个filter逐步覆盖全部的过程。

为什么能检测边缘?

以一个更简单直接的灰度图为例,图左是一个左边为高光右边为黑色的灰度图,最终通过卷积后得出来的结果如右边所示,是一个有着中间为高光的区域,因为左边的输入图像比较小(6×6)(6 \times 6),因此显得边界比较宽,但是如果图像放大到(1000×1000)(1000 \times 1000)后,就非常好用了。

更多边缘检测内容(More Edge Detection)

在下图中可以看出在filter不变(垂直边缘检测)的情况下将照片旋转180度,最终得到的结果是不同的,虽然还是达到了垂直检测的目的,甚至绝对值都是相同的,但是一个数值是正的一个是负的,这就说明了从亮变暗或者从暗变亮都是可以做检测的,如果不需要相关特征可以直接加一个绝对值即可。

那么将上面的filter水平旋转90°就可以检测水平边缘了:

image-20180703131950118

那么结果什么时候会大,什么时候会小呢?

image-20180703132935394

根据上面所学习的知识,这个filter是一个水平边缘检测器而且是一个期望上亮下暗的,因此越符合该期望的值越大。绿色对应的部分基本上是完全符合了该该特征,于是其值就完全大于左边橙色部分,因为橙色部分在上方有一个暗色块,最下方有一个亮色块。

对于filter的尺寸是(3×3)(3 \times 3)的可以有9个参数,不一定是上面的缺省数值,目前还有2种比较有特色的,分别是Sobel Filter和Scharr Filter。

image-20180703141505091

Padding

在上面的例子中不难看出,每次应用一次卷积运算的时候其结果都是比原图小的,说明这个是忽略了一些特征,因此如果多进行几轮之后很有可能最终输出会越来越小直到不能再进行一次卷积,因此如果我们不希望我们的输出越来越小的时候我们就需要添加一定的padding。

假设我们的原图大小为n×nn \times n,filter大小为f×ff \times f,那么最终的输出大小为(nf+1)×(nf+1)(n - f + 1) \times (n - f + 1),如果我们填充了一个pp行的padding,那么最终的输出尺寸为(n+2×pf+1)×(n+2×pf+1)(n + 2 \times p - f + 1) \times (n + 2 \times p - f + 1),如果想让原始图片与输出图片相等,就需要n=n+2×pf+1n = n + 2 \times p - f + 1最终p=f12p = \frac{f -1}{2}。就以上一个例子为例,f=3f = 3,因此p=1p = 1。特别的在大多数情况下ff都为奇数。

卷积步长(Convolution Stride)

卷积步长是控制每次filter在输入中移动的距离,步长越大则卷积计算速度越小。假设图片尺寸为6,步长为2,filter尺寸为3,则卷积计算过程如下图所示:

image-20180703150327826

之前的卷积计算实际上是s=1s = 1的情况,因此加入了步长之后最终的输出尺寸进一步的改变了,保持上面的参数不变,加入步长ss,则最终输出图像的尺寸为 n+2pfs+1 ×n+2pfs+1  \lfloor \frac{n + 2p - f}{s} + 1 \rfloor \times \lfloor \frac{n + 2p - f}{s} + 1 \rfloor,在不是整数的时候注意向下取整。

多通道卷积(Convolutions Over Volumes)

现在想如果在处理过程中不仅仅处理一张灰度图,而是一个RGB图,这种图最大的特点在于多通道堆叠,如下图:

image-20180703153018183

变化是输入图像不再是平面的了,filter也不是平面的了,但是最终的输出是一个平面的,第一个6是高度nHn_H,第二个6是宽度nWn_W,第三个数字是信道数量nCn_C,其过程是:

  1. 图片的R层与filter对应的R层相互卷积,最终得出一个4×44 \times 4的输出;
  2. 同理G层和B层重复步骤1,都获取了一个4×44 \times 4的输出;
  3. 将这3个输出相加后成为一个平面输出。

例如我们想识别红色的垂直边缘,就可以在步骤1中的R通道的filter加入垂直检测器,其他通道filter全部置为0,那么如何既检测图片的水平边缘有检测图片的垂直边缘呢?

image-20180704084858634

核心思路是每个filter与图片做卷积运算后,将最终的结果叠加,这样最终的结果就不是一个平面的了,最后的堆叠层数是filter的数量,filter越多堆叠的层数就越大。

单层卷积神经网络(One Layer of A Convolutional Network)

其实这个神经网络与多通道网络差不多,只是加上了bias以及activation function,最左边是输入图片,一般来说是一个RGB图片因此是一个立方体,后面有多个filter以过滤不同的特征,同样也是立方体信道数量于输入的图片相同,最终得到的输出对应到神经网络中加上bias后就是ZZ ,这是一个平面的,之后再经过激励函数(Activation Function)后变为AA,最后将这些输出堆叠为最终该层神经网络的输出。

image-20180704085839941

那么梳理一下在该层中各个参数的维度:

  • f[l]f^{[l]}是filter的尺寸,p[l]p^{[l]}是padding,s[l]s^{[l]}是步长,nC[l]n_C^{[l]}是filter的数量(这是在l层的输出,最后是堆叠filter的数量)
  • 输入变量尺寸为(nH[l1]×nW[l1]×nC[l1])(n_H^{[l-1]} \times n_W^{[l-1]} \times n_C^{[l-1]})
  • 输出变量尺寸为(nH[l]×nW[l]×nC[l])(n_H^{[l]} \times n_W^{[l]} \times n_C^{[l]})

因此其他的变量维度为:

  • nH[l]=n[l1]+2p[l]fls[l]+1n_H^{[l]} = \lfloor \frac{n^{[l-1]} + 2p^{[l]} - f^{l}}{s^{[l]}} \rfloor + 1,同理可以计算出nW[l]n_W^{[l]}
  • 每一个filter的尺寸为(f[l]×f[l]×nC[l1])(f^{[l]} \times f^{[l]} \times n_C^{[l-1]})
  • Activation(a[l]a^{[l]})的尺寸为(nH[l]×nW[l]×1)(n_H^{[l]} \times n_W^{[l]} \times 1),如果考虑向量化时其尺寸为(m×nH[l]×nW[l]×1)(m \times n_H^{[l]} \times n_W^{[l]} \times 1),其中mm是样本数量
  • Weight实质上是filter的堆叠,其尺寸为(f[l]×f[l]×nC[l1]×nC[l])(f^{[l]} \times f^{[l]} \times n_C^{[l-1]} \times n_C^{[l]})
  • bias的尺寸为nC[l]n_C^{[l]},准确来说是(1×1×1×nC[l])(1 \times 1 \times 1 \times n_C^{[l]})

简单卷积网络示例(A Simple Convolution Network Example)

image-20180704102441588

上图是一个基本的卷积神经网络示例,忽略了每一层的具体实现,这里面也没有加入池化层(Pooling Layer)和全连接层(Fully Connected Layer),具体实现步骤如下:

  • 接受一个尺寸为(39×39×3)(39 \times 39 \times 3)的输入
  • 经过一个f[1]=3,s[1]=1,#filters=10f^{[1]} = 3, s^{[1]} = 1, \#filters = 10的卷积层后其输出大小为(37×37×10)(37 \times 37 \times 10)
  • 经过一个f[2]=5,s[2]=2,#filters=20f^{[2]} = 5, s^{[2]} = 2, \#filters = 20的卷积层后其输出大小为(17×17×20)(17 \times 17 \times 20)
  • 经过一个f[3]=5,s[3]=2,#filters=40f^{[3]} = 5, s^{[3]} = 2, \#filters = 40的卷积层后其输出大小为(7×7×40)(7 \times 7 \times 40)
  • 将上一层的全部特征7×7×40=1,9607 \times 7 \times 40 = 1,960展开
  • 使用logistic或softmax层进行预测得到y^\hat{y}
  • 反向传播更新参数

总结一下可以看出CNNs的特点是高度和宽度不断缩小,但是信道不断在增加。

池化层(Pooling Layer)

其作用在于缩减模型大小,提高计算速度,因为没有参数因此不参与反向传播过程的更新参数,目前来说共计分为两大类:最大池化层(Max Pooling)和平均池化层(Average Pooling)。

MAX POOLING

我们选用f=2,s=2f = 2, s = 2的最大池化层,这个是一个每次覆盖一个2×22 \times 2的范围并且移动2步的最大池化层,在这其中调出每一个区域最大的数值,如下图,第一次的数值是1,3,2,9则最大是9,接着移动两格后是2,1,1,1最大是2,以此类推。

image-20180704112040466

一般来说数值越大说明具有某种特征越强,因此通过这种方式来提取一小片区域内特征最强的值,如果该区域曾在特征较强的就提取,如果没有则即便是最大值值也会相对小。

AVERAGE POOLING

极少情况下会使用average pooling,其过程与max pooling基本一致,只是不再寻找区域内最大值而是把值平均。

超参数(Hyperparameters)

目前共有三个超参数:

  • ff: filter size
  • ss: stride
  • Max or Average Pooling

其中f=2,s=2f = 2, s = 2的组合使用频率较高,应用后会将源输入的大小减半,在一个输入为(nH×nW×nC)(n_H \times n_W \times n_C),经过池化层处理后最终的尺寸为(nHfs+1×nWfs+1×nC)(\lfloor \frac{n_H - f}{s} + 1 \rfloor \times \lfloor \frac{n_W - f}{s} + 1 \rfloor \times n_C)

卷积神经网络示例(Neural Network Example)

image-20180704185953959

上图是一个加入池化层和全连接层的卷积神经网络示例,池化层已经在上一节中描述了,全连接层其实就是一个DNN,通过传统的Z=WX+bZ = WX+b实现。具体过程如下:

  1. 输入一个尺寸为(32×32×3)(32 \times 32 \times 3)的图像
  2. 第一层包含一个卷积层(CONV1)和一个最大池化层(POOL1)
    • 卷积层的参数设置是f=5,s=1,#filters=8f = 5, s = 1, \#filters = 8,最终输出的大小为(28×28×8)(28 \times 28 \times 8)
    • 最大池化层的参数设置是f=2,s=2f = 2, s = 2,最终输出的大小为(14×14×8)(14 \times 14 \times 8)
  3. 第二层重复第一层,最终的输出是(5×5×16)(5 \times 5 \times 16)
  4. 将输出展开得到一个维度为(400,1)(400, 1)的特征向量
  5. 通过Z=WX+bZ = WX + b,其中WW的维度为(120,400)(120, 400)bb为一个实数,最后可以得到一个维度为(120,1)(120, 1)的向量
  6. 重复上述可以获取一个维度为(84,1)(84, 1)的向量
  7. 最终通过softmax获取最终输出

Andrew对此的建议:

  • 一个或多个卷积层后跟一个池化层,之后跟一些全连接层,最后使用logistic或者softmax做输出
  • 这里会涉及非常多的超参数,尽量不要自己设计这些,而是从文献看别人如何设置,其中有一些设置就能很好的应用在自己的神经网络中

参数

Layers Activation Shape Activation Size Number of Parameters
Input (32, 32, 3) 32×32×3=307232 \times 32 \times 3 = 3072 0
Conv1 (28, 28, 8) 6272 (5×5+1)×8=208(5 \times 5 + 1) \times 8 = 208
Pool1 (14, 14, 8) 1568 0
Conv2 (10, 10, 16) 1600 416
Pool2 (5, 5, 16) 400 0
FC3 (120, 1) 120 120×400+1=48001120 \times 400 + 1 = 48001
FC4 (84, 1) 84 10081
Softmax (10, 1) 10 841

需要说明的是:

  1. 对于Conv*层的参数数量的计算公式为(f[l]×f[l]+b[l])×#filters(f^{[l]} \times f^{[l]} + b^{[l]}) \times \#filters,其中f[l]f^{[l]}是第l层filter的尺寸,b[l]b^{[l]}是第l层bias,注意每一个filter都有一个自己的bias,最后#filters\#filters是filter的数量
  2. 对于FC*层的参数数量的计算公式为W的尺寸相乘+一个bias
  3. 对于Pool*来说是没有参数的
  4. 卷积层相比于全连接层参数会少很多
  5. Activation Size会随着网络的层数加深逐渐减小,如果下降太快同样会影响性能

为什么使用卷积?(Why Convolution?)

就像上一个示例的训练来看第一层是(32,32,3)(32, 32, 3),第二层是(28,28,8)(28, 28, 8),如果直接全连接则计算数为32×32×3×28×28×819,000,00032 \times 32 \times 3 \times 28 \times 28 \times 8 \thickapprox 19,000,000,这还仅仅是一张(32,32,3)(32, 32, 3),因此对于更大的图片这种计算是目前无法承受的,因此使用卷积后可以大大降低计算开销。

那么为什么可以降低计算开销呢?

  1. 参数共享(Parameter Sharing):在图片中某一区域的特征(如垂直边缘)适用于其他区域,因此在检测这种特征的时候就不需要额外增加该特征的检测器了。
  2. 稀疏连接(Sparsity of Connections):输出的结果的每一个单元与每次filter覆盖的范围有依赖,除此之外的其他地方均无依赖。