经典神经网络(2)AlexNet及其在Fashion-MNIST数据集上的应用

2、深度卷积神经网络AlexNet

  1. ImageNet 数据集:一个开源的图片数据集,包含超过 1400万张图片和图片对应的标签,包含2万多个类别。

    自从2010 年以来,ImageNet 每年举办一次比赛,即:ImageNet 大规模视觉识别挑战赛ILSVRC ,比赛使用 1000 个类别图片。

    2017年7月,ImageNet 宣布ILSVRC2017 年正式结束,因为图像分类、物体检测、物体识别任务中计算机的正确率都远超人类,计算机视觉在感知方面的问题基本得到解决,后续将专注于目前尚未解决的问题。

  2. ImageNet 中使用两种错误率作为评估指标:

    • top-5 错误率:对一个图片,如果正确标记在模型输出的前 5 个最佳预测(即:概率最高的前5个)中,则认为是正确的,否则认为是错误的。

      最终错误预测的样本数占总样本数的比例就是 top-5 错误率。

    • top-1 错误率:对一个图片,如果正确标记等于模型输出的最佳预测(即:概率最高的那个),则认为是正确的,否则认为是错误的。

      最终错误预测的样本数占总样本数的比例就是 top-1 错误率。

2.1 AlexNet简述

2.1.1 AlexNet概述

在LeNet提出后,卷积神经⽹络在计算机视觉和机器学习领域中很有名⽓。但卷积神经⽹络并没有主导这些领域。这是因为虽然LeNet在⼩数据集上取得了很好的效果,但是在更⼤、更真实的数据集上训练卷积神经⽹络的性能和可⾏性还有待研究。事实上,在上世纪90年代初到2012年之间的⼤部分时间⾥,神经⽹络往往被其他机器学习⽅法超越,如⽀持向量机(support vector machines)。

Alex Krizhevsky【Hinton的博士,AlexNet一作】、Ilya Sutskever【OpenAI联合创始人和首席科学家】和Geoff Hinton【2018 年,与 Yoshua Bengio、Yann LeCun 共同获得了图灵奖】提出了⼀种新的卷积神经⽹络变体AlexNet,在2012年ImageNet挑战赛中,AlexeNet 以远超第二名的成绩夺冠,使得深度学习重回历史舞台,具有重大历史意义。

AlexNet论文下载地址:https://proceedings.neurips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf

扫描二维码关注公众号,回复: 15374279 查看本文章

【论文摘要翻译如下】

"""
我们训练了一个大型的深度卷积神经网络,将ImageNet LSVRC-2010竞赛中的120万张高分辨率图像分类成
1000种不同的类别。在测试数据上,我们取得了Top1 37.5%和Top-5 17.0%的错误率,这个结果已经远超
以前的最高水平。该神经网络具有6千万个参数和650,000个神经元,它由5个卷积层和3个全连接层构成,其
中部分卷积层后边跟有最大池化层,全连接层后边则是一个1000路的softmax分类器。为了使训练速度更
快,我们使用了非饱和神经元和一种卷积操作的非常高效的GPU实现。为了减少全连接层中的过拟合,我们使
用了一个最近开发的被称作“暂退法”的正则化方法,该方法已被证明十分有效。在IOLSVRC-2012竞赛中,我
们正式提出这个模型的一种变体,并以15.3%的top-5测试误差率赢得榜首,相比而言,第二好的参赛队伍则
只取得26.2%的成绩。
"""

2.1.2 LeNet和AlexNet的对比

AlexNet和LeNet的架构⾮常相似,这⾥我们提供了⼀个稍微精简版本的AlexNet,去除了当年需要两个⼩型GPU同时运算的设计特点。

左边为LeNet,右边为AlexNet

在这里插入图片描述

AlexNet和LeNet的设计理念⾮常相似,但也存在显著差异。

⾸先,AlexNet⽐相对较小的LeNet5要深得多。

AlexNet由⼋层组成:五个卷积层、两个全连接隐藏层和⼀个全连接输出层。

其次,AlexNet使⽤ReLU⽽不是sigmoid作为其激活函数。

2.1.3 AlexNet的网络结构

在这里插入图片描述

