「PyTorch深度学习入门」5. 学习的机制(中)

来源 | Deep Learning with PyTorch

作者 |  Stevens, et al.

译者 | 杜小瑞

校对 | gongyouliu

编辑 | auroral-L

全文共3602字,预计阅读时间25分钟。

第五章  学习的机制(中)

 

4.  沿梯度往下走

    4.1  减少损失

    4.2  进行分析

    4.3  通过迭代拟合模型

    4.4  输入规范化

    4.5  再次可视化

4. 沿梯度往下走


我们将使用梯度下降算法优化关于参数的损失函数。在本节中,我们将根据第一原理建立梯度下降的直觉,这在将来对我们有很大帮助。正如我们提到的,有一些方法可以更有效地解答我们的例子,但这些方法不适用于大多数深度学习任务。梯度下降实际上是一个非常简单的想法,它可以很好地扩展到具有数百万个参数的大型神经网络模型。

21a21c007afdbb8f901a3b985cfbee54.png

图 5.5 优化过程的卡通描述,其中一个人用w和b的旋钮搜索使损失减少的方向转动旋钮

让我们从一个心理图像开始,我们在图5.5中很方便地勾勒出了它。假设我们在一台有两个旋钮的机器前面,标有w和b。我们可以在屏幕上看到损失的值,我们被告知要最小化这个值。由于不知道旋钮对损失的影响,我们开始摆弄它们,并为每个旋钮决定哪个方向使损失减小。我们决定将两个旋钮朝着减少损失的方向旋转。假设我们远离最佳值:我们可能会看到损失迅速减少,然后随着接近最小值而减慢。我们注意到,在某个点上,损失再次上升,所以我们反转一个或两个旋钮的旋转方向。我们还了解到,当损失变化缓慢时,最好更精细地调整旋钮,以避免达到损失回升的程度。过了一会儿,我们终于收敛到最小值。


4.1 减少损失

梯度下降和我们刚才描述的场景没有太大区别。其思想是计算每个参数的损失变化率,并朝着减少损失的方向修改每个参数。就像我们摆弄旋钮时一样,我们可以通过在w和b上加一个小数字来估计变化率,并观察该区域的损失变化:

76e2064c88e51fa11e7a856507cf95aa.png

这就是说,在w和b的值附近,w的单位增加会导致损失的一些变化。如果变化为负,那么我们需要增加w以最小化损失,而如果变化为正,我们需要减少w。那么应该变化多少呢?对w应用一个与损失变化率成比例的变化是一个好主意,特别是当损失有几个参数时:我们应该调整那些对损失产生显著变化的参数。一般来说,缓慢地改变参数也是明智的,因为在距离当前w值附近的距离处,变化率可能会有很大的不同。因此,我们通常应该微调变化率参数。这个比例因子有很多名字;我们在机器学习中使用的是学习率:

7dcfd58ba29b8d2a1a6f66b1e2bac56c.png

我们可以对参数b做同样的事情:

acd15faa6e20e8ebedf5b2ec54ac5b06.png

这表示梯度下降的基本参数更新步骤。通过重复这些调整公式(如果我们选择一个足够小的学习率),我们将收敛到一个参数的最佳值,在给定的数据上计算的损失是最小的。我们将很快展示完整的迭代过程,但是我们刚刚计算变化率的方法相当粗糙,需要在继续之前进行升级。让我们看看原因和方法。

4.2 进行分析

通过对模型和损失的重复评估来计算变化率,以探索w和b附近的损失函数的行为,对于具有多个参数的模型来说并不能很好地进行扩展。而且,人们并不总是清楚这个范围应该有多大。在上一节中,我们选择delta等于0.1,但是作为w和b的函数,它完全取决于损失的形状。如果损失比起delta变化太快,我们就不太清楚损失在哪个方向上减少最大。

如果我们能使邻域变得无穷小,如图5.6所示,那会怎样?这正是当我们解析地取损失对一个参数的导数时所发生的情况。在一个有两个或多个参数的模型中,比如我们正在处理的那个,我们计算损失对每个参数的导数,然后把它们放在一个导数向量中,也就是梯度。

0b0a9ae35f0428cadc0ed05937d52c21.png

图 5.6 在离散位置评估下降方向时估计的下降方向与分析方向的差异

计算导数

为了计算损失对参数的导数,我们可以应用链式法则,计算损失对输入(即模型的输出)的导数,乘以模型对参数的导数: 

234f1a7f186a527ad4ca16dc76c7d118.png

回想一下,我们的模型是一个线性函数,我们的损失是平方和。让我们算出导数的表达式。回忆一下损失的定义:

d39fa386b6533fcbb54551dd259e720e.png

记住dx^2/dx=2x,我们得到

32f2f764398b47497f0d8de26287fe1e.png

将导数应用于模型

对于模型,回想一下我们的模型是

4d1c9339849918aae0f7612768f41513.png

我们得到这些导数:

dae99b77904c7eb985279bae22f9d580.png

定义梯度函数

把所有这些放在一起,返回关于w和b的损失梯度的函数是:

b562b1f65a716ad3bba952501d9d5a3a.png

用数学符号表示的相同想法如图5.7所示。同样,我们对所有数据点进行平均(即,求和并除以一个常数),以得到损失的每个偏导数对应的标量。

48ad027eb7b1d49d956d2bc977c5d9ce.png

图 5.7 损失函数对权重的导数


4.3 通过迭代拟合模型

我们现在已经准备好了优化参数的一切。从一个参数的初始值开始,我们可以迭代地对其应用更新,以获得固定的迭代次数,或者直到w和b停止更改为止。停止迭代的判定准则可以有很多种;现在,我们使用固定的迭代次数来停止。

