「AI 工程地基」系列第八篇。啃的是 GitHub 58k star 的开源课 ai-engineering-from-scratch,Rohit Ghumare 写的,一课一课来。今天第 08 课:优化,梯度下降家族,Build 型,75 分钟。前两课攒下的导数和梯度今天正式上场——知道哪边更陡之后,怎么下山。
想象你站在浓雾里的山谷中。脚下的坡度你能感觉到,这就是梯度,它免费告诉你哪个方向更陡。但你看不见整座山。方向不用想:往梯度反方向走。真正要命的问题是步子迈多大。这门课的测试函数叫 Rosenbrock,一条香蕉形的窄谷,谷底在 (1,1),起点定在 (-1,1)。我拿课程的代码原样跑了一遍:学习率 0.001,五千步后损失 0.0036,稳稳在谷里;学习率 0.005,第十步 x 的数值爆到 10 的 52 次方,Python 当场抛 OverflowError 崩溃。差五倍的步子,一边是答案,一边是天文数字的尸体。方向是免费的,步子才要命。
最朴素的走法一行就写完:权重减去学习率乘梯度。全程只靠脚底板的坡度感,从不看地图。在 Rosenbrock 上这就不够了。这条谷有个狠毒的几何性质:横切谷壁的方向极陡,顺着谷底走的方向极平,两个方向的曲率差了两千五百倍——曲率就是坡度变化有多猛。同一个标量的学习率,伺候不了差两千五百倍的两个方向。后面所有花样都从这句不满出发。
第一个花样叫动量。小球滚下山,不会在每个坑洼处停下重来。动量把过去的梯度累加成一个速度:每步的速度等于九成旧速度加新梯度,权重按速度走。来回震荡的方向自己抵消,方向一致的分量越滚越快。窄谷里之字形爬行的毛病,就这么治。
第二个花样叫 Adam。它给每个权重记两本账:一本是梯度的滑动平均,记方向;一本是梯度平方的滑动平均,记大小。更新时用第一本除以第二本的开方。这个除法的效果我实测了一把:梯度从 0.28 涨到 4.4,步子的长度几乎不变,始终贴着学习率 0.01 走。陡处自动小步,平处自动大步——更准确说,它把「步子跟着坡度缩放」这件事整个拆掉了,只留方向和恒定步长。
三个优化器同场竞技,起点 (-1,1),目标损失降到 10⁻⁶:Adam 用了约一千步。SGD 加动量用了一万五千步。纯梯度下降配 0.0005 的学习率,两万步都没到,五千步时损失 0.041,人还在半山腰。
过一遍海关。先说干净的部分。Rosenbrock 的梯度公式,我用数值差分逐点核对,误差在 10⁻⁹ 以下。Step 5 的预期输出三条——Adam 最快、动量更顺、vanilla 慢——实测全部成立。Adam 的公式跟 Kingma & Ba 2014 原文逐行对上,epsilon 的位置在根号外面,和原文一致。余弦退火的公式对,一百万个权重对应一百万零一维的损失地形,这个算术也对。
第一处不对,是这句:「合适的学习率没有公式」。有,只是用不起。数学上,二次函数上梯度下降收敛的门槛是学习率小于 2 除以最大曲率。在课程自己的起点 (-1,1) 上,最大曲率 1001.6,算出阈值 0.001997。课程练习 1 让你扫 0.0001 到 0.01 五档:0.001 收敛,0.005 发散,边界正好卡在这条公式上。课程用自己的练习演示了公式,正文却宣布公式不存在。真话的完整版是:公式需要曲率信息,真实网络算不起,所以只能实验。「没有公式」是「公式太贵」的口语省略。
第二处,发散的死法值得看清。学习率 0.005 时 x 的轨迹:-1.0、-0.98、-1.04、-0.78、-1.50、3.42、-61、4.6 万、-2×10¹⁷、1.5×10⁵²。前五步真是「在两壁之间来回弹」,第六步开始就不是震荡了,是爆炸——每步幅度乘一个大于一的系数,几何级数增长,十步炸穿 float64。损失序列更直观:4、4.1、5.5、26、274、8982、1.4×10⁹、4.6×10²⁴。顺带一个坑:正文示意图里「刚好」那档写的是 lr=0.01——在课程自己的 Rosenbrock 上,0.01 九步就炸,能活的是 0.001。示意图里的数字是别家函数的数,不是这门课自己的数。
第三处,动量的学习率被静默砍了五倍,课程没解释。纯 GD 配 0.0005,SGD 加动量配 0.0001。账在这:速度会累加,β=0.9 时稳态速度是单步梯度的十倍,等效步长等于十倍学习率乘梯度。0.0001 的十倍正好落回 0.001 的量级。我做了对照实验:GD 配 0.001 和 SGD 加动量配 0.0001,五千步后的损失 0.00363 对 0.00356,几乎同一个点。所以这不是调参技巧,是守恒关系:加了动量,学习率的含义就变了,不同步缩,等效步长翻十倍。课程把这条藏在了超参数的选择里。
第四处在练习 4,诚实跑出来的结果和直觉相反。给纯 GD 加 0.999 的指数衰减:不加衰减五千步损失 0.041,加了衰减反而 2.11,慢五十倍。原因是 0.999 的五千次方只剩 0.0067,学习率还在半山腰就被掐灭。衰减是给「长途已走完、只剩末端微调」的场景准备的工具,而 Rosenbrock 的五千步全程都在长征路上。这条练习会亲手教你课程没写的话:日程是后期工具,不是全程工具。
最后是口号审计。这课开篇的口号:「训练一个神经网络,不过是找一条山谷的底。」藏了两件事。第一,你走的是训练损失这座山,你真正关心的是测试损失,那是另一座山。两座山不重合。课程结尾讲尖谷底泛化差、平谷底泛化好,其实就是在搭这两座山之间的桥,但没把话挑明:你优化的函数,不是你关心的函数。第二,这节课自己的演示里 Adam 赢得干脆,快十五倍;结尾的结论却是 SGD 加动量常在最终精度上赢 Adam,理由是噪声防尖谷。两句话都对,但住在两个世界——演示是确定性梯度,一座山自己当自己的考官;结论说的是带 mini-batch 噪声的真实训练。赢的理由(噪声),恰恰是演示里不存在的东西。顺带半句:尖谷泛化差本身是假说不是定理,2017 年有论文证明换个参数化方式,尖谷能变平谷。课程引的 Li 2018 那篇,实际贡献是损失地形的可视化方法。
练习 2 的答案可以先剧透一半,因为实测结果比题目本身有意思。动量 0.99 配 0.0001 的学习率,1957 步就到损失 10⁻⁸;动量 0.9 同样的学习率要两万步。0.99 的等效步长是 0.9 的十倍,按直觉该更危险,实测反而又快又稳:第 500 步冲过头到 (1.22, 1.48),第 1000 步荡回 (1.03, 1.05),第 2000 步停在谷底,损失 2.4×10⁻⁹。就停在这三个坐标上。
讨论回复
加载中...正在加载回复...
推荐
智谱 GLM-5 已上线
我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。