JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

梯度下降,一步一步来

一个会学习的模型,不过是在一片由它自己的错误堆成的曲面上往下坡走。这篇就把这趟下坡之旅讲清楚——一步一个脚印,以及下山途中可能出岔子的地方。

一片由错误堆成的曲面

在上一篇里你认识了损失函数:一个数字,告诉你模型当前在训练数据上错得有多离谱。现在把模型每一个可调的参数——每一个权重偏置——都想象成一个你能拧的旋钮。把这些旋钮在它们所有的取值里扫一遍,对每一种组合都量一下损失。这就给了你一片地貌:模型错的地方它隆起、对的地方它凹陷。这就是损失曲面,而梯度下降说穿了,不过是在它上面往下坡走的一种方法。

诚实的版本更难想象,但值得说出来。一个真实的网络有数百万乃至数十亿个旋钮,所以这片地貌不是三维空间里的一座小山——它是一个数百万维空间里的曲面。没有人能看见它。但是*局部*的下坡走法,在百万维里和在山坡上是一模一样的:就在你站着的地方感觉一下哪边是下坡,然后朝那个方向迈一步。

哪边是下坡?梯度告诉你

站在雾里的山坡上,你看不见山谷——但你仍然能感觉到脚下的坡度。这种感觉的数学版本,就是梯度。对每一个旋钮,我们都问一个偏导数:如果我把*这一个*旋钮往上拧一丝丝、其余旋钮都按住不动,损失是涨还是落,又有多陡?把每个旋钮的这些答案收进一支大箭头里,你就得到了梯度——它指向*最陡的上坡*方向。想下坡,你只要朝相反方向迈步就行。

我们究竟怎样才能得到所有这些偏导数,而不必为了每个旋钮把模型重跑一百万遍?这正是反向传播的活儿,你早先已经见过它:它在一次向后的扫掠中,逐层套用链式法则,就把*每一个*权重的梯度都算了出来。梯度descent是策略——逆着梯度迈步;反向传播则是那台高效的机器,把梯度递给它好让它迈步。

# one step of gradient descent
grad = backprop(loss, params)      # arrow pointing uphill
for each p in params:
    p = p - learning_rate * grad[p]  # step the opposite way
# repeat until the loss stops dropping
整个算法就这四行:算出指向上坡的箭头,再把每个参数朝下坡方向挪一点点。

步子迈多大?学习率说了算

梯度告诉你该往哪个方向迈,但没告诉你迈多远。这个距离由一个单一却极其重要的旋钮决定:学习率。它是一个超参数——一个由*你*在训练前选定、而不是由模型学出来的设置。把梯度乘上学习率,这个乘积就是你这一步的大小。调对了,下降又快又稳;调错了,整件事会以两种截然相反的方式翻车。

太小,你就下山下得畏畏缩缩,训练慢得没有尽头——还可能远没到谷底就卡在一块平台上。太大,你就会冲过头:一跃就横跨整个山谷,落到一个*比出发点还高*的地方,接着又朝另一边冲过头,越弹越宽,直到损失炸到无穷大。最佳点是这样一步:大胆到足以取得真正的进展,又温柔到不至于一跃跳过山谷。找到它多半靠反复试错——这也正是为什么人们要盯着一条损失随时间变化的学习曲线,一眼就能看出自己正陷在哪一种翻车里。

踩着样本走,而不是踩着整张地图

这里有个现实的麻烦。要算出*真正*的梯度,你得先在整个数据集上量一遍损失,才能迈出一步。当例子有上百万条时,这一步慢得让人痛苦。所以在实践里我们会作弊——而且作得很划算。随机梯度下降每次只用少数几个例子、也就是一个小批量来估计坡度,然后踩着这个估计迈步。每一步都带点噪声,像隔着雾去读坡度,但在迈出一个真正步子的时间里,你能迈出成千上万步。

把全部训练数据完整走一遍,叫做一个轮次;训练通常要跑很多轮,每一轮里都迈出许多带噪声的小批量步子。出乎意料的是,这点噪声往往是*特性*而非缺陷:那随机的抖动能把行走者从一些浅坑里颠出来,否则它本会卡在里头。现代优化器把这一套又做了精进——动量让步子在持续的下坡方向上攒起速度,而Adam优化器会自动为每个旋钮调整步长——但它们说到底,仍然都是这同一趟下坡之走。

它何时停下——又停在哪里?

当你接近谷底,地面渐渐变平,梯度随之变小,于是你的步子自然就短了下来。当损失不再有任何实质性的下降时,我们就说训练达到了收敛。实践中没人会等梯度精确归零;进展停滞时你就停手,或者更早——早停会在模型在留出数据上的表现不再提升的那一刻喊停,这同时也防住了过拟合(这一阶后面有一篇专讲它)。

可它*停在哪里*呢?这里有个老教科书曾反复念叨的诚实陷阱。一趟下坡之走,只能找到*出发点附近*的一个低点——一个局部最小值——而无法保证那座山谷就是整片地貌里最深的那个(全局最小值)。更糟的是,训练可能在一个鞍点上慢到近乎停滞:那是个在某些方向往下、在另一些方向往上的地方,活像马鞍的座面,那里梯度几乎是平的,行走者便磨蹭起来。这些险情都归在局部最小值与鞍点名下。

接下来是真正出人意料的地方,也是低维直觉会把人带偏的地方。在大网络那数百万维的地貌里,被困在一个*糟糕*的局部最小值中,结果是很罕见的:要让一个点成为真正的最小值,地面必须在*每一个*方向上同时向上弯曲,而当方向有数百万个时,这简直不可能凑齐。大多数平坦处其实是鞍点,动量能把它滚过去;而大多数走得到的山谷,深浅其实都差不多。这正是为什么梯度下降在实践中远比当年那套忧心忡忡的理论所预言的要好用——不是因为危险消失了,而是因为高维几何比一座三维小山所暗示的要仁慈得多。

梯度下降到底承诺了什么

剥去那层神秘感,这个方法其实很谦卑:它是一个倒过来跑的爬山者,没有地图,也不记得更远处的地形,只是朝此刻感觉最下坡的方向,迈出一小步。它无法保证最好的模型,只能保证一个局部不错的模型——而且它需要损失曲面是可微的,这样你才总能读出一个坡度来。这是个实打实的限制,却是个出了名地好用的限制。几乎每一个现代模型,从线性回归到一个大语言模型,都是用这同一趟下坡之走的某种变体训练出来的。

所以下次有人说一个模型在「学习」,你现在就能精确地想象出这个词底下究竟在发生什么了。那里没有理解,没有洞见——只有一个叫损失的数字、一个叫梯度的坡度,以及数百万个朝下坡迈出的微小步子。叫人称奇的不是每一步有多聪明;而是这么多笨拙却小心、朝对的方向迈出的步子,累加起来竟能做出会翻译语言、会认出人脸的东西。这一阶后面的几篇,会展开那些为这些步子掌舵的优化器,以及惩罚走得太远的过拟合。