2026-10-29 梯度下降到底在做什么
数据是 100 个点,它们背后其实是 y = 3x + 2 加了一点噪声。但程序不知道 3 和 2,它只能从数据里一点点逼近。我想看清楚"逼近"这个动作具体发生在哪一步上。
一、先说清楚在优化什么
猜一组 (w, b),用它算预测值,跟真实 y 比,误差平方求平均:
loss(w, b) = (1/N) · Σ (w·x_i + b - y_i)^2
这是个凸函数,形状像一个碗。w 和 b 是这个碗的坐标,loss 是高度。训练就是从这个碗的某个点走到碗底。
怎么走?求导。对 w 和 b 分别求偏导:
∂L/∂w = (2/N) · Σ (w·x_i + b - y_i) · x_i
∂L/∂b = (2/N) · Σ (w·x_i + b - y_i)
梯度指向"上坡"方向,所以往反方向走一步:
w ← w - η · ∂L/∂w
这个 η 就是学习率。
二、跑起来是这样
初始 w = b = 0,也就是"什么都不知道",这时候 loss 是 345.91。
=== lr=0.01 时,loss 每 100 步的样子 ===
第 1 步 loss = 345.9084 w = 2.0603 b = 0.3315
第 101 步 loss = 4.0161 w = 3.1519 b = 1.0310
第 201 步 loss = 3.9065 w = 3.1010 b = 1.3539
第 301 步 loss = 3.8634 w = 3.0691 b = 1.5564
第 401 步 loss = 3.8465 w = 3.0491 b = 1.6835
第 501 步 loss = 3.8398 w = 3.0365 b = 1.7632
第 601 步 loss = 3.8372 w = 3.0286 b = 1.8132
第 901 步 loss = 3.8356 w = 3.0186 b = 1.8766
真实值是 w=3、b=2。跑完 w 到 3.0186,b 到 1.8766。
有一条要特别注意:第 1 步 w 就从 0 冲到 2.06,而 b 才从 0 挪到 0.33。同样一个学习率,w 一次就走了 b 要走上百步的距离。
三、为什么 w 和 b 不一样快
看梯度公式:w 的梯度里有个 x_i 乘子,b 的梯度里没有。
这批数据 x 在 0 到 10 之间,mean(x^2) = 31.10。也就是说 w 方向上的"斜率"被 x 放大了三十倍,所以它一步能走很远;b 方向的斜率小,每步都挪不了多少。
这件事的后果是:你设一个让 w 舒服的学习率,b 就会太慢;给 b 加速,w 就冲过头来回震荡。
第 101 步时 w 是 3.1519,已经冲过 3 了,而 b 还在 1.03 爬。这就是两个方向尺度不同带来的典型现象。
实际训练里为什么要做特征归一化,根源就在这儿——把所有特征拉到同一个尺度,w 和 b 才能用一个学习率同时跑好。
四、学习率不是"学习效率",是"每步走多远"
上面那张表里我用了 0.001 到 0.03,都收敛。换成 0.05:
第 1 步 loss = 345.91
第 2 步 loss = 1638.03
第 3 步 loss = 7817.59
第 4 步 loss = 37371.13
第 7 步 loss = 4087321.08
第 10 步 loss = 447084496.36
第 12 步 loss = 10225639312.01
每一步都在变成上一步的 4 倍多。12 步之后已经过百亿,再跑下去就是 inf。
这不是"学得慢"或"学得快"的问题,是直接崩了。0.05 的时候,学习率比我第一次以为的合理值大太多——我一开始凭直觉选了 0.1 和 0.5,结果全是 nan。
五、那条线其实能算出来
发散不是因为运气差。这个碗的形状由二阶导决定:
loss 对 w 的二阶导 = 2·mean(x^2) = 62.20
所以 lr 超过 2 / 62.20 = 0.0322 就会越过谷底、越走越远
实测结果正好卡在这条线两边:lr = 0.03 收敛(勉强),lr = 0.05 发散。
这条 2/曲率 的规则是梯度下降的硬约束:学习率的上限由数据决定,不由你的感觉决定。 x 的取值越大,曲率越大,能用的学习率就越小。这也是为什么归一化之后学习率可以用得更大——曲率被压小了。
六、和闭式解对一下
一元线性回归其实有精确解:w = cov(x,y)/var(x),b = ȳ - w·x̄。跑出来:
真实值 w = 3.0000 b = 2.0000
闭式解 w = 3.015326 b = 1.897399 loss = 3.835456
梯度下降 w = 3.015326 b = 1.897399 loss = 3.835456
差 w 差 1.64e-11,b 差 1.04e-10
差到小数点后十位。也就是说梯度下降最后确实走到了碗底——只是它是一步一步走过去的,而闭式解是一步算出来的。
那为什么还要用梯度下降?因为这个例子有闭式解,绝大多数问题没有。神经网络几百万个参数,那个碗是高维的、算不出精确解,只能走。
七、结论
梯度下降在做的事情三句话能说完:算误差、求梯度、往反方向挪一步。循环。
真正需要记住的是那个 η。它不是"学习效率"这种模糊的东西,是每步的物理步长。太大直接跨过谷底飞出去,太小要走几千步才到。而它能取多大,由数据的曲率决定,可以由二阶导算出来,不是调参玄学。
至于 w 和 b 走得不一样快——那不是 bug,是两个方向的曲率不同。这正是特征归一化存在的理由。
附:核心代码
def gd(x, y, lr, epochs):
w, b = 0.0, 0.0
hist = []
for _ in range(epochs):
pred = w * x + b
err = pred - y
loss = float(np.mean(err ** 2))
gw = float(2 * np.mean(err * x)) # ∂L/∂w
gb = float(2 * np.mean(err)) # ∂L/∂b
w -= lr * gw
b -= lr * gb
hist.append((loss, w, b))
if not np.isfinite(loss) or loss > 1e12:
break
return w, b, hist
浙公网安备 33010602011771号