AlexNet论文复现
文章目录
- AlexNet论文复现
- AlexNet介绍
- 深度学习模型
- 学习表征
- 数据
- 硬件
- 论文内容
- 激活函数
- Local Response Normalization(局部响应归一化)
- Overlapping Pooling(重叠池化)
- dropout
- 数据增强
- 结果
- 模型架构
- AlexNet整体流程
- 图像读取
- 模型
- 预测
- 代码
- 参考文献
AlexNet介绍
深度学习模型
在LeNet提出后,卷积神经网络在计算机视觉和机器学习领域开始被人熟知,但卷积神经网络并没有主导这些领域。这是因为LeNet是在小数据集上取得了很好的效果,但是在更大数据集上训练卷积神经网络的性能和可行性还没有得到验证。
在计算机视觉中,直接将神经网络与其他机器学习方法进行比较并不公平。这是因为,卷积神经网络的输入是由原始像素值或是经过简单预处理(例如居中、缩放)的像素值组成的。但在使用传统机器学习方法时,研究者不会将原始像素作为输入。在传统机器学习方法中,计算机视觉处理流程是由经过人的手工设计的特征组成的。对于这些传统方法,大部分的进展都来自于对特征有了更聪明的想法,并且学习到的算法往往归于事后的解释。
虽然上世纪90年代就有了一些神经网络加速卡,但仅靠它们还不足以开发出有大量参数的深层多通道多层卷积神经网络。此外,当时的数据集仍然相对较小。除了这些障碍,训练神经网络的一些关键技巧仍然缺失,包括参数初始化、随机梯度下降的变体、非挤压激活函数和有效的正则化技术。
因此,与训练端到端(从像素到分类结果)系统不同,经典机器学习的流程看起来更像下面这样:
- 获取一个有趣的数据集。在早期,收集这些数据集需要昂贵的传感器(在当时最先进的图像也就100万像素)。
- 根据光学、几何学、其他知识以及偶然的发现,手工对特征数据集进行预处理。
- 通过标准的特征提取算法,如SIFT、LBP或其他手动调整的特征来输入数据。
- 将提取的特征送入分类器中(如SVM),以训练分类器。
机器学习是一个正在蓬勃发展、严谨且非常有用的领域。然而,当人们和计算机视觉研究人员交谈,会听到一个完全不同的故事。计算机视觉研究人员会告诉一个诡异事实——推动领域进步的是数据特征,而不是学习算法。计算机视觉研究人员相信,从对最终模型精度的影响来说,更大或更干净的数据集、或是稍微改进的特征提取,比任何学习算法带来的进步要大得多。
学习表征
另一种预测这个领域发展的方法————观察图像特征的提取方法。在2012年前,图像特征都是机械地计算出来的。事实上,设计一套新的特征函数、改进结果,并撰写论文是盛极一时的潮流。SIFT 、HOG、BOW和类似的特征提取方法占据了主导地位。
另一组研究人员,包括Yann LeCun、Geoff Hinton、Yoshua Bengio、Andrew Ng、Shun ichi Amari和Juergen Schmidhuber,想法则与众不同:他们认为特征本身应该被学习。此外,他们还认为,在合理地复杂性前提下,特征应该由多个共同学习的神经网络层组成,每个层都有可学习的参数。在机器视觉中,最底层可能检测边缘、颜色和纹理。事实上,Alex Krizhevsky提出了一种新的卷积神经网络变体AlexNet,在2012年ImageNet挑战赛中取得了轰动一时的成绩。
在AlexNet网络的最底层,模型学习到了一些类似于传统滤波器的特征抽取器。 如下图所示,描述了底层图像
AlexNet的更高层建立在这些底层表示的基础上,以表示更大的特征,如眼睛、鼻子、草叶等等。而更高的层可以检测整个物体,如人、飞机、狗或飞盘。最终的隐藏神经元可以学习图像的综合表示,从而使属于不同类别的数据易于区分。尽管一直有一群执着的研究者不断钻研,试图学习视觉数据的逐级表征,然而很长一段时间里这些尝试都未有突破。深度卷积神经网络的突破出现在2012年。突破可归因于两个关键因素。
数据
包含许多特征的深度模型需要大量的有标签数据,才能显著优于基于凸优化的传统方法(如线性方法和核方法)。然而,限于早期计算机有限的存储和90年代有限的研究预算,大部分研究只基于小的公开数据集。例如,不少研究论文基于加州大学欧文分校(UCI)提供的若干个公开数据集,其中许多数据集只有几百至几千张在非自然环境下以低分辨率拍摄的图像。这一状况在2010年前后兴起的大数据浪潮中得到改善。2009年,ImageNet数据集发布,并发起ImageNet挑战赛:要求研究人员从100万个样本中训练模型,以区分1000个不同类别的对象。ImageNet数据集由斯坦福教授李飞飞小组的研究人员开发,利用谷歌图像搜索(Google Image Search)对每一类图像进行预筛选,并利用亚马逊众包(Amazon Mechanical Turk)来标注每张图片的相关类别。这种规模是前所未有的。这项被称为ImageNet的挑战赛推动了计算机视觉和机器学习研究的发展,挑战研究人员确定哪些模型能够在更大的数据规模下表现最好。
硬件
深度学习对计算资源要求很高,训练可能需要数百个迭代轮数,每次迭代都需要通过代价高昂的许多线性代数层传递数据。这也是为什么在20世纪90年代至21世纪初,优化凸目标的简单算法是研究人员的首选。然而,用GPU训练神经网络改变了这一格局。图形处理器(Graphics Processing Unit,GPU)早年用来加速图形处理,使电脑游戏玩家受益。GPU可优化高吞吐量的矩阵和向量乘法,从而服务于基本的图形任务。幸运的是,这些数学运算与卷积层的计算惊人地相似。由此,英伟达(NVIDIA)和ATI已经开始为通用计算操作优化gpu,甚至把它们作为通用GPU(general-purpose GPUs,GPGPU)来销售。
那么GPU比CPU强在哪里呢?
首先,我们深度理解一下中央处理器(Central Processing Unit,CPU)的核心。CPU的每个核心都拥有高时钟频率的运行能力,和高达数MB的三级缓存(L3Cache)。它们非常适合执行各种指令,具有分支预测器、深层流水线和其他使CPU能够运行各种程序的功能。然而,这种明显的优势也是它的致命弱点:通用核心的制造成本非常高。它们需要大量的芯片面积、复杂的支持结构(内存接口、内核之间的缓存逻辑、高速互连等等),而且它们在任何单个任务上的性能都相对较差。现代笔记本电脑最多有4核,即使是高端服务器也很少超过64核,因为它们的性价比不高。
相比于CPU,GPU由个小的处理单元组成(NVIDIA、ATI、ARM和其他芯片供应商之间的细节稍有不同),通常被分成更大的组(NVIDIA称之为warps)。虽然每个GPU核心都相对较弱,有时甚至以低于1GHz的时钟频率运行,但庞大的核心数量使GPU比CPU快几个数量级。例如,NVIDIA最近一代的Ampere GPU架构为每个芯片提供了高达312 TFlops的浮点性能,而CPU的浮点性能到目前为止还没有超过1 TFlops。之所以有如此大的差距,原因其实很简单:首先,功耗往往会随时钟频率呈二次方增长。对于一个CPU核心,假设它的运行速度比GPU快4倍,但可以使用16个GPU核代替,那么GPU的综合性能就是CPU的倍。其次,GPU内核要简单得多,这使得它们更节能。此外,深度学习中的许多操作需要相对较高的内存带宽,而GPU拥有10倍于CPU的带宽。
回到2012年的重大突破,当Alex Krizhevsky和Ilya Sutskever实现了可以在GPU硬件上运行的深度卷积神经网络时,一个重大突破出现了。他们意识到卷积神经网络中的计算瓶颈:卷积和矩阵乘法,都是可以在硬件上并行化的操作。于是,他们使用两个显存为3GB的NVIDIA GTX580 GPU实现了快速卷积运算。他们的创新cuda-convnet几年来它一直是行业标准,并推动了深度学习热潮。
论文内容
激活函数
AlexNet将sigmoid激活函数改为更简单的ReLU激活函数。一方面,ReLU激活函数的计算更简单,它不需要如sigmoid激活函数那般复杂的求幂运算。另一方面,当使用不同的参数初始化方法时,ReLU激活函数使训练模型更加容易。当sigmoid激活函数的输出非常接近于0或1时,这些区域的梯度几乎为0,因此反向传播无法继续更新一些模型参数。相反,ReLU激活函数在正区间的梯度总是1。因此,如果模型参数没有正确初始化,sigmoid函数可能在正区间内得到几乎为0的梯度,从而使模型无法得到有效的训练。Relu激活函数公式如(1)所示。
Local Response Normalization(局部响应归一化)
在这篇论文中,作者在多个数据集上验证所提模型,使用该模块可以提高模型的泛化性。
LRN对局部神经元的活动创建竞争机制, 使其中响应比较大对值变得相对更大, 并抑制其他反馈较小的神经元, 增强了模型的泛化能力,计算公式如式(2)所示
其中i表示第i个核在位置运用激活函数Relu后的输出,是同一位置上临近的kernal map的数目,是kernal的总数。参数,,,都是超参数,一般设置,,,。
Overlapping Pooling(重叠池化)
作者在模型中使用最大池化来进行处理卷积后的特征图,但在池化时将kernal=3,stride=2,将部分特征图重叠池化,这样可以提高模型的精度。
dropout
在模型训练时,作者在前两个全连接层后使用dropout层,将50%的神经单元设置为0。在测试的时候使用所有神经元,但将它们的输出乘以 0.5,0.5是取的几何平均值。
数据增强
作者在进行实验时,使用了两种数据增强的方法:
(1)压缩图像的分辨率至,在尺寸的图像上随机剪切出的图像,并将这些图像进行平移和反射。pytorch中图像增强方法如下:
data_transform = {
"train": transforms.Compose(
[transforms.Resize(256),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
"val": transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
}
(2)改变训练图像中 RGB 通道的强度
对训练集中图像的RGB 的像素值集使用PCA(主成分分析)方法获取RGB权重。对于每个训练图像,找到的主成分的倍数,其大小与相应的特征值乘以从均值为零和标准差为的高斯分布中抽取的随机变量成正比。
设RGB图像像素为:
并对图像进行如下操作:
其中和分别是RGB像素值的3×3协方差矩阵的第个特征向量和特征值,是上述随机变量。
结果
AlexNet在ILSVRC-2010数据集上取的了下面的效果,在当时是SOTA(state-of-the-art)。
在ImageNet数据集上取的了下面效果,在当时也是SOTA。
模型架构
模型的架构如下图所示,共包含5个卷积层和3个全连接层,经卷积层提取出图像的语义信息,输送到全连接进行分类。
卷积操作如下:
绿色的卷积核不断地对输入的蓝色特征图进行划窗扫描,对于划窗区域,卷积核矩阵与对应划窗区域的矩阵进行矩阵乘法并通道累加求和,求和结果作为新特征图上某一点的数值,当完成全部划窗扫描操作后,得到最终输出的新特征图,计算公式如公式(1)所示。
KaTeX parse error: Unknown column alignment: * at position 24: …{\begin{array}{*̲{20}{c}} a&b&c\…
就是对应新特征图的第一个元素,卷积核从左到右从上到下按照设定的步长进行卷积操作,得到一个新的特征图,这个新的特征图就是经过一次卷积操作后的特征图。新的特征图的尺寸与原特征图的尺寸存在一下对应关系如公式(2)所示:
在第一、二、五卷积层的后面还跟着一个最大池化层,对输入的特征图进行压缩,一方面使特征图变小,简化网络计算复杂度,另一方面进行特征压缩,提取主要特征。最大池化方法如下图所示,按照步长为2的间隔获取一个区域内最大的值,放将此值放在对应的位置上。
全连接结构如下图所示,全连接中每一个节点的权重均与下一层节点的权重有连接,所以全连接层的权重非常庞大,模型很容易出现过拟合的现象,AlexNet再每一层全连接后面都加入了dropout,使部分神经元失活,降低模型过拟合的可能性。途中实线连接的为权重,虚线连接的则为使用dropout丢弃后的权重。
AlexNet整体流程
下面几个部分主要介绍AlexNet整体的流程,包括图像读取、模型、预测模块。
图像读取
在做分类任务的时候,第一步首先要把分类的数据集划分为训练集、测试集和验证集。需要在训练集上训练模型,在验证集上对模型进行验证,这两个过程是同时进行的。当模型训练结束后,需要对模型进行测试,这就需要使用测试集对模型进行测试,划分后的数据集格式如下图所示,图上都是文件夹,最后一层为某个类别文件夹所包含的图像。
一幅图像,它的格式是RGB,包括通道数C(RGB是3通道)、图像的高H、图像的宽W,可以使用python中的Image和opencv来读取图像,但是我们的神经网络采用的是pytorch架构写的,所以我们需要将图像格式转为pytorch所能处理的格式,即Tensor格式,可用一个矩阵来表示图像:。
上述我们只是将图像转为可以使用pytorch操作的图像格式了,但是还不能将图像输入神经网络中训练,需要再写一个dataloader,将我们所要处理的图像放到dataloader里,这样神经网络在进行训练时,只需要在dataloader中读取图像即可。dataloader可以理解为一个大箱子,我们事先将训练集和验证集放入其中,这样减少了在本地读取图像的操作,减少了模型的训练时间。
pytorch中的dataloader定义为如下:
train_dataset = datasets.ImageFolder(img_path, transform=data_transform["train"])
train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size, shuffle=True, num_workers=nw)
其中train_dataset是训练集的图像,使用datasets.ImageFolder将图像收集起来,datasets.ImageFolder(a,b)里包含两个参数,a为训练集的路径,b为将图像转化为Tensor,和数据增强部分一样。train_loader使用torch.utils.data.DataLoader(a,b,c,d)方法来定义,一共包含4个参数,分别是,这个参数是把转化为Tensor格式的图像放入dataloader中;代表每次训练在dataloader中选择几张图像,当设置为8的时候,此时图像矩阵表示发生变化,变为,其中B就是这里的batch_size;参数是指是否打乱训练集,当我们在训练的时候会将此参数设置为True,这是因为防止模型出现偶然现象的情况,包括在某一个类别上准确率非常高,当换了类别训练后,准确率又变得特别低,在测试集上该参数设置为False;代表工作进程,设置越大代表越多的进程去加载batch_size数据,可以减少训练时间,但是建议笔记本电脑设置为2,台式机设置为4或8。以上,所有的数据准备结束,这样就可以将数据传入神经网络训练。
模型
数据处理结束后,将图像输入到神经网络中,此时的图像格式为,我们将batch_size设置为16,即一次训练16张图像,这个数值在训练的时候是不会改变的,此时图像格式为,根据下图网络格式进行训练。
图像首先经过一个卷积核为11,步长为4,填充为2,输入通道为3,输出通道为96的卷积层,在卷积层中进行卷积操作。在第一次卷积中可以计算为:,故经过一次卷积后的特征图大小为。在这里说明一下,在pytorch框架中,如果卷积的时候有小数,此时pytorch会舍弃,只取整数。但在TensorFlow框架中,则会取上,若使用TensorFlow,则在这层卷积后,特征图大小为。
这只是一层卷积操作,在Alex一共有5层卷积,每一层都和这一层一样,当经过最后一层卷积后,特征图的尺寸为。
在每一个卷积层后面还跟着一个Relu激活函数,增加网络的非线性,提高神经网络的分类能力。
整个卷积操作的特征图大小流程如公式(1):
经过卷积操作后,只是提取图像的特征,但是很多物体可能都有同一类特征,比如猫、狗、鸟都有眼睛,如果只用局部特征的话不足以确定具体类别。这时就需要使用组合特征来判别了,全连接就是组合这些特征来最终确定是哪一个分类。但是全连接输入的是一个二维向量,不是一个四维的特征图,所以首先将特征图拉伸为一个一维向量,在pytorch中使用torch.flatten()方法进行。
最后一层全连接输出的形状为,其中16为batch size,5为要分类的类别数目。输出的类别是否和真实的类别一样,这需要我们对输出的类别和真实类别做判断,这里就要用到损失函数的概念。损失函数就是计算模型输出的结果与真实结果之间的误差,在AlexNet中损失函数使用的是交叉熵损失函数,交叉熵损失函数如下:
是batch size的大小;是预测向量维度,因为需要在输出的特征向量维度上一个个计算并求和;是真实值;为预测值。
得到了损失函数,需要将这个损失值回传给神经网络,神经网络根据损失值调整每一层的参数,通过多次训练、回传,最后得到一个损失值比较低的神经网络,这样算模型训练完成。在模型训练的时候,有一个概念叫学习率,学习率是用来控制优化器调整网络的优化程度的。而优化器就是根据损失函数返回的值对网络器优化作用的函数。
预测
模型训练只是一个过程,最终的结果是将训练好的模型权重应用到实际任务中,因此,当模型训练结束后,需要对模型进行预测。
模型预测不同于模型训练,只需要将训练好的权重加载到模型中,把要预测的图像传入网络即可进行预测。
代码
https://github.com/Li2Silence/AlexNet