转载:Network In Network学习笔记

转载原文1:http://blog.csdn.net/hjimce/article/details/50458190c++

转载原文2:http://blog.csdn.net/mounty_fsc/article/details/51746111git

1、相关理论github

本篇博文主要讲解2014年ICLR的一篇很是牛逼的paper:《Network In Network》,过去一年已经有了好几百的引用量,这篇paper改进了传统的CNN网络,采用了少许的参数就松松击败了Alexnet网络,Alexnet网络参数大小是230M,采用这篇paper的算法才29M,减少了将近10倍啊。这篇paper提出的网络结构,是对传统CNN网络的一种改进(这种文献少之又少,因此感受颇有必要学习)。算法

传统的卷积神经网络通常来讲是由:线性卷积层、池化层、全链接层堆叠起来的网络。卷积层经过线性滤波器进行线性卷积运算,而后在接个非线性激活函数,最终生成特征图。以Relu激活函数为例,特征图的计算公式为:网络

 

其中(i,j)表示图片像素点的位置索引,xij表示咱们卷积窗口中的图片块,k则表示咱们要提取的特征图的索引。架构

通常来讲,若是咱们要提取的一些潜在的特征是线性可分的话,那么对于线性的卷积运算来讲这是足够了。然而通常来讲咱们所要提取的特征通常是高度非线性的。在传统的CNN中,也许咱们能够用超完备的滤波器,来提取各类潜在的特征。好比咱们要提取某个特征,因而我就用了一大堆的滤波器,把全部可能的提取出来,这样就能够把我想要提取的特征也覆盖到,然而这样存在一个缺点,那就是网络太恐怖了,参数太多了。maven

 

这篇文章有两个很重要的观点:ide

  1. 1×1卷积的使用 
    文中提出使用mlpconv网络层替代传统的convolution层。mlp层其实是卷积加传统的mlp(多层感知器),由于convolution是线性的,而mlp是非线性的,后者可以获得更高的抽象,泛化能力更强。在跨通道(cross channel,cross feature map)状况下,mlpconv等价于卷积层+1×1卷积层,因此此时mlpconv层也叫cccp层(cascaded cross channel parametric pooling)。函数

  2. CNN网络中不使用FC层(全链接层) 
    文中提出使用Global Average Pooling取代最后的全链接层,由于全链接层参数多且易过拟合。作法即移除全链接层,在最后一层(文中使用mlpconv)层,后面加一层Average Pooling层。学习

以上两点,之因此重要,在于,其在较大程度上减小了参数个数,确可以获得一个较好的结果。而参数规模的减小,不只有利用网络层数的加深(因为参数过多,网络规模过大,GPU显存等不够用而限制网络层数的增长,从而限制模型的泛化能力),并且在训练时间上也获得改进。

2、MLP卷积层(文献创新点1)

这个是文献的大创新点,也就是提出了mlpconv层。Mlpconv层能够当作是每一个卷积的局部感觉野中还包含了一个微型的多层网络。其实在之前的卷积层中,咱们局部感觉野窗口的运算,能够理解为一个单层的网络,以下图所示:

线性卷积层

CNN层的计算公式以下:

 

然而如今不一样了,咱们要采用多层的网络,提升非线性,

单通道mlpconv层 

跨通道mlpconv层(cccp层) 

    • 由图可知,mlpconv=convolution+mlp(图中为2层的mlp)。

    • 在caffe中实现上,mlpconv=convolution+1×1convolution+1×1convolution(2层的mlp)

因而mlpconv层的网络结构图以下:

 

Mlpconv层

从上面的图能够看到,说的简单一点呢,利用多层mlp的微型网络,对每一个局部感觉野的神经元进行更加复杂的运算,而之前的卷积层,局部感觉野的运算仅仅只是一个单层的神经网络罢了。对于mlpconv层每张特征图的计算公式以下:

通常来讲mlp是一个三层的网络结构。
下面是一个单层的mlpconv网络的caffe网络结构文件,源码来自于: https://gist.github.com/mavenlin/d802a5849de39225bcc6 :
layers {
  bottom: "data"
  top: "conv1"
  name: "conv1"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 96
    kernel_size: 11
    stride: 4
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "conv1"
  top: "conv1"
  name: "relu0"
  type: RELU
}
layers {
  bottom: "conv1"
  top: "cccp1"
  name: "cccp1"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 96
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp1"
  top: "cccp1"
  name: "relu1"
  type: RELU
}
layers {
  bottom: "cccp1"
  top: "cccp2"
  name: "cccp2"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 96
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp2"
  top: "cccp2"
  name: "relu2"
  type: RELU
}
3、全局均值池化(文献创新点2)

