AlexNet使用了8层卷积神经网络,并以很大的优势赢得了ImageNet 2012图像识别挑战赛。它首次证明了学习到的特征可以超越手工设计的特征,从而一举打破计算机视觉研究的前状。

AlexNet与LeNet的设计理念非常相似,但也有显著的区别。

  1. 与相对较小的LeNet相比,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。(①)
  2. AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。(②)
  3. AlexNet通过丢弃法(dropout)来控制全连接层的模型复杂度。而LeNet并没有使用丢弃法。
  4. AlexNet引入了大量的图像增广,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合。

AlexNet网络结构
AlexNet网络结构

①:

- AlexNet第一层中的卷积窗口形状是$11\times11$ 。因为ImageNet中绝大多数图像的高和宽均比MNIST图像的高和宽大 10倍以上,ImageNet图像的物体占用更多的像素,所以需要更大的卷积窗口来捕获物体。
- 第二层中的卷积窗口形状减小到$5\times5$ ,之后全采用$3\times3$ 。
- 此外,第一、第二和第五个卷积层之后都使用了窗口形状为$3\times3$ 、步幅为$2$ 的最大池化层
- 而且,AlexNet使用的卷积通道数也大于LeNet中的卷积通道数数十倍。
- 紧接着最后一个卷积层的是两个输出个数为4096的全连接层
- 由于早期显存的限制,最早的AlexNet使用双数据流的设计使一个GPU只需要处理一半模型(如上图所示)。幸运的是,显存在过去几年得到了长足的发展,因此通常我们不再需要这样的特别设计了。

AlexNet与LeNet的结构
AlexNet与LeNet的结构



②:

一方面,ReLU激活函数的计算更简单,例如它并没有sigmoid激活函数中的求幂运算。

另一方面,ReLU激活函数在不同的参数初始化方法下使模型更容易训练。这是由于当sigmoid激活函数输出极接近0或1时,这些区域的梯度几乎为0,从而造成反向传播无法继续更新部分模型参数;而ReLU激活函数在正区间的梯度恒为1。因此,若模型参数初始化不当,sigmoid函数可能在正区间得到几乎为0的梯度,从而令模型无法得到有效训练。

代码实现

import time
import torch
from torch import nn, optim
import torchvision
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 96, 11, 4), # in_channels, out_channels, kernel_size, stride, padding
            nn.ReLU(),
            nn.MaxPool2d(3, 2), # kernel_size, stride
            # 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
            nn.Conv2d(96, 256, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(3, 2),
            # 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,进一步增大了输出通道数。
            # 前两个卷积层后不使用池化层来减小输入的高和宽
            nn.Conv2d(256, 384, 3, 1, 1),
            nn.ReLU(),
            nn.Conv2d(384, 384, 3, 1, 1),
            nn.ReLU(),
            nn.Conv2d(384, 256, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(3, 2)
        )
         # 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
        self.fc = nn.Sequential(
            nn.Linear(256*5*5, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            # 输出层。由于这里使用Fashion-MNIST,所以用类别数为10
            nn.Linear(4096, 10),
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output