1.自适应学习率
很多时候训练陷入瓶颈,损失不再下降,不是因为陷入了局部最小值,大部分情况下是在局部最小值附近震荡(学习率太大),这个时候应该通过降低学习率的方式,从而能够继续往最小值方向下降。
举个例子,我们有两个参数和
,这两个参数值不一样的时候,损失值也不一样,得到了下图(上)所示的误差表面,该误差表面的最低点在叉号处。事实上,该误差表面是凸的形状。凸的误差表面的等高线是椭圆形的,椭圆的长轴非常长,短轴相比之下比较短,其在横轴的方向梯度非常小,坡度的变化非常小,非常平坦;其在纵轴的方向梯度变化非常大,误差表面的坡度非常陡峭。现在我们要从黑点(初始点)来做梯度下降。
学习率的结果如下图(下左)所示。参数在峡谷的两端,参数在山壁的两端不断地“震荡”,损失降不下去,但是梯度仍然是很大的。我们可以试着把学习率设小一点,学习率决定了更新参数的时候的步伐,学习率设太大,步伐太大就无法慢慢地滑到山谷里面。调学习率从
调到
的结果如下图(下右)所示,参数不再“震荡”了。参数会滑到山谷底后左转,但是这个训练永远走不到终点,因为学习率已经太小了。AB段的坡度很陡,梯度的值很大,还能够前进一点。左拐以后,BC段的坡度已经非常平坦了,这种小的学习率无法再让训练前进。事实上在BC段有10万个点(10万次更新),但都无法靠近局部最小值,所以显然就算是一个凸的误差表面,梯度下降也很难训练。
这个例子说明了不同的参数的梯度变化可能会有显著不同,所以不应该对所有参数用同一个学习率。同时,在训练的不同阶段,学习率也可以有所不同,比如早期学习率可以更大,后期学习率可以变小。
因此,在梯度下降里面,所有的参数都是设同样的学习率,显然是不够的,应该要为每一个参数定制化学习率,即引入自适应学习率(adaptive learning rate)的方法,给每一个参数不同的学习率。如下图所示,如果在某一个方向,梯度的值很小,非常平坦,我们会希望学习率调大一点;如果在某一个方向上非常陡峭,坡度很大,我们会希望学习率可以设的小一点。
总而言之,自适应学习率(Adaptive Learning Rate)是深度学习中一种重要的优化策略,它能够根据训练过程中的动态变化自动调整学习率。这种策略可以提高训练效率,加快收敛速度,并有助于模型在训练过程中跳出局部最小值或鞍点。
1.1 AdaGrad
AdaGrad (Adaptive Gradient)是典型的自适应学习率方法,其能够根据梯度大小自动调整学习率。AdaGrad可以做到梯度比较大的时候,学习率就减小,梯度比较小的时候,学习率就放大。
AdaGrad 使用和累积梯度平方来调整每个参数的学习率。
累积梯度平方和公式:
学习率计算公式:
1.2 RMSProp
RMSprop 是由 Geoff Hinton 在他 Coursera 课程中提出的一种适应性学习率方法,至今仍未被公开发表。
在下图中的误差表面中,如果考虑横轴方向,绿色箭头处坡度比较陡峭,需要较小的学习率,但是走到红色箭头处,坡度变得平坦了起来,需要较大的学习率。因此同一个参数的同个方向,学习率也是需要动态调整的,于是就有了一个新的方法———RMSprop (Root Mean Squared propagation)。RMSprop方法解决AdaGrad的学习率缩减问题。
![](https://i-blog.csdnimg.cn/direct/c1701d66ab954f16a9992454abd80907.png)
RMSprop第一步跟Adagrad的方法相同,即:
RMSProp通过可以决定,
相较于之前存在
,里面的
,
,……,
的重要性有多大。如果使用RMSprop,就可以动态调整
这一项。上图中黑线是误差表面,球就从A走到B,AB段的路很平坦,
很小,更新参数的时候,我们会走比较大的步伐。走动BC段后梯度变大了,AdaGrad 反应比较慢,而RMSprop 会把
设小一点,让新的、刚看到的梯度的影响比较大,很快地让
的值变大,很快地让步伐变小,RMSprop可以很快地“踩刹车”。如果走到CD段,CD 段是平坦的地方,可以调整
,让其比较看重最近算出来的梯度,梯度一变小,
的值就变小了,走的步伐就变大了。
1.3 Adam
最常用的优化的策略或者优化器(optimizer)是Adam (Adaptive moment estima-tion)。Adam可以看作RMSprop 加上动量,其使用动量作为参数更新方向,并且能够自适应调整学习率。Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。PyTorch里面已经写好了Adam 优化器,这个优化器里面有一些超参数需要人为决定,但是往往用PyTorch预设的参数就足够好了。
动量(Momentum):动量方法通过将过去梯度的指数加权平均值(称为一阶矩估计)加入到当前梯度中,以平滑梯度的波动,从而加速收敛并减少震荡。在 Adam 中,一阶矩估计定义为:
其中, 是第
次迭代的一阶矩估计,
是第
次迭代的梯度,
是超参数,通常设为 0.9。
RMSprop:
RMSprop 通过将过去梯度平方的指数加权平均值(称为二阶矩估计)加入到参数更新中,以自适应地调整每个参数的学习率。在 Adam 中,二阶矩估计定义为:
其中,是第
次迭代的二阶矩估计,
是梯度的平方,
是超参数,通常设为 0.999。
偏差校正:
由于一阶矩和二阶矩估计在初始阶段会受到初始值的影响,Adam 引入了偏差校正的概念,以确保估计值的准确性。经过偏差校正的一阶矩和二阶矩估计分别为:
其中,和
分别是经过偏差校正的一阶矩和二阶矩估计。
参数更新:
使用经过偏差校正的一阶矩和二阶矩估计,参数更新公式为:
其中, 是第
次迭代的参数,
是学习率,
是一个非常小的数(如
),用来防止分母为零。
以下是一个使用Adam优化器进行深度学习模型训练的基本示例代码:
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单的神经网络模型
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(20, 64) # 输入层到隐藏层
self.fc2 = nn.Linear(64, 64) # 隐藏层
self.fc3 = nn.Linear(64, 1) # 隐藏层到输出层
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = torch.sigmoid(self.fc3(x))
return x
# 实例化模型
model = SimpleNet()
# 定义损失函数和优化器
criterion = nn.BCELoss() # 二分类问题的损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器,设置学习率为0.001
# 假设我们有一些随机生成的数据
X_train = torch.rand(100, 20) # 100个样本,每个样本20个特征
y_train = torch.randint(0, 2, (100, 1)).float() # 100个样本的标签,二分类问题
# 训练模型
num_epochs = 10
for epoch in range(num_epochs):
for i in range(len(X_train)):
# 前向传播
outputs = model(X_train[i])
loss = criterion(outputs, y_train[i])
# 反向传播和优化
optimizer.zero_grad() # 清空过往梯度
loss.backward() # 反向传播计算梯度
optimizer.step() # 根据梯度更新参数
# 打印每个epoch的损失
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 测试模型
with torch.no_grad(): # 测试时不需要计算梯度
correct = 0
total = 0
for i in range(len(X_train)):
outputs = model(X_train[i])
_, predicted = torch.max(outputs.data, 1)
total += y_train[i].size(0)
correct += (predicted == y_train[i]).sum().item()
print(f'Accuracy of the network on the 100 training samples: {100 * correct / total} %')
2.学习率调度
上面第一个图简单的误差表面,我们都训练不起来,加上自适应学习率以后,使用AdaGrad 方法优化的结果如下图所示。一开始优化的时候很顺利,在左转的时候,有 AdaGrad 以后,可以再继续走下去,走到非常接近终点的位置。走到 BC 段时,因为横轴方向的梯度很小,所以学习率会自动变大,步伐就可以变大,从而不断前进。接下来的问题走到图中红圈的地方,快走到终点的时候突然“爆炸”了。是把过去所有的梯度拿来作平均。在 AB段梯度很大,但在 BC 段,纵轴的方向梯度很小,因此纵轴方向累积了很小的
,累积到一定程度以后,步伐就变很大,但有办法修正回来。因为步伐很大,其会走到梯度比较大的地方。走到梯度比较大的地方后,
会慢慢变大,更新的步伐大小会慢慢变小,从而回到原来的路线。
通过学习率调度可以解决这个问题。之前的学习率调整方法中 是一个固定的值,而在学习率调度中
跟时间有关,如下面式子所示。学习率调度中最常见的策略是学习率衰减,也称为学习率退火。
3.分类
3.1 分类与回归的关系
回归和分类是机器学习中两种基本的预测方法,它们的本质区别在于输出变量的类型。回归问题输出的是连续的数值,分类问题输出的是有限的、离散的类别标签。两者都是监督学习的一部分,都依赖于带有标签的训练数据来学习模型。
回归是输入一个向量,输出
,我们希望
跟某一个标签
越接近越好,
是要学习的目标。而分类可当作回归来看,输入
后,输出仍然是一个标量
,要让它跟正确答案的那个类越接近越好。
是一个数字,我们可以把类也变成数字。如上图所示,类1是编号1,类2是编号2,类3是编号3,
跟类的编号越接近越好。但该方法在某些状况下会有问题,假设类1、2、3有某种关系。比如根据一个人的身高跟体重,预测他的年级,一年级、二年级还是三年级。一年级跟二年级关系比较近,一年级跟三年级关系比较远。用数字来表示类会预设1和2有比较近的关系,1和3有比较远的关系。但假设三个类本身没有特定的关系,类1是1,类2是2类3是3。这种情况,需要引入独热向量来表示类。实际上,在做分类的问题的时候,比较常见的做法也是用独热向量表示类。
如果有三个类,标签 就是一个三维的向量,比如类 1 是
,类 2 是
,类3是
。如果每个类都用一个独热向量来表示,就没有类 1 跟类 2 比较接近,类 1 跟类 3 比较远的问题。如果用独热向量计算距离的话,类两两之间的距离都是一样的。
如果目标是一个向量,比如
是有三个元素的向量,网络也要输出三个数字才行。如下图所示,输出三个数值就是把本来输出一个数值的方法,重复三次。把
、
和
乘上三个不同的权重,加上偏置,得到
;再把
、
和
乘上另外三个权重,再加上另外一个偏置得到
;把
、
、
再乘上另外一组权重,再加上另外一个偏置得到
。输入一个特征向量,产生
、
、
跟目标越接近越好。
3.2 带有softmax的分类
按照上述的设定,分类实际过程是:输入 ,乘上
,加上
,通过激活函数
,乘上
,再加上
得到向量
。但实际做分类的时候,往往会把
通过 softmax 函数得到
,才去计算
跟
之间的距离。
Softmax函数是一种在多类分类问题中常用的激活函数,特别是在神经网络的输出层。它的主要作用是将一个向量或一组实数输入转换成概率分布,使得每一个元素的值都在0到1之间,并且所有元素的和为1。这使得softmax函数非常适合用来处理多类分类问题中的输出,因为它可以直接被解释为属于各个类别的概率。
softmax的计算式子如下:
上图是softmax 的块,输入、
和
,产生
、
、
。比如,取完指数的时候,exp(3) = 20、exp(1) = 2.7 和 exp(−3) = 0.05,做完归一化后,就变成 0.88、0.12 跟 0。−3取完指数,再做归一化以后,会变成趋近于 0 的值。所以 softmax 除了归一化,让
、
和
,变成 0 到 1 之间,和为 1 以外,它还会让大的值跟小的值的差距更大。
上图案例中考虑了三个类的状况,两个类也可以直接套softmax函数。但一般有两个类的时候,我们不套softmax,而是直接取 sigmoid。当只有两个类的时候,sigmoid和 softmax是等价的。
3.6.3 分类损失
当我们把 输入到一个网络里面产生
后,通过 softmax 得到
,再去计算
跟
之间的距离
,如下图所示:
交叉熵更常用,当跟
相同时,可以最小化交叉嫡的值,此时均方误差也是最小的。最小化交叉嫡其实就是最大化似然(maximize likelihood)。计算式如上(Cross-entropy)。
做分类时,选均方误差的时候,如果没有好的优化器,有非常大的可能性会训练不起来。如果用Adam,虽然上图(左)中圆圈的梯度很小,但Adam 会自动调大尝率,还有机会走到右下角,不过训练的过程比较困难。总之,改变损失函数可以改变优化的难度。
4.实践(HW3(CNN)卷积神经网络-图像分类)
step 1 准备算力
链接:https://university.aliyun.com/mobile?userCode=1h9ofupt
完成认证
step 2 一键运行代码
1. 获得的数据集和代码文件
创建一个实例,启动,然后点击进入JupyterLab。接着,点击“Terminal”打开命令行窗口,输入以下代码并按下回车键。稍等片刻,数据集和代码文件(notebook)将会自动下载,大约需要一分钟。
git clone https://www.modelscope.cn/datasets/Datawhale/LeeDL-HW3-CNN.git
2.一键运行代码
点击打开LeeDL-HW3-CNN
文件夹
等待约12分钟后即可获得结果。通过单元格(cell)查看模型的训练准确率。生成的`submission.csv`文件包含分类结果,可提交至Kaggle进行评估。
下面是运行结果:
step 3 关闭实例