【神经网络与深度学习】卷积神经网络(CNN)

【神经网络与深度学习】卷积神经网络(CNN)

标签:【神经网络与深度学习】git


实际上前面已经发布过一次,可是此次从新复习了一下,决定再发博一次。github

说明:之后的总结,还应该以个人认识进行总结,这样比较符合我认知的习惯,而不是单纯的将别的地方的知识复制过来,这样并起不到好的总结效果。相反,若是可以将本身的体会写下来,当有所遗忘时还能顺着当时总结的认识思路,从新“辨识”起来,因此,要总结,而不要搬运知识。web

起初并不理解卷积神经的卷积与结构是什么,后来经过了一个比较好的例子才对卷积神经网络有了初步的认识,这里对这一阶段的认识进行总结。算法


首先,一提到卷积,首先想到的是在信号与系统、数字信号处理中所讲的,作一下翻转平移而后再作点积求和,由于这是第一次引入卷积。后来到了数字图像处理的课程中学习了卷积核,实现的是其实是空域滤波,好比边缘检测技术中经常使用的sobel算子、梯度算子、laplace算子等。网络


而后是分析卷积神经网络的结构。能够经过下图所示的一个例子进行分析:svg

此处输入图片的描述
卷积神经网络是一个多层神经网络,图示中包括输入层、卷积层(C1)、子采样层(S2)、卷积层(C3)、子采样层(S4)、卷积层(C5)和输出层。
其工做流程是:输入层有 32×32 个感知节点构成,接收原始图像。卷积层C1由8个特征映射构成,每一个特征映射由 28×28 个神经元组成,每一个神经元指定一个 5×5 的接受域。实际上就是利用一个 5×5 的卷积核在 32×32 的input图像上进行扫描,获得了 28×28 的滤波后的图像。这里要说明三点重要的内容:函数

经过局部感知与参数共享,进一步的下降了自由参数的个数,从而下降了VC维,提升了泛化能力。

  • 局部感知
    指的是接受域,以下图所示。通常认为人对外界的认知是从局部到全局的,而图像空间联系也是局部像素联系较为紧密,而距离较远的像素相关性较弱。所以,每一个神经元没有必要对全局像素进行感知,只须要对局部像素进行感知,而后在更高层将局部信息进行综合获得全局信息。网络部分连通的思想,受启发于生物学的视觉系统结构,视觉皮层神经元就是局部接收信息的(即这些神经元只响应某些特定区域的刺激)。下图左边为全链接,右图为局部链接。假设隐含层的神经元个数与输入图像同样大,为 1000×1000 ,那么全链接所须要的参数个数为 1000000×1000000 ,实在太大了。而经过指定一个 10×10 的接受域,每一个hidden层的神经元只与 10×10 的像素有链接,这样只须要 1000000×100 个参数就能够,相比之下参数个数减小为原来的万分之一。
    此处输入图片的描述
  • 参数共享
    指的是局部链接中每一个神经元对应着100个像素,也就是100个参数,若是让每一个hidden神经元对应的参数都相等,即全部的hidden神经元共享这100个参数,即共享卷积模板,这样参数个数就变为了100个。以下图所示,将卷积操做视为特征提取的方式(好比模板为sobel算子,就是对边缘特征的提取嘛),该方式与位置无关。即图像的一部分统计特性与其余部分是同样的,经过这个共享参数,实现对图像全部位置上的某一特定(与卷积模板有关)特征的提取。
    此处输入图片的描述
  • 多卷积核
    只采用一种卷积核只能提取图像的一个局部特征,特征提取不充分,所以,能够添加多个卷积核,好比32个卷积核,学习32种特征,以下图所示的多卷积核:
    此处输入图片的描述
    不一样的颜色标识不一样的卷积核,每一个卷积核都将生成另外一幅图像,能够看作是一张图象的不一样同道。

