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
posted @ 2026-10-29 11:28  呱哒  阅读(17)  评论(0)    收藏  举报