文章目录
1 模型介绍
2012年, A l e x K r i z h e v s k y Alex Krizhevsky AlexKrizhevsky、 I l y a S u t s k e v e r Ilya Sutskever IlyaSutskever在多伦多大学Geoff Hinton的实验室设计出了一个深层的卷积神经网络AlexNet,夺得了2012年ImageNet LSVRC的冠军,且准确率远超第二名(top5错误率为15.3%,第二名为26.2%),引起了很大的轰动。AlexNet可以说是具有历史意义的一个网络结构。
2 模型结构
从图中可以看出,AlexNet网络结构分为上下两层,分别对应两个GPU的操作过程,除了中间某些层(
C
3
C_3
C3卷积层和
F
6
−
8
F_{6-8}
F6−8全连接层会有GPU间的交互)外,其他层两个GPU分别计算结果。除去局部响应规范化操作(Local Responsible Normalization, LRN),AlexNet一共包含8层,前5层由卷积层(其中卷积层1、2、5后含有下采样层)组成,而剩下的3层为全连接层。最后一层全连接层的输出作为
s
o
f
t
m
a
x
softmax
softmax的输入,得到1000个图像分类标签对应的概率值。除了GPU并行结构的设计,AlexNet网络结构与LeNet十分相似,其网络的参数配置如下表所示。
Layer Name | Kernel Size | Kernel Num | Stride | Padding | Input Size | Output Size |
---|---|---|---|---|---|---|
Conv1 | 11 × 11 11\times11 11×11 | 48 ( × 2 G P U ) 48(\times2_{GPU}) 48(×2GPU) | 4 4 4 | [ 1 , 2 ] [1,2] [1,2] | 224 × 224 × 3 224\times224\times3 224×224×3 | 55 × 55 × 96 55\times55\times96 55×55×96 |
Maxpool1 | 3 × 3 3\times3 3×3 | / / / | 2 2 2 | 0 0 0 | 55 × 55 × 96 55\times55\times96 55×55×96 | 27 × 27 × 96 27\times27\times96 27×27×96 |
Conv2 | 5 × 5 5\times5 5×5 | 128 ( × 2 G P U ) 128(\times2_{GPU}) 128(×2GPU) | 1 1 1 | [ 2 , 2 ] [2,2] [2,2] | 27 × 27 × 96 27\times27\times96 27×27×96 | 27 × 27 × 256 27\times27\times256 27×27×256 |
Maxpool2 | 3 × 3 3\times3 3×3 | / / / | 2 2 2 | 0 0 0 | 27 × 27 × 256 27\times27\times256 27×27×256 | 13 × 13 × 256 13\times13\times256 13×13×256 |
Conv3 | 3 × 3 3\times3 3×3 | 192 ( × 2 G P U ) 192(\times2_{GPU}) 192(×2GPU) | 1 1 1 | [ 1 , 1 ] [1,1] [1,1] | 13 × 13 × 256 13\times13\times256 13×13×256 | 13 × 13 × 384 13\times13\times384 13×13×384 |
Conv4 | 3 × 3 3\times3 3×3 | 192 ( × 2 G P U ) 192(\times2_{GPU}) 192(×2GPU) | 1 1 1 | [ 1 , 1 ] [1,1] [1,1] | 13 × 13 × 384 13\times13\times384 13×13×384 | 13 × 13 × 384 13\times13\times384 13×13×384 |
Conv5 | 3 × 3 3\times3 3×3 | 128 ( × 2 G P U ) 128(\times2_{GPU}) 128(×2GPU) | 1 1 1 | [ 1 , 1 ] [1,1] [1,1] | 13 × 13 × 384 13\times13\times384 13×13×384 | 13 × 13 × 256 13\times13\times256 13×13×256 |
Maxpool3 | 3 × 3 3\times3 3×3 | / / / | 2 2 2 | 0 0 0 | 13 × 13 × 256 13\times13\times256 13×13×256 | 6 × 6 × 256 6\times6\times256 6×6×256 |
FC1 | 2048 2048 2048 | / / / | / / / | / / / | 6 × 6 × 256 6\times6\times256 6×6×256 | 4096 |
FC2 | 2048 2048 2048 | / / / | / / / | / / / | 4096 | 4096 |
FC3 | 1000 1000 1000 | / / / | / / / | / / / | 4096 | 1000 |
为了简化网络结构,将作者原论文中的在两个GPU上的并行结构合并,接下来我们对AlexNet的每一层作详细的分析。
1、Conv1: kernels:48×2=96;kernel_size:11;padding:[1, 2] ;stride:4
2、Maxpool1: kernel_size:3;pading:0;stride:2
3、Conv2: kernels:128×2=256; kernel_size:5; padding: [2, 2]; stride:1
4、Maxpool2: kernel_size:3; pading:0; stride:2
5、Conv3: kernels:192×2=384; kernel_size:3; padding: [1, 1]; stride:1
6、Conv4: kernels:192×2=384; kernel_size:3; padding: [1, 1]; stride:1
7、Conv5: kernels:128×2=256; kernel_size:3; padding: [1, 1]; stride:1
8、Maxpool3: kernel_size:3 padding: 0 stride:2
9、全连接层FC1、FC2、FC3
3 模型创新
1、使用ReLU作为激活函数代替了传统的Sigmoid和Tanh
ReLU为非饱和函数,论文中验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。
2、在多个GPU上进行模型的训练,不但可以提高模型的训练速度,还能提升数据的使用规模
3、使用LRN对局部的特征进行归一化
结果作为ReLU激活函数的输入能有效降低错误率
4、使用随机丢弃技术(dropout)选择性地忽略训练中的单个神经元
在AlexNet的最后几个全连接层中使用了Dropout来避免模型的过拟合
5、重叠最大池化(overlapping max pooling)
即池化范围z与步长s存在关系 z > s z>s z>s(如 S m a x S_{max} Smax中核尺度为 3 × 3 / 2 3\times3/2 3×3/2),避免平均池化(average pooling)的平均效应
4 Pytorch模型搭建
注:由于LRN层对训练结果影响不大,故代码中去除了LRN层
import torch
import torch.nn as nn
class AlexNet(nn.Module):
def __init__(self, num_classes=1000):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2), # [None, 3, 224, 224] --> [None, 96, 55, 55]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # [None, 96, 55, 55] --> [None, 96, 27, 27]
nn.Conv2d(96, 256, kernel_size=5, padding=2), # [None, 96, 27, 27] --> [None, 256, 27, 27]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # [None, 256, 27, 27] --> [None, 256, 13, 13]
nn.Conv2d(256, 384, kernel_size=3, padding=1), # [None, 256, 27, 27] --> [None, 384, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(384, 384, kernel_size=3, padding=1), # [None, 384, 13, 13] --> [None, 384, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1), # [None, 384, 13, 13] --> [None, 256, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2) # [None, 256, 13, 13] --> [None, 256, 6, 6]
)
self.classifier = nn.Sequential(
nn.Dropout(p=0.2),
nn.Linear(256 * 6 * 6, 2048),
nn.ReLU(inplace=True),
nn.Dropout(p=0.2),
nn.Linear(2048, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes)
)
def forward(self, inputs):
x = self.features(inputs)
x = torch.flatten(x, start_dim=1)
outputs = self.classifier(x)
return outputs