读完这一课,你将能够
- 对给定多元函数(如 \(x^2+xy+3y^2\))逐变量求偏导,把它们排成列向量写出梯度 \(\nabla f\)。
- 用 \(D_u f=\nabla f\cdot u=\|\nabla f\|\cos\theta\) 算方向导数,并说出梯度为何指向最速上升、\(-\nabla f\) 为何是最速下降方向。
- 给定起点、梯度和学习率,手算一步 \(w\leftarrow w-\eta\nabla f\),并验证 loss 确实下降。
- 解释梯度为何处处垂直于等高线(沿等高线 \(f\) 不变 \(\Rightarrow\) 点积为 0)。
- 区分极小、极大与鞍点,并说出梯度下降在这三类临界点都会停下。
- 解释学习率太大会震荡甚至发散、太小则收敛极慢,并知道更新一定用减号。
上一课我们用线性代数搭好了"向量与矩阵"的地基;这一课,我们把高中熟悉的导数 derivative 推广到多变量,得到机器学习里最核心的工具——梯度 gradient,它会告诉我们:在一座由参数构成的"损失之山"上,往哪个方向走才下坡最快。
1. 从一元导数说起
回忆高中的导数。对一元函数 \(f:\mathbb{R}\to\mathbb{R}\),在点 \(x\) 处的导数定义为差商的极限:
\[ f'(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}. \]它的几何意义是切线斜率 slope:当 \(x\) 增加一个极小量 \(h\),函数值大约增加 \(f'(x)\,h\)。把这句话写成式子,就是局部线性化 local linearization:
\[ f(x+h)\approx f(x)+f'(x)\,h. \]导数的本质是"放大后看局部":把曲线在一点附近不断放大,它会越来越像一条直线,这条直线的斜率就是导数。这个"曲线局部像直线、曲面局部像平面"的思想,是整门微分学(以及深度学习反向传播)的灵魂。
2. 多元函数与偏导数
机器学习里的损失函数往往依赖成千上万个参数。我们先看最朴素的多元标量函数 scalar function:
\[ f:\mathbb{R}^n\to\mathbb{R},\qquad f(x_1,x_2,\dots,x_n). \]输入是 \(n\) 个数(一个向量 \(x=[x_1,\dots,x_n]\)),输出是一个实数。例如 \(f(x,y)=x^2+3y^2\) 把平面上每个点映成一个"高度",画出来是一个碗状曲面。
怎么对它求导?关键想法:一次只动一个变量,其余当常数。这样多元就退化回一元,照搬一元导数即可。这就是偏导数 partial derivative:
\[ \frac{\partial f}{\partial x_i} =\lim_{h\to 0}\frac{f(x_1,\dots,x_i+h,\dots,x_n)-f(x_1,\dots,x_n)}{h}. \]对 \(f(x,y)=x^2+3y^2\):把 \(y\) 当常数对 \(x\) 求导得 \(\partial f/\partial x=2x\);把 \(x\) 当常数对 \(y\) 求导得 \(\partial f/\partial y=6y\)。每个偏导数衡量"只沿这一个坐标轴走"时函数变化的快慢。
3. 梯度:把所有偏导数打包成一个向量
单看一个偏导数,只知道沿某条坐标轴的陡峭程度。把所有偏导数排成一个列向量,就得到梯度 gradient,记作 \(\nabla f\)(读作 "nabla f" 或 "del f"):
\[ \nabla f(x)= \begin{bmatrix} \dfrac{\partial f}{\partial x_1}\\[4pt] \dfrac{\partial f}{\partial x_2}\\[2pt] \vdots\\[2pt] \dfrac{\partial f}{\partial x_n} \end{bmatrix}. \]注意:\(\nabla f\) 与输入 \(x\) 同形(都在 \(\mathbb{R}^n\) 里),它在不同位置取不同的值(是 \(x\) 的函数)——即空间每个点都挂着一个向量,这样的对象叫向量场 vector field。对 \(f(x,y)=x^2+3y^2\):
\[ \nabla f(x,y)=\begin{bmatrix}2x\\6y\end{bmatrix}. \]例如在点 \((1,1)\) 处,\(\nabla f=[2,6]\)。
4. 方向导数:朝任意方向走有多陡
偏导数只问了坐标轴方向。但我们想问更一般的问题:站在点 \(x\),沿任意单位方向 unit vector \(u\)(\(\|u\|=1\))走,函数上升多快?这就是方向导数 directional derivative:
\[ D_u f(x)=\lim_{h\to 0}\frac{f(x+hu)-f(x)}{h}. \]要算它,我们需要把第 1 节的"局部线性化"推广到多元。怎么推广?先固定其余变量、只让 \(x_i\) 动一点点 \(\Delta x_i\),由偏导数的定义,这一个方向贡献的函数变化约为 \(\dfrac{\partial f}{\partial x_i}\Delta x_i\)。在一阶近似下,各个坐标方向的贡献可以叠加,把它们求和:
\[ f(x+\Delta)-f(x)\approx\sum_{i=1}^{n}\frac{\partial f}{\partial x_i}\,\Delta x_i . \]而这个求和 \(\sum_i \dfrac{\partial f}{\partial x_i}\Delta x_i\),恰好就是梯度与位移的点积 \(\nabla f(x)\cdot\Delta\)。于是多元的局部线性化自然落地(把一元的 \(f'(x)h\) 换成 \(\nabla f(x)\cdot\Delta\)):
\[ f(x+\Delta)\approx f(x)+\nabla f(x)\cdot\Delta . \]这也让我们看清:\(\nabla f\) 不是凭空"打包"出来的,它正是线性化里自然冒出来的系数向量。(严格成立需要 \(f\) 可微,本课默认涉及的光滑函数都满足。)
现在取 \(\Delta=hu\) 代入,相减除以 \(h\) 再令 \(h\to 0\),立刻得到方向导数等于梯度在该方向上的点积:
\[ \boxed{\,D_u f(x)=\nabla f(x)\cdot u\,} \]换句话说,方向导数就是 \(\nabla f\) 在单位方向 \(u\) 上的标量投影 scalar projection(上一课的概念)。因为 \(\|u\|=1\),标量投影 \((\nabla f\cdot u)/\|u\|\) 正好等于点积 \(\nabla f\cdot u\)。
5. 核心定理:梯度指向最速上升方向
现在到本课的高潮。设梯度非零,把点积写成模长与夹角的形式(直接复用上一课的几何点积 \(a\cdot b=\|a\|\,\|b\|\cos\theta\),这里夹角仍用上一课的记号 \(\theta\)):
\[ D_u f=\nabla f\cdot u=\|\nabla f\|\,\|u\|\cos\theta=\|\nabla f\|\cos\theta, \]其中 \(\theta\) 是 \(u\) 与 \(\nabla f\) 的夹角(用了 \(\|u\|=1\))。\(\|\nabla f\|\) 是固定的,所以 \(D_u f\) 完全由 \(\cos\theta\) 决定:
- 当 \(\theta=0\)(\(u\) 与 \(\nabla f\) 同向):\(\cos\theta=1\),方向导数最大,等于 \(\|\nabla f\|\)。这就是最速上升方向。
- 当 \(\theta=\pi\)(\(u\) 与 \(\nabla f\) 反向):\(\cos\theta=-1\),方向导数最小(最负),等于 \(-\|\nabla f\|\)。这是最速下降方向。
- 当 \(\theta=\pi/2\)(垂直):\(\cos\theta=0\),沿该方向函数瞬时不变。
要点
- 梯度 \(\nabla f\) 指向函数上升最快的方向,其模长 \(\|\nabla f\|\) 就是这个最大上升率。
- 因此 \(-\nabla f\) 指向下降最快的方向——这正是梯度下降要走的方向。
6. 梯度与等高线正交
把曲面 \(f\) 在某高度切平,得到等高线 / 等值集 level set:所有满足 \(f(x)=c\) 的点。沿等高线移动时,函数值恒为 \(c\)、不变,所以方向导数为 0。设 \(t\) 是等高线的切向(单位向量),则
\[ D_t f=\nabla f\cdot t=0. \]点积为零意味着正交 orthogonal。于是得到一个漂亮的几何事实:
要点
梯度处处垂直于等高线,并指向函数值增大的一侧(上坡)。这就是为什么在等高线图上,梯度场总是"横穿"等高线、与之成直角。
7. 例题:梯度、方向导数、走一步梯度下降
例题
设 \(f(x,y)=x^2+xy+3y^2\)。
(a) 求梯度。 对 \(x\) 求偏导(\(y\) 当常数):注意交叉项 \(xy\) 对 \(x\) 求导时把 \(y\) 当常数,得 \(y\),所以 \(\partial f/\partial x=2x+y\);对 \(y\) 求偏导(\(x\) 当常数,交叉项 \(xy\) 对 \(y\) 求导得 \(x\)):\(\partial f/\partial y=x+6y\)。故
\[ \nabla f(x,y)=\begin{bmatrix}2x+y\\[2pt]x+6y\end{bmatrix}. \](b) 在点 \(p=(1,2)\) 求梯度。 代入:\(2(1)+2=4\),\(1+6(2)=13\)。所以
\[ \nabla f(1,2)=\begin{bmatrix}4\\13\end{bmatrix}. \](c) 在 \(p\) 处沿方向 \(d=(3,4)\) 求方向导数。 先把 \(d\) 单位化:\(\|d\|=\sqrt{3^2+4^2}=5\),故 \(u=(3/5,\,4/5)=(0.6,\,0.8)\)。则
\[ D_u f(p)=\nabla f(p)\cdot u=4(0.6)+13(0.8)=2.4+10.4=12.8. \](正数,说明沿这个方向是上坡,上升率为 12.8。)
(d) 从 \(p\) 做一步梯度下降,学习率 \(\eta=0.1\)。 更新规则 \(w\leftarrow w-\eta\nabla f(w)\):
\[ \begin{bmatrix}1\\2\end{bmatrix}-0.1\begin{bmatrix}4\\13\end{bmatrix} =\begin{bmatrix}1-0.4\\2-1.3\end{bmatrix} =\begin{bmatrix}0.6\\0.7\end{bmatrix}. \]验证函数值确实下降:\(f(1,2)=1+2+12=15\);\(f(0.6,0.7)=0.36+0.42+1.47=2.25\)。从 15 降到 2.25,一步就大幅下坡。
8. 梯度下降与学习率
把上面的"走一步"重复很多次,就是梯度下降 gradient descent:
\[ w_{k+1}=w_k-\eta\,\nabla f(w_k). \]每一步都沿当前点的最速下降方向 \(-\nabla f\) 迈出一小步,步长由学习率 learning rate \(\eta>0\) 控制。直觉上像一个盲人下山:每走到一处,用脚感受哪边最陡(梯度),然后朝最陡的下坡方向迈 \(\eta\) 那么长一步。
易错
- 梯度本身不是步长,它只给方向(和局部陡度)。真正走多远由 \(\eta\) 决定,要乘上去。
- \(\eta\) 太大:会越过谷底、在两壁间来回震荡 oscillate 甚至发散(loss 越来越大)。
- \(\eta\) 太小:每步挪一点点,收敛极慢,浪费大量计算。
- 更新要用减号。写成加号就成了"梯度上升"——朝 loss 增大的方向走,与训练目标背道而驰(对常见的无下界损失,会让 loss 不断升高)。这是初学者最常见的符号 bug。
9. 临界点:极小、极大与鞍点
梯度下降走到 \(\nabla f=0\) 的地方就停下(没有下坡方向了)。这种点叫临界点 critical point,分三类:
- 局部极小 local minimum:四面都是上坡,像碗底。梯度下降想找的就是它。
- 局部极大 local maximum:四面都是下坡,像山顶。
- 鞍点 saddle point:一些方向上坡、另一些方向下坡,像马鞍。\(f(x,y)=x^2-y^2\) 在原点就是典型鞍点。
在高维深度学习里,鞍点比局部极小多得多,是优化的主要障碍之一。区分这三类点需要看二阶信息(曲率),由下一课的海森(Hessian)矩阵刻画——下一课会先讲雅可比、再讲海森。
ML 和 ML 的联系
训练一个模型,本质就是在损失曲面上做梯度下降。模型参数 \(w\)(权重)是坐标,损失 \(L(w)\) 是高度,我们要找让 \(L\) 最小的 \(w\)。反向传播 backpropagation 高效地算出 \(\nabla L\)(即 \(\partial L/\partial w\)),优化器再执行 \(w\leftarrow w-\eta\nabla L\)。
本课记号铺垫了后续约定:我们采用分母布局 denominator layout,让 \(\partial L/\partial W\) 与参数 \(W\) 同形,这样"参数减去梯度"才在形状上对得齐——这个约定会在第 9 课正式声明。学习率 \(\eta\) 是最重要的超参数之一:调大了 loss 震荡发散,调小了训练龟速,这正是上面数学的直接后果。
10. 代码:用 numpy 最小化一个凸二次函数
我们对 \(f(x,y)=x^2+3y^2\)(梯度 \(\nabla f=[2x,\,6y]\),最小值在原点)做梯度下降,每步打印 \(x\)、\(y\) 与 loss。注意 \(y\) 方向曲率(系数 6)比 \(x\) 方向(系数 2)大得多,等高线是狭长椭圆。下面特意把学习率取在 \(\eta\in(1/6,\,1/3)\) 之间(取 \(0.3\)),这时 \(y\) 方向的迭代因子 \(1-6\eta=-0.8\) 为负,\(y\) 分量会正负交替、来回震荡着收敛,而 \(x\) 方向(因子 \(1-2\eta=0.4\))平滑收敛——这就是经典的之字形 zig-zag 轨迹。
import numpy as np
# f(x,y) = x^2 + 3 y^2 , 最小值在 (0,0)
def f(w):
return w[0]**2 + 3.0 * w[1]**2
def grad(w):
return np.array([2.0 * w[0], 6.0 * w[1]])
w = np.array([4.0, 4.0]) # 起点
eta = 0.3 # 学习率(落在 (1/6, 1/3) 内,y 方向会之字形震荡)
print("step | x y | loss")
for k in range(12):
L = f(w)
print(f"{k:3d} | {w[0]: .5f} {w[1]: .5f} | {L: .6f}")
w = w - eta * grad(w) # 核心一行:朝 -梯度 走一步
print("final w =", w, " final loss =", f(w))
你会看到 \(y\) 列在 \(4\to-3.2\to2.56\to-2.048\to\dots\) 之间正负跳动、幅度缓慢衰减,而 \(x\) 列平滑趋于 0——典型的之字形。把 eta 改成 0.34 试试:此时 \(y\) 方向因子 \(|1-6\times0.34|=1.04>1\),\(y\) 会一边变号一边发散(每步放大 1.04 倍,loss 越来越大);改成 0.05 则两个方向都平滑、但收敛得很慢。亲手感受学习率的威力。
调一调,观察现象
下面三个小实验都基于本课已验证的函数与公式,改一个数、跑一下、对照"预期现象",亲手把抽象的梯度变成能看见的东西。三段都只用 numpy 与 print,几秒就能跑完。
实验 1:改学习率,看收敛 / 之字形 / 发散
改什么:对 \(f(x,y)=x^2+3y^2\)(梯度 \([2x,6y]\)),把 \(\eta\) 依次设成 0.05、0.3、0.34,起点都取 \((4,4)\)。
预期看到:\(\eta=0.05\) 两个方向都平滑下降,但 12 步后终点 \(x\) 仍在 1.13 附近、离原点还很远(loss≈1.29,收敛慢);\(\eta=0.3\) 时 \(y\) 列在 \(4\to-3.2\to2.56\to-2.05\to\dots\) 正负交替、幅度缓慢衰减(之字形,loss≈0.23);\(\eta=0.34\) 时 \(y\) 列同样正负跳动,但每步绝对值越跳越大(\(4\to-4.16\to4.33\to\dots\)),loss 飙到上百(发散,约 123)。
为什么:\(y\) 方向每步乘以因子 \(1-6\eta\):\(\eta=0.05\) 得 \(+0.70\)(同号、慢慢缩小),\(0.3\) 得 \(-0.80\)(变号但 \(|{\cdot}|<1\),震荡着收敛),\(0.34\) 得 \(-1.04\)(变号且 \(|{\cdot}|>1\),震荡着发散)。曲率大的方向决定了学习率的上限。
import numpy as np
def f(w): return w[0]**2 + 3.0*w[1]**2
def grad(w): return np.array([2.0*w[0], 6.0*w[1]])
for eta in [0.05, 0.3, 0.34]:
w = np.array([4.0, 4.0])
ys = []
for k in range(12):
ys.append(round(float(w[1]), 3))
w = w - eta * grad(w)
print(f"eta={eta}: y轨迹={ys}")
print(f" final w={np.round(w,4)} loss={f(w):.4f}")
print(f" y方向因子 1-6eta={1-6*eta:+.2f}\n")
实验 2:沿不同方向取方向导数,看梯度方向最大
改什么:在点 \((1,1)\) 处对 \(f=x^2+3y^2\)(梯度 \([2,6]\)),分别沿"与梯度同向、反向、垂直"三个特殊方向算 \(D_u f=\nabla f\cdot u\),再让单位方向 \(u\) 绕一圈采样。
预期看到:同向时方向导数最大,等于 \(\|\nabla f\|\approx6.3246\);反向时最小,约 \(-6.3246\);垂直时几乎为 0(数值上是 1e-16 量级的舍入误差)。绕圈采样里没有任何采样角恰好对准梯度(梯度方向约 71.6°),所以采样到的最大值落在最接近梯度的 60°(≈6.20),由此能直观看出峰就在梯度方向附近。
为什么:\(D_u f=\|\nabla f\|\cos\theta\),整条曲线就是一条余弦:峰在同向、谷在反向、零在垂直,这正是"梯度指向最速上升"的直接体现。
import numpy as np
g = np.array([2.0, 6.0]) # (1,1) 处的梯度
gn = np.linalg.norm(g) # ≈ 6.3246
print("||grad|| =", round(gn, 4))
ug = g / gn # 沿梯度方向(同向,单位向量)
uperp = np.array([-ug[1], ug[0]]) # 与梯度垂直的方向
print("同向 Du =", f"{g.dot( ug): .4f}", " == +||grad||")
print("反向 Du =", f"{g.dot(-ug): .4f}", " == -||grad||")
print("垂直 Du =", f"{g.dot(uperp): .2e}", " (数值 ~0)")
print("-- 绕一圈采样,看峰在梯度方向附近 --")
for deg in [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]:
t = np.deg2rad(deg)
u = np.array([np.cos(t), np.sin(t)]) # 单位方向
print(f"角度{deg:>3}度: Du = {g.dot(u): .4f}")
实验 3:用差商逼近偏导,看 h 越小越准
改什么:不用解析公式,对 \(f(x,y)=x^2+xy+3y^2\) 在 \((1,2)\) 处用差商 \((f(x+h,y)-f(x,y))/h\) 近似 \(\partial f/\partial x\),让 \(h\) 取 1e-1、1e-3、1e-5。
预期看到:近似值逐步逼近解析答案 \(2x+y=4\):\(h=0.1\) 时恰为 4.1,\(h=10^{-3}\) 时为 4.001,\(h=10^{-5}\) 时为 4.00001,误差与 \(h\) 同阶地缩小(依次约 1e-1、1e-3、1e-5)。
为什么:偏导数本就是 \(h\to0\) 的差商极限,"一次只动一个变量"让多元退化回一元导数;缩小 \(h\) 就是在数值上逼近这个极限。(此处误差恰好等于 \(h\),是因为这个二次函数沿 \(x\) 的二阶项让前向差商多出一个 \(h\) 的线性偏差。)
import numpy as np
def f(x, y): return x*x + x*y + 3*y*y
x, y = 1.0, 2.0
analytic = 2*x + y # ∂f/∂x = 2x+y = 4
for h in [1e-1, 1e-3, 1e-5]:
approx = (f(x+h, y) - f(x, y)) / h
print(f"h={h:.0e}: 差商={approx:.6f} 误差={abs(approx-analytic):.2e}")
print("解析 ∂f/∂x =", analytic)
动手练习
- 手算梯度。 对 \(f(x,y)=2x^2+y^2-xy\),求 \(\nabla f\),并在点 \((1,1)\) 求出梯度向量。再求沿 \(u=(0,1)\) 的方向导数。(验证:\(\nabla f=[4x-y,\,2y-x]\),在 \((1,1)\) 为 \([3,1]\),方向导数 \(=1\)。)
- 验证正交。 对 \(f(x,y)=x^2+y^2\),等高线是圆。在 \((3,4)\) 处求梯度,并验证它与该点处圆的切向量正交(提示:圆在该点的切向与半径垂直,半径方向就是 \((3,4)\))。
- 改代码做一步对比。 用下面骨架,比较 \(\eta=0.1\) 与 \(\eta=0.5\) 走一步后的 loss,看谁下降更多、谁可能反而上升。
import numpy as np def f(w): return w[0]**2 + 3.0*w[1]**2 def grad(w): return np.array([2.0*w[0], 6.0*w[1]]) w0 = np.array([1.0, 1.0]) for eta in [0.1, 0.5]: w1 = w0 - eta * grad(w0) print(f"eta={eta}: w0->w1 = {w0} -> {w1} , loss {f(w0):.4f} -> {f(w1):.4f}") - 找鞍点。 对 \(f(x,y)=x^2-y^2\),求 \(\nabla f\) 并解 \(\nabla f=0\)。从 \((0.01,\,0.01)\) 跑十步梯度下降(\(\eta=0.1\)),观察 \(y\) 分量是被吸引还是被排斥,体会鞍点的不稳定。
- 数值偏导。 不用解析公式,用差商 \(\big(f(x+h,y)-f(x,y)\big)/h\)(取 \(h=10^{-5}\))近似 \(\partial f/\partial x\),与解析梯度对比误差,验证偏导数的定义。
掌握自检
- 我能说出偏导数的定义,并对 \(x^2+xy+3y^2\) 正确写出 \(\nabla f\)。
- 我能解释"为什么梯度指向最速上升方向"——并写出 \(D_u f=\|\nabla f\|\cos\theta\) 这一步。
- 我能说清为什么梯度与等高线正交(沿等高线 \(f\) 不变 \(\Rightarrow\) 点积为 0)。
- 给定点、梯度和学习率,我能手算一步 \(w\leftarrow w-\eta\nabla f\) 并验证 loss 下降。
- 我能解释学习率太大/太小各自的后果,并知道更新用减号。
- 我能区分极小、极大、鞍点,并知道梯度下降在它们处都会停。
下一课,我们把导数从"标量函数"推广到向量值函数:当输出也是一个向量时,所有偏导数排成一个矩阵,就是雅可比 Jacobian;而损失曲面的二阶曲率(用来区分极小与鞍点)则由海森 Hessian 矩阵刻画。
可以先放过的点
- 多元局部线性化里"各坐标方向贡献可叠加"那一步,依赖 \(f\) 可微的严格条件。现在只需相信光滑函数成立,不必去抠可微的精确定义,真要细究可放到以后看分析教材。
- 如何用二阶曲率区分极小、极大、鞍点——本课只让你认得这三类点,具体怎么判别要等下一课《雅可比与海森》讲完海森矩阵再回来,那时自然水到渠成。
- 第 10 节"之字形震荡"里 \(1-6\eta\)、\(1-2\eta\) 这些迭代因子的推导,背后是把对角二次型逐分量解开。看不透公式没关系,先把代码跑一遍、用眼睛确认 \(y\) 列正负跳动即可。
- "分母布局"这个记号约定只是提前打个招呼,第 9 课才正式声明。这里不必纠结布局,记住"参数减梯度、形状要对齐"的直觉就够了。