AlexNet深度卷积神经网络
一、AlexNet结构
AlexNet输入为RGB三通道的224 × 224 × 3大小的图像(也可填充为227 × 227 × 3 )。
AlexNet 由5 个卷积层(在第1、2、5个卷积层后面跟一个最大池化层)和 3 个全连接层组成。
最终输出层为softmax,将网络输出转化为概率值,用于预测图像的类别。
卷积+池化层(前五层)
AlexNet共有五个卷积层,每个卷积层都包含卷积核、偏置项、ReLU激活函数和局部响应归一化(LRN)模块。
卷积层C1:卷积核大小11×11,特征矩阵96个,步长4
使用96个核对224 × 224 × 3的输入图像进行滤波,卷积核大小为11 × 11 × 3,步长为4。将一对55×55×48(其实是55×55×96,因为在最早的实验中使用两个GPU进行计算所以分成了两个)的特征图分别放入ReLU激活函数,生成激活图。激活后的图像进行最大池化,size为3×3,stride为2,池化后的特征图size为27×27×48(一对,共96)。池化后进行LRN处理。
卷积层C2:使用卷积层C1的输出(响应归一化和池化)作为输入,并使用256个卷积核进行滤波,核大小为5 × 5 × 48(96)。
卷积层C3:有384个核,核大小为3 × 3 × 256,与卷积层C2的输出(归一化的,池化的)相连。
卷积层C4:有384个核,核大小为3 × 3 × 192(384)。
卷积层C5:有256个核,核大小为3 × 3 × 192(384)。卷积层C5与C3、C4层相比多了个池化,池化核size同样为3×3,stride为2。
其中,卷积层C3、C4、C5互相连接,中间没有接入池化层或归一化层。
全连接层(后三层)
全连接层F6:因为是全连接层,卷积核size为6×6×256,4096个卷积核生成4096个特征图,尺寸为1×1。然后放入ReLU函数、Dropout处理。值得注意的是AlexNet使用了Dropout层,以减少过拟合现象的发生。
全连接层F7:同F6层。
全连接层F8:最后一层全连接层的输出是1000维softmax的输入,softmax会产生1000个类别预测的值。
二、相较于LeNet卷积神经网络做出的改进
Dropout丢弃法
- 防止过拟合
Dropout工作流程
- 首先随机(临时)删掉网络中一半的隐藏神经元,输入输出神经元保持不变
- 然后把输入x通过修改后的网络前向传播,然后把得到的损失结果通过修改的网络反向传播。一小批训练样本执行完这个过程后,在没有被删除的神经元上按照随机梯度下降法更新对应的参数(w,b)
- 恢复被删掉的神经元(此时被删除的神经元保持原样,而没有被删除的神经元已经有所更新)
从隐藏层神经元中随机选择一个一半大小的子集临时删除掉(备份被删除神经元的参数)。
对一小批训练样本,先前向传播然后反向传播损失并根据随机梯度下降法更新参数(w,b) (没有被删除的那一部分参数得到更新,删除的神经元参数保持被删除前的结果)。
为什么Dropout可以解决过拟合
(1) 对生成的不同网络取平均
用相同的训练数据去训练不同的神经网络,一般会得到不同的结果。此时可以采取对不同结果取均值的策略来防止整体网络的过拟合。因为不同的网络可能产生不同的过拟合,取平均则有可能让一些“相反的”拟合互相抵消。
Dropout选择丢弃(隐藏)不同神经元就类似训练不同网络,虽然这些不同的网络也存在过拟合问题,但最终的网络能够在整体上减少过拟合。
(2) 减少神经元之间复杂的共适应关系(削弱神经元之间存在的依赖)
因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况。迫使网络去学习更加鲁棒的特征,这些特征在其它的神经元的随机子集中也存在。
换句话说假如我们的神经网络是在做出某种预测,它不应该对一些特定的线索片段太过敏感,即使丢失特定的线索,它也应该可以从众多其它线索中学习一些共同的特征。
从这个角度看dropout就有点像L1,L2正则,减少权重使得网络对丢失特定神经元连接的鲁棒性提高。
Dropout代码实现
# Dropout
import numpy as np
def dropout(x, level):
# level是0~1之间的概率值
if level < 0. or level >= 1:
raise ValueError('Dropout level must be in interval [0, 1.]')
retain_prob = 1. - level
# binomial函数,生成与x一样的维数向量。binomial函数就像抛硬币一样,我们可以把每个神经元当做抛硬币一样
# 硬币 正面的概率为p,n表示每个神经元试验的次数
# 因为我们每个神经元只需要抛一次就可以了所以n=1,size参数是我们有多少个硬币。
random_tensor = np.random.binomial(n=1, p=retain_prob, size=x.shape) # 即将生成一个0、1分布的向量,0表示这个神经元被屏蔽,不工作了,也就是dropout了
print(random_tensor)
x *= random_tensor
print(x)
# 为了保持每层输出和训练时一样的期望,对向量进行缩放
x /= retain_prob
return x
注:Dropout中的缩放
-
vanilla 版本(在测试时做rescale):
训练时,对于网络中的某个输出值x,期望变为 (1-p)x + p0 = (1-p)x
预测时相应层输出乘以1-p -
inverted 版本(在训练时做rescale):
训练时,对于网络中的某个输出值x,期望变为 (1-p)x + p0 = (1-p)x
训练时, 对包含dropout的输出乘1/(1-p), 使得期望仍然为1/(1-p) * (1-p)x = x
三、Pytorch代码实现
import torch
import torch.nn as nn
from d2l import torch as d2l
# 网络构造
net = nn.Sequential(
# C1 步幅为4,以减少输出的高度和宽度
# 二维卷积 输入通道、输出通道、卷积核大小、步长、填充
nn.Conv2d(1, 96, 11, 4, 1), nn.ReLU(),
# 最大池化 卷积核、步长、填充、膨胀(在元素间插入n-1个空元素再进行卷积)、是否返回最大值位置索引、是否使用向上取整
nn.MaxPool2d(3, 2),
# C2 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2d(96, 256, 5, 1,2), nn.ReLU(),
nn.MaxPool2d(3, 2),
# C3, C4, C5 继续增加输出通道数量
nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.MaxPool2d(3, 2),
# 矩阵0维展开(平面化处理),准备进行全连接层处理
nn.Flatten(),
# CF1 + dropout 全连接层 + 保留一半数据,减轻过拟合
nn.Linear(6400, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
# CF2 + dropout
nn.Linear(4096, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
# CF3 输出层
nn.Linear(4096, 10), # 这里使用Fashion-MNIST,类别数设置为10
# nn.Linear(4096, 1000)
)
# 构造一个高度和宽度都为224的单通道数据,来观察每一层输出的形状
X = torch.randn(1, 1, 224, 224)
for layer in net:
X = layer(X)
print(layer.__class__.__name__, 'output shape:\t', X.shape)
# 数据集(Fashion-MNIST)读取
batch_size = 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
# 网络训练
# 设置学习率和轮次
lr, num_epochs = 0.01, 10
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
# 构造一个高度和宽度都为224的单通道数据,来观察每一层输出的形状
X = torch.randn(1, 1, 224, 224)
for layer in net:
X = layer(X)
print(layer.__class__.__name__, 'output shape:\t', X.shape)
四、补充
1、池化方法比较
最大池化
保留输入数据中最显著的特征,对于图像中的纹理和边缘信息提取非常有效,但可能会丢失一些有用的信息。
平均池化
更加平滑,更关注整体的数据特征,能够在一定程度上减少噪声。在某些任务中,如语义分割和对象检测,平均池化可能会表现得更好。
全局最大池化
将整个特征图作为池化窗口,选择每个通道中的最大值作为输出。减少参数数量,提高模型的泛化性能。全局最大池化还具有定位的作用,能够突出最重要的特征,在分类问题中得到了广泛应用。
全局平均池化
将整个特征图作为池化窗口,计算每个通道的平均值作为输出。与全局最大池化相比,全局平均池化更加平滑,能够在一定程度上减少噪声。此外,全局平均池化还具有正则化的作用,能够防止模型过拟合,在分类问题中也得到了广泛应用。
2、LRN局部响应归一化
对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。LRN通过在相邻卷积核生成的feature map之间引入竞争,从而有些本来在feature map中显著的特征在A中更显著,而在相邻的其他feature map中被抑制,这样让不同卷积核产生的feature map之间的相关性变小。
b
x
,
y
i
=
a
x
,
y
i
(
k
+
α
∑
j
=
m
a
x
(
0
,
i
−
n
/
2
)
m
i
n
(
N
−
1
,
i
+
n
/
2
)
)
β
b_{x,y}^i = \frac {a_{x,y}^i}{(k + \alpha \sum_{j=max(0,i-n/2)}^{min(N-1,i+n/2)})^\beta}
bx,yi=(k+α∑j=max(0,i−n/2)min(N−1,i+n/2))βax,yi
这个公式中的a表示卷积层(包括卷积操作和池化操作)后的输出结果,这个输出结果的结构是一个四维数组
[
b
a
t
c
h
,
h
e
i
g
h
t
,
w
i
d
t
h
,
c
h
a
n
n
e
l
]
[batch,height,width,channel]
[batch,height,width,channel] ([批次(第i张),图片高,图片宽,神经元个数(通道数)])。
a x , y i a_{x,y}^i ax,yi 表示在这个输出结构中的一个位置;
N表示通道数;
n是同一位置上临近的核映射的数目;
k、n、α 和 β 是超参数, k = 2 、 n = 5 、 α = 1 0 − 4 和 β = 0.75 k = 2、n = 5、α = 10^{−4} \ 和\ β = 0.75 k=2、n=5、α=10−4 和 β=0.75
注:叠加的方向是沿着通道方向的,即每个点值的平方和是沿着a中的第3维channel方向的,也就是一个点同方向的前面n/2个通道(最小为第0个通道)和后n/2个通道(最大为第d-1个通道)的点的平方和(共n+1个点)。
3、softmax
将各个输出节点的输出值范围映射到 [ 0 , 1 ] [0, 1] [0,1] ,并且约束各个输出节点的输出值的和为 1 。
S z i = e z i ∑ j = 1 n e z j S_{z_i} = \frac {e^{z_i}}{\sum_{j=1}^ne^{z_j}} Szi=∑j=1nezjezi
softmax为每个输出分类的结果都赋予一个概率值,表示属于每个类别的可能性。
-
引入指数函数的优点:
指数函数曲线呈现递增趋势,最重要的是斜率逐渐增大,也就是说在x轴上一个很小的变化,可以导致y轴上很大的变化。这种函数曲线能够将输出的数值拉开距离。在深度学习中通常使用反向传播求解梯度进而使用梯度下降进行参数更新的过程,而指数函数在求导的时候比较方便。 -
引入指数函数的缺点:
当 z i z_i zi 值非常大的话,计算得到的数值也会变的非常大,数值可能会溢出。
针对数值溢出有其对应的优化方法:将每一个输出值减去输出值中最大的值。
D
=
m
a
x
(
z
)
D = max(z)
D=max(z)
S
z
i
=
e
z
i
−
D
∑
j
=
1
n
e
z
j
−
D
S_{z_i} = \frac {e^{z_i-D}}{\sum_{j=1}^ne^{z_j-D}}
Szi=∑j=1nezj−Dezi−D