学习的机制:从开普勒的椭圆到梯度下降
这一章讲三件事: 「机器会学习」这件事拆开来到底有哪几个零件;梯度下降是怎么一小步一小步把参数挪到位的;以及它怎么会翻车——书里把两次翻车都原样演给你看。 它是全书的地基:后面所有章——卷积、Transformer(靠「回头看前文」生成文本的结构,第 10 章拆)、扩散、肺癌项目——内部跑的都是这一章这台机器。
1. 一份 1609 年的数据科学手册
书里开场不讲神经网络,讲开普勒1。
1600 年前后,开普勒拿到他老师第谷·布拉赫几十年肉眼观测行星的数据(肉眼,记在纸上),花了六年盯数据、试形状,最后选定椭圆——行星轨道是椭圆,太阳在一个焦点上。他没有牛顿万有引力可用(牛顿反而是踩着他的工作),没有微积分,没有计算机。
书里把他的过程总结成七步,并评论:这就是一份 1609 年的数据科学手册1:
① 拿到好数据(还费了一番周折)
② 拼命画图看数据,因为直觉告诉他「有猫腻」
③ 选一个有可能拟合数据的最简模型(椭圆)
④ 把数据切成两半:一半用来干活,一半留着
⑤ 给椭圆的偏心率和大小编个初值,迭代修正到拟合
⑥ 拿留着的那半数据做验证
⑦ 「难以置信地回头看了一眼」(书里的原文步骤)
图说:四百年后我们做的事,一步不多。
第 ④ 步和第 ⑥ 步会在第 05 章变成「训练集/验证集」——留一手数据不参与调整、专供最后验收,这个念头是开普勒的。
从开普勒带走的定义是本章的承重墙2:
模型 = 一个带若干未知参数的函数;学习 = 从数据里把参数估出来。 这本书里,「拟合数据」和「让算法从数据里学」是同一句话。
开普勒的椭圆是「为一个问题量身定做的模型」;神经网络的不同在于它是一个通用模型——不预设形状,什么关系都能逼近,代价是:开普勒能说出「为什么是椭圆」,神经网络说不出「为什么是这个数」。这个代价会在第 06 章展开。
2. 主走查开工:一支不显示单位的温度计
现在把这套流程跑一遍,全章用同一个例子3:
你旅游带回来一支漂亮的挂墙温度计,唯一的毛病是它不显示单位。你的计划:同时记录它的读数和一支正常温度计的摄氏度,攒成数据集,然后「学」出两者的换算关系。
攒了两周,11 对读数(单位未知的那列叫 t_u,摄氏那列叫 t_c)3:
t_u(未知读数) 35.7 55.9 58.2 81.9 56.3 48.9 33.9 21.8 48.4 60.4 68.4
t_c(摄氏度) 0.5 14.0 15.0 28.0 11.0 8.0 3.0 -4.0 6.0 13.0 21.0
先画图(第 ② 步):点有噪声、跳来跳去,但大致排成一条直线。于是选模型(第 ③ 步):t_c = w × t_u + b——一条直线,w 是斜率、b 是截距。这两个数就是参数:w 管「输入变化一格,输出变几格」,b 管「输入全为零时输出是多少」4。这两个名字——权重(weight)和偏置(bias)——会跟你到全书最后一章。
再定损失函数(第 01 章介绍过:给「离答案多远」打分的函数):每个样本的预测减真值、平方、求平均——这个分数叫均方误差(「误差」就是预测和真值的差距)。平方有两个好处:正负误差都变成正的;而且错得越远罚得越狠5。
零件齐了。w=1, b=0 起步,预测 t_p = t_u,损失算出来是 1763.885。现在的问题是:怎么调 w 和 b,让 1763.88 往下掉?
3. 梯度:每个参数 该往哪边挪
先建立直觉:旋钮机
想象面前有台神秘机器,两个旋钮 w 和 b,屏幕上一个数:损失。你的玩法自然是6:把 w 往右拧一点——损失涨了,拧回来往左——降了,好;两个旋钮都找到「好方向」,一起拧;越接近最优,每一步拧得越小;拧过头损失会反弹,就退回来。这个试错的骨架,就是梯度下降。 它不靠猜,靠的是算出「往哪边是降」。
笨办法:数值扰动
「往哪边降」可以用蛮力量出来:把 w 加一个小量 delta=0.1 算一次损失,减 0.1 再算一次,两差除以 0.2——得到「w 附近,损失对 w 的变化率」7。这个「变化率」就是梯度(gradient):损失这个函数在某个参数方向上的坡度。坡朝上,参数就往下调;坡朝下就往上调;调的幅度和坡度成正比,再乘一个小系数——学习率(learning rate),它管每一步迈多大。
升级:解析求导
数值扰动有两个毛病:每个参数要额外算两遍模型(几百万个参数就几百万遍),而且 delta 取多大没有标准。正解是微积分的链式法则:损失 = 均方(预测),预测 = w·t_u + b,那么「损失对 w 的导数」可以一层层拆开写成解析式——对我们的线性模型,推出来是8:
d损失/d预测 = 2 × (t_p − t_c) / N (每个样本一项)
d预测/dw = t_u (模型的导数)
d损失/dw = d损失/d预测 × t_u,对全部样本求和
一次代入,所有参数的变化率同时算出——不用扰动、不用猜 delta。这就是「反向传播(backpropagation,导数从损失出发、沿计算的逆方向一路乘回每个参数)」最朴素的样子。 它现在是手写的;第 05 章你会看到 PyTorch 把这件事自动化(autograd)。
一个名词:epoch
顺手续一个后面天天见的词:一轮(epoch)指把整个训练集完整过一遍。我们这个小数据集每轮就是一个批次;大数据集要切成很多小批次,一轮里有好多步更新9。
4. 翻车现场一:步子大了,直接起飞
开跑:学习率 1e-2,一百轮。结果10:
Epoch 1 Loss 1763.88 Params: [-44.17, -0.83] Grad: [4517, 83]
Epoch 2 Loss 5,802,485 Params: [2568, 45] Grad: [-261257, -4599]
Epoch 3 Loss 19,408,035,840 ...
...
Epoch 11 Loss inf Params: [-1.86e19, -3.27e17]
图说:第一步迈得太大,冲过了谷底冲到对面山坡更高处;
下一步梯度更大、步子更大——每步都在过度补偿,振荡放大到溢出。
损失不降反升到无穷大,这叫发散(diverge)。 直觉解释:梯度大的地方坡陡,该小步走;步子大到一步跨过谷底,落点比起点还高,下一步梯度更陡、步更大——雪崩。书里的图是一个抛物线上左右横跳、越跳越远的点10。
药方一:学习率砍到 1e-4。这次稳了——损失从 1763 降到 29.0 不再动,但看参数更新量:每轮只挪一丁点,收敛(逐步逼近最低点、损失不再明显下降)是收敛了,慢得令人发指11。学习率是「训练能不能成」的第一旋钮;它不由数据学出来,是人设的——这类「管训练怎么训」的设定叫超参数(hyperparameter),和模型参数相对:参数是学出来的,超参数是定的12。
5. 翻车现场二:两个参数住在不同尺 度的世界
回头看第一轮的两个梯度:w 的梯度 4517,b 的梯度 83——差 50 多倍13。
这就是病根:输入 t_u 的数值范围是 21–82,而 b 直接加在输出上。梯度 = 损失变化率 × 输入,输入大,梯度天然大。结果是:对学习率,w 嫌大(发散),b 嫌小(不动)——一个学习率伺候不了两个尺度。
药方二不动学习率,动数据:把输入归一化——t_un = 0.1 × t_u,让输入大致落在 −1 到 1 附近(第 03 章说的「网络喜欢 0 到 1」现在有了具体含义)。再跑,学习率回到 1e-2:两个梯度变成 −77.6 和 −10.6,同一个量级,训练又稳又快14。
这一节值得背下来:归一化输入不是洁癖,是让「一个学习率管所有参数」成立的前提。 书里特意加了一段注:这个小问题其实不归一化也能硬解,但大模型上,「归一化是改善收敛最省事也最关键的工具」14。