Network In Network学习笔记
原文地址:http://blog.csdn.net/hjimce/article/details/50458190
作者:hjimce
一、相关理论
本篇博文主要讲解2014年ICLR的一篇非常牛逼的paper:《Network In Network》,过去一年已经有了好几百的引用量,这篇paper改进了传统的CNN网络,采用了少量的参数就松松击败了Alexnet网络,Alexnet网络参数大小是230M,采用这篇paper的算法才29M,减小了将近10倍啊。这篇paper提出的网络结构,是对传统CNN网络的一种改进(这种文献少之又少,所以感觉很有必要学习)。
传统的卷积神经网络一般来说是由:线性卷积层、池化层、全连接层堆叠起来的网络。卷积层通过线性滤波器进行线性卷积运算,然后在接个非线性激活函数,最终生成特征图。以Relu激活函数为例,特征图的计算公式为:
其中(i,j)表示图片像素点的位置索引,xij表示我们卷积窗口中的图片块,k则表示我们要提取的特征图的索引。
一般来说,如果我们要提取的一些潜在的特征是线性可分的话,那么对于线性的卷积运算来说这是足够了。然而一般来说我们所要提取的特征一般是高度非线性的。在传统的CNN中,也许我们可以用超完备的滤波器,来提取各种潜在的特征。比如我们要提取某个特征,于是我就用了一大堆的滤波器,把所有可能的提取出来,这样就可以把我想要提取的特征也覆盖到,然而这样存在一个缺点,那就是网络太恐怖了,参数太多了。
我们知道CNN高层特征其实是低层特征通过某种运算的组合。于是作者就根据这个想法,提出在每个局部感受野中进行更加复杂的运算,提出了对卷积层的改进算法:MLP卷积层。另一方面,传统的CNN最后一层都是全连接层,参数个数非常之多,容易引起过拟合(如Alexnet),一个CNN模型,大部分的参数都被全连接层给占用了,故这篇paper提出采用了:全局均值池化,替代全连接层。因此后面主要从这两个创新点进行讲解。
二、MLP卷积层(文献创新点1)
这个是文献的大创新点,也就是提出了mlpconv层。Mlpconv层可以看成是每个卷积的局部感受野中还包含了一个微型的多层网络。其实在以前的卷积层中,我们局部感受野窗口的运算,可以理解为一个单层的网络,如下图所示:
线性卷积层
CNN层的计算公式如下:
然而现在不同了,我们要采用多层的网络,提高非线性,于是mlpconv层的网络结构图如下::
Mlpconv层
从上面的图可以看到,说的简单一点呢,利用多层mlp的微型网络,对每个局部感受野的神经元进行更加复杂的运算,而以前的卷积层,局部感受野的运算仅仅只是一个单层的神经网络罢了。对于mlpconv层每张特征图的计算公式如下:
一般来说mlp是一个三层的网络结构。
下面是一个单层的mlpconv网络的caffe网络结构文件,源码来自于:https://gist.github.com/mavenlin/d802a5849de39225bcc6 :
<span style="font-size:18px;">layers {bottom: "data"top: "conv1"name: "conv1"type: CONVOLUTIONblobs_lr: 1blobs_lr: 2weight_decay: 1weight_decay: 0convolution_param {num_output: 96kernel_size: 11stride: 4weight_filler {type: "gaussian"mean: 0std: 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: CONVOLUTIONblobs_lr: 1blobs_lr: 2weight_decay: 1weight_decay: 0convolution_param {num_output: 96kernel_size: 1stride: 1weight_filler {type: "gaussian"mean: 0std: 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: CONVOLUTIONblobs_lr: 1blobs_lr: 2weight_decay: 1weight_decay: 0convolution_param {num_output: 96kernel_size: 1stride: 1weight_filler {type: "gaussian"mean: 0std: 0.05}bias_filler {type: "constant"value: 0}}
}
layers {bottom: "cccp2"top: "cccp2"name: "relu2"type: RELU
}
</span>
三、全局均值池化(文献创新点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
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。
四、总体网络架构
根据上面的作者对传统CNN的两个改进,利用其进行1000物体分类问题,于是作者最后设计了一个:4层的NIN+全局均值池化,网络如下:
个人总结:个人感觉这篇文献很有价值,实现方式也很简单,一开始我还以为需要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结构,对其不知做出改进,所以我们需要一篇一篇的学。
参考文献:
1、《Network In Network》
2、https://github.com/BVLC/caffe/wiki/Model-Zoo
3、https://gist.github.com/mavenlin/d802a5849de39225bcc6
4、《Maxout Networks》
**********************作者:hjimce 时间:2016.1.4 联系QQ:1393852684 原创文章,版权所有,转载请保留本行信息***************
版权声明:未经允许请勿用于商业用途,转载请注明出处:http://blog.csdn.net/mounty_fsc/ https://blog.csdn.net/mounty_fsc/article/details/51746111
本文地址:http://blog.csdn.net/mounty_fsc/article/details/51746111
《Network in Network》论文笔记
1.综述
这篇文章有两个很重要的观点:
-
1×1卷积的使用
文中提出使用mlpconv网络层替代传统的convolution层。mlp层实际上是卷积加传统的mlp(多层感知器),因为convolution是线性的,而mlp是非线性的,后者能够得到更高的抽象,泛化能力更强。在跨通道(cross channel,cross feature map)情况下,mlpconv等价于卷积层+1×1卷积层,所以此时mlpconv层也叫cccp层(cascaded cross channel parametric pooling)。 -
CNN网络中不使用FC层(全连接层)
文中提出使用Global Average Pooling取代最后的全连接层,因为全连接层参数多且易过拟合。做法即移除全连接层,在最后一层(文中使用mlpconv)层,后面加一层Average Pooling层。
以上两点,之所以重要,在于,其在较大程度上减少了参数个数,确能够得到一个较好的结果。而参数规模的减少,不仅有利用网络层数的加深(由于参数过多,网络规模过大,GPU显存等不够用而限制网络层数的增加,从而限制模型的泛化能力),而且在训练时间上也得到改进。
2.网络结构
-
传统的convolution层
-
单通道mlpconv层
-
跨通道mlpconv层(cccp层)
-
由图可知,mlpconv=convolution+mlp(图中为2层的mlp)。
-
在caffe中实现上,mlpconv=convolution+1×1convolution+1×1convolution(2层的mlp)
3.Caffe中的实现
原文3层mlpconv的完整网络结构
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。
4. 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
NIN: Network In Network
摘要:我们提出了一个名为‘Network In Network (NIN)’的深度网络,去提高模型对于局部感受野的local pathes的判别力。传统的卷积层使用线性filters结构。取而代之,我们构建了一个更复杂的神经网络结构去abstract局部感受野的data。我们使用通用近似模块MLP来构建这个复杂神经网络。改进后的网络的feature maps的计算方式与CNN一致使用滑动窗的方式。深度NIN通过堆叠多个上述结构来实现。除了用微型网络增强网络的局部建模能力,在分类任务中,我们还实施了全局平均池化over feature maps,全局平均池化很容易去解释并且不容易过拟合than FC。NIN在CIFAR-10、CIIFAR-100取得了state of art,在SVHN和MNIST上取得了不错的性能。
关键点:mlpconv层,global average pooling层
文章总结:
NIN改进了传统的CNN,采用了少量参数就取得了超过AlexNet的性能,AlexNet网络参数大小是230M,NIN只需要29M。本文的最主要贡献是改进了CNN。
1. 为什么要在conv前加MLP?
卷积层对下层的数据快来说是一个GLM(generalized linear model,广义线性模型),作者认为广义线性模型的抽象层次较低。作者认为“特征是不变的”,即同一概念的不同变种的特征是相同的。当sample的latent概念是线性可分时,CNN的抽象是合理的。但同一概念的数据一般是存在于一个非线性流形中,因此特征和输入之间的关系是一个高度非线性的函数。这就是作者使用MLP来增强conv的原因。
传统的CNN用超完备滤波器来提取潜在特征:使用大量的滤波器来提取某个特征,把所有可能的提取出来,这样就可以把我想要提取的特征也覆盖到。这种通过增加滤波器数量的办法会增大网络的计算量和参数量。
2. mlpconv层(创新点1)
CNN高层特征是低层特征通过某种运算的组合。所以作者提出了局部感受野中进行更复杂的运算。mlpconv层可以看成局部感受野上进行conv运算之前,还进行mlp运算。
作者将改进后的层称为mlpconv层,并将其和CNN进行了对比。下图是结构对比图:
conv和mlpconv的对比
改进后的mlpconv层的计算方式和conv层类似,通过将mlp+filter滑过整个输入,最终得到feature maps。
从图上可以看到,每个局部感受野的神经元进行了更复杂的运算。
其实NIIN和Maxout的想法有很多相似的地方,但是mlpconv相比maxout更巧妙
3. 全局平局池化层(创新点2)
在NIN网络中,作者用feature maps的空间池化(spatial average)代替了常用的FC层,这种池化被成为全局平均池化(global average pool)。作者将全局平均池化层的输出当做各类别的概率。使用FC(黑箱)时很难解释各类别的概率信息是怎么反向传播回前面的卷积层的。全局平均池化层有实际意义,它将feature maps和类别对应起来。并且,FC层容易过拟合并且严重依赖Dropout正则。
传统的卷积网络用卷积提取特征,然后得到的特征再用FC+softmax逻辑回归分类层进行分类。使用全局平均池化层,每个特征图作为一个输出。这样参数量大大减小,并且每一个特征图相当于一个输出特征(表示输出类的特征)。
AlexNet网络的参数很大一部分来自于FC,本文用全局平均池化层取代FC,大大减少了参数量,所以,NIN在性能超AleNet的情况下,参数量从230M降到29M,很大一部分的减少来自于FC的去除。
4. NIN网络架构
NIN架构主要的创新点是mlpconv层和global average pooling层。下面将给出这两个部分的数学公式。
传统conv层的数学表达式
单个感受野上的计算:
这几个公式非常不理解
mlpconv层的数学表达式
这几个公式非常不理解
从跨通道池化(cross channel (cross feature map) pooling)的角度来看,上面的公式等效于在一个正常的卷积层上实施级联跨通道加权池化(cascaded cross channel parametric pooling)。池化层对输入的feature map进行加权线性求和,然后用ReLU进行激活 。这种级联跨通道加权池化使得模型能够学习到通道之间的关系
这种跨通道加权池化也等效于1x1卷积层。这个解释使得理解NIN的结构变得容易
一般来说mlp是一个3层的网络结构。
global average pooling层的数学表达式
在全局平均池化层,将最后一层的每一个feature map的平均值,然后将平均值组成的向量输入到softmax层。
网络架构图
NIN文章使用的网络架构如图(总计4层):3层的mlpconv + 1层global_average_pooling。
NIN网络架构NIN网络架构
作者提供了NIN的Caffe版模型,感兴趣的可以查看。
下面是TensorFlow实现的NIN网络:
#coding:utf-8
import tensorflow as tfdef print_activation(x):print(x.op.name, x.get_shape().as_list())def inference(inputs,num_classes=10,is_training=True,dropout_keep_prob=0.5,scope='inference'):with tf.variable_scope(scope):x = inputsprint_activation(x)with tf.variable_scope('mlpconv1'):x = tf.layers.Conv2D(192, [5,5], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.layers.Conv2D(160, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.layers.Conv2D(96, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.nn.max_pool(x, [1,3,3,1], [1,2,2,1], padding='SAME')print_activation(x)if is_training:with tf.variable_scope('dropout1'):x = tf.nn.dropout(x, keep_prob=0.5)print_activation(x)with tf.variable_scope('mlpconv2'):x = tf.layers.Conv2D(192, [5,5], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.layers.Conv2D(192, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.layers.Conv2D(192, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.nn.max_pool(x, [1,3,3,1], [1,2,2,1], padding='SAME')print_activation(x)if is_training:with tf.variable_scope('dropout2'):x = tf.nn.dropout(x, keep_prob=0.5)print_activation(x)with tf.variable_scope('mlpconv3'):x = tf.layers.Conv2D(192, [3,3], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.layers.Conv2D(192, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)x = tf.layers.Conv2D(10, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)print_activation(x)with tf.variable_scope('global_average_pool'):x = tf.reduce_mean(x, reduction_indices=[1,2])print_activation(x)x = tf.nn.softmax(x)print_activation(x)return xif __name__ == '__main__':x = tf.placeholder(tf.float32,[None,32,32,3])logits = inference(inputs=x,num_classes=10,is_training=True)
'''
也许有人问为什么用1x1卷积实现NIN,这个其实在作者文章的3.2节已经阐述了。懒得翻原文的,查看本博客中,加粗的两段文字。
'''
mlpconv和maxout的对比:
maxout可以拟合任意的凸激活函数,所以maxout对samples是凸集的情况很有效。mlpconv层与maxout层假设凸函数的情况不同的是,它采用了通用拟合函数MLP而不是凸函数,所以mlpconv适用于各种分布的latent concepts。