?写在前面:这篇文章是一篇最最基础的深度学习的文章,可以作为深度学习的Hello World?。阅读这篇文章的前提是已经阅读过了原Paper,对paper的内容有印象、最好有疑问,以及掌握部分基础知识。这篇文章主要是对AlexNet论文的个人理解,会包含部分原文的内容。如果对AlexNet这篇paper有任何问题或者对这篇文章有任何疑问,欢迎在评论区提问,大家一起讨论。???
掌握这篇论文需要的基础知识有: ⭐⭐⭐
机器学习的基本概念和方法:监督学习、无监督学习、深度学习;
神经网络的基本结构和工作原理:包括前馈神经网络、卷积神经网络;
CV的基本概念和方法:图像分类、目标检测、语义分割;
基础的编程能力:能够使用python等编程语言进行数据处理和模型训练;
文章的结构
摘要 | 介绍了图像分类的重要性和挑战,以及深度学习和卷积神经网络在图像分类中的作用。 |
---|---|
引言 | 介绍与本文相关的研究工作,包括深度学习、卷积神经网络、图像分类这些方面的研究。作者对这些研究进行了分类和总结,指出了他们的优缺点和不足之处,并指出了本文的创新点和改进之处。 |
数据集和预处理 | 介绍了本文使用的数据集和数据预处理方法。 |
模型架构 | 介绍了本文提出的深度卷积神经网络的架构,包括卷积层、池化层、全连接层等; |
训练细节 | 介绍了本文使用的训练方法和技巧,包括随机梯度下降、数据增。 |
结果分析 | 介绍了本文实验的结果和分析,包括在ImageNet数据集上的分类准确率、不同模型的比较、误差分析等。 |
结论 | 总结了本文的工作和贡献,并对未来的研究方向进行了展望 |
参考文献 |
2. 数据集和图像尺寸调整:
前两段介绍了ImageNet数据集,使用了ILSVRC2012(因为是当时唯一一个可以获得测试集标签的数据集),提到了两个常用的错误率:top-1 和top-5。
第三段介绍了因为文中提出的框架需要一个固定的输入,所以将图像的大小固定在了256 x 256,(原文:we first rescaled the image such that the shorter side was of length 256, and then cropped out the central 256×256 patch from the resulting image),作者强调这里没有做任何的预处理,除了从每个像素中减去训练集上的平均活动(类似于BN),所以作者是在中心化处理后的RGB图像上进行的训练。
3. 模型架构
八个学习层:五个卷积层+三个全连接层。这部分作者花了四个小部分讲解了论文中创新点(或者说叫新奇的不寻常的部分)
3.1 Relu Nonlinearity
这里作者在激活函数上做了创新,同一个数据集CIFAR-10上,以训练误差率(Training error rate)下降到25%需要的迭代次数(Epochs)作为性能指标,对比了ReLU函数和tanh函数这两个激活函数所需要的迭代次数。
| |
实验数据集: CIFAR-10 |
---|
模型:四层的卷积神经网络(ReLU VS tanh) |
性能指标:训练误差率(Training error rate)下降到25%需要的迭代次数(Epochs) |
这里作者指出他们不是第一个考虑替换CNNs中的传统神经模块的团队,指出Jarrett团队在Caltech-101数据集上,在平均池化后使用的非线性激活函数f (x) = |tanh(x)|取得了非常好的效果。Jarrett团队是为了防止过拟合,即观察防止过拟合能力,而作者是为了加快训练速度,即观察eopchs数。作者也强调,更快的学习速度在大数据上的大模型上拥有很重要的影响。
3.2在多GPU上训练
作者将模型放到了两个GPU上进行训练,因为当时小的显存限制了网络模型的尺寸。GPU非常适合交叉GPU并行训练,GPU能够直接从另一个显存中直接进行读写,不需要再经过主存,作者采用的并行化方案基本上是将一半的内核(神经元)放到每个GPU上,并采用了一个额外的技巧:GPU只在某些层进行通信。例如,第三层的神经元接受来自第二层的神经元的输出,然后,第四层的神经元只接受在同一个GPU上第三层的神经元的输出。选择这个这种连接模式对于交叉验证是一个问题为(因为不一致的连接模式选择会导致不可靠的性能评估结果,而为了进行有效的交叉验证,需要保持连接模式的一致性),但这允许我们精确地调整通信量(是如何调整的?),直到它是可接受的计算量的一部分。
第二段作者指出最终的结构有点类似Ciresan提出的柱状CNN,不同的是,他们的柱子是不独立的。然后作者指出这个方案在top-1和top-5的错误率上有下降,并且训练时间比单GPU网络要短。
第二段下面作者指出,单GPU网络和双GPU网络在最终的卷积层上面有相同的参数,因为大多数参数都是在第一个全连接层上,而这些参数是最后一个卷积层提供的。所以为了让这两个网络拥有近乎相同的参数,没有减半最终卷积层以及紧随其后的全连接层的尺寸。因此,这个比较是倾向于单GPU网络的,因为它比双GPU网络的一半还要大。
(这里我并不完全懂,作者表达两个意思:
单GPU网络和双GPU网络具有相同数量的参数,因为它们共享相同的卷积层参数。在相同参数上,双GPU网络的训练时间更少。
单GPU网络比双GPU网络的一半更大,这样他的参数量就更多,可以更好地拟合数据)
对比实验二 | 结果 |
---|---|
单GPU网络 | 作者知识给出 采用双GPU方案后的效果 |
双GPU网络(compared with a net with half as many kernels in each convolutional layer trained on one GPU) | This scheme reduces our top-1 and top-5 error rates by 1.7% and 1.2% |
3.3局部响应归一化
ReLU具有理想的特性,即它们不需要输入规范化来防止它们饱和。作者发现局部响应归一化的方法也有助于结果。表示在位置(x,y)上将被核i进行计算的神经元响应,然后应用ReLU非线性激活函数。最终局部响应活动的
由下面这个公式得到:
其中求和覆盖了n个“相邻的”位于相同空间位置的核映射,N是该层中的核总数。核映射的顺序当然是任意的,且在训练开始前就确定。受到在真实神经元中发现的类型启发,这种响应归一化实现了一种侧向抑制,在使用不同核计算神经元输出的过程中创造对大激活度的竞争,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。常数k,n,α和β是超参数,它们的值要用验证集来确定;我们使用 k = 2, n = 5, α = 10−4, and β = 0.75 。我们在某些层应用ReLU归一化后再应用这种归一化。
3.4重叠池化
使用重叠池化相较于传统的池化,可能防止过拟合。
重叠池化:移动的步长比相邻的池化单元小,就能达到重叠的效果。
3.5整体架构
第二段较为详细的介绍了网络框架,包含了哪些层,层与层之间如何进行通信,哪些层后面又分别包含了哪些模块(上文中提及的)。
第三段进一步讲解了网络的结构,每一层内部的卷积核个数及其大小进行说明。包括全连接层的神经元个数。
第三段原文:
第一个卷积层,使用96个11x11x3大小的卷积核,移动步长为4(相邻神经元在一个卷积核映射中感受野中心之间的距离),对224x224x3的图片进行卷积提取特征。
第二个卷积层,使用256个5x5x48大小的卷积核,对输入(对第一层的输出进行局部响应归一化以及池化操作之后)进行特征提取。
第三和第四以及第四和第五层之间没有任何的池化和归一化层。
第三个卷积层,使用384个3x3x256大小的卷积核,对输入(对第二层的输出进行局部响应归一化以及池化操作之后)进行特征提取。
第四个卷积层,使用384个3x3x192大小的卷积核
第五个卷积层,使用256个3x3x192大小的卷积核
每个全连接层有4096个神经元。
关于一些参数的选取,其中是根据某些依据计算得到来的,不是随便瞎设置的,例如第一个卷积核的移动步长。
以下是torchvision.models中实现AlexNet网络。这个网络与论文中的描述的网络结构有部分区别。(两个GPU之间的通信的代码不在本文的讨论范围之内,我们现在就看单GPU的)
class AlexNet(nn.Module):
def __init__(self, num_classes: int = 1000, dropout: float = 0.5) -> None:
super().__init__()
# 卷积层部分
self.features = nn.Sequential(
# 输入图像大小为 256 x 256 x 3
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
# 输出大小为 64 x 64 x 64,输出大小N = (W − F + 2P )/S+1=64,这里是64个卷积核,,卷积核的个数确定了输出的channel数。
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # 输出大小为 32 x 32 x 64
nn.Conv2d(64, 192, kernel_size=5, padding=2), # 输出大小为 32 x 32 x 192
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # 输出大小为 16 x 16 x 192
nn.Conv2d(192, 384, kernel_size=3, padding=1), # 输出大小为 16 x 16 x 384
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1), # 输出大小为 16 x 16 x 256
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1), # 输出大小为 16 x 16 x 256
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # 输出大小为 8 x 8 x 256
)
# 全连接层部分
self.avgpool = nn.AdaptiveAvgPool2d((6, 6)) # 输出大小为 6 x 6 x 256
self.classifier = nn.Sequential(
nn.Dropout(p=dropout), # 随机丢弃 50% 的神经元
nn.Linear(256 * 6 * 6, 4096), # 全连接层,输出大小为 4096
nn.ReLU(inplace=True),
nn.Dropout(p=dropout), # 随机丢弃 50% 的神经元
nn.Linear(4096, 4096), # 全连接层,输出大小为 4096
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes), # 全连接层,输出大小为 num_classes(类别数)
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
4. 减少过拟合
这里作者讲了两个减少过拟合的方法
4.1 数据增强
最简单最常用的在图像数据上减少过拟合的方法,就是人为的进行标签保留式的转换。作者使用了两种数据增强方式,每一种都只需要很少的计算量,且这些转换的图片不需要保存在磁盘上。CPU生成转换的图片,GPU在之前的batch的图片上进行训练。因此,这些数据增强方案在实际中是计算(时间方面)上免费的。
第一种数据增强形式包括生成图像平移和水平反射。我们通过从256×256图像中截取随机的224×224大小的图像块(及其水平反射)并在这些截取的图片块上训练我们的网络来实现这一点。虽然训练集大小增加了2048倍,但是作者提出这种方式生成的训练样本是高度相关的。如果没有这个方案,网络会遭受严重的过拟合(对过拟合的理解:参数之间高度相关,参数之间和训练数据高度相关),这将迫使我们使用更小的网络。在测试时,网络通过提取五个224×224图片块(四个角图片块和中心图片块)以及它们的水平反射来进行预测。
第二种形式的数据增强包括在训练集上改变RGB通道的强度。
Trick训练集上改变RGB通道的强度 | |
---|---|
如何改变的? | |
对每个像素的RGB值![]() ![]() |
具体而言,我们对整个ImageNet训练集中的RGB像素值集合执行PCA。获得对应的主成分。每张训练的图像都要增加几倍的主成分。从均值为零,标准差为0.1的高斯分布中随机抽取一个数(α),然后将其乘以对应的特征值(λ),再乘以对应的特征向量(p),最后将这个结果加到RGB图像像素值上。每个αi只会在特定的训练图像中被随机抽取一次,直到该图像再次被用于训练 |
为什么能这么改变? | 自然图像有一个重要特性,即物体的身份对照明强度和颜色的变化是不变的。 |
**改了之后效果如何? ** | reduces the top-1 error rate by over 1% |
4.1 Dropout
结合很多不同模型的预测结果能够有效减少错误率,但是对于大型神经网络很费时间。这里作者引入了一种有效的结合方案:Dropout。对每个隐层神经元的输出,有一半的概率将其设置为0。这些被‘Dropout out’的神经元不参与前向和反向传播。每一次神经网络都采样了不同的结构,但是这些结构都共享权重参数。由于一个神经元不能依赖其他神经元的存在,所以dropout减少神经元之间的复杂共适应性(每个神经元都必须学习一些对于不同的随机子集都是有用的特征,而不能依赖于其他特定的神经元)。因此,神经元不得不学习一些对于不同的随机子集(每次前向传递时随机选择的神经元子集,也就是dropout一部分神经元之后,剩下的神经元)都是有用的特征。在测试阶段,对所有的神经元的输出乘以0.5(这是对由指数多个dropout网络产生的预测分布取几何平均数的一个合理近似)。
5. 学习细节
使用随机梯度下降算法训练模型,每次迭代使用128个样本进行训练,同时使用动量为0.9的方法来加速训练过程,使用了权重衰减(weight decay)技术,其中衰减系数为0.0005。作者发现小的权重衰减系数对于模型的学习很重要。即权重衰减不仅仅达到了正则化的作用,还减小了训练误差。权重w的更新规则如下:
i是迭代次数,v是动量,![]() ![]() |
---|
从标准差为 0.01 的零均值高斯分布 初始化权重,这种初始化方式可以通过为 ReLU 提供的正输入,来加速早期的学习阶段(加速)。在剩下的层中,我们使用常数0来初始化偏置层。
第三段作者指出,所有的层使用的相同的学习率,在学习的过程中通过手动去调整。作者根据经验,若当前学习率下的验证集错误率不再下降时,就将学习率除以10。最后作者指出,他们的学习率减小了三次,数据集迭代训练了大概90次。
6. 结果
这一部分以top-1和top5为性能指标,在 ILSVRC-2010 和 ILSVRC-2012数据集上,和其他模型进行的对比。
6.1定性评价
![]() |
图3这是第一层96个11113的卷积核在2242243的输入图像上,经过学习之后的样子。上面48个是GPU1上。下面48个是GPU2上。 |
---|
图三展示了由网络的两个数据连接层学习的卷积核,网络已经学习了各种频率和方向选择内核,以及各种颜色的斑点,结合3.5部分的网络结构图,GPU 1上的卷积核在很大程度上不考虑颜色,而GPU 2上的卷积核在很大程度上考虑颜色,这种专业化在每次运行时都会发生,并且与任何特定的随机权重初始化无关(除了重新编号GPU之外)。
![]() |
展示了八张测试集中的图片,及其由Alexnet预测的五个类别top-5。即使不在中心的目标也可以被识别,例如第一个螨虫的图。大多数top-5预测的类别都是合理的。例如,只有其他类型的猫被认为是豹子的合理标签。也有一些对照片的预期焦点确实存在模糊不清的地方,例如车预测成栅栏,狗预测成樱桃。 |
---|
![]() |
第一列是ILSVRC-2010 测试集的五张图,后面的六张图是从训练集中检索出来的相似图片。依据是这六张图与第一张图在提取的特征向量上,拥有最小的欧几里得距离。 |
---|
这两张图展示了两种评估模型学习到的视觉知识的方法。
一种是看预测的种类。
另一种是考虑考虑由图像在最后一个4096维隐藏层激活的特征。如果两个图片产生的特征向量有一个很小的欧几里得距离,那么神经网络更高的学习层会认为他们是相似的。作者指出,这些检索出来的图片在L2距离上并不相近。
使用两个4096维实值向量之间的欧几里得距离计算相似性是低效的,但是可以通过训练自编码器将这些向量压缩为短的二进制代码来提高效率。参考文献14指出这应该会产生比对原始像素应用自编码器更好的图像检索方法,后者不使用图像标签,因此有倾向于检索具有相似边缘模式的图像,无论它们是否在语义上相似。
2. 讨论
几个短语总结一下:
有监督学习、极具挑战性的数据集、获得突破性的成果、大的深的卷积神经网络。深度(层数)很重要(移除任意一层,top-1都会下降2%)、没有使用无监督的方法、仍有很多工作要做来模仿人类的下颞通路、未来在视频上使用大型深度卷积网络(时间结构提供很有帮助的信息,而静态图像上没有)
这篇文章的创新点:
(这肯定是一个新的网络架构,其中也必然包含一些之前的论文提出的有效模块)
1:提出了一种基于深度卷积神经网络的图像分类方法,该方法在当时的图像分类任务中取得了最好的结果;(SOTA)
2:训练了一个非常大的深度卷积神经网络,该网络包含了超过6000万个参数,但是通过GPU的高效计算和优化的实现,使得该网络的训练成为可能;
3:提出了Relu激活函数,可以降低过拟合以及可以将加快训练速度。
4:提出了局部响应归一化,降低训练错误率
5:采用重叠池化,放置过拟合
6:提出了一种数据增强的方法,通过对训练数据进行变换,增加了训练数据的多样性,从而提高了模型的泛化能力;采用dropout,增强模型的泛化能力。
文章中提及的数据集
NOBR数据集 | NORB数据集是美国空军研究实验室发布的一个立体物体识别数据集,包含50,000个灰度图像,用于测试算法在立体物体识别方面的性能。 |
---|---|
Caltech数据集 | 加州理工学院发布的一个图像分类数据集,包含101个类别的图像,共计9,144张图像。该数据集用于测试算法在图像分类方面的性能。 |
CIFAR数据 | 加拿大计算机科学家Alex Krizhevsky等人发布的一个图像分类数据集,包含10个类别的图像,共计60,000张32×32像素的彩色图像。该数据集被广泛用于测试算法在图像分类方面的性能。 |
LabelMe | 图像标注和分割数据集,由麻省理工学院人工智能实验室发布。该数据集包含超过10,000个图像,每个图像都有详细的像素级别的标注和分割信息。 |
ImageNet | ImageNet数据集被广泛用于图像分类、目标检测、图像分割等计算机视觉任务的研究和评估 |
涉及的实验和对比
3.1 Relu Nonlinearity这里作者在激活函数上做了创新,同一个数据集CIFAR-10上,以训练误差率(Training error rate)下降到25%需要的迭代次数(Epochs)作为性能指标,对比了ReLU函数和tanh函数这两个激活函数所需要的迭代次数。 | 自制一个简单的四层卷积神经网络,将其中的激活函数作为修改变量(RELU和tanh),训练并记录迭代和对应training error |
---|
# 定义四层卷积神经网络模型
class FourLayerCNNRelu(nn.Module):
def __init__(self, num_classes):
super(FourLayerCNNRelu, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
nn.ReLU()
)
self.fc = nn.Linear(131072, num_classes) # 调整全连接层的输入大小
def forward(self, x):
out = self.features(x)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out
# 定义四层卷积神经网络模型
class FourLayerCNNTanh(nn.Module):
def __init__(self, num_classes):
super(FourLayerCNNTanh, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1),
nn.Tanh(),
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1),
nn.Tanh(),
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
nn.Tanh(),
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
nn.Tanh()
)
self.fc = nn.Linear(131072, num_classes)
def forward(self, x):
out = self.features(x)
out = out.view(out.size(0), -1)
out = self.fc(out)
return out
通过这两个模型去对比,只是将激活函数更换。
3.2在多GPU上训练 | |
---|---|
3.3局部响应归一化(后期争议在2015年 Very Deep Convolutional Networks for Large-Scale Image Recognition.提到LRN基本没什么用。![]() |
![]() |
3.4重叠池化 | 步长小于窗口 |
6结果与两个当时的SOTA模型进行对比这部分在以后的论文对比方面,可以直接拿官方的数据。 | ![]() |
写在最后:
万事开头难,可以多花一些时间在理解这篇文章的提出创新点的思想上,深度学习是一个以实验为重点的方向,将论文中涉及的模块和数学方面的知识结合代码,可以理解的更加充分。???