上面三点内容说明了卷积层所进行的工做,经过8个不一样的 5×5 卷积核扫描图像获得8个特征映射,对应抽取了8中不一样的图像特征。
下面有卷积层C1到子采样层S2实现的是子抽样和局部平均,将C1层获得的8个 28×28 的特征图像进行降采样,在S2层获得8个 14×14 的降采样特征图。这里要解释一下为什么进行降采样。学习


经过卷积得到特征以后,若是直接训练分类器,计算量特别大,并且如此大维度的特征向量对分类器来说,容易出现过拟合现象。好比对于一个 96×96 像素的图像,假设咱们已经学习获得了400个定义在8X8输入上的特征,每个特征和图像卷积都会获得一个 (96 − 8 + 1) × (96 − 8 + 1) = 7921 维的卷积特征,因为有 400 个特征,因此每一个样例 (example) 都会获得一个 892 × 400 = 3,168,400 维的卷积特征向量。特征的维度实在太大了。
解决的方法就是降采样,经过某一个区域的特征平均值或最大值等这些概要统计特征来对不一样位置上的特征进行聚合统计。具备将低维度和改善结果(不容易过拟合)的做用。这种聚合的操做叫作池化,也称为平均池化或最大池化等。以下图所示:
此处输入图片的描述
红色区域内的平均值做为该区域的统计特性,实现降采样过程,也就是池化过程。atom


此处输入图片的描述
所以,再通过一次卷积层C3和子采样层S4,便获得了较小的特征描述20个 5×5 的特征图像,以后通过一个全连结(由于图像大小与接收域大小相同)的卷积操做,获得一维特征向量表示。以后经过全连结层到达输出层。在这里理解中容易出现问题的地方是由S2层获得C3层的过程,即S2到C3的链接到底是怎样的?下面解决这个困惑。
稍微细心一点就能发现,由S2层到C3层,从8个特征映射层到20个特征映射层,并非整数倍的关系,这说明C3层的每一个特征映射并非将多少个卷积核分别做用在S2层的特征映射上,即须要注意C3中每个特征map是链接到S2中的全部8个,或者几个特征map上的,表示本层的特征map是上一层提取到的特征map的不一样组合,即所谓相似边缘构成形状或者目标的部分等特色。但为何不让S2中的全部特征map全连结到C3层的特征图上呢?缘由有2:一是不彻底链接机制将链接的数量保持在合理的范围内;二是破坏网络的对称性,因为不一样的特征图有不一样的输入,迫使之抽取不一样的特征(互补)。这里盗用一张图说明一下组合状况:
好比C3层有16个,S2层有6个,如何map的链接呢,能够参看下图的组合形式:
此处输入图片的描述spa

C3层的0号map由S2层的0-2号map映射而来,以此类推。以C3层的0号特征图获取过程为例,将0号卷积核分别在S2层的前三个map上进行卷积,并求和,以后加入偏移量,经过sigmoid函数进行激发,就能够获得C3层的0号特征图了。

卷积以后紧跟着子抽样的思想是受到动物视觉系统的“简单的”细胞后面跟着“复杂的”细胞的想法的启发而产生的。具体意义在上面已经分析过。


最后要将的就是CNN的学习,上面讲的例子的CNN网络结构能够简化为以下:
此处输入图片的描述
其中input层到C1,S4到C5,C5到output是全连结,C1到S二、C3到S4 是一一对应的链接,而S2到C3为了消除对称性,去掉了部分链接,可让特征映射具备多样性。须要注意的是C5层卷积核的尺寸要与S4层输出相同,保证输出为1维向量。


卷积层的典型结构以下:
此处输入图片的描述
卷积层的前馈输出经过以下的公式进行计算:

output=sigmoid(Σ()+)
,其中卷积核和偏移量都是能够训练的。其核心代码以下:

