深度学习图像分类(二):AlexNet

深度学习图像分类(二):AlexNet



前言

2012年,Alex Krizhevsky、Ilya Sutskever在多伦多大学Geoff Hinton的实验室设计出了一个深层的卷积神经网络AlexNet,夺得了2012年ImageNet LSVRC的冠军,且准确率远超第二名(top5错误率为15.3%,第二名为26.2%),引起了很大的轰动。AlexNet可以说是具有历史意义的一个网络结构,在此之前,深度学习已经沉寂了很长时间,自2012年AlexNet诞生之后,后面的ImageNet冠军都是用卷积神经网络(CNN)来做的,并且层次越来越深,使得CNN成为在图像识别分类的核心算法模型,带来了深度学习的大爆发。
小tips: Alex并没有给自己的网络起名字,所以后人为了方便将这个网络成为AlexNet。同学们如果不想让别人给自己的网络起名字,写论文的时候记得起个名字~


一、AlexNet理论

AlexNet其实跟LeNet很像很像,几乎可以说是LeNet的升级版,都是有卷积和全连接组成。AlexNet之所以能够成功,跟这个模型设计的特点有关,主要有:

  • 使用了非线性激活函数:ReLU
  • 随机失活:Dropout
  • 数据扩充:Data augmentation
  • 其他:多GPU实现,LRN归一化层的使用

1. 激活函数:ReLU

传统的神经网络普遍使用Sigmoid或者tanh等非线性函数作为激励函数,然而它们容易出现梯度弥散或梯度饱和的情况。以Sigmoid函数为例,如下图所示,当输入的值非常大或者非常小的时候,这些神经元的梯度接近于0(梯度饱和现象),如果输入的初始值很大的话,梯度在反向传播时因为需要乘上一个Sigmoid导数,会造成梯度越来越小,导致网络变的很难学习。
在这里插入图片描述
relu函数直到现在也是学术界和工业界公认的最好用的激活函数之一,在各个不同领域不同模型下的使用非常之多

2. 随机失活:Dropout

引入Dropout主要是为了防止网络在训练过程中由于参数冗余出现的过拟合现象。在神经网络中Dropout通过修改神经网络本身结构来实现,对于某一层的神经元,通过定义的概率将神经元置为0,这个神经元就不参与前向和后向传播,就如同在网络中被删除了一样,同时保持输入层与输出层神经元的个数不变,然后按照神经网络的学习方法进行参数更新。在下一次迭代中,又重新随机删除一些神经元(置为0),直至训练结束。
在这里插入图片描述

3. 数据扩充:Data augmentation

由于神经网络算法是基于数据驱动的,因此,有一种观点认为神经网络是靠数据喂出来的,如果能够增加训练数据,提供海量数据进行训练,则能够有效提升算法的准确率,因为这样可以避免过拟合,从而可以进一步增大、加深网络结构。而当训练数据有限时,可以通过一些变换从已有的训练数据集中生成一些新的数据,以快速地扩充训练数据。
其中,最简单、通用的图像数据变形的方式:水平翻转图像,从原始图像中随机裁剪、平移变换,颜色、光照变换,如下图所示:
在这里插入图片描述
数据增广确实是提升模型的有效手段,而且最近的增广方式也不仅限于这种随即裁剪,也可以使用生成对抗网络进行图像生成来达到图像增广的目的

4. 多GPU实现

AlexNet当时使用了GTX580的GPU进行训练,由于单个GTX 580 GPU只有3GB内存,这限制了在其上训练的网络的最大规模,因此他们在每个GPU中放置一半核(或神经元),将网络分布在两个GPU上进行并行计算,大大加快了AlexNet的训练速度。

5. 局部响应归一化(LRN)

Local Response Normalization(LRN)技术主要是深度学习训练时的一种提高准确度的技术方法。LRN一般是在激活、池化后进行的一种处理方法。LRN归一化技术首次在AlexNet模型中提出这个概念。通过实验确实证明它可以提高模型的泛化能力,但是提升的很少,以至于后面不再使用,甚至有人觉得它是一个“伪命题”,因而它饱受争议。现在基本上已经被Batch Normalization代替