可以看到,由于早期GPU显存有限,原版的AlexNet采⽤了双数据流设计,使得每个GPU只负责存储和计算模型的⼀半参数。幸运的是,现在GPU显存相对充裕,所以我们现在很少需要跨GPU分解模型

AlexNet 有5个广义卷积层和3个广义全连接层。

  • 广义的卷积层:包含了卷积层、池化层、ReLULRN 层等。
  • 广义全连接层:包含了全连接层、ReLUDropout 层等。

网络结构如下表所示:

  • 输入层会将3@224x224 的三维图片预处理变成3@227x227 的三维图片。

  • 第二层广义卷积层、第四层广义卷积层、第五层广义卷积层都是分组卷积,仅采用本GPU 内的通道数据进行计算。

    第一层广义卷积层、第三层广义卷积层、第六层连接层、第七层连接层、第八层连接层执行的是全部通道数据的计算。

  • 第二层广义卷积层的卷积、第三层广义卷积层的卷积、第四层广义卷积层的卷积、第五层广义卷积层的卷积均采用same 填充。

    当卷积的步长为1,核大小为3x3 时,如果不填充0,则feature map 的宽/高都会缩减 2 。因此这里填充0,使得输出feature map 的宽/高保持不变。

    其它层的卷积,以及所有的池化都是valid 填充(即:不填充 0 )。

  • 第六层广义连接层的卷积之后,会将feature map 展平为长度为 4096 的一维向量。

编号 网络层 子层 核/池大小 核数量 步长 填充 激活函数 输入尺寸 输出尺寸
第0层 输入层 - - - - - - - 3@224x224
第1层 广义卷积层 卷积 11x11 96 4 - ReLU 3@227x227 96@55x55
第1层 广义卷积层 池化 3x3 - 2 - - 96@55x55 96@27x27
第1层 广义卷积层 LRN - - - - - 96@27x27 96@27x27
第2层 广义卷积层 卷积 5x5 256 1 2 ReLU 96@27x27 256@27x27
第2层 广义卷积层 池化 3x3 - 2 - - 256@27x27 256@13x13
第2层 广义卷积层 LRN - - - - - 256@13x13 256@13x13
第3层 广义卷积层 卷积 3x3 384 1 1 ReLU 256@13x13 384@13x13
第4层 广义卷积层 卷积 3x3 384 1 1 ReLU 384@13x13 384@13x13
第5层 广义卷积层 卷积 3x3 256 1 1 ReLU 384@13x13 256@13x13
第5层 广义卷积层 池化 3x3 - 2 - - 256@13x13 256@6x6
第6层 广义连接层 卷积 6x6 4096 1 - ReLU 256@6x6 4096@1x1
第6层 广义连接层 dropout - - - - - 4096@1x1 4096@1x1
第7层 广义连接层 全连接 - - - - ReLU 4096 4096
第7层 广义连接层 dropout - - - - - 4096 4096
第8层 广义连接层 全连接 - - - - - 4096 1000

我们用pytorch进行实现

import torch.nn as nn
import torch

class AlexNet(nn.Module):

    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
                # 使⽤⼀个11*11的更⼤窗⼝来捕捉对象。
                # 同时,步幅为4,以减少输出的⾼度和宽度。
                # 另外,输出通道的数⽬远⼤于LeNet
                nn.Conv2d(1, 96, kernel_size=11, stride=4),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=3, stride=2),

                # 减⼩卷积窗⼝,使⽤填充为2来使得输⼊与输出的⾼和宽⼀致,且增⼤输出通道数
                nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
                nn.MaxPool2d(kernel_size=3, stride=2),

                # 使⽤三个连续的卷积层和较⼩的卷积窗⼝。
                # 除了最后的卷积层,输出通道的数量进⼀步增加。
                # 在前两个卷积层之后,汇聚层不⽤于减少输⼊的⾼度和宽度
                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(kernel_size=3, stride=2),
                nn.Flatten(),

                # 这⾥,全连接层的输出数量是LeNet中的好⼏倍。使⽤dropout层来减轻过拟合
                nn.Linear(9216, 4096), nn.ReLU(),
                nn.Dropout(p=0.5),
                nn.Linear(4096, 4096), nn.ReLU(),
                nn.Dropout(p=0.5),
                # 最后是输出层。由于这⾥使⽤Fashion-MNIST,所以⽤类别数为10,⽽⾮论⽂中的1000
                nn.Linear(4096, 10)
        )

    def forward(self, X):
        X = self.model(X)
        return X