传统的卷积神经网络卷积运算通常是出如今低层网络。对于分类问题,最后一个卷积层的特征图经过量化而后与全链接层链接,最后在接一个softmax逻辑回归分类层。这种网络结构,使得卷积层和传统的神经网络层链接在一块儿。咱们能够把卷积层看作是特征提取器,而后获得的特征再用传统的神经网络进行分类。

然而,全链接层由于参数个数太多,每每容易出现过拟合的现象,致使网络的泛化能力不尽人意。因而Hinton采用了Dropout的方法,来提升网络的泛化能力。

本文提出采用全局均值池化的方法,替代传统CNN中的全链接层。与传统的全链接层不一样,咱们对每一个特征图一整张图片进行全局均值池化,这样每张特征图均可以获得一个输出。这样采用均值池化,连参数都省了,能够大大减少网络,避免过拟合,另外一方面它有一个特色,每张特征图至关于一个输出特征,而后这个特征就表示了咱们输出类的特征。这样若是咱们在作1000个分类任务的时候,咱们网络在设计的时候,最后一层的特征图个数就要选择1000,下面是《Network In Network》网络的源码,倒数一层的网络相关参数:

layers {
  bottom: "cccp7"
  top: "cccp8"
  name: "cccp8-1024"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    #num_output: 1000
    num_output: 4
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

全局均值池化层的相关参数以下:

layers {
  bottom: "cccp8"
  top: "pool4"
  name: "pool4"
  type: POOLING
  pooling_param {
    pool: AVE
    kernel_size: 6
    stride: 1
  }
}

由于在Alexnet网络中,最后一个卷积层输出的特征图大小恰好是6*6,因此咱们pooling的大小选择6,方法选择:AVE。

4、整体网络架构

根据上面的做者对传统CNN的两个改进,利用其进行1000物体分类问题,因而做者最后设计了一个:4层的NIN+全局均值池化,网络以下:

Caffe中4层网络示意图(ImageNet) 

说明:

1.方框为网络层,椭圆为blob 
2.黄色pool4为Average Pooling

caffe网络数据数据以下(crop size=224)

Layer channels Filter size Filter stride Padding size Input size
conv1 96 11 4 - 224×224
cccp1 96 1 1 - 54×54
cccp2 96 1 1 - 54×54
pool1 96 3 2 - 54×54
conv2 256 5 1 2 27×27
cccp3 256 1 1 - 27×27
cccp4 256 1 1 - 27×27
pool2 256 3 2 - 27×27
conv3 384 3 1 1 13×13
cccp5 384 1 1 - 13×13
cccp6 384 1 1 - 13×13
pool3 384 3 2 - 13×13
conv4-1024 1024 3 1 1 6×6
cccp7-1024 1024 1 1 - 6×6
cccp8-1000 1000 1 1 - 6×6
pool4-ave 1000 6 1 - 6×6
accuracy 1000 - - - 1×1
    • 对于crop size = 227,则input size的变化为227, 55, 27, 13, 6, 1。

 

我的总结:我的感受这篇文献颇有价值,实现方式也很简单,一开始我还觉得须要caffe的c++源码来实现NIN网络,结果发现实现NIN的源码实现方式其实就是一个1*1的卷积核,实现卷积运算,因此实现起来至关容易,不须要本身写源码,只须要简简单单的把卷积核的大小变一下,而后最后一层的全链接层直接用avg pooling替换一下就ok了。我的评价:网络浅显易懂,简单实现,却能够改进原来的网络,提升精度,减少模型大小,因此是一篇很值得学习的文献。后续即将讲解另外几篇2015年,也是对CNN网络结构改进的牛逼文献:《Spatial Transformer Networks》、《Striving For Simplicity:The All Convolutional Net》、《Stacked What-Where Auto-encoders》,敬请期待,毕竟这样的文章勇于挑战传统的CNN结构,对其不知作出改进,因此咱们须要一篇一篇的学。

参考文献:

一、《Network In Network》

二、https://github.com/BVLC/caffe/wiki/Model-Zoo

三、https://gist.github.com/mavenlin/d802a5849de39225bcc6 

四、《Maxout Networks》

 

整个网络结构:

name: "nin_work"
layers {
  top: "data"
  top: "label"
  name: "data"
  type: DATA
  data_param {
    source: "./data/nintrain"
    backend: LMDB
    batch_size: 64
  }
  transform_param {
    crop_size: 224
    mirror: true
    mean_file: "./data/net_mean.binaryproto"
  }
  include: { phase: TRAIN }
}
layers {
  top: "data"
  top: "label"
  name: "data"
  type: DATA
  data_param {
    source: "./data/nintest"
    backend: LMDB
    batch_size: 89
  }
  transform_param {
    crop_size: 224
    mirror: false
    mean_file: "./data/net_mean.binaryproto"
  }
  include: { phase: TEST }
}
layers {
  bottom: "data"
  top: "conv1"
  name: "conv1"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 96
    kernel_size: 11
    stride: 4
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "conv1"
  top: "conv1"
  name: "relu0"
  type: RELU
}
layers {
  bottom: "conv1"
  top: "cccp1"
  name: "cccp1"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 96
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp1"
  top: "cccp1"
  name: "relu1"
  type: RELU
}
layers {
  bottom: "cccp1"
  top: "cccp2"
  name: "cccp2"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 96
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp2"
  top: "cccp2"
  name: "relu2"
  type: RELU
}
layers {
  bottom: "cccp2"
  top: "pool0"
  name: "pool0"
  type: POOLING
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}
layers {
  bottom: "pool0"
  top: "conv2"
  name: "conv2"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 256
    pad: 2
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "conv2"
  top: "conv2"
  name: "relu3"
  type: RELU
}
layers {
  bottom: "conv2"
  top: "cccp3"
  name: "cccp3"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 256
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp3"
  top: "cccp3"
  name: "relu5"
  type: RELU
}
layers {
  bottom: "cccp3"
  top: "cccp4"
  name: "cccp4"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 256
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp4"
  top: "cccp4"
  name: "relu6"
  type: RELU
}
layers {
  bottom: "cccp4"
  top: "pool2"
  name: "pool2"
  type: POOLING
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}
layers {
  bottom: "pool2"
  top: "conv3"
  name: "conv3"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 384
    pad: 1
    kernel_size: 3
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "conv3"
  top: "conv3"
  name: "relu7"
  type: RELU
}
layers {
  bottom: "conv3"
  top: "cccp5"
  name: "cccp5"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 384
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp5"
  top: "cccp5"
  name: "relu8"
  type: RELU
}
layers {
  bottom: "cccp5"
  top: "cccp6"
  name: "cccp6"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 384
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp6"
  top: "cccp6"
  name: "relu9"
  type: RELU
}
layers {
  bottom: "cccp6"
  top: "pool3"
  name: "pool3"
  type: POOLING
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}
layers {
  bottom: "pool3"
  top: "pool3"
  name: "drop"
  type: DROPOUT
  dropout_param {
    dropout_ratio: 0.5
  }
}
layers {
  bottom: "pool3"
  top: "conv4"
  name: "conv4-1024"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 1024
    pad: 1
    kernel_size: 3
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "conv4"
  top: "conv4"
  name: "relu10"
  type: RELU
}
layers {
  bottom: "conv4"
  top: "cccp7"
  name: "cccp7-1024"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 1024
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.05
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp7"
  top: "cccp7"
  name: "relu11"
  type: RELU
}
layers {
  bottom: "cccp7"
  top: "cccp8"
  name: "cccp8-1024"
  type: CONVOLUTION
  blobs_lr: 1
  blobs_lr: 2
  weight_decay: 1
  weight_decay: 0
  convolution_param {
    num_output: 1000
    kernel_size: 1
    stride: 1
    weight_filler {
      type: "gaussian"
      mean: 0
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layers {
  bottom: "cccp8"
  top: "cccp8"
  name: "relu12"
  type: RELU
}
layers {
  bottom: "cccp8"
  top: "pool4"
  name: "pool4"
  type: POOLING
  pooling_param {
    pool: AVE
    kernel_size: 6
    stride: 1
  }
}
layers {
  name: "accuracy"
  type: ACCURACY
  bottom: "pool4"
  bottom: "label"
  top: "accuracy"
  include: { phase: TEST }
}
layers {
  bottom: "pool4"
  bottom: "label"
  name: "loss"
  type: SOFTMAX_LOSS
  include: { phase: TRAIN }
}

1×1卷积的做用

如下内容摘抄自:http://www.caffecn.cn/?/question/136

问:发现不少网络使用了1X1卷积核,这能起到什么做用呢?另外我一直以为,1X1卷积核就是对输入的一个比例缩放,由于1X1卷积核只有一个参数,这个核在输入上滑动,就至关于给输入数据乘以一个系数。不知道我理解的是否正确

答1: 
对于单通道的feature map和单个卷积核之间的卷积来讲,题主的理解是对的,CNN里的卷积大都是多通道的feature map和多通道的卷积核之间的操做(输入的多通道的feature map和一组卷积核作卷积求和获得一个输出的feature map),若是使用1x1的卷积核,这个操做实现的就是多个feature map的线性组合,能够实现feature map在通道个数上的变化。接在普通的卷积层的后面,配合激活函数,就能够实现network in network的结构了(本内容做者仅受权给CaffeCN社区(caffecn.cn)使用,如需转载请附上内容来源说明。)

答2: 
我来讲说个人理解,我认为1×1的卷积大概有两个方面的做用吧: 
1. 实现跨通道的交互和信息整合 
2. 进行卷积核通道数的降维和升维

下面详细解释一下: 
1. 这一点孙琳钧童鞋讲的很清楚。1×1的卷积层(可能)引发人们的重视是在NIN的结构中,论文中林敏师兄的想法是利用MLP代替传统的线性卷积核,从而提升网络的表达能力。文中同时利用了跨通道pooling的角度解释,认为文中提出的MLP其实等价于在传统卷积核后面接cccp层,从而实现多个feature map的线性组合,实现跨通道的信息整合。而cccp层是等价于1×1卷积的,所以细看NIN的caffe实现,就是在每一个传统卷积层后面接了两个cccp层(其实就是接了两个1×1的卷积层)。 
2. 进行降维和升维引发人们重视的(可能)是在GoogLeNet里。对于每个Inception模块(以下图),原始模块是左图,右图中是加入了1×1卷积进行降维的。虽然左图的卷积核都比较小,可是当输入和输出的通道数很大时,乘起来也会使得卷积核参数变的很大,而右图加入1×1卷积后能够下降输入的通道数,卷积核参数、运算复杂度也就跟着降下来了。以GoogLeNet的3a模块为例,输入的feature map是28×28×192,3a模块中1×1卷积通道为64,3×3卷积通道为128,5×5卷积通道为32,若是是左图结构,那么卷积核参数为1×1×192×64+3×3×192×128+5×5×192×32,而右图对3×3和5×5卷积层前分别加入了通道数为96和16的1×1卷积层,这样卷积核参数就变成了1×1×192×64+(1×1×192×96+3×3×96×128)+(1×1×192×16+5×5×16×32),参数大约减小到原来的三分之一。同时在并行pooling层后面加入1×1卷积层后也能够下降输出的feature map数量,左图pooling后feature map是不变的,再加卷积层获得的feature map,会使输出的feature map扩大到416,若是每一个模块都这样,网络的输出会愈来愈大。而右图在pooling后面加了通道为32的1×1卷积,使得输出的feature map数降到了256。GoogLeNet利用1×1的卷积降维后,获得了更为紧凑的网络结构,虽然总共有22层,可是参数数量却只是8层的AlexNet的十二分之一(固然也有很大一部分缘由是去掉了全链接层)。 

最近大热的MSRA的ResNet一样也利用了1×1卷积,而且是在3×3卷积层的先后都使用了,不只进行了降维,还进行了升维,使得卷积层的输入和输出的通道数都减少,参数数量进一步减小,以下图的结构。(否则真不敢想象152层的网络要怎么跑起来TAT) 

[1]. https://gist.github.com/mavenlin/d802a5849de39225bcc6 
[2]. http://www.caffecn.cn/?/question/136

相关文章
相关标签/搜索