> 文档中心 > AlexNet

AlexNet


1. 前言

本文使用飞桨(PaddlePaddle)复现卷积神经网络AlexNet。
本人全部文章请参见:博客文章导航目录
本文归属于:卷积神经网络复现系列

2. AlexNet模型结构

2012年,Alex Krizhevsky等人提出的AlexNet以很大优势获得了ImageNet比赛的冠军。这一成果极大的激发了产业界对神经网络的兴趣,开创了使用深度神经网络解决图像问题的途径。
AlexNet包含5层卷积、2层LRN(Local Response Normalization)和3个全连接层。除最后一个全连接层外,每个卷积层和全连接层激活函数均为ReLU。同时,AlexNet使用了Dropout抑制过拟合,模型具体结构如下图所示。(示意图图中不包含LRN层,具体参见模型复现代码)
图一
AlexNet共包含8个模块,对输入227x227x3的图片依次进行如下处理:

  1. 11x11的96通道步长为4的卷积 + local_size为5的LRN +步长为2的3x3池化;
  2. 5x5的256通道padding为2group为2步长为1的卷积 + local_size为5的LRN + 步长为2的3x3池化;
  3. 3x3的384通道padding为1步长为1的卷积;
  4. 3x3的384通道padding为1步长为1的卷积;
  5. 3x3的256通道padding为1步长为1的卷积 + 步长为2的3x3池化;
  6. 4096个节点的全连接层 + 概率为0.5的Dropout;
  7. 4096个节点的全连接层 + 概率为0.5的Dropout;
  8. 节点数等于输出类别数的全连接输出层。

其中除输出层外,所有卷积层和全链接层激活函数均为ReLU。

3. AlexNet中的结构组件

3.1 ReLU激活函数

线性整流函数(Rectified Linear Unit, ReLU),又称修正线性单元,是一种人工神经网络中常用的激活函数。其数学表达式如下:
f ( x ) = m a x ( 0 , x ) f(x) = max(0, x) f(x)=max(0,x)
ReLU函数的导数在 x x x小于或等于0时等于0,在 x x x大于0时等于1.因此在自变量 x x x大于0时,神经网络上一层的梯度能够直接传导到下一层,避免了梯度消失现象。
ReLU函数把自变量小于0时的函数值置为0,假设某一层神经元的输出中大于0和小于0的个数差不多,则使用ReLU函数作为激活函数可以关闭一半左右的神经元,使之处于非激活状态。因此ReLU函数保证了每一次网络中只有少部分神经元的参数被更新,这样做可以促进深度神经网络的收敛。

3.2 最大池化(MaxPooling)

池化(Pooling):把邻近的像素作为一个“池子”来重新考虑。
最大池化(Max Pooling):对每一个邻近的像素组成的池子,选取最大值作为输出。
实践证明,相比平均池化(Average Pooling,将每一个邻近的像素组成的池子,将池子中所有像素点的平均值作为输出),最大池化能够促进网络收敛。原因是在最大池化下,梯度直接传到到最大值,而不是平均分布到池子中的每一个元素,这样每次更新的参数将会进一步减少。
深度学习实践发现有重叠的最大池化能够很好的克服过拟合问题,提升深度学习模型性能。可能的原因有两个:1. 最大池化相当于同时做了降采样和非线性操作;2. 池化层激活的神经元变少。

3.3 随机丢弃(Dropout)

为了避免系统参数更新过快导致过拟合,在利用训练样本更新参数时候,随机“丢弃”一定比例的神经元。被丢弃的神经元将不参加训练过程,这些神经元的输入和输出权重系数也不做更新,这样每次训练时,训练的网络架构都不一样,而这些不同的网络架构分享共通的权重系数。
假设每个不同的网络均存在一定的过拟合情况,而这些过拟合方向基本是随机的。综合多个网络结构,可以使得整体网络的过拟合情况在一定程度上相互抵消,是的整体网络效果更好。

3.4 局部响应归一化(LRN)

LRN对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。LRN通过在相邻卷积核生成的特征图之间引入竞争,从而有些本来在特征图中显著的特征更显著,而在相邻的其他特征图中的特征被抑制,这样让不同卷积核产生的特征之间的相关性变小。
深度学习实践确实证明LRN可以提高模型的泛化能力,但是提升的很少,甚至有一些研究者觉得LRN是一个“伪命题”,因而饱受争议,以至于后面不再使用。在PddlePaddle框架并没有实现LRN层(2021年11月3日更新:PaddlePaddle框架实现了LRN层,使用paddle.nn.LocalResponseNorm()即可创建LRN层,具体参见链接),本文为了还原AlexNet论文中提出的模型,参考了资料链接[4]中的实现方法。

