ImageNet Classification with Deep Convolutional Neural Networks
文章目录
AlexNet是hinton的学生
0 摘要
- AlexNet在ImageNet上(LSVRC-2010)取得了最优性能。其top-1和top-5性能均超过之前最好的模型
- 网络结构:5个卷积层(部分带有最大池化)+3个全连接层+1000-way的
softmax
,6千万个参数、65万个神经元 - 为了训练更快:非饱和神经元(ReLU) + GPU上实现卷积
- 防止全连接层过拟合:dropout方法
- LSVRC-2012超出亚军十个百分点
1 引言
提升性能方法:更大数据集、更强模型、更好的防止过拟合的技术
-
数据集:较小数据集上的简单识别任务性能很好(尤其使用了保留标签的变换来增大数据集时),但是现实环境中的物体表现出很大的可变性,需要大数据集
-
目标识别任务的巨大复杂性 意味着即使是像ImageNet这样大的数据集也无法解决,所以,模型应该有先验知识弥补没有的数据。卷积网络深度宽度易调节,对真实图像的假设强大且正确(假设了统计信息的平稳性和像素相关性的局部性),参数少易训练,同时性能差别不是特别大
-
GPU 上实现二维卷积很高效,足以训练CNN,且ImageNet有足够的样例不会出现严重的过拟合
-
贡献:
- (当时)最大的卷积神经网络,效果显著(ILSVRC-2012冠军)
- 在GPU上实现了二维卷积及相关操作
- 提出了新特性,提升性能(ReLU、LRN、GPU并行、重叠池化)
- 解决过拟合的方法(dropout)
- 层数很重要
-
受限于GPU内存和训练时间
以前都是特征工程,实现了端到端学习
2 数据集
ILSVRC从ImageNet数据集挑选子集1000个类。
ILSVRC-2010(ILSVRC中唯一测试集标签可见的数据集),也用了ILSVRC-2012。给出top-1和top-5错误率
ImageNet上图像分辨率不同,做法:原始图像下采样到 256 ∗ 256 256*256 256∗256:较短边到256,长边等比例缩放,从中心裁剪成 256 ∗ 256 256*256 256∗256。除了从每个像素中减去训练集上的平均值以外,没有对图像进行任何预处理,因此,我们在像素的(居中)原始RGB值上训练了我们的网络。
*3 架构
3.1 ReLU非线性变换
传统激活函数: f ( x ) = t a n h ( x ) = e x − e − x e x + e − x f(x)=tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}} f(x)=tanh(x)=ex+e−xex−e−x或者 f ( x ) = s i g m o i d ( x ) = 1 1 + e − x f(x)=sigmoid(x)=\frac{1}{1+e^{-x}} f(x)=sigmoid(x)=1+e−x1。
这些激活函数比非饱和非线性变换 f ( x ) = m a x ( 0 , x ) f(x)=max(0,x) f(x)=max(0,x)慢很多, R e L U ReLU ReLU(修正线性单元)快很多(差不多6倍)。
饱和和非饱和激活函数
- 右饱和: 当x趋向于正无穷时,函数的导数趋近于0,此时称为右饱和。
- 左饱和: 当x趋向于负无穷时,函数的导数趋近于0,此时称为左饱和。
- 饱和函数和非饱和函数: 当一个函数既满足右饱和,又满足左饱和,则称为饱和函数,否则称为非饱和函数。
- 常用的饱和激活函数和非饱和激活函数: 饱和激活函数有如Sigmoid和tanh,非饱和激活函数有ReLU;相较于饱和激活函数,非饱和激活函数可以解决“梯度消失”的问题,加快收敛。
我认为ReLU这里的“快”有两方面:1.计算快(没有指数运算) 2. 收敛速度快,如下图
更快的学习对大数据集上的大模型的性能有很大影响
- 防止梯度消失
- 使网络具有稀疏性(负半轴)
3.2 多GPU训练
虽然,以现在视角来看,多GPU训练是非常正常的,没有什么特别之处,尤其是出现了pytorch之后,基本上是标配。。。
单GPU限制了网络训练的最大尺寸,所以可以将网络放到两个GPU上去,跨GPU并行化(可以直接读写彼此内存,无需经过主机)。
- 对于整层的卷积核,一半放在一个GPU上,另一半放在另一个GPU上。
- GPU只在某些层通信。选择连接模式是交叉验证的问题,但这使我们可以精确地调整通信量,直到它是计算量的可接受的一部分为止。
3.3 LRN局部响应正则化
实验发现LRN能帮助模型泛化,虽然后面VGG说LRN不仅增加了计算代价,还降低了正确率[狗头]
真实神经元的侧向抑制现象
R
e
L
U
ReLU
ReLU的输入是要有一项是正的就能学习。局部归一化有助于泛化:
b
x
,
y
i
=
a
x
,
y
i
/
(
k
+
α
∑
j
=
m
a
x
(
0
,
i
−
n
/
2
)
m
i
n
(
N
−
1
,
i
+
n
/
2
)
(
a
x
,
y
j
)
2
)
β
b^i_{x,y}=a^i_{x,y}/(k+\alpha\sum^{min(N-1, i+n/2)}_{j=max(0, i-n/2)}(a^j_{x,y})^2)^\beta
bx,yi=ax,yi/(k+αj=max(0,i−n/2)∑min(N−1,i+n/2)(ax,yj)2)β
- i i i是核的序号
- a x , y i a^i_{x,y} ax,yi是在第 i i i个核对应的层在位置坐标 ( x , y ) (x,y) (x,y)的神经元经过 R e L U ReLU ReLU激活后的值
- b x , y i b^i_{x,y} bx,yi是最终结果
- N N N是核的总数(通道数)
- n n n是邻居个数,人为定义的
- k , n , α , β k,n,\alpha,\beta k,n,α,β都是超参数
实现了神经元的侧向抑制。
我们将其更正确地称为“亮度归一化”,因为没有减去平均值。
3.4 重叠池化
池化层汇总了同一核特征映射上相邻神经元的输出。可以将池化层视为由间隔为 s s s个像素(步长)的池化单元的网格组成,每个网格都汇总了以池化单元的位置为中心的大小 z ∗ z z*z z∗z的邻域。
- s = z s=z s=z,传统的池化
- s < z s<z s<z,池化带有重叠(参数2,3),公式与卷积相同
现象:具有重叠池化层的模型更不容易发生过拟合。
3.5 整体架构
- 5个卷积层+3个全连接层+1000-way的
softmax
( S i = e i ∑ j e j S_i=\frac{e^i}{\sum_je^j} Si=∑jejei)得到1000类别标签的分布 - 卷积层2、4、5只与同一GPU的上一层有连接,卷积层3与卷积层2的所有核都有映射,全连接层也是全部映射
- LRN只在卷积层1、2后面有
- 最大池化位于LRN和卷积层5后面
- ReLU在每一层后面都有
单GPU情况:
参数主要是第一个全连接层
4 防止过拟合
4.1 数据增强
最简单、最常用:保留图像标签的转换,人工扩充数据集
这两种方法计算很少,意味着不用特意存到磁盘上。CPU上生成图片,GPU上训练
-
图像平移和水平翻转(位置)
-
训练阶段
从256x256图像中随机提取224x224的块(及其水平翻转),使用这些224大小的块训练。
扩大了2048倍(256-224=32,32x32x2=2048,但是我感觉是33*33*2)(虽然不同,但是密切相关,都是一张图片来回造,都差不多样子,能不密切相关吗)
-
测试阶段
裁剪出5张(四个角+中心)及翻转,扩大了10倍,获得的预测值平均
-
-
更改RGB图像强度(颜色)
对整个训练集的RGB像素值进行PCA(PCA不是将降维用的吗),进行颜色扰动。
在每个训练图像中,我们添加找到的主成分的倍数,其大小与 相应的特征值乘以服从高斯分布 N ( 0 , 0.1 ) N(0,0.1) N(0,0.1)的随机变量 成比例。
RGB图像像素表示为: [ I x y R , I x y G , I x y B ] [I^R_{xy},I^G_{xy},I^B_{xy}] [IxyR,IxyG,IxyB],增加的项:
[ p 1 , p 2 , p 3 ] [ α 1 λ 1 , α 2 λ 2 , α 3 λ 3 ] T [p_1,p_2,p_3][\alpha_1\lambda_1,\alpha_2\lambda_2,\alpha_3\lambda_3]^T [p1,p2,p3][α1λ1,α2λ2,α3λ3]T- p i , λ i p_i,\lambda_i pi,λi是3x3的协方差矩阵的特征向量和特征值
- α i \alpha_i αi是随机变量(服从高斯分布)。 对于一个特定训练图像的所有像素,每个 α i \alpha_i αi只会被绘制一次,直到该图像再次用于训练为止,此时将其重新绘制。
4.2 dropout
随机失活。动机:结合多个模型的预测对降低测试误差很有帮助,但是对于大型网络代价昂贵。
FC层
- 训练时,隐藏层神经元以一半的概率(随机)输出0(失活),不参与前向传播和反向传播。因此,每次输入时,神经网络都会对不同的架构进行采样,但是这些架构共享权重,减少了神经元的复杂共适性(神经元不能依赖于特定其他神经元的存在)。
- 测试时,所有神经元输出乘0.5(随机失活概率),数据尺度变化,合理近似于采用随机失活产生的预测分布的几何平均值。(使得输出均值不变。)
前两个全连接层使用dropout。
5 细节
- 随机梯度下降SGD
- batch_size=128
- momentum=0.9(即动量,它模拟的是物体运动时的惯性,即更新的时候在一定程度上保留之前更新的方向,同时利用当前batch的梯度微调最终的更新方向。这样一来,可以在一定程度上增加稳定性,从而学习地更快,并且还有一定摆脱局部最优的能力: Δ x t = ρ x t − 1 − η g t Δx_t=ρx_{t−1}−ηg_t Δxt=ρxt−1−ηgt)
- weight decay=0.0005(很重要!这里的权重衰减不仅仅是一个正则化器,防止过拟合,它可以减少模型的训练误差)
- 更新公式
v i + 1 = 0.9 ⋅ v i − 0.0005 ⋅ ε ⋅ w i − ε ⋅ < ∂ L ∂ w ∣ w i > D i w i + 1 = w i + v i + 1 v_{i+1}=0.9\cdot v_i-0.0005\cdot\varepsilon \cdot w_i-\varepsilon \cdot<\frac{\partial L}{\partial w}|w_i>_{D_i}\\ w_{i+1}=w_i+v_{i+1} vi+1=0.9⋅vi−0.0005⋅ε⋅wi−ε⋅<∂w∂L∣wi>Diwi+1=wi+vi+1
-
i i i:迭代次数
-
v v v:动量变量
-
ε \varepsilon ε:学习率
-
< ∂ L ∂ w ∣ w i > D i <\frac{\partial L}{\partial w}|w_i>_{D_i} <∂w∂L∣wi>Di:第 i i i批 D i D_i Di的,在 w i w_i wi条件下,目标函数的导数相对于 w w w的偏导的 平均值
初始化,高斯分布 N ( 0 , 0.01 ) N(0,0.01) N(0,0.01)初始化每一层的权重,卷积层2、4、5和全连接层的偏置为1,其余层偏置为0
对所有层使用了相等的学习率,训练过程中手动调整。当验证集错误率稳定时,除以10,初始化为0.01
6 实验
ILSVRC-2010,测试集
- 稀疏编码
- SIFT+Fisher vector
- CNN
ILSVRC-2012,验证集
- 1CNN 18.2
- 5CNNs(多个alexnet取平均值) 16.4
- 1CNN* 预训练的微调+第六个卷积层 16.6
- 7CNN* 2预训练的微调+5CNNs 取平均值 15.4
ImageNet 2009Fall
一半训练集,一半测试集
top-1:67.4%(之前最好78.1%)
top-5:40.9% (之前最好60.9%)
6.1 定性评估
- 卷积核可视化:学习到了频率、方向、颜色。第一层的卷积核(11x11x3)GPU1与颜色无关,GPU2颜色相关(分工学习)。这种特殊化发生在每次运行期间,并且与任何特定的随机权重初始化(对GPU进行重新编号)无关。
- 即使对象不在图片中心也能正确识别,大多数top-5预测标签可信,极少数错误。
- 特征相似性:最后4096维隐藏层中的图像的特征激活。 如果两个图像产生的特征向量欧氏距离较小,则可以说神经网络的较高级别认为它们是相似的。在像素级别,检索到的训练图像通常在
L
2
L^2
L2范数中不接近第一列中的查询图像。 相似图片具有“相近”的高级特征
- 通过使用两个4096维向量之间的欧氏距离来计算相似度效率低下,可以通过训练自动编码器将这些向量压缩为短二进制码来提高效率。 与将自动编码器应用于原始像素相比,这应该产生一种更好的图像检索方法,该方法不使用图像标签,因此有检索具有相似边缘模式的图像的趋势,而无论它们在语义上是否相似。
7 探讨
- 大型、深度、卷积神经网络能够使用纯监督学习的方式,在具有挑战性的数据集上实现创纪录的结果
- 层次的重要性,层次只要改变,正确率下降
- 为简化实验,没有任何无监督的预训练
- 未来方向:视频序列,时间结构提供了很有有帮助的信息
8 关键代码
class AlexNet(nn.Module):
def __init__(self, num_classes=1000):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.features(x)
x = self.avgpool(x)
x = x.view(x.size(0), 256 * 6 * 6)
x = self.classifier(x)
return x