if __name__ == '__main__':
    net = AlexNet()
    # 测试神经网络是否可运行
    # inputs = torch.rand(size=(1, 1, 227, 227), dtype=torch.float32)
    # outputs = net(inputs)
    # print(outputs.shape)
    X = torch.rand(size=(1, 1, 227, 227), dtype=torch.float32)
    for layer in net.model:
        X = layer(X)
        print(layer.__class__.__name__, 'output shape:', X.shape)
"""
第1层:广义卷积层
Conv2d output shape: torch.Size([1, 96, 55, 55])
ReLU output shape: torch.Size([1, 96, 55, 55])
MaxPool2d output shape: torch.Size([1, 96, 27, 27])

第2层:广义卷积层
Conv2d output shape: torch.Size([1, 256, 27, 27])
ReLU output shape: torch.Size([1, 256, 27, 27])
MaxPool2d output shape: torch.Size([1, 256, 13, 13])

第3层:广义卷积层
Conv2d output shape: torch.Size([1, 384, 13, 13])
ReLU output shape: torch.Size([1, 384, 13, 13])

第4层:广义卷积层
Conv2d output shape: torch.Size([1, 384, 13, 13])
ReLU output shape: torch.Size([1, 384, 13, 13])

第5层:广义卷积层
Conv2d output shape: torch.Size([1, 256, 13, 13])
ReLU output shape: torch.Size([1, 256, 13, 13])
MaxPool2d output shape: torch.Size([1, 256, 6, 6])

第6层:广义连接层
Flatten output shape: torch.Size([1, 9216])
Linear output shape: torch.Size([1, 4096])
ReLU output shape: torch.Size([1, 4096])
Dropout output shape: torch.Size([1, 4096])

第7层:广义连接层
Linear output shape: torch.Size([1, 4096])
ReLU output shape: torch.Size([1, 4096])
Dropout output shape: torch.Size([1, 4096])

第8层:广义连接层
Linear output shape: torch.Size([1, 10])
"""

网络参数数量:总计约 6237万。

  • 第6层广义连接层的卷积的参数数量最多,约3770万,占整体六千万参数的 60%。原因是该子层的卷积核较大、输入通道数量较大、输出通道数量太多。
    该卷积需要的参数数量为:256✖6✖6✖4096=37,748,736

2.2 AlexNet的创新点

AlexNet 成功的主要原因在于:

  • 使用ReLU 激活函数。
  • 使用dropout、数据集增强 、重叠池化等防止过拟合的方法。
  • 使用百万级的大数据集来训练。
  • 使用GPU训练,以及的LRN 使用。
  • 使用带动量的 mini batch 随机梯度下降来训练。

2.2.1 局部响应规范化

局部响应规范层LRN:目地是为了进行一个横向抑制,使得不同的卷积核所获得的响应产生竞争。

  • LRN 层现在很少使用,因为效果不是很明显,而且增加了内存消耗和计算时间。

2.2.2 数据集增强

  1. AlexNet 中使用的数据集增强手段:

    • 随机裁剪、随机水平翻转:原始图片的尺寸为256xx256,裁剪大小为224x224

      • 每一个epoch 中,对同一张图片进行随机性的裁剪,然后随机性的水平翻转。理论上相当于扩充了数据集 倍。

      • 在预测阶段不是随机裁剪,而是固定裁剪图片四个角、一个中心位置,再加上水平翻转,一共获得 10 张图片。

        用这10张图片的预测结果的均值作为原始图片的预测结果。

    • PCA 降噪:对RGB空间做PCA 变换来完成去噪功能。同时在特征值上放大一个随机性的因子倍数(单位1 加上一个 的高斯绕动),从而保证图像的多样性。

      • 每一个epoch 重新生成一个随机因子。
      • 该操作使得错误率下降1%
  2. AlexNet 的预测方法存在两个问题:

    • 这种固定裁剪四个角、一个中心的方式,把图片的很多区域都给忽略掉了。很有可能一些重要的信息就被裁剪掉。
    • 裁剪窗口重叠,这会引起很多冗余的计算。

    改进的思路是:

    • 执行所有可能的裁剪方式,对所有裁剪后的图片进行预测。将所有预测结果取平均,即可得到原始测试图片的预测结果。
    • 减少裁剪窗口重叠部分的冗余计算。

