深度学习分类网络总结
- AlexNet
前言
AlexNet是由Hinton和其学生Alex所设计,获得了2012 ILSVRC(ImageNet Large Scale Visual Recognition Challenge)竞赛的冠军,top5错误率为15.3%,远超第二名的26.2%,证明了卷积神经网络在图像识别任务中的优越性[1]。
一、网络结构
引用原论文中的网络结构图,可以看出AlexNet共有8层,5个卷积层和3个全连接层。使用torchstat工具打印pytorch官方实现的AlexNet中每层的输入输出尺寸、参数量(parameters)和计算量(FLOPs,MACs):
import torchvision.models as models
from torchstat import stat
alexnet = models.alexnet()
stat(alexnet, (3,224,224))
AlexNet(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(4): ReLU(inplace=True)
(5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(7): ReLU(inplace=True)
(8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(9): ReLU(inplace=True)
(10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
(classifier): Sequential(
(0): Dropout(p=0.5, inplace=False)
(1): Linear(in_features=9216, out_features=4096, bias=True)
(2): ReLU(inplace=True)
(3): Dropout(p=0.5, inplace=False)
(4): Linear(in_features=4096, out_features=4096, bias=True)
(5): ReLU(inplace=True)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
简单总结一下输入输出尺寸、参数量和计算量的计算方法:
1. 输入输出尺寸
卷积层和池化层的输出尺寸可根据下式计算:
W
o
u
t
=
W
i
n
?
K
+
2
P
S
+
1
W_{out}=\frac{W_{in}-K+2P}{S}+1
Wout?=SWin??K+2P?+1上式中:
W
i
n
W_{in}
Win?为输入特征图尺寸,K为卷积核尺寸,P为padding大小,
W
o
u
t
W_{out}
Wout?为输出特征图尺寸。 以第一个卷积层为例:输入shape为(3,224,224),卷积核为11×11,步长为4,padding为(2,2),参数代入可得:
W
o
u
t
=
224
?
11
+
4
4
+
1
=
55.25
W_{out}=\frac{224-11+4}{4}+1=55.25
Wout?=4224?11+4?+1=55.25向下取整为55,卷积核数量为64,因此输出shape为(64,55,55)。
2. 参数量
卷积层参数量 =
(
K
?
K
?
C
i
n
)
?
C
o
u
t
+
C
o
u
t
(K*K*C_{in})*C_{out}+C_{out}
(K?K?Cin?)?Cout?+Cout?,全连接层看作K=1的卷积层即可。 以第一个卷积层为例:params = 11×11×3×64+64=23296 以第一个全连接层为例:params = 1×1×9216×4096+4096=37752832
3. 计算量(FLOPs,MACs/MAdd)
- FLOPs(Float Point Operations):浮点运算次数,用来衡量算法或模型的复杂度,每一个加、减、乘、除都算一次浮点运算。
- MACs(Multiply-Accumulate Operations):乘加累积操作次数,一次乘加累积操作包括一个乘法操作和一个加法操作。因此,在数值上通常有FLOPs=2*MACs这种关系。
FLOPs计算方法: 以卷积层为例,假设经过卷积后输出
C
o
u
t
C_{out}
Cout?个尺寸为
H
o
u
t
×
W
o
u
t
H_{out}×W_{out}
Hout?×Wout?的特征图,单个特征图中的每个值都是经过一次卷积计算而得,那么卷积层的总FLOPs=
H
o
u
t
×
W
o
u
t
×
C
o
u
t
H_{out}×W_{out}×C_{out}
Hout?×Wout?×Cout? × 一次卷积的FLOPs。一次卷积计算可以简化为
y
=
w
x
+
b
y=wx+b
y=wx+b,这里的
y
y
y就是输出特征图中的某个值,
w
w
w为
K
×
K
×
C
i
n
K×K×C_{in}
K×K×Cin?的权值矩阵,
w
x
wx
wx包含
K
×
K
×
C
i
n
K×K×C_{in}
K×K×Cin?个乘法操作和
K
×
K
×
C
i
n
?
1
K×K×C_{in}-1
K×K×Cin??1个加法操作,
+
b
+b
+b包含1个加法操作,因此一次卷积的FLOPs=
(
K
×
K
×
C
i
n
)
+
(
K
×
K
×
C
i
n
?
1
)
+
1
=
2
?
K
2
?
C
i
n
(K×K×C_{in})+(K×K×C_{in}-1)+1=2*K^2*C_{in}
(K×K×Cin?)+(K×K×Cin??1)+1=2?K2?Cin?。那么卷积层的总FLOPs可按下式计算:
F
L
O
P
s
(
c
o
n
v
)
=
H
o
u
t
×
W
o
u
t
×
C
o
u
t
×
2
×
K
2
×
C
i
n
≈
2
×
p
a
r
a
m
s
×
H
o
u
t
×
W
o
u
t
FLOPs(conv) =H_{out}×W_{out}×C_{out}×2×K^2×C_{in} \approx2×params×H_{out}×W_{out}
FLOPs(conv)=Hout?×Wout?×Cout?×2×K2×Cin?≈2×params×Hout?×Wout?(torchstat中的FLOPs和MAdd貌似反过来了?)
二、亮点
1. ReLU激活函数
作者在论文中提到“对于梯度下降的训练时间,sigmoid和tanh这样的饱和非线性函数比非饱和非线性函数ReLU慢得多”,速度慢的原因是sigmoid和tanh函数中涉及到了指数运算,因此在AlexNet中选择ReLU函数作为激活函数。
import numpy as np
import matplotlib.pyplot as plt
plt.rc('font',family='Times New Roman', size=15)
x = np.linspace(-10,10,500)
sigmoid = 1 / (1+np.exp(-x))
tanh = (np.exp(x)-np.exp(-x)) / (np.exp(x)+np.exp(-x))
relu = np.where(x<0, 0, x)
fig = plt.figure()
ax = fig.add_subplot(211)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
plt.plot(x, sigmoid, label='sigmoid')
plt.plot(x, tanh, label='tanh')
plt.grid(linestyle='-.')
plt.legend()
ax2 = fig.add_subplot(212)
ax2.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax2.spines['left'].set_position(('data',0))
ax2.spines['bottom'].set_position(('data',0))
plt.plot(x, relu, label='ReLU')
plt.grid(linestyle='-.')
plt.legend()
plt.tight_layout()
plt.show()
- 作者通过实验证明了ReLU函数相比tanh函数的优势:使用ReLU激活函数的四层CNN在CIFAR-10数据集上达到25%错误率的速度比tanh激活函数大约快6倍。(下图中实线为ReLU,虚线为tanh)
- 除了速度快,ReLU激活函数还能够避免饱和函数所引起的梯度消失问题。
2. GPU并行训练
论文中提到一个GTX 580GPU只有3GB的内存,限制了可以用其进行训练的网络的最大大小,因此将网络拆分到两个GPU上,两个GOU可以直接读取和写入彼此的内存,而不需要经过主机内存,双gpu网络比单gpu网络花费更少的训练时间。作者所采用的并行化方案是将神经元的一半放在单个GPU上,另外还使用了一个技巧:GPU只在特定层中进行通信。
3. 局部响应归一化(LRN)
受神经生物学中“侧抑制”概念(一个被兴奋的神经元能降低周围神经元活性)的启发,作者提出了局部响应归一化,计算方法如下: 式中:
b
x
,
y
i
b_{x,y}^{i}
bx,yi?表示第
i
i
i个特征图在位置
(
x
,
y
)
(x,y)
(x,y)处经过局部响应归一化后的值
a
x
,
y
i
a_{x,y}^{i}
ax,yi?表示第
i
i
i个特征图在位置
(
x
,
y
)
(x,y)
(x,y)处经过局部响应归一化前的值
k
,
α
,
β
k,\alpha, \beta
k,α,β为超参数
N
N
N是特征图总数,
n
n
n表示取多少个相邻特征图,利用它们在位置
(
x
,
y
)
(x,y)
(x,y)处的值(也就是式中的
a
x
,
y
j
a_{x,y}^{j}
ax,yj?)进行求和 Pytorch中的实现方式与原论文中有一处不同,将
α
\alpha
α改为了
α
n
\frac{\alpha}{n}
nα?:
import torch
import torch.nn as nn
import torch.nn.functional as F
lrn = nn.LocalResponseNorm(size=3, alpha=1, beta=1, k=1)
input_tensor = F.relu(torch.randn((1,4,3,3)))
output_tensor = lrn(input_tensor)
tensor([[[[0.0000, 0.0000, 0.0000],
[1.5463, 1.2684, 1.5114],
[0.6285, 1.6448, 0.0000]],
[[0.5272, 0.0000, 0.3121],
[0.9505, 0.0000, 0.0000],
[0.0000, 0.0000, 0.0000]],
[[1.1392, 0.0000, 0.0000],
[0.0000, 0.0000, 0.0000],
[0.1394, 0.0000, 0.5774]],
[[1.0331, 1.0747, 0.0000],
[1.0267, 0.9921, 0.0000],
[0.0000, 0.0000, 0.0000]]]])
tensor([[[[0.0000, 0.0000, 0.0000],
[0.7370, 0.8256, 0.8580],
[0.5554, 0.8649, 0.0000]],
[[0.3457, 0.0000, 0.3023],
[0.4530, 0.0000, 0.0000],
[0.0000, 0.0000, 0.0000]],
[[0.6056, 0.0000, 0.0000],
[0.0000, 0.0000, 0.0000],
[0.1385, 0.0000, 0.5197]],
[[0.5777, 0.7760, 0.0000],
[0.7598, 0.7470, 0.0000],
[0.0000, 0.0000, 0.0000]]]])
推算一下LRN的计算过程:以第二个特征图在(0,0)位置处的值0.5272为例,求和通道的下限为
m
a
x
(
0
,
1
?
3
/
2
)
max(0,1-3/2)
max(0,1?3/2)=0,上限为
m
i
n
(
4
?
1
,
1
+
3
/
2
)
=
2
min(4-1,1+3/2)=2
min(4?1,1+3/2)=2,因此考虑0,1,2三个通道,代入公式有:
b
0
,
0
1
=
0.5272
1
+
(
0
+
0.527
2
2
+
1.139
2
2
)
/
3
=
0.3457
b_{0,0}^1 = \frac{0.5272}{1+(0+0.5272^2+1.1392^2)/3}=0.3457
b0,01?=1+(0+0.52722+1.13922)/30.5272?=0.3457
4. 重叠池化
使用小于池化窗口尺寸的步长,能够获取更丰富的特征,top1和top5错误率分别降低了0.4%和0.3%。
5. 防止过拟合
5.1 数据增强
5.2 Dropout
6. 训练策略
6.1 SGD with momentum and weight decay
6.2 权重初始化
6.3 学习率下降
参考资料
[1] Krizhevsky A, Sutskever I, Hinton G E. ImageNet classification with deep convolutional neural networks[C]// International Conference on Neural Information Processing Systems. Curran Associates Inc. 2012:1097-1105. [2] 【深度学习理论 卷积神经网络02】 卷积的一般知识(根据卷积核大小和步长计算输出结果形状) [3] CNN 模型所需的计算力flops是什么?怎么计算? [4] 【局部响应归一化】Local Response Normalization
待填坑…
|