因此,这里简单介绍一下:
局部归一化的灵感来源
在神经生物学中,有一个概念叫做侧抑制(lateral inhibitio ),指的是被激活的神经元会抑制它周围的神经元,而归一化(normalization)的的目的不就是“抑制”吗,两者不谋而合,这就是局部归一化的动机,它就是借鉴“侧抑制”的思想来实现局部抑制,当我们使用RELU损失函数的时候,这种局部抑制显得很有效果。

归一化的好处
(1)为了后面数据处理的方便,归一化的确可以避免一些不必要的数值问题。
(2)为了程序运行时收敛加快。
(3)同一量纲。样本数据的评价标准不一样,需要对其量纲化,统一评价标准。这算是应用层面的需求。
(4)避免神经元饱和。就是当神经元的激活在接近0或者1时会饱和,在这些区域,梯度几乎为0,这样,在反向传播过程中,局部梯度就会接近0,这会有效地“杀死”梯度。
(5)保证输出数据中数值小的不被吞食。

实验总结
由于LRN模仿生物神经系统的侧抑制机制,对局部神经元的活动创建竞争机制,使得响应比较大的值会更大,提高了模型的泛化能力,Hinton在ImageNet中的实验准确率分别提升了1.4%和1.2%。

二、 AlexNet代码

这里给出模型搭建的python代码(基于pytorch实现)。完整的代码是基于图像分类问题的(包括训练和推理脚本,自定义层等)详见我的GitHub: 完整代码链接
ps:里面还有其他经典模型的代码复现,希望对大家有用

from turtle import forward
import torch
import torch.nn as nn

from custom_layers.CustomLayers import ConvActivation

# 由于分类层结构是全连接,因此限制模型输入的图像大小为224*224
# 卷积后特征图尺寸不变的常用参数组合:(K=7,s=1,p=3)  (K=5,s=1,p=2) (K=3,s=1,p=1)

class AlexNet(nn.Module):
    def __init__(self, num_classes, input_channels=3, init_weights = True):
        super().__init__()
        self.features = nn.Sequential(
            # in[3,224,224] ==> out[48,55,55] ==> out[48,27,27]
            ConvActivation(input_channels=input_channels, output_channels=48, kernel_size=11, stride=4, padding=2),
            nn.MaxPool2d(kernel_size=3, stride=2),

            # in[128,27,27] ==> out[128,27,27] ==> out[128,13,13]
            ConvActivation(input_channels=48, output_channels=128, kernel_size=5, padding=2),
            nn.MaxPool2d(kernel_size=3, stride=2),

            # in[128,13,13] ==> out[192,13,13]
            ConvActivation(input_channels=128, output_channels=192, kernel_size=3, padding=1),

            # in[192,13,13] ==> out[192,13,13] 
            ConvActivation(input_channels=192, output_channels=192, kernel_size=3, padding=1),
 
            # in[192,13,13] ==> out[128,13,13] ==> out[128,6,6]
            ConvActivation(input_channels=192, output_channels=128, kernel_size=3, padding=1),
            nn.MaxPool2d(kernel_size=3, stride=2)
        )
        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5),
            nn.Linear(128*6*6, 2048),
            nn.ReLU(True),

            nn.Dropout(p=0.4),
            nn.Linear(2048, 2048),
            nn.ReLU(True),

            nn.Linear(2048, num_classes)
        )

        if init_weights:
            self._initialize_weights()           
    
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight,0,0.01)
                nn.init.constant_(m.bias,0)

    def forward(self, x):
        assert x.shape[2] == 224 and x.shape[3] == 224, " input images size should be 224*224 "
        x = self.features(x)
        x = torch.flatten(x, start_dim=1)
        x = self.classifier(x)
        return x
    

小结

AelxNet可以说掀起了人工智能的第三次浪潮:深度学习的时代。其历史影响深远。
虽然里面的技术在现在看来没什么创新点,但是放在当时,其性能也甩了其他算法好几条街。

猜你喜欢

转载自blog.csdn.net/qq_39297053/article/details/123839843