4. AlexNet模型复现

使用飞桨(PaddlePaddle)复现AlexNet,首先实现LRN层。定义继承自paddle.nn.LayerLRN类,在__init__方法中初始化各模块,并在forward函数中实现LRN层计算流程。具体代码如下:

# -*- coding: utf-8 -*-# @Time    : 2021/8/8 10:51# @Author  : He Ruizhi# @File    : alexnet.py# @Software: PyCharmimport paddleclass LRN(paddle.nn.Layer):    """    目前该网络已经很少使用了,这里为了原生的AlexNet而实现    LRN实现参考链接:https://github.com/sloth2012/AlexNet/blob/master/AlexNet.ipynb    """    def __init__(self, local_size=1, alpha=1.0, beta=0.75, bias=1.0, ACROSS_CHANNELS=False): super(LRN, self).__init__() self.alpha = paddle.to_tensor(alpha) self.beta = paddle.to_tensor(beta) self.bias = paddle.to_tensor(bias) self.ACROSS_CHANNELS = ACROSS_CHANNELS if self.ACROSS_CHANNELS:     self.average = paddle.nn.AvgPool3D(kernel_size=(local_size, 1, 1), stride=1,     padding=(int((local_size - 1.0) / 2), 0, 0)) else:     self.average = paddle.nn.AvgPool3D(kernel_size=local_size, stride=1,     padding=int((local_size - 1.0) / 2))    def forward(self, x): if self.ACROSS_CHANNELS:     div = x.pow(2).unsqueeze(1)     div = self.average(div).squeeze(1)     div = div.multiply(self.alpha).add(self.bias).pow(self.beta) else:     div = x.pow(2)     div = self.average(div)     div = div.multiply(self.alpha).add(self.bias).pow(self.beta) x = x.divide(div) return x

搭建AlexNet,定义继承自paddle.nn.LayerAlexNet类,在__init__方法中定义各卷积、池化、LRN和全连接层,在forward函数中实现网络前向计算流程。具体代码如下:

class AlexNet(paddle.nn.Layer):    """AlexNet模型搭建"""    def __init__(self, num_classes=1000): super(AlexNet, self).__init__() # 第一个模块:11x11的96通道步长为4的卷积 + local_size为5的LRN +步长为2的3x3池化 self.block1 = paddle.nn.Sequential(     paddle.nn.Conv2D(in_channels=3, out_channels=96, kernel_size=11, stride=4),     paddle.nn.ReLU(),     LRN(local_size=5, alpha=1e-4, beta=0.75, ACROSS_CHANNELS=True),     paddle.nn.MaxPool2D(kernel_size=3, stride=2) ) # 第二个模块:5x5的256通道padding为2group为2步长为1的卷积 + local_size为5的LRN + 步长为2的3x3池化 self.block2 = paddle.nn.Sequential(     paddle.nn.Conv2D(in_channels=96, out_channels=256, kernel_size=5, groups=2, stride=1, padding=2),     paddle.nn.ReLU(),     LRN(local_size=5, alpha=1e-4, beta=0.75, ACROSS_CHANNELS=True),     paddle.nn.MaxPool2D(kernel_size=3, stride=2) ) # 第三个模块:3x3的384通道padding为1步长为1的卷积 self.block3 = paddle.nn.Sequential(     paddle.nn.Conv2D(in_channels=256, out_channels=384, kernel_size=3, stride=1, padding=1),     paddle.nn.ReLU() ) # 第四个模块:3x3的384通道padding为1步长为1的卷积 self.block4 = paddle.nn.Sequential(     paddle.nn.Conv2D(in_channels=384, out_channels=384, kernel_size=3, stride=1, padding=1),     paddle.nn.ReLU() ) # 第五个模块:33x3的256通道padding为1步长为1的卷积 + 步长为2的3x3池化 self.block5 = paddle.nn.Sequential(     paddle.nn.Conv2D(in_channels=384, out_channels=256, kernel_size=3, stride=1, padding=1),     paddle.nn.ReLU(),     paddle.nn.MaxPool2D(kernel_size=3, stride=2) ) self.flatten = paddle.nn.Flatten() # 全连接层1 self.block6 = paddle.nn.Sequential(     paddle.nn.Linear(in_features=6*6*256, out_features=4096),     paddle.nn.ReLU(),     paddle.nn.Dropout(0.5) ) # 全连接层2 self.block7 = paddle.nn.Sequential(     paddle.nn.Linear(in_features=4096, out_features=4096),     paddle.nn.ReLU(),     paddle.nn.Dropout(0.5) ) # 输出层 self.out_fc = paddle.nn.Linear(in_features=4096, out_features=num_classes)    def forward(self, x): # 实现前向计算流程 x = self.block1(x) x = self.block2(x) x = self.block3(x) x = self.block4(x) x = self.block5(x) x = self.flatten(x) x = self.block6(x) x = self.block7(x) self.out_fc(x) return x
  • 你也许会问我,搭建AlexNet为什么不在一个paddle.nn.Sequential中堆到底。原因有二:
  1. 将各个模块用paddle.nn.Sequential打包起来,而不将整个网络如此堆叠,可以更加清晰地查看和理解AlexNet网络结构;
  2. 建议使用这种在__init__方法中初始化各模块,在forward方法中实现前向计算流程的方式搭建神经网络,而不建议使用在paddle.nn.Sequential中堆叠网络,因为第一种方法网络搭建灵活度更高。
  • 类似ReLUMaxPool2D等无训练参数,且完全相同的模块可以只初始化一次,然后多次复用。甚至可以不用初始化,直接在forward前向计算函数中通过paddle.nn.functional.*调用相关函数。但是如果不在__init__函数中多次显式地定义出来,则在paddle.summary中无法清晰地看到模型结构。为了清晰地在paddle.summary中清晰地看到模型结构,本系列文章均会多次显式地初始化ReLUMaxPool2D等模块。

