损失函数是一个曲面,参数是曲面上的一个点。梯度指向上升最快的方向,所以下降就是沿负梯度走。 一步走多远由学习率决定:太小挪不动,太大越过谷底来回跳、甚至发散。 稳定的界线是学习率小于 2 除以曲率。 实测一个谷底在 w=2 的二次函数:学习率 0.05 走十步到 1.302643,0.3 到 1.999790, 0.9 到 1.785252,1.1 直接飞到 -10.383473。
上一节定下了「要最小化什么」,这一节讲「怎么往下走」。 这一节假设梯度已经拿到了,4.7 节才回答「梯度怎么算得快」。 4.2 节讲的激励函数导数在这里第一次起作用:它决定损失曲面有多陡、有多平。
一、梯度到底是什么
把每个一阶偏导排成一个向量,就是梯度。f(w₁, w₂) 的梯度是 (∂f/∂w₁, ∂f/∂w₂)。 它的几何意义有两层:方向是函数值增加最快的那个方向,长度是那个最快的增长率。所以沿 -∇f 走, 函数值下降最快。 这个方法的名字因此也叫最速下降法,台版教材里的「梯度法」是同一件事。 注意它只用了一阶信息。 曲率(二阶导)完全没进来,所以它不知道前面是谷底还是悬崖。所有「走得慢、来回跳」的毛病, 根源都在这里。
二、沿负方向走
更新式一行就写完:w ← w - η∇f(w)。η 是学习率。 在狭长的谷地里,这个式子会走出「之」字:峡谷横截面很陡、纵向很平,梯度几乎垂直于谷底方向, 于是左右横跳、往前只挪一点点。 动量法就是为这个发明的——把历史梯度累加成一个「速度」,横向的来回分量互相抵消,纵向的分量越积越大。
三、学习率:一步走多远
拿最简单的二次函数 f = ½a·w² 试一下就清楚了。梯度是 a·w,更新后 w ← w(1 - ηa)。 要让 w 收敛到 0,必须 |1 - ηa| < 1,也就是 0 < η < 2/a。 a 就是 f 的二阶导数。所以判据写成一般形式:η < 2 / f''。 Demo 里那个函数 f'' = 2,界线正好是 1。 学习率 1.1 只超了 0.1,结果从「收敛到 2」变成「飞到 -10.38」。 发散不是慢慢变坏,是每步乘一个大于 1 的因子,指数放大。 多维时取最陡方向的曲率,也就是 Hessian 的最大特征值,条件变成 η < 2/λ_max。 深度网络的 λ_max 往往很大,这就是初始学习率通常设得很小的原因。 实践中 λ_max 不可知,所以大家用「先试 0.1,损失不降就除以十」。 Adam、RMSProp 这类方法则在替你自动调,按历史梯度的大小缩放步长。
四、为什么会卡住:局部极小与鞍点
梯度为零的点有三种:局部极小、局部极大、鞍点。 高维空间里鞍点远比局部极小多。要让所有方向都朝上(局部极小),得每一维都恰好为正曲率; 只要有一维是负的,那个点就是鞍点。 所以深层网络的难点不是「陷进局部极小」,而是「在鞍点附近长时间爬不出来」——那里梯度很小, 走得像蜗牛,但又不是走不出去。 另一种卡住叫平台:梯度极小但不为零,sigmoid 的饱和区就是典型。 这也解释了 ReLU 加 Adam 为什么稳:一个不平坦,一个按历史梯度放大步长。
五、随机梯度下降
每次只用一小批样本估梯度,所以拿到的是「带噪声的真梯度」。 噪声通常被当成缺点,但它让点有机会从鞍点附近被推出去。 名字里的「随机」指的是抽样随机,不是方向随机。方向仍然由梯度决定。 学习率一般随轮数衰减(比如每十轮乘 0.1):前期大步找方向,后期小步收精度。
六、动手 Demo:往哪个方向走、一步走多远
拿一个最简单的二次函数 f(w) = (w-3)² + 2w 练手。 先用中心差分验证解析梯度,再换四个学习率各走十步,看它们分别停在哪儿。
def f(x):
return (x - 3.0) * (x - 3.0) + 2.0 * x
def g(x):
return 2.0 * (x - 3.0) + 2.0
h = 1e-5
num = (f(0.0 + h) - f(0.0 - h)) / (2 * h)
print("解析 %.4f 数值 %.4f 差 %.2e" % (g(0.0), num, abs(g(0.0) - num)))
for lr in (0.05, 0.3, 0.9, 1.1):
x = 0.0
for _ in range(10):
x = x - lr * g(x)
print("lr=%.2f 10 步后 w=%.6f f=%.6f" % (lr, x, f(x)))
实跑输出:
--------------------------------------------------------------------
梯度法:往哪个方向走、一步走多远
--------------------------------------------------------------------
f(w) = (w-3)² + 2w,在 w = 0 处:解析梯度 -4.0000,数值梯度 -4.0000,差 1.15e-10
(这个函数的谷底在 w = 2,f 的最小值 4。)
学习率 10 步后的 w f(w) 状态
-----------------------------------------
0.05 1.302643 5.486307 没到
0.30 1.999790 5.000000 收敛
0.90 1.785252 5.046117 没到
1.10 -10.383473 158.350400 没到
**学习率 0.05 走得太慢,10 步才挪到 1.302643;0.3 最干脆,一步比一步近;**
**0.9 已经在谷底两侧来回跳了,10 步还没停稳;1.1 干脆越跳越远,直接发散。**
为什么 1.1 会炸?这个函数的二阶导数是 2,梯度下降稳定的条件是
**学习率小于 2 除以二阶导数**,也就是小于 1。1.1 越过了这条线。
这条规律推广到多维就是:**学习率要小于 2 除以损失曲面在最陡方向上的曲率。**
深度网络里这个曲率没法直接算,所以大家用「先试 0.1,不行就除十」这种土办法。
第一行是「对拍」:解析 -4.0000,数值 -4.0000,差 1.15e-10。 这个差小到只受浮点精度限制,说明求导那一步没写错。 顺手看清这个函数:f = w² - 6w + 9 + 2w = w² - 4w + 9 = (w-2)² + 5。 谷底在 w=2,不在 3。展开一遍就看得出来,别被 (w-3)² 里那个 3 骗了。 四个学习率分成四档:0.05 到 1.302643(十步才走了一半路)、0.3 到 1.999790(基本到位)、 0.9 到 1.785252(在谷底两侧来回跳,十步还没停稳)、1.1 到 -10.383473(发散)。 分界线就是 η < 2/f''。这个函数 f'' = 2,所以界线是 1,1.1 越界。 换个说法:学习率不是「越大越快」,过了界线之后越大越糟。 多维时把 f'' 换成 Hessian 的最大特征值。 深度网络里这个数常常在几十上百,所以 1/(λ_max) 这个量级才是能用的上限,不是 1。
30 秒实验
- 把学习率改成 1.0,走十步会停在哪?(提示:刚好踩在界线上,不会收敛也不会发散)
- 把函数换成 f(w) = (w-3)⁴ + 2w,在 w=2 附近的 f'' 是多少?稳定上限变了吗?
- 用 0.3 的学习率走 100 步,w 会精确等于 2 吗?为什么?
常见误区
- 以为梯度就是下降方向。梯度是上升最快的方向,下降方向是它的相反数。 符号搞反,损失会一路涨——而且涨得很有规律,容易被误当成「学习率太大」。
- 以为学习率越大收敛越快。这只在界线以内成立。 越过 2/f'' 之后每一步都在放大误差,看起来像在抖,实际上是在爆。
- 以为卡住就是陷进局部极小。高维里更常见的是鞍点和平台。判断办法是看梯度模:接近 0 才算卡住; 梯度还挺大却在打转,那是狭长谷地的问题。
自测三问(点开对答案)
1. 梯度指向哪个方向? —— 函数值增加最快的方向。它的长度是那个最快的增长率。所以要让函数值变小,得沿梯度的相反方向走。 2. 学习率的上限由什么决定? —— 2 除以曲率。二次函数就是 2/f'';多维就是 2 除以 Hessian 的最大特征值。 超过这条线,每步都会把误差放大。 3. SGD 的噪声为什么反而有用? —— 在鞍点上真梯度是 0,梯度法就此停住; 但一小批样本估出来的梯度通常不是 0,这个噪声能把点从鞍点附近推出去。梯度法假设梯度是白给的。 可一个网络有几十万个参数, 逐个做数值微分要跑几十万次前向——下一节的反向传播,一次前向加一次反向就能拿到全部梯度。
- ☐ (本节完,下一节 4.7 反向传播算法)