Skip to content

第 11 章 多元函数与偏导数 ​

学习目标 ​

  • 理解二元函数及其几何意义
  • 掌握偏导数的定义与计算
  • 理解全微分与近似计算
  • 理解方向导数与梯度
  • 掌握多元链式法则
  • 会用 PyTorch 计算偏导与梯度

11.1 二元函数:输入变成一对 ​

前面研究的函数,输入是一个数 。可现实里很多量取决于两个或更多输入。比如:

  • 房间里的温度 取决于位置:左右 、前后 ,;
  • 山坡的海拔 取决于经纬度:;
  • 机器的成本取决于产量和原料价格。

二元函数:对每一对输入 ,按照确定规则给出唯一输出 ,记作 。、 叫自变量, 叫因变量。

几何上,二元函数是一张曲面:在 平面上取一点 ,向上(或向下)量出高度 ,所有这样的点组成一张曲面。

二元函数曲面

python
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
    if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
        font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False

import numpy as np

fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection="3d")

x = np.linspace(-2, 2, 60)
y = np.linspace(-2, 2, 60)
X, Y = np.meshgrid(x, y)
Z = X ** 2 + Y ** 2

ax.plot_surface(X, Y, Z, cmap="viridis", alpha=0.9)
ax.contour(X, Y, Z, zdir="z", offset=0, cmap="viridis", alpha=0.5)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z = f(x, y)")
ax.set_title("z = x² + y²:一张碗形曲面")
fig.savefig("book/public/figs/fig11-surface.png", dpi=120, bbox_inches="tight")
plt.close(fig)

曲面上的「高度」可以用等高线压缩到平面:同一条等高线上的点,函数值相等,就像地图上的海拔线。

11.2 偏导数:固定一个变量,只看另一个 ​

二元函数在一点有多「陡」?方向太多了:沿 方向、沿 方向、沿任何斜方向……所以先把方向限定为坐标轴方向。

偏导数:把 当作常数、只对 求导,得到 对 的偏导数;把 当作常数、只对 求导,得到对 的偏导数:

记号:、,或 、(偏导数用圆体 ,区别于一元导数 )。

求 的偏导数:

求偏导和一元求导完全一样,只是每次只动一个变量。验证:

python
import numpy as np

def f(x, y):
    return x ** 2 * y

h = 1e-6
x, y = 2.0, 3.0
fx = (f(x + h, y) - f(x - h, y)) / (2 * h)
fy = (f(x, y + h) - f(x, y - h)) / (2 * h)
print("fx(2,3) 数值 ≈", fx, " 公式 2xy =", 2 * x * y)
print("fy(2,3) 数值 ≈", fy, " 公式 x²  =", x ** 2)
text
fx(2,3) 数值 ≈ 12.000000000789157 公式 2xy = 12.0
fy(2,3) 数值 ≈ 4.000000000559112 公式 x²  = 4.0

11.3 偏导数的几何意义 ​

的几何意义:在点 处,沿 轴方向切曲面一刀,得到一条截线,这条截线在 处的切线斜率就是 。 同理,是沿 轴方向截线的斜率。

换句话说:偏导数回答「只往东走,坡度是多少」「只往北走,坡度是多少」,两个方向分开看。

11.4 全微分 ​

两个方向同时动一点,函数值变化多少?设 、 都很小,由「各自贡献相加」:

写成微分形式:

这叫全微分。它是一元微分「」的推广:每个自变量贡献一项「偏导数 × 自变量的变化」。

例:,在 处, 增加 0.01、 减少 0.02:

实际变化 ,与全微分近似 0.04 只差约 0.0005。

python
def f(x, y):
    return x ** 2 * y

x, y = 2.0, 3.0
dx, dy = 0.01, -0.02

dz_approx = 2 * x * y * dx + x ** 2 * dy
dz_exact = f(x + dx, y + dy) - f(x, y)
print("全微分近似:", dz_approx)
print("实际变化  :", dz_exact)
text
全微分近似: 0.039999999999999994
实际变化  : 0.03949799999999648

11.5 方向导数与梯度 ​

只沿坐标轴不够。任意方向 (单位向量)上的变化率,叫方向导数。可以证明:

把偏导数排成向量,得到梯度:

方向导数可以写成梯度与方向向量的点积:

由点积公式 ( 是两个向量的夹角),方向导数最大当 ,即 与 同向。所以:

梯度的方向是函数上升最快的方向;梯度的大小是这个方向的变化率。

这解释了为什么爬山要「对着最陡的方向走」,也预告了第 13 章机器学习里梯度的核心地位。

梯度与等高线

python
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
    if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
        font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False

import numpy as np

