Datawhale X 李宏毅苹果书 AI夏令营Task2笔记

1.自适应学习率

很多时候训练陷入瓶颈,损失不再下降,不是因为陷入了局部最小值,大部分情况下是在局部最小值附近震荡(学习率太大),这个时候应该通过降低学习率的方式,从而能够继续往最小值方向下降。

举个例子,我们有两个参数\omegab,这两个参数值不一样的时候,损失值也不一样,得到了下图(上)所示的误差表面,该误差表面的最低点在叉号处。事实上,该误差表面是凸的形状。凸的误差表面的等高线是椭圆形的,椭圆的长轴非常长,短轴相比之下比较短,其在横轴的方向梯度非常小,坡度的变化非常小,非常平坦;其在纵轴的方向梯度变化非常大,误差表面的坡度非常陡峭。现在我们要从黑点(初始点)来做梯度下降。

学习率\eta =10^{-2}的结果如下图(下左)所示。参数在峡谷的两端,参数在山壁的两端不断地“震荡”,损失降不下去,但是梯度仍然是很大的。我们可以试着把学习率设小一点,学习率决定了更新参数的时候的步伐,学习率设太大,步伐太大就无法慢慢地滑到山谷里面。调学习率从10^{-2}调到10^{-7}的结果如下图(下右)所示,参数不再“震荡”了。参数会滑到山谷底后左转,但是这个训练永远走不到终点,因为学习率已经太小了。AB段的坡度很陡,梯度的值很大,还能够前进一点。左拐以后,BC段的坡度已经非常平坦了,这种小的学习率无法再让训练前进。事实上在BC段有10万个点(10万次更新),但都无法靠近局部最小值,所以显然就算是一个凸的误差表面,梯度下降也很难训练。

这个例子说明了不同的参数的梯度变化可能会有显著不同,所以不应该对所有参数用同一个学习率。同时,在训练的不同阶段,学习率也可以有所不同,比如早期学习率可以更大,后期学习率可以变小。

因此,在梯度下降里面,所有的参数都是设同样的学习率,显然是不够的,应该要为每一个参数定制化学习率,即引入自适应学习率(adaptive learning rate)的方法,给每一个参数不同的学习率。如下图所示,如果在某一个方向,梯度的值很小,非常平坦,我们会希望学习率调大一点;如果在某一个方向上非常陡峭,坡度很大,我们会希望学习率可以设的小一点。

总而言之,自适应学习率(Adaptive Learning Rate)是深度学习中一种重要的优化策略,它能够根据训练过程中的动态变化自动调整学习率。这种策略可以提高训练效率,加快收敛速度,并有助于模型在训练过程中跳出局部最小值或鞍点。 

1.1 AdaGrad

AdaGrad (Adaptive Gradient)是典型的自适应学习率方法,其能够根据梯度大小自动调整学习率。AdaGrad可以做到梯度比较大的时候,学习率就减小,梯度比较小的时候,学习率就放大。

AdaGrad 使用和累积梯度平方来调整每个参数的学习率。

累积梯度平方和公式:G_t = G_{t-1} + g_t^2

学习率计算公式:\eta_t = \frac{\eta_0}{\sqrt{G_t + \epsilon}}

1.2 RMSProp

RMSprop 是由 Geoff Hinton 在他 Coursera 课程中提出的一种适应性学习率方法,至今仍未被公开发表。

在下图中的误差表面中,如果考虑横轴方向,绿色箭头处坡度比较陡峭,需要较小的学习率,但是走到红色箭头处,坡度变得平坦了起来,需要较大的学习率。因此同一个参数的同个方向,学习率也是需要动态调整的,于是就有了一个新的方法———RMSprop (Root Mean Squared propagation)。RMSprop方法解决AdaGrad的学习率缩减问题。

AdaGrad的问题

 RMSprop第一步跟Adagrad的方法相同,即:\sigma _{0}^{i} =\sqrt{({g_{0}^{i} })^2 } =\left | g_{0}^{i} \right |

