CNN经典模型——AlexNet

AlexNet网络结构剖析

2012年AlexNet在ImageNet大赛上一举夺冠,展示了CNN在图像识别上的惊人表现,掀起了CNN研究的热潮,深度学习自此引爆,在AlexNet之前,深度学习已经沉寂了很久。该网络是以论文的第一作者 Alex Krizhevsky 的名字命名的,另外两位合著者是 Ilya Sutskever 和 Geoffery Hinton。
该网络结构如下(为了讲清楚AlexNet结构,找了不同的示意图):

需要注意的是,论文发表时的GPU运算能力达不到该网络的训练需求,所以使用的是两个GPU并行,中间为了提高模型效果使用了不少交互相关的Trick。我们下面只关注网络结构,不关注两个GPU处理之间的交互。

下面对该结构做详细介绍,数据预处理首先将所有图像都处理为256×256×3的图像,随后AlexNet通过随机采样的方式从中选取一张227×227×3的图片作为输入(原论文AlexNet结构图示写的是224×224×3,AlexNet2是吴恩达老师讲义中的图示,经计算确实应该是227)。
注:以下卷积操作由于卷积核通道数和上一层次输出FeatureMap个数相同,故没有写进Kernel中,Kernel的最后一个参数是卷积核个数。

  • 第一个卷积层Kernel=(11,11,96),Padding=0,Stride= 4,因此输出大小为\frac{(227-11+2×0)}{4}+1=55,将输出经过Relu激活层后,进行Local Response Normalization(LRN局部响应归一化),之后经过一个3×3的Maxpooling,Stride=2,Padding=0,于是输出大小为\frac{(55-3+2×0)}{2}+1=27
  • 第二个卷积层,Kernel=(5,5,256),Padding=2,Stride=1,于是输出为\frac{(27-5+2×2)}{1}+1=27,接着Relu,LRN,之后经过一个3×3的Maxpooling,Stride=2,Padding=0,于是输出大小为\frac{(27-3+2×0)}{2}+1=13
  • 第三个卷积层,Kernel=(3,3,384),Padding=1,Stride=1,输出为\frac{(13-3+2×1)}{1}+1=13,接着经过Relu层;
  • 第四个卷积层和第三层操作一样;
  • 第五个卷积层,Kernel=(3,3,256),Padding=1,Stride=1,输出为\frac{(13-3+2×1)}{1}+1=13,Relu,之后经过一个3×3的Maxpooling,Stride=2,Padding=0,于是输出大小为\frac{(13-3+2×0)}{2}+1=6;-
  • 全连接层1,这一层实际上是4096个(6,6,256)的Kernel,只是正好可以看做拉直后的全连接,接着依次经过Relu,Dropout;
  • 全连接层2,4096,依次经过Relu,Dropout。
  • 全连接层3,输出1000个结果,Softmax给出分类。

在原论文的双GPU并行计算结构下,卷积层 Conv2,Conv4,Conv5中的卷积核只和位于同一GPU的上一层的FeatureMap相连,于是需要训练的参数数量是:

卷积层的参数 = 卷积核的数量 * 卷积核 + 偏置
Conv1: 96个11×11×3的卷积核,96×11×11×3+96=34848
Conv2: 2组,每组128个5×5×48的卷积核,(128×5×5×48+128)×2=307456
Conv3: 384个3×3×256的卷积核,3×3×256×384+384=885120
Conv4: 2组,每组192个3×3×192的卷积核,(3×3×192×192+192)×2=663936
Conv5: 2组,每组128个3×3×192的卷积核,(3×3×192×128+128)×2=442624
FuulC6: 4096个6×6×256的卷积核,6×6×256×4096+4096=37752832
FuulC7: 4096∗4096+4096=16781312
output(FuulC8): 4096∗1000=4096000