ConvolutionLayer::fprop(input,output) {
  //取得卷积核的个数
  int n=kernel.GetDim(0);
  for (int i=0;i<n;i++) {
      //第i个卷积核对应输入层第a个特征映射,输出层的第b个特征映射
      //这个卷积核能够形象的看做是从输入层第a个特征映射到输出层的第b个特征映射的一个连接
      int a=table[i][0], b=table[i][10];
      //用第i个卷积核和输入层第a个特征映射作卷积
      convolution = Conv(input[a],kernel[i]);
      //把卷积结果求和
      sum[b] +=convolution;
  }
  for (i=0;i<(int)bias.size();i++) {
      //加上偏移量
      sum[i] += bias[i];
  }
  //调用Sigmoid函数
  output = Sigmoid(sum);
}

其中,input是 n1×n2×n3 的矩阵,n1是输入层特征映射的个数,n2是输入层特征映射的宽度,n3是输入层特征映射的高度。output, sum, convolution,bias是n1×(n2-kw+1)×(n3-kh+1)的矩阵,kw,kh是卷积核的宽度高度(图中是5×5)。kernel是卷积核矩阵。table是链接表,即若是第a输入和第b个输出之间有链接,table里就会有[a,b]这一项,并且每一个链接都对应一个卷积核。
卷积层的反馈运算的核心代码以下:

ConvolutionLayer::bprop(input,output,in_dx,out_dx) {
  //梯度经过DSigmoid反传
  sum_dx = DSigmoid(out_dx);
  //计算bias的梯度
  for (i=0;i<bias.size();i++)    {
      bias_dx[i] = sum_dx[i];
  }
  //取得卷积核的个数
  int n=kernel.GetDim(0);
  for (int i=0;i<n;i++)
  {
      int a=table[i][0],b=table[i][11];
      //用第i个卷积核和第b个输出层反向卷积(即输出层的一点乘卷积模板返回给输入层),并把结果累加到第a个输入层
      input_dx[a] += DConv(sum_dx[b],kernel[i]);
      //用一样的方法计算卷积模板的梯度
      kernel_dx[i] += DConv(sum_dx[b],input[a]);
  }
}

其中in_dx,out_dx 的结构和 input,output 相同,表明的是相应点的梯度。


子采样层的学习
自采样层的典型结构以下:
此处输入图片的描述

output=sigmoid(×+)

其核心代码以下:

SubSamplingLayer::fprop(input,output) {
  int n1= input.GetDim(0);
  int n2= input.GetDim(1);
  int n3= input.GetDim(2);
  for (int i=0;i<n1;i++) {
      for (int j=0;j<n2;j++) {
          for (int k=0;k<n3;k++) {
              //coeff 是可训练的权重,sw 、sh 是采样窗口的尺寸。
              sub[i][j/sw][k/sh] += input[i][j][k]*coeff[i];
          }
      }
  }
  for (i=0;i<n1;i++) {
      //加上偏移量
      sum[i] = sub[i] + bias[i];
  }
  output = Sigmoid(sum);
}

子采样层的反馈运算的核心代码以下:

SubSamplingLayer::bprop(input,output,in_dx,out_dx) {
  //梯度经过DSigmoid反传
  sum_dx = DSigmoid(out_dx);
  //计算bias和coeff的梯度
  for (i=0;i<n1;i++) {
      coeff_dx[i] = 0;
      bias_dx[i] = 0;
      for (j=0;j<n2/sw;j++)
          for (k=0;k<n3/sh;k++) {
              coeff_dx[i] += sub[j][k]*sum_dx[i][j][k];
              bias_dx[i] += sum_dx[i][j][k]);
          }
  }
  for (i=0;i<n1;i++) {
      for (j=0;j<n2;j++)
          for (k=0;k<n3;k++) {
              in_dx[i][j][k] = coeff[i]*sum_dx[i][j/sw][k/sh];
          }
  }
}

全连结层实际上就相似BP网络了,具体能够参考BP算法。
关于CNN的完整代码能够参考https://github.com/ibillxia/DeepLearnToolbox/tree/master/CNN中的Matlab代码


主要参考资料:
http://ibillxia.github.io/blog/2013/04/06/Convolutional-Neural-Networks/
http://blog.csdn.net/nan355655600/article/details/17690029
http://www.36dsj.com/archives/24006


2016-9-27 22:40 张朋艺 pyZhangBIT2010@126.com