x = np.linspace(-2, 2, 300)
y = np.linspace(-2, 2, 300)
X, Y = np.meshgrid(x, y)
Z = X ** 2 + Y ** 2

fig, ax = plt.subplots(figsize=(6.5, 5.5))
cs = ax.contour(X, Y, Z, levels=[0.5, 1, 2, 4, 6, 8], colors="tab:blue")
ax.clabel(cs, inline=True, fontsize=9)

# 梯度向量:指向外,即上升最快的方向
for px, py in [(-1.2, -1.2), (0, 1.3), (1.2, 0.8), (-0.6, 1.6), (1.4, -0.4)]:
    gx, gy = 2 * px, 2 * py
    scale = 0.18
    ax.arrow(px, py, gx * scale, gy * scale,
             head_width=0.08, color="tab:red", length_includes_head=True)
ax.annotate("梯度方向 = 上升最快", (0.4, -1.5), color="tab:red")

ax.set_xlim(-2.2, 2.2)
ax.set_ylim(-2.2, 2.2)
ax.set_aspect("equal")
ax.grid(True, linestyle=":", alpha=0.5)
ax.set_title("z = x² + y²:等高线与梯度(红色箭头)")
fig.savefig("book/public/figs/fig11-gradient.png", dpi=120, bbox_inches="tight")
plt.close(fig)
python
import numpy as np

x, y = 1.0, 2.0
grad = np.array([2 * x, 2 * y])
u = np.array([1 / np.sqrt(2), 1 / np.sqrt(2)])

Du = np.dot(grad, u)
print("梯度 ∇f =", grad)
print("方向导数(沿 45° 方向) =", Du)
text
梯度 ∇f = [2. 4.]
方向导数(沿 45° 方向) = 4.242640687119285

沿梯度方向 的方向导数最大,是 。

11.6 多元链式法则 ​

若 ,而 、 都随 变化,则 对 的变化率是两条路径贡献之和:

直觉:沿每条路径,「对 的敏感度 × 自身的变化」,两条路径加起来。这正是神经网络反向传播的雏形:损失函数对参数的导数,要沿着计算路径逐层相乘再相加(第 14 章展开)。

例:,,,则

11.7 用 PyTorch 计算梯度 ​

PyTorch 的 backward() 自动算梯度,和手算一致:

python
import torch

x = torch.tensor(1.0, requires_grad=True)
y = torch.tensor(2.0, requires_grad=True)
z = x ** 2 + y ** 2
z.backward()

print("z =", z.item())
print("∂z/∂x =", x.grad.item())
print("∂z/∂y =", y.grad.item())
text
z = 5.0
∂z/∂x = 2.0
∂z/∂y = 4.0

常见错误 ​

错误写法/理解原因
求 时把 也当变量一起求导偏导数的定义就是固定 、只动
把 与 混用偏导数用 ,一元导数用
梯度方向当成「下降最快」梯度是上升最快的方向;下降最快是负梯度方向
全微分漏掉某一项有几个自变量就有几项,每项是「对应偏导 × 变化量」
把方向导数 里的 忘掉归一化方向向量必须是单位向量,否则结果随长度变化

章末练习 ​

基础

  1. 求 的 、。
  2. 求 的偏导数。
  3. 求 在 处的梯度。

提高

  1. 用全微分近似计算:在 处, 增加 0.02、 减少 0.01, 变化多少?
  2. 求 、、 的 。

挑战

  1. 证明方向导数 中,当 与 同向时取得最大值 。
  2. 用 PyTorch 求 在 、 处的梯度,并与手算比较。

章末自测 ​

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. 二元函数 的图像是?
    • A. 一条曲线
    • B. 一张曲面
    • C. 一个平面点集
    • D. 一根数轴
  2. 求 时,正确的做法是?
    • A. 当常数,只对 求导
    • B. 当常数,只对 求导
    • C. 对 、 同时求导
    • D. 先对 积分
  3. ,则 是?
    • A.
    • B.
    • C.
    • D.
  4. ,则 是?
    • A.
    • B.
    • C.
    • D.
  5. 偏导数的几何意义是?
    • A. 曲面某方向的截线斜率
    • B. 曲面面积
    • C. 等高线长度
    • D. 函数的最大值
  6. 全微分 等于?
    • A.
    • B.
    • C.
    • D.
  7. 梯度的方向是?
    • A. 函数下降最快的方向
    • B. 函数上升最快的方向
    • C. 等高线方向
    • D. 轴方向
  8. 在 处的梯度是?
    • A.
    • B.
    • C.
    • D.
  9. 方向导数公式是?
    • A.
    • B.
    • C.
    • D.
  10. 多元链式法则 等于?
    • A.
    • B.
    • C.
    • D.