实例化AlexNet,使用paddle.summary函数打印模型结构信息:

if __name__ == '__main__':    model = AlexNet()    paddle.summary(model, input_size=(None, 3, 227, 227))

模型结构信息如下:

---------------------------------------------------------------------------- Layer (type) Input Shape   Output Shape  Param #    ============================================================================   Conv2D-1      [[1, 3, 227, 227]]     [1, 96, 55, 55] 34,944  ReLU-1 [[1, 96, 55, 55]]     [1, 96, 55, 55]    0  AvgPool3D-1   [[1, 1, 96, 55, 55]]   [1, 1, 96, 55, 55]  0     LRN-1 [[1, 96, 55, 55]]     [1, 96, 55, 55]    0  MaxPool2D-1     [[1, 96, 55, 55]]     [1, 96, 27, 27]    0   Conv2D-2[[1, 96, 27, 27]]     [1, 256, 27, 27]307,456 ReLU-2[[1, 256, 27, 27]]     [1, 256, 27, 27]   0  AvgPool3D-2   [[1, 1, 256, 27, 27]] [1, 1, 256, 27, 27]  0     LRN-2[[1, 256, 27, 27]]     [1, 256, 27, 27]   0  MaxPool2D-2    [[1, 256, 27, 27]]     [1, 256, 13, 13]   0   Conv2D-3      [[1, 256, 13, 13]]     [1, 384, 13, 13]885,120 ReLU-3[[1, 384, 13, 13]]     [1, 384, 13, 13]   0   Conv2D-4      [[1, 384, 13, 13]]     [1, 384, 13, 13]      1,327,488ReLU-4[[1, 384, 13, 13]]     [1, 384, 13, 13]   0   Conv2D-5      [[1, 384, 13, 13]]     [1, 256, 13, 13]884,992 ReLU-5[[1, 256, 13, 13]]     [1, 256, 13, 13]   0  MaxPool2D-3    [[1, 256, 13, 13]]      [1, 256, 6, 6]    0   Flatten-1      [[1, 256, 6, 6]]  [1, 9216]0   Linear-1   [[1, 9216]]    [1, 4096]  37,752,832ReLU-6    [[1, 4096]]    [1, 4096]0   Dropout-1  [[1, 4096]]    [1, 4096]0   Linear-2   [[1, 4096]]    [1, 4096]  16,781,312ReLU-7    [[1, 4096]]    [1, 4096]0   Dropout-2  [[1, 4096]]    [1, 4096]0   Linear-3   [[1, 4096]]    [1, 1000]   4,097,000   ============================================================================Total params: 62,071,144Trainable params: 62,071,144Non-trainable params: 0----------------------------------------------------------------------------Input size (MB): 0.59Forward/backward pass size (MB): 18.40Params size (MB): 236.78Estimated Total Size (MB): 255.77----------------------------------------------------------------------------

5. 参考资料链接

  1. https://aistudio.baidu.com/aistudio/projectdetail/2169490
  2. https://www.icourse163.org/learn/ZJU-1206573810?tid=1206902211#/learn/content?type=detail&id=1235286004&cid=1254982007
  3. https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
  4. https://github.com/sloth2012/AlexNet/blob/master/AlexNet.ipynb
  5. https://blog.csdn.net/qq_27825451/article/details/88745034
  6. https://zhuanlan.zhihu.com/p/29786939
  7. https://blog.csdn.net/sunbaigui/article/details/39938097