具体做法为:将全连接层用等效的卷积层替代,然后直接使用原始大小的测试图片进行预测。将输出的各位置处的概率值按每一类取平均(或者取最大),则得到原始测试图像的输出类别概率。

2.2.3 重叠池化

  1. 一般的池化是不重叠的,池化区域的大小与步长相同。Alexnet 中,池化是可重叠的,即:步长小于池化区域的大小。

    重叠池化可以缓解过拟合,该策略贡献了0.4% 的错误率。

  2. 为什么重叠池化会减少过拟合,很难用数学甚至直观上的观点来解答。一个稍微合理的解释是:重叠池化会带来更多的特征,这些特征很可能会有利于提高模型的泛化能力。

2.2.4 优化算法

在这里插入图片描述

2.3 AlexNet在Fashion-MNIST数据集上的应用代码

2.3.1 创建AlexNet网络模型

import torch.nn as nn
import torch

class AlexNet(nn.Module):

    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
                # 这⾥,我们使⽤⼀个11*11的更⼤窗⼝来捕捉对象。
                # 同时,步幅为4,以减少输出的⾼度和宽度。
                # 另外,输出通道的数⽬远⼤于LeNet
                nn.Conv2d(1, 96, kernel_size=11, stride=4,padding=1), # 1、填充设置为1
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=3, stride=2),

                # 减⼩卷积窗⼝,使⽤填充为2来使得输⼊与输出的⾼和宽⼀致,且增⼤输出通道数
                nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
                nn.MaxPool2d(kernel_size=3, stride=2),

                # 使⽤三个连续的卷积层和较⼩的卷积窗⼝。
                # 除了最后的卷积层,输出通道的数量进⼀步增加。
                # 在前两个卷积层之后,汇聚层不⽤于减少输⼊的⾼度和宽度
                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(kernel_size=3, stride=2),
                nn.Flatten(),

                # 这⾥,全连接层的输出数量是LeNet中的好⼏倍。使⽤dropout层来减轻过拟合
                nn.Linear(6400, 4096), nn.ReLU(), # 2、输出相对2.1.3降为6400
                nn.Dropout(p=0.5),
                nn.Linear(4096, 4096), nn.ReLU(),
                nn.Dropout(p=0.5),
                # 最后是输出层。由于这⾥使⽤Fashion-MNIST,所以⽤类别数为10,⽽⾮论⽂中的1000
                nn.Linear(4096, 10)
        )

    def forward(self, X):
        X = self.model(X)
        return X

if __name__ == '__main__':
    net = AlexNet()
    # 测试神经网络是否可运行
    inputs = torch.rand(size=(1, 1, 224, 224), dtype=torch.float32)
    outputs = net(inputs)
    print(outputs.shape)
    # X = torch.rand(size=(1, 1, 224, 224), dtype=torch.float32)
    # for layer in net.model:
    #     X = layer(X)
    #     print(layer.__class__.__name__, 'output shape:', X.shape)

2.3.2 读取Fashion-MNIST数据集

其他所有的函数,与经典神经网络(1)LeNet及其在Fashion-MNIST数据集上的应用完全一致。

'''
Fashion-MNIST图像的分辨率(28×28像素)低于ImageNet图像。为了解决这个问题,增加到224×224
'''
batch_size = 128
train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=224)

2.3.2 模型训练

from _02_AlexNet import AlexNet

# 初始化模型
net = AlexNet()


# 与LeNet相比,使⽤更⼩的学习速率训练,这是因为⽹络更深更⼴、图像分辨率更⾼,训练卷积神经⽹络就更昂贵
# 注意需要用GPU进行训练

lr, num_epochs = 0.01, 10
train_ch(net, train_iter, test_iter, num_epochs, lr, try_gpu())
loss 0.327, train acc 0.881, test acc 0.885
4149.6 examples/sec on cuda:0

在这里插入图片描述
注:AlexNet用GPU才能跑,如果自己电脑没有合适的GPU,可以参考下面文章进行租借

AutoDL平台租借GPU详解

猜你喜欢

转载自blog.csdn.net/qq_44665283/article/details/130647711