RMSProp通过\alpha可以决定,g_{t}^{i}相较于之前存在\sigma _{t-1}^{i},里面的g_{1}^{i}g_{2}^{i},……,g_{t-1}^{i}的重要性有多大。如果使用RMSprop,就可以动态调整\sigma _{t}^{i}这一项。上图中黑线是误差表面,球就从A走到B,AB段的路很平坦,g很小,更新参数的时候,我们会走比较大的步伐。走动BC段后梯度变大了,AdaGrad 反应比较慢,而RMSprop 会把 \alpha设小一点,让新的、刚看到的梯度的影响比较大,很快地让\sigma _{t}^{i} 的值变大,很快地让步伐变小,RMSprop可以很快地“踩刹车”。如果走到CD段,CD 段是平坦的地方,可以调整\alpha,让其比较看重最近算出来的梯度,梯度一变小,\sigma _{t}^{i}的值就变小了,走的步伐就变大了。 

1.3 Adam

最常用的优化的策略或者优化器(optimizer)是Adam (Adaptive moment estima-tion)。Adam可以看作RMSprop 加上动量,其使用动量作为参数更新方向,并且能够自适应调整学习率。Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。PyTorch里面已经写好了Adam 优化器,这个优化器里面有一些超参数需要人为决定,但是往往用PyTorch预设的参数就足够好了。

动量(Momentum):动量方法通过将过去梯度的指数加权平均值(称为一阶矩估计)加入到当前梯度中,以平滑梯度的波动,从而加速收敛并减少震荡。在 Adam 中,一阶矩估计定义为:

m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t

其中, m_t是第 t 次迭代的一阶矩估计,g_t是第t次迭代的梯度,\beta_1 是超参数,通常设为 0.9。

RMSprop

RMSprop 通过将过去梯度平方的指数加权平均值(称为二阶矩估计)加入到参数更新中,以自适应地调整每个参数的学习率。在 Adam 中,二阶矩估计定义为:

v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2

其中,v_t是第 t次迭代的二阶矩估计,g_t^2 是梯度的平方,\beta_2是超参数,通常设为 0.999。

偏差校正

由于一阶矩和二阶矩估计在初始阶段会受到初始值的影响,Adam 引入了偏差校正的概念,以确保估计值的准确性。经过偏差校正的一阶矩和二阶矩估计分别为:

\hat{m}_t = \frac{m_t}{1 - \beta_1^t}

\hat{v}_t = \frac{v_t}{1 - \beta_2^t}

其中,\hat{m}_t\hat{v}_t分别是经过偏差校正的一阶矩和二阶矩估计。

参数更新

使用经过偏差校正的一阶矩和二阶矩估计,参数更新公式为:

\theta_t = \theta_{t-1} - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t

其中, \theta_t是第t 次迭代的参数,\eta 是学习率,\epsilon是一个非常小的数(如10^{-8}),用来防止分母为零。

以下是一个使用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} %')
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

2.学习率调度

上面第一个图简单的误差表面,我们都训练不起来,加上自适应学习率以后,使用AdaGrad 方法优化的结果如下图所示。一开始优化的时候很顺利,在左转的时候,有 AdaGrad 以后,可以再继续走下去,走到非常接近终点的位置。走到 BC 段时,因为横轴方向的梯度很小,所以学习率会自动变大,步伐就可以变大,从而不断前进。接下来的问题走到图中红圈的地方,快走到终点的时候突然“爆炸”了。\sigma _{t}^{i}是把过去所有的梯度拿来作平均。在 AB段梯度很大,但在 BC 段,纵轴的方向梯度很小,因此纵轴方向累积了很小的\sigma _{t}^{i},累积到一定程度以后,步伐就变很大,但有办法修正回来。因为步伐很大,其会走到梯度比较大的地方。走到梯度比较大的地方后,\sigma _{t}^{i}会慢慢变大,更新的步伐大小会慢慢变小,从而回到原来的路线。

 通过学习率调度可以解决这个问题。之前的学习率调整方法中\eta 是一个固定的值,而在学习率调度中 \eta 跟时间有关,如下面式子所示。学习率调度中最常见的策略是学习率衰减,也称为学习率退火。

3.分类

3.1 分类与回归的关系