而实际如果Conv2,Conv4,Conv5也合在一块计算的话,这3个层次中相应的参数数量会翻倍。
不过,从上面也可以看出,参数大多数集中在全连接层,在卷积层由于局部连接和权值共享,权值参数相对较少。

AlexNet的特点及其解释

AlexNet首次在CNN中成功应用了ReLU和Dropout等Trick,同时也使用了GPU进行运算加速。

  • 激活函数ReLu:
    在最初,Sigmoid和Tanh函数最常用的激活函数。在网络层数较少时,Sigmoid函数的特性能够很好地向后传递信息。但它有一个很大的问题就是梯度饱和(梯度消失)。
    \sigma(x)=\frac{1}{1+e^{-x}}

所谓梯度饱和就是当输入的数字很大(或很小)时,其导数值接近于0。这样在深层次网络结构中进行反向传播时,由于链式法则,很多个很小的sigmoid导数相乘,导致其结果趋于0,权值更新将非常缓慢。
而ReLU是一个分段线性函数:
ReLu=max(x,0)

相比于Sigmoid不仅运算更快,且导数是恒定值,一般不会发生严重的梯度饱和。另外Relu会使一部分神经元的输出为0,这样就形成了网络的稀疏性,减少了参数的相互依赖,从而一定程度抑制了过拟合。

  • Dropout
    对Dropout可以有以下几个层面的解释。
    组合解释:每次的Dropout都相当于训练了一个子网络,最终结果是这些子网络的组合;
    动机解释:Dropout的使用降低了参数之间的依赖性,减弱了神经元之间相互依赖协作的工作模式,从而提高了神经元的独立学习能力,增加了模型泛化能力;
    数据解释:对于Dropout后的训练结果总能找到与之对应的样本,这相当于数据增强。

AlexNet逻辑的Keras实现

由于是用个人的PC跑程序,相当于只有一个运算中心,所以不可能实现论文最原始的网络了,我们只实现AlexNet的核心逻辑,忽略其关于双GPU的交互。另外,原论文中使用了LRN,LRN的基本思路是,比如 一张13×13×256的FeatureMap,LRN 要做的就是选取一个位置,从这个位置穿过整个通道,能得到 256 个数字,并进行归一化。进行LRN的动机是,对于这张 13×13 的图像中的每个位置来说,我们可能并不需要太多的高激活神经元。不过后来的实验发现LRN会让前向传播和反向传播的速度降低,但最终对模型效果提升却不明显,所以只有AlexNet用LRN,其之后的模型都放弃了。这里实现的网络中虽然加入了LRN,但为了训练速度可以自行删除LRN过程:

class AlexNet:
    @staticmethod
    def build(width,height,depth,classes,reg=0.0002):
        model = Sequential()
        inputShape = (height,width,depth)
        chanDim = -1

        if K.image_data_format() == "channels_first":
            inputShape = (depth,height,width)
            chanDim = 1

        model.add(Conv2D(96,(11,11),strides=(4,4),input_shape=inputShape,padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))
        model.add(Dropout(0.25))

        model.add(Conv2D(256,(5,5),padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))
        model.add(Dropout(0.25))

        model.add(Conv2D(384,(3,3),padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(384,(3,3),padding="same",kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization(axis=chanDim))
        model.add(Conv2D(256,(3,3),padding="same",kernel_regularizer=l2(reg)))
        model.add(MaxPooling2D(pool_size=(3,3),strides=(2,2)))
        model.add(Dropout(0.25))

        model.add(Flatten())
        model.add(Dense(4096,kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization())
        model.add(Dropout(0.25))

        model.add(Dense(4096,kernel_regularizer=l2(reg)))
        model.add(Activation("relu"))
        model.add(BatchNormalization())
        model.add(Dropout(0.25))

        model.add(Dense(classes,kernel_regularizer=l2(reg)))
        model.add(Activation("softmax"))

        return model

原数据集太大,可使用小一点的数据集图片进行测试,重点只需放在关注AlexNet网络结构上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章