这篇补充了上篇的代码实现,可直接参见第二部分。
《ImageNet Classification with Deep Convolutional Neural Networks》基于深度卷积神经网络的Image分类 ImageNet:是一个图片分类数据集,分成1000类,每类大约1000多张图片 用到的技术:图像增强(两种方法)、ReLU、Dropout
第一部分:论文解读
0、摘要
(写法不怎么好)
- 第一步:告诉我们干了什么事情:我们训练了一个大型的深度卷积神经网络,将LSVRC-2010竞赛中的ImageNet 120万张高分辨率图像分类到1000个不同的类别。
- 第二步:结果:在测试数据上,top-1的错误率是37.5%,top-5的错误率为17.0%,这大大优于之前的最先进水平。(2010年的竞赛给了测试集)
- 该神经网络有6000万个参数和65万个神经元,由5个卷积层组成,其中一些是最大池化层,还有三个全连接层,最后是1000路的softmax。
- 为了使训练更快,采用GPU。
- 为了减少全连接层的过拟合,采用名为“dropout”的正则化方法。
- 参加了LSVRC-2012比赛,2012年没有给测试集,我们的模型top-5错误率为15.3%,第二名的错误率为26.2%。表示我们的模型比第二名的模型好了很多。
1、序言
- 有了足够的计算和足够的数据,对于需要集成许多不同的、嘈杂的线索的复杂任务,学习胜过编程。(训练很重要)
- 反向传播很重要。整个网络的分类性能取决于每个连接上的权重值。
- 错误的结论:随机初始权重学习神经网络太难了。
- 可以学习:但是要大量的标记数据和大量的计算。
现在无监督学习的发展,可以在无标记数据里学到想要的知识,例如GAN。
2、引言
- 本文干了什么:提出了一个物体识别方法。
- 为了提高方法的性能:要大的数据集,学习更大的模型,还要防止过拟合。
过拟合(使用正则化)是深度学习的一个分支,重点是网络架构,网络架构很好的话正则化也没有那么重要。
- 本文提出的模型:CNNs,广度深度同样重要,CNNs的连接和参数要少很多,并且更容易训练,理论上的性能只是略差。(自己写论文的时候不能只说自己的东西很好)
- 但是CNNs不好训练:用GPU训练,并且ImageNet够大,过拟合也不会太严重。
本文具体贡献如下:
- 训练了一个最大的CNNs,有着最好的准确率。但是现在看来AlexNet是小网络。
- 编写了一个高度优化的二维卷积的GPU实现,以及CNNs固有的功能。
- 一些新的不寻常的功能(在第四节中详细介绍),提高了性能、减少了训练时间。
- 采用了以下有效的技术来防止过拟合(在第五节中详细介绍)。
最终的网络:
文章说深度似乎很重要:我们去除任何的卷积层,都会导致性能下降。这个说法现在看来似乎不太对,深度和宽度同样重要,即使去掉一层,参数调好也能达到同样的效果。
3、数据集
使用的数据集:
- 使用ImageNet的一个子集,1000个类别,每个类别1000张图片。大约有120万张训练图像,5万张验证图像和15万张测试图像。
图像预处理:
- 预处理:仅仅做了剪裁,没有做任何的特征提取
- 方法:最后要将图像下采样成256×256。先将短边缩放至256,长边不够256的话拉成256,长边够256的话从图像中裁剪出中央的256×256块。所以我们在像素的(居中)原始RGB值上训练我们的网络(没有抽取抽取特征,直接在原始的pixels上,原始的RGB上训练网络)
这个预处理很重要!现在绝大多数都采用原始的RGB图像,采用end to end,原始的图片,原始的文本直接放进去,不做任何的特征提取,神经网络直接做出来。这是在当时一个很重要的创新点,但是这篇文章没有细说。
4、架构
4.1 Rectified Linear Unit nonlinearity(非线性激活ReLU)
非线性激活一般方法是:Tanh或sigmoid。(当时用的几乎都是这两个,现在也用,ReLU用的比较多)
从图上就可以看出来走的很慢,该文章用ReLU激活函数:小于0的为0,大于0的是本身。 本文使用ReLU是因为tanh(X)慢,ReLU快。现在看来也没有快多少,但还是用ReLU,因为简单。 实验结果:
4.2 Training on multiple GPUs 在多个GPU上进行训练
我们自己训练肯定不会多个GPU,大型人工智能机构的肯定会。这里就略过。
4.3 Local response normalization LRN归一化
对局部神经元的活动创建了竞争机制,使得其中相应较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力,但是现在不用了。
-因为ReLU不像tanh、sigmoid有值域区间,所以在ReLU后面做一个normalization,本篇文章就是LRN。
4.4 池化层
对传统的pooling有一定的改动。采用了重叠的最大池化,此前的CNNs中普遍使用平均池化,采用最大池化,避免了平均池化的模糊化效果;并且重叠和覆盖,提升了特征的丰富性。
4.5 整体架构
- 前五个是卷积层,后三个是全连接层。最后一个全连接层的输入被送入一个1000路的softmax,产生1000个类别标签的分布。
- 二、四、五卷积层的内核只与前一层中位于同一GPU的内核图相连。
- 第三层卷积层的神经元与第二层的所有内核图相连。
- 全连接层的神经元与上一层的所有神经元相连。
- LRN加在第一层、第二层卷积层后。
- 最大池化层在LRN后面和第五层卷积层后。
- ReLU用在每个卷积层和全连接层后。
第一层卷积层:卷积层、ReLU、LRN、最大池化层
- 卷积核:96个11×11×3的卷积核。
- 图像大小:227×227×3的输入图像。
- stride:(4,4)。
第二层卷积层:卷积层、ReLU、LRN、最大池化层
- 卷积核:256个5×5×48的卷积核。
- 与第一层的输出(LRN和池化)相连。
第三层卷积层:卷积层、ReLU
- 卷积核:384个3×3×256的卷积核。
- 与第二层的输出(LRN和池化)相连。
第四层卷积层:卷积层、ReLU
第五层卷积层:卷积层、ReLU、最大池化层
全连接层每个有4096个神经元。
长为4096的向量能够很好的抓住语义信息,如果两张图片4096向量很近的话,这两个图片可能就是同一个物体的图片。 一个图片,通过模型,把他压缩成4096的向量,这个向量把中间的语义信息都能表示起来(很像感受野),变成机器能懂得一个东西。机器用来做搜索、做分类、做什么事情都可以。 前面的原始数据,不管是图片、文字,还是语音、视频,通过中间的模型,最后压缩成一个向量,这个向量,机器能够去识别,能够识别的话,就可以在上面做各种各样的事情。
模型架构: 现在已经不这么写了!第二部分代码实现展示现在怎么写。
5 减少过拟合
本文提供了两种减少过拟合的方式。
5.1数据增强(数据扩充)
说我们的数据增强都是在CPU上计算的,因此是无计算的。现在都放在GPU上进行计算。
第一种方式:增加图片数量
- 人为进行数据扩充
- 训练集:随机的在256×256的图像上扣一块227×227的图像,再水平翻转。相当于增加了2*(256-227)^2的数据量。
- 测试集:随机的在256×256的图像上扣五块227×227的图像,再水平翻转。(十个),对他们进行预测并对10次结果求均值。
第二种方式:PCA加一些高斯扰动
- 改变训练图像中的RGB通道的强度(颜色会有一些改变)。
- 我们对整个ImageNet训练集的RGB像素值的集合进行PCA主成分分析。在每张训练图像中,我们添加所发现的主成分的倍数,其大小与相应的特征值乘以从高斯中抽取的mean为0,std为0.1的随机变量相乘。
- 对主成分做一个(0,0.1)的高斯扰动(标准差为0.1),对颜色光照做了变换,添加了一些噪声,错误率下降了1%。
- 得到了一个重要属性,物体本身对光照强度和颜色的变化是不变的。就是光照和颜色再变化,啥物体就是啥物体。
- 错误率降低了。
5.2 Dropout
- 0.5的概率随机的丢弃神经元。
- 在两个全连接层后使用。
- 使用后缓解了过拟合。
Dropout在现在用的也是蛮多的,等价于一个L2正则项,效果差不多。 AlexNet用了三个全连接,4096全连接是他的一大瓶颈,是设计的一个缺陷,导致了模型特别大,放不进GPU里,现在的CNNs设计不会设计那么大的连接,而GPU的发展,导致Dropout防止过拟合也没有那么重要了。但是Dropout在全连接上还是非常有用的,在RNN,Attension上使用的较多。
6 训练细节
- 使用SGD(SGD的噪音对模型泛化还是有好处的)。
- batch_size为128。
- 冲量momentum为0.9。momentum当你的优化表面非常的不平滑的时候,可以保持一个冲量,从过去那个方向,沿着一个比较平缓的方向往前走。
- 权重衰减weight decay为0.0005。
- 权重衰减的实现方式:不是加在模型上,而是加在优化算法上。
- 均值为0,方差0.01的高斯随机变量来初始化权重。0.01不大不小,更复杂的网络用0.02差不多。
- 2、4、5卷积层以及全连接隐藏层中的神经元偏置初始化为常数1。比较奇怪,数据好的时候偏置一般初始化为0,现在全部初始化为0效果也不差,并且也不需要调参。
- 学习率手动调整的,当验证错误率不随当前的学习率提高时,将学习率÷10。
学习率刚开始设计的大,后面要变小。当验证错误不增加的时候,手动将学习率×0.1,降低10倍。盯着你的训练,发现不动了,就手动调一下。现在不这样做了。 比如ResNet训练120个epoch,每30轮下降0.1。或者前面训练长一点60轮或100轮,后面再下降学习率。现在也不这样了。 现在通常的做法是:选择cos函数,一个比较平缓的下降。学习率不能选的太大,一下子会爆炸;也不能太小,太小训练不动。主流的做法是冲0开始,慢慢的上去,再cos慢慢的下来。warm up学习率预热,参见pytorch的学习率优化。
7 结果
7.1定性评估
- 结果:GPU1上的内核在很大程度上是不分颜色的,而GPU2上的内核在很大程度上是特定颜色的。(其实没有关系,本来就是一个随机性的东西,现在也几乎是在一个GPU上进行训练的)。
- 对后面的研究有一定的启发性(可解释性),比如底部的神经元学到了是一些局部的信息,比如纹理,方向;偏上的神经元学习到的比较全局,例如这是一个头,一个手,一个动物这种的,更多的信息在神经元里面。看的是我们到底是想学一个东西的形状,还是一个东西的纹理。
8 讨论
(一般论文写的都是结论。)
- 删除一个卷积层,导致性能损失2%,因此深度非常重要。不严谨,如果去掉一层,再调参,也可以达到原来的效果,深度和宽度同样重要。
- 监督学习研究的较多,无监督学习unsupervised兴起,可以从没有标签的数据集里面进行学习,例如GAN。
9 收场白
第二部分:AlexNet代码实现
1、数据预处理
首先对整个训练集的各个通道的像素值计算各个通道的主成分,然后将主成分添加随机数来叠加到图片上。 高斯扰动:mean:0,std:0.1
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
writer=SummaryWriter("../logs_gaosiraodong")
img=Image.open("../a_base/images/wang.jpg")
print('PIL读取图片的格式:{}'.format(type(img)))
'''ToTensor()'''
trans_totensor=transforms.ToTensor()
img_tensor=trans_totensor(img)
writer.add_image("ToTensor",img_tensor,0)
'''Normalize(torch.nn.Module)'''
print('三通道的值:{}'.format(img_tensor[0][0][0]))
trans_norm=transforms.Normalize(0,0.1,inplace=False)
img_norm=trans_norm(img_tensor)
print('归一化后三通道的值:{}'.format(img_norm[0][0][0]))
writer.add_image("Normalize",img_norm,1)
writer.close()
控制台: 结果: 这个结果应该是有问题的,论文里只是对光照进行了增强,这个结果直接面目全非了,猜测是我没有提取原图的主成分,有会的大佬麻烦评论区指点一下! 要注意的是:这种方法慎用,可能会导致模型不收敛。
随机剪裁:也使用pytorch进行实现。
2、模型结构
直接用的预训练好的网络。
import torchvision
alexnet=torchvision.models.alexnet(pretrained=True,progress=True)
print(alexnet)
结果:
AlexNet(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(4): ReLU(inplace=True)
(5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(7): ReLU(inplace=True)
(8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(9): ReLU(inplace=True)
(10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
(classifier): Sequential(
(0): Dropout(p=0.5, inplace=False)
(1): Linear(in_features=9216, out_features=4096, bias=True)
(2): ReLU(inplace=True)
(3): Dropout(p=0.5, inplace=False)
(4): Linear(in_features=4096, out_features=4096, bias=True)
(5): ReLU(inplace=True)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
测试:
inp_tensor=torch.randint(10,size=(1,3,227,227),dtype=torch.float32)
print("输入图片的形状:{}".format(inp_tensor))
output=alexnet(inp_tensor)
print("测试网络的输出结果:{}".format(output))
print("测试网络输出的形状:{}".format(output.shape))
结果:
|