回归和分类是机器学习中两种基本的预测方法,它们的本质区别在于输出变量的类型。回归问题输出的是连续的数值,分类问题输出的是有限的、离散的类别标签。两者都是监督学习的一部分,都依赖于带有标签的训练数据来学习模型。

回归是输入一个向量x,输出\hat{y},我们希望 \hat{y}跟某一个标签 y越接近越好,y 是要学习的目标。而分类可当作回归来看,输入 x 后,输出仍然是一个标量 \hat{y},要让它跟正确答案的那个类越接近越好。\hat{y} 是一个数字,我们可以把类也变成数字。如上图所示,类1是编号1,类2是编号2,类3是编号3,\hat{y}跟类的编号越接近越好。但该方法在某些状况下会有问题,假设类1、2、3有某种关系。比如根据一个人的身高跟体重,预测他的年级,一年级、二年级还是三年级。一年级跟二年级关系比较近,一年级跟三年级关系比较远。用数字来表示类会预设1和2有比较近的关系,1和3有比较远的关系。但假设三个类本身没有特定的关系,类1是1,类2是2类3是3。这种情况,需要引入独热向量来表示类。实际上,在做分类的问题的时候,比较常见的做法也是用独热向量表示类。

如果有三个类,标签y 就是一个三维的向量,比如类 1 是[1,0,0]^{T},类 2 是 [0,1,0]^{T},类3是[0,0,1]^{T}。如果每个类都用一个独热向量来表示,就没有类 1 跟类 2 比较接近,类 1 跟类 3 比较远的问题。如果用独热向量计算距离的话,类两两之间的距离都是一样的。

如果目标y是一个向量,比如 y 是有三个元素的向量,网络也要输出三个数字才行。如下图所示,输出三个数值就是把本来输出一个数值的方法,重复三次。把a_{1}a_{2}a_{3}乘上三个不同的权重,加上偏置,得到\hat{y_{1} };再把a_{1}a_{2}a_{3}乘上另外三个权重,再加上另外一个偏置得到 \hat{y_{2} };把a_{1}a_{2}a_{3}再乘上另外一组权重,再加上另外一个偏置得到 \hat{y_{3} }。输入一个特征向量,产生 \hat{y_{1} }\hat{y_{2} }\hat{y_{3} }跟目标越接近越好。

3.2 带有softmax的分类

按照上述的设定,分类实际过程是:输入 x,乘上W,加上 b,通过激活函数 \sigma,乘上W^{'},再加上 b^{'}得到向量 \hat{y}。但实际做分类的时候,往往会把\hat{y}通过 softmax 函数得到 y^{'},才去计算 y^{'}\hat{y} 之间的距离。 

Softmax函数是一种在多类分类问题中常用的激活函数,特别是在神经网络的输出层。它的主要作用是将一个向量或一组实数输入转换成概率分布,使得每一个元素的值都在0到1之间,并且所有元素的和为1。这使得softmax函数非常适合用来处理多类分类问题中的输出,因为它可以直接被解释为属于各个类别的概率。

softmax的计算式子如下:

上图是softmax 的块,输入y_{1}y_{2}y_{3},产生y_{1}^{'}y_{2}^{'}y_{3}^{'}。比如,取完指数的时候,exp(3) = 20、exp(1) = 2.7 和 exp(−3) = 0.05,做完归一化后,就变成 0.88、0.12 跟 0。−3取完指数,再做归一化以后,会变成趋近于 0 的值。所以 softmax 除了归一化,让 y_{1}^{'}y_{2}^{'}y_{3}^{'},变成 0 到 1 之间,和为 1 以外,它还会让大的值跟小的值的差距更大。

上图案例中考虑了三个类的状况,两个类也可以直接套softmax函数。但一般有两个类的时候,我们不套softmax,而是直接取 sigmoid。当只有两个类的时候,sigmoid和 softmax是等价的。 

3.6.3 分类损失

当我们把 x 输入到一个网络里面产生 \hat{y} 后,通过 softmax 得到 y^{'},再去计算 y^{'}y之间的距离 e,如下图所示:

 交叉熵更常用,当\hat{y}y^{'}相同时,可以最小化交叉嫡的值,此时均方误差也是最小的。最小化交叉嫡其实就是最大化似然(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 关闭实例

  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值