简介
2012年,AlexNet横空出世。这个模型的名字来源于论文第一作者的姓名Alex Krizhevsky。AlexNet使用了8层卷积神经网络,首次讲relu函数、LRN、Dropout等技巧应用到卷积神经网络。其以很大的优势赢得了ImageNet 2012图像识别挑战赛。它首次证明了学习到的特征可以超越手工设计的特征,从而一举打破计算机视觉研究的前状。其网络结构如下图所示:
AlexNet与LeNet的区别
AlexNet与LeNet的设计理念非常相似,但也有显著的区别。
第一,与相对较小的LeNet相比,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。下面我们来详细描述这些层的设计。
AlexNet第一层中的卷积窗口形状是11×11。因为ImageNet中绝大多数图像的高和宽均比MNIST图像的高和宽大10倍以上,ImageNet图像的物体占用更多的像素,所以需要更大的卷积窗口来捕获物体。第二层中的卷积窗口形状减小到5×5,之后全采用3×3。此外,第一、第二和第五个卷积层之后都使用了窗口形状为3×3、步幅为2的最大池化层。而且,AlexNet使用的卷积通道数也大于LeNet中的卷积通道数数十倍。
紧接着最后一个卷积层的是两个输出个数为4096的全连接层。这两个巨大的全连接层带来将近1 GB的模型参数。由于早期显存的限制,最早的AlexNet使用双数据流的设计使一个GPU只需要处理一半模型。幸运的是,显存在过去几年得到了长足的发展,因此通常我们不再需要这样的特别设计了。
第二,AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。一方面,ReLU激活函数的计算更简单,例如它并没有sigmoid激活函数中的求幂运算。另一方面,ReLU激活函数在不同的参数初始化方法下使模型更容易训练。这是由于当sigmoid激活函数输出极接近0或1时,这些区域的梯度几乎为0,从而造成反向传播无法继续更新部分模型参数;而ReLU激活函数在正区间的梯度恒为1。因此,若模型参数初始化不当,sigmoid函数可能在正区间得到几乎为0的梯度,从而令模型无法得到有效训练。
第三,AlexNet通过丢弃法来控制全连接层的模型复杂度。而LeNet并没有使用丢弃法。
第四,AlexNet引入了大量的图像增广,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合。
AlexNet主要使用技巧
使用relu()作为CNN的激活函数,并验证了其效果在较深的网络上超过了sigmoid,成功解决了sigmoid在网络较深时梯度弥散问题。
训练时使用dropout随机忽略一部分神经元,以避免模型过拟合。
此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。
提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制了其他反馈较小的神经元,增强了模型的泛化能力。前文提到,池化层分别出现在第一、二、五个卷积层,而LRN则在第一、二个卷积层和池化层的中间夹杂着。由于LRN会让前馈、反馈的速度大大降低(下降1/3),但最终效果不明显,所以只有AlexNet用LRN,其他模型都放弃了。
使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵乘积。
网络结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
class AlexNet(nn.Module): """ 由 AlexNet 论文提出的层组成的神经网络模型 """ def __init__(self, num_classes=1000): """ 为此神经网络定义和分配层 Args(参数): num_classes 表示用这个模型预测的类数 """ super().__init__() # 原论文中并未提到卷积层填充的问题,但是如果没有填充在图像维度3 x 224 x 224的情况下 # 只能得到54.25 x 54.25的结果,若填充为2,则可得到55.25 x 55.25 # 若输入图像维度为3 x 227 x 227则可完美解决,此时填充为0,cs231n上便是如此解决的 # 以下头铁按照论文中方法,如有报错可尝试修改输入size为227 x 227,并调整填充为0 self.net = nn.Sequential( # input size : (b x 3 x 224 x 224) nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2), # (b x 96 x 55 x 55) nn.ReLU(), nn.LocalResponseNorm(size=5, alpha=0.0001, beta=0.75, k=2), nn.MaxPool2d(kernel_size=3, stride=2), # (b x 96 x 27 x 27) nn.Conv2d(96, 256, 5, padding=2), # (b x 256 x 27 x 27) nn.ReLU(), nn.LocalResponseNorm(size=5, alpha=0.0001, beta=0.75, k=2), nn.MaxPool2d(kernel_size=3, stride=2), # (b x 256 x 13 x 13) nn.Conv2d(256, 384, 3, padding=1), # (b x 384 x 13 x 13) nn.ReLU(), nn.Conv2d(384, 384, 3, padding=1), # (b x 384 x 13 x 13) nn.ReLU(), nn.Conv2d(384, 256, 3, padding=1), # (b x 256 x 13 x 13) nn.ReLU(), nn.MaxPool2d(kernel_size=3, stride=2), # (b x 256 x 6 x 6) ) # classifier is just a name for linear layers self.classifier = nn.Sequential( nn.Dropout(p=0.5, inplace=True), nn.Linear(in_features=(256 * 6 * 6), out_features=4096), nn.ReLU(), nn.Dropout(p=0.5, inplace=True), nn.Linear(in_features=4096, out_features=4096), nn.ReLU(), nn.Linear(in_features=4096, out_features=num_classes), ) def forward(self, x): """ Pass the input through the net. Args: x (Tensor): input tensor Returns: output (Tensor): output tensor """ x = self.net(x) x = x.view(-1, 256 * 6 * 6) # reduce the dimensions for linear layer input return self.classifier(x) |
AlexNet跟LeNet结构类似,但使用了更多的卷积层和更大的参数空间来拟合大规模数据集ImageNet。它是浅层神经网络和深度神经网络的分界线。
虽然看上去AlexNet的实现比LeNet的实现也就多了几行代码而已,但这个观念上的转变和真正优秀实验结果的产生令学术界付出了很多年。
同济子豪兄的论文解读:https://blog.csdn.net/zziahgf/article/details/79619059
(现在要赶毕业论文,有时间了俺要自己解读,不过感觉要一直鸽了,哈哈哈哈)(跟着论文所说的网络架构推导的时候总是感觉哪里不对,他这个双GPU并行给我绕晕了;并且论文有几个明显漏洞,上面的代码中有粗略地叙述)
完整代码可参考:https://github.com/dansuh17/alexnet-pytorch/blob/master/model.py
pytorch官方不带LRN层版:https://github.com/pytorch/vision/blob/main/torchvision/models/alexnet.py
暂无评论