训练循环

既然谈到这,让我们介绍另一个术语。我们将训练迭代称为一个epoch,在此过程中,我们更新所有训练样本的参数。

完整的训练循环如下所示(code/p1ch5/1_parameter_estimation.ipynb):

edb7a503f47cec80985dc409863647a0.png

本文中用于输出的实际日志逻辑更为复杂(参见同一notebook中的单元格15:http://mng.bz/pBB8)但这些差异对于理解本章的核心概念并不重要。

现在,让我们调用我们的训练循环:

6b7b7ba4ee3d71debdc949c946b4d4ba.png

过拟合

等等,发生什么事了?我们的训练过程真的失败了,导致损失成为inf。这是一个明显的迹象,表明params正在接收太大的更新,并且它们的值开始来回振荡,因为每次更新都超出了范围,下一次更是超出了范围。优化过程是不稳定的:它发散而不是收敛到最小值。我们希望看到params的更新越来越小,而不是越来越大,如图5.8所示。

187f0736ee09447c7b5a80ff810ee9cd.png

图 5.8 顶部:在凸函数(抛物线状)上由于步长太大而导致优化过程发散。底部:小步优化最终收敛

我们如何限制学习率的幅度?嗯,看起来很简单。我们可以简单地选择一个较小的学习率,事实上,学习率是我们通常会改变的事情之一,当训练结果不如我们所愿。我们通常更改学习速率的数量级,因此我们可以尝试使用1e-3或1e-4,这将按数量级减少更新的速率。让我们使用1e-4,看看它是如何工作的:

6644ea2c6185e48b0a4d575c322578c7.png 

f92bae312f986b66ccdbba716757e745.png

很好,现在行为稳定了。但还有另一个问题:参数的更新非常小,因此损失的减少非常缓慢,最终会停滞。我们可以通过使学习率自适应来避免这个问题:也就是说,根据更新的大小而改变。有一些优化方案可以做到这一点,我们将在本章末尾的第5.5.2节中看到。

然而,在更新术语中还有另一个潜在的麻烦制造者:梯度本身。让我们回头看看优化过程中epoch 1的梯度。

4.4 输入规范化

我们可以看到,第一个epoch权重的梯度大约是偏差(bias)梯度的50倍。这意味着权重和偏差存在于不同比例的空间中。如果是这种情况,一个足够大的学习率,更新第一个刚刚好,但是对第二个的更新来说就改变太大了;而对第二个更新合适的学习率又对第一个的更新太小了。这意味着,除非我们对问题的公式有所改变,否则我们将无法更新我们的参数。我们可以对每个参数有单独的学习率,但是对于有许多参数的模型来说,这太麻烦了,无法满足这么多需求。

有一个更简单的方法来控制事情:改变输入,这样梯度就不会有太大的差别。粗略地说,我们可以确保输入的范围不会离–1.0到1.0太远。在我们的例子中,我们可以通过简单地将t_u乘以0.1来获得足够接近的结果:

1af08bf60a8862d0715f320cdaaf95fe.png

这里,我们通过在变量名后面加一个n来表示t_u的规范化版本。此时,我们可以在标准化输入上运行训练循环:

ccd6467f488758f14d9493ba0c4129f2.png

即使我们将学习速率设置回1e-2,参数也不会在迭代更新期间爆炸。让我们看看梯度:它们的大小相似,所以对这两个参数使用一个单一的学习速率就可以了。我们也许可以做一个更好的标准化工作,而不是一个简单的10倍缩放,但既然这样做就解决了问题,我们先就这样处理。

注意 

这里的规范化绝对有助于训练网络,但是你可以提出一个论点,即不需要严格地为这个特定问题优化参数。那绝对是真的!这个问题很小,有很多方法可以处理参数。然而,对于更大、更复杂的问题,规范化是一种简单有效的工具(如果不是关键的话!),可以改进模型收敛性。

让我们运行循环足够多的迭代次数,以看到参数中的更改变小。我们将把n_epochs改为5000:

142cf784b4f17cc016e3db48b6ae3b11.png

很好:当我们沿着梯度下降的方向改变参数时,我们的损失减少了。它不完全归零;这可能意味着迭代的次数太少还不足以收敛到零,或者数据点不完全位于一条直线上。正如我们所预料的,我们的测量结果并不完全准确,或者读数中有噪音。

但是你看:w和b的值看起来非常像我们需要用来将摄氏度转换成华氏度的数字(考虑到我们之前将输入乘以0.1时的标准化)。精确值为w=5.5556和b=-17.7778。我们奇特的温度计一直显示华氏温度。这不是一个什么宏伟的工程,除了我们的梯度下降优化过程真的起作用了!

4.5 再次可视化

让我们重温一下我们刚开始做的事情:绘制数据。说真的,这是做数据科学的人应该做的第一件事。始终绘制数据的检查结果:

9aef3559cbbff6e4135b0f48c120510e.png

我们在这里使用一个名为argument unpacking的Python技巧:*params意味着将params的元素作为单个参数传递。在Python中,这通常是通过列表或元组来完成的,但是我们也可以使用PyTorch张量的argument unpacking,这些张量是沿着第一个维度拆分的。所以这里,model(t_un,*params)等价于model(t_un,params[0],params[1])。

此代码生成图5.9。我们的线性模型似乎是一个很好的数据模型。我们的测量结果似乎也有些不稳定。我们要么给验光师打电话要一副新眼镜,要么考虑把我们的高级温度计还给他。

8af234cd5220d61835a105364d710fa6.png

图 5.9 线性拟合模型(实线)与输入数据(圆)的关系图

剩下的内容,请继续关注「数据与智能」~

fd884e864c7793909f70966097ef3324.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据与智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值