本文是日常学习总结,内容均为原创,未经博主允许不得转载。
AlexNet简介
论文:AlexNet原文(可能加载较慢,稍等即可)
1.背景介绍
AlexNet由Alex Krizhevsky于2012年提出,夺得2012年ILSVRC比赛的冠军,top5预测的错误率为16.4%,它以领先第二名10%的准确率夺得冠军,并且成功的向世界展示了深度学习的魅力。
2.AlexNet创新点
此部分很重要,我上篇文章讲的LeNet,身为深度学习的小白,喜欢边学习边总结。为什么AlexNet会比LeNet好?他有哪些创新点?这些点是否对我们自己的网络有作用,比如LRN。我们是否可以受到启发,从而改进自己网络!
- 使用ReLU作为CNN的激活函数,验证了其效果在较深的网络中超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。(ReLU激活函数很早就被提出,却没有被发扬光大。启发:在激活函数方面,大胆思考,大胆创新,大胆实验!!
- 使用重叠的最大池化(步长小于卷积核),即在池化的时候,每次移动的步长小于池化的边长。重叠效果提升特征的丰富性,降低了top-1和top-5的错误率,同时缓解了过拟合!(启发:大家都用的,不一定是最好的!)
- 局部响应归一化(LRN),对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。(注意:LRN的效果其实是有争议的)
- 训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。
3.网络结构
AlexNet为一个8层深度网络,其中5层卷积层和3层全连接层(不包括LRN和池化层)。如下图所示:
上图可能看的比较混乱,那时因为当时GPU计算能力不强,而AlexNet又比较复杂,所以Alex使用了两个GPU并行来做运算。现在完全以及可以用一个GPU代替了,所以我们接下来皆以一个GPU介绍。
各层详细介绍:(嫌多的可以直接看Pytorch代码实现)
Conv1:
网络最初的输入是224*224*3(RGB图像),但实际上会经过预处理变为227*227*3,即对于Conv1来说,输入为227*227*3。
这个图像被11*11*3(3代表深度,例如RGB的3通道)的卷积核进行卷积运算,依据公式:【input_size - kernel_size + 2*padding】/ stride + 1 = output_size (其中【】表示向下取整),所以我们的得到的第一个特征图大小为:[227 - 11 + 2*0] / 4 + 1 = 55 , 由于一共有96个卷积核,故特征图规格为:55*55*96。Ps:在图一中我们看到48的深度是因为当时用的双GPU,数据被分为两组了,即48*2 = 96。
重叠pool池化层:这些像素层还需要经过pool运算(池化运算)的处理,池化运算的尺度由预先设定为3*3,运算的步长为2,则池化后的图像的尺寸为:(55-3)/2+1=27。即经过池化处理过的规模为27*27*96。
Conv2:
具体运算过程与第一层大致相同,运算结果见图,故不重复叙述。(下同)
Conv3:
Conv4:
Conv5:
FC6:
这里使用4096个神经元,对256个大小为6*6特征图,进行一个全链接,也就是将6*6大小的特征图,进行卷积变为一个特征点,然后对于4096个神经元中的一个点,是由256个特征图中某些个特征图卷积之后得到的特征点乘以相应的权重之后,再加上一个偏置得到。
再进行一个dropout随机从4096个节点中丢掉一些节点信息(也就是值清0),然后就得到新的4096个神经元。(dropout可以有效的减轻过拟合现象)。(下同)
FC7:
FC8:
4.代码
import torch
import torch.nn as nn
class AlexNet(nn.Module): #定义网络,推荐使用Sequential,结构清晰
def __init__(self):
super(AlexNet,self).__init__()
self.conv1 = torch.nn.Sequential( #input_size = 227*227*3
torch.nn.Conv2d(in_channels=3,out_channels=96,kernel_size=11,stride=4,padding=0),
torch.nn.ReLU(),
torch.nn.MaxPool2d(kernel_size=3, stride=2) #output_size = 27*27*96
)
self.conv2 = torch.nn.Sequential( #input_size = 27*27*96
torch.nn.Conv2d(96, 256, 5, 1, 2),
torch.nn.ReLU(),
torch.nn.MaxPool2d(3, 2) #output_size = 13*13*256
)
self.conv3 = torch.nn.Sequential( #input_size = 13*13*256
torch.nn.Conv2d(256, 384, 3, 1, 1),
torch.nn.ReLU(), #output_size = 13*13*384
)
self.conv4 = torch.nn.Sequential( #input_size = 13*13*384
torch.nn.Conv2d(384, 384, 3, 1, 1),
torch.nn.ReLU(), #output_size = 13*13*384
)
self.conv5 = torch.nn.Sequential( #input_size = 13*13*384
torch.nn.Conv2d(384, 256, 3, 1, 1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(3, 2) #output_size = 6*6*256
)
#网络前向传播过程
self.dense = torch.nn.Sequential(
torch.nn.Linear(9216, 4096),
torch.nn.ReLU(),
torch.nn.Dropout(0.5),
torch.nn.Linear(4096, 4096),
torch.nn.ReLU(),
torch.nn.Dropout(0.5),
torch.nn.Linear(4096, 50)
)
def forward(self, x): #正向传播过程
conv1_out = self.conv1(x)
conv2_out = self.conv2(conv1_out)
conv3_out = self.conv3(conv2_out)
conv4_out = self.conv4(conv3_out)
conv5_out = self.conv5(conv4_out)
res = conv5_out.view(conv5_out.size(0), -1)
out = self.dense(res)
return out