Skip to content

第 5 章 导数 ​

学习目标 ​

  • 由平均变化率取极限,得到导数的定义
  • 理解导数的几何意义(切线斜率)与物理意义(瞬时速度)
  • 掌握常数、幂函数的求导公式
  • 掌握和差、乘积、商与链式法则
  • 会用 PyTorch 自动求导验证

5.1 导数的定义 ​

第 2 章和第 3 章铺垫了完整的工具链:平均变化率 极限 瞬时变化率。现在正式命名。

导数(derivative):函数 在点 处的导数定义为

如果这个极限存在,就说 在 处可导。记号:除了 ,也写作 (读作「d y 比 d x」),其中 。两种记号含义相同:

几何意义:切线斜率。 就是曲线 在点 处切线的斜率。

物理意义:瞬时变化率。若 是路程、 是时间, 就是瞬时速度。

把定义用起来,求 在任意点 的导数:

每一步都有依据:展开平方;同类项 相消;提取公因式 后约分();最后 时 。结论:

验证:在 处,,与第 2 章数值表一致。

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

for h in [1.0, 0.1, 0.01, 0.001]:
    avg_rate = (f(2 + h) - f(2)) / h
    print(f"h = {h:<6} 平均变化率 = {avg_rate:.4f}")
text
h = 1.0    平均变化率 = 5.0000
h = 0.1    平均变化率 = 4.1000
h = 0.01   平均变化率 = 4.0100
h = 0.001  平均变化率 = 4.0010

导数的几何意义

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, axes = plt.subplots(1, 2, figsize=(12, 4.6))

# 左图:f(x) = x² 及其切线
x = np.linspace(-2.4, 2.4, 400)
axes[0].plot(x, x**2, color="tab:blue", label="f(x) = x²")
for a in [-1.5, -0.5, 0.5, 1.5]:
    slope = 2 * a
    xt = np.array([a - 0.8, a + 0.8])
    axes[0].plot(xt, a**2 + slope * (xt - a), color="tab:red", linewidth=1.4)
    axes[0].annotate(f"斜率 {slope:.0f}", (a, a**2),
                     textcoords="offset points", xytext=(6, 8), fontsize=9, color="tab:red")
axes[0].set_title("切线斜率 = 导数")
axes[0].set_xlim(-2.6, 2.6)
axes[0].set_ylim(-0.8, 6.2)

# 右图:f'(x) = 2x
axes[1].plot(x, 2 * x, color="tab:green", label="f'(x) = 2x")
axes[1].set_title("导数函数:每一点的切线斜率")
axes[1].set_xlim(-2.6, 2.6)
axes[1].set_ylim(-5.5, 5.5)

for ax in axes:
    ax.axhline(0, color="black", linewidth=0.8)
    ax.axvline(0, color="black", linewidth=0.8)
    ax.grid(True, linestyle=":", alpha=0.5)
    ax.set_aspect("equal")

fig.savefig("book/public/figs/fig05-tangent_slopes.png", dpi=120, bbox_inches="tight")
plt.close(fig)

5.2 可导与连续 ​

可导和连续有确定的关系,先给结论:

定理:若 在 处可导,则 在 处连续。

证明:

当 时,第一项趋于 (可导的定义),第二项趋于 0,乘积趋于 。所以 ,即 ,连续成立。

反过来不成立:连续不一定可导。经典反例是绝对值函数 在 处。它连续(图像不断),但

左右分别是 1 和 ,极限不存在,所以不可导。图像上 处是一个尖角,切线「立不起来」。

python
for h in [0.1, 0.01, -0.01, -0.1]:
    print(f"h = {h:7.3f}   (|h| - 0)/h = {abs(h)/h:7.3f}")
text
h =   0.100   (|h| - 0)/h =   1.000
h =   0.010   (|h| - 0)/h =   1.000
h =  -0.010   (|h| - 0)/h =  -1.000
h =  -0.100   (|h| - 0)/h =  -1.000

绝对值函数不可导

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, 400)
fig, ax = plt.subplots(figsize=(6, 4.5))
ax.plot(x, np.abs(x), color="tab:blue", label="f(x) = |x|")
ax.scatter([0], [0], color="tab:red", s=42, zorder=4)
ax.annotate("尖角:没有唯一切线", (0, 0), textcoords="offset points", xytext=(8, -18), color="tab:red")
ax.set_xlim(-2.2, 2.2)
ax.set_ylim(-0.3, 2.4)
ax.axhline(0, color="black", linewidth=0.8)
ax.axvline(0, color="black", linewidth=0.8)
ax.grid(True, linestyle=":", alpha=0.5)
ax.legend()
ax.set_title("连续但不可导:|x| 在 0 处")
fig.savefig("book/public/figs/fig05_abs.png", dpi=120, bbox_inches="tight")
plt.close(fig)

5.3 常数与幂函数 ​

常数函数的导数为 0:

因为常数函数的图像是水平线,斜率处处为 0。从定义验证:,。

幂函数求导公式:

以 为例推导。利用立方展开式:

当 ,后两项消失,得 。对一般的 ,展开 用二项式定理,第二项一定是 ,其余项都含 及以上,除以 取极限后全部消失,只剩 。

由此得到一组常用公式( 可推广到任意实数,第 6 章会验证):

5.4 和差与常数倍 ​

和差法则:

常数倍法则:

推导(以加法为例):

两项分别趋于 、,由极限的加法法则,和趋于 。常数倍法则同理,把 提到分数线外即可。

用法:

5.5 乘法法则 ​

加法法则很直观,乘法法则需要动点脑筋—— 不等于 。正确的法则是:

推导:

分子不太好直接化简,技巧是「加一项、减一项」::

当 : 中 ,(可导必连续); 中 。合起来就是 。

验证 、 在 处:

python
def f(x):
    return x ** 3

def g(x):
    return x ** 2 + 1

def fp(x):
    return 3 * x ** 2

def gp(x):
    return 2 * x

x = 1.5
product_rule = fp(x) * g(x) + f(x) * gp(x)
direct = 5 * x ** 4 + 3 * x ** 2   # (x^5 + x^3)' = 5x^4 + 3x^2
print("乘法法则:", product_rule)
print("直接展开:", direct)
text
乘法法则: 32.0625
直接展开: 32.0625

5.6 商法则 ​

记忆口诀:「上导乘下,减上乘下导,除以下的平方」。它是乘法法则的推论:把 看成 ,用乘法法则与幂函数求导推出。这一章直接给结论,重点是会用:

5.7 链式法则:函数的函数 ​

考虑 。它先算 ,再算 ——一个函数套在另一个函数里面,叫复合函数。

链式法则(chain rule):

或者写成:

直觉:外层变化率 内层变化率。好比 每增加 1, 增加 ; 每增加 1, 增加 ;那么 每增加 1, 增加 。

算 :

验证:设 ,;外层 ,。两者相乘,。

python
def y(x):
    return (x ** 2 + 1) ** 3

h = 1e-6
x = 2.0
numerical = (y(x + h) - y(x - h)) / (2 * h)
chain_rule = 6 * x * (x ** 2 + 1) ** 2
print("中心差商 ≈", numerical)
print("链式法则 =", chain_rule)
text
中心差商 ≈ 300.00000000995897
链式法则 = 300.0

5.8 用 PyTorch 自动求导验证 ​

PyTorch 的自动微分(autograd)能直接算出导数,它的原理就是链式法则:把每个基本运算的导数记下来,按复合结构逐层相乘。这正好验证我们手算的结果。

python
import torch

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

print("y =", y.item())
print("dy/dx 在 x=2 处 =", x.grad.item())
text
y = 125.0
dy/dx 在 x=2 处 = 300.0

与手算、差商三方一致。第 13、14 章会看到,深度学习训练的核心——反向传播——就是链式法则的大规模应用。

5.9 动手实践:求切线方程 ​

求 在 处的切线方程。

斜率:。切点:。切线方程:

python
import numpy as np
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

x = np.linspace(0.5, 4.5, 300)
fig, ax = plt.subplots(figsize=(6.5, 5))
ax.plot(x, x**2, color="tab:blue", label="f(x) = x²")
ax.plot(x, 6 * x - 9, color="tab:red", label="切线 y = 6x - 9")
ax.scatter([3], [9], color="tab:red", s=40, zorder=4)
ax.annotate("(3, 9), 斜率 6", (3, 9), textcoords="offset points", xytext=(8, -14), color="tab:red")
ax.set_xlim(0.5, 4.5)
ax.set_ylim(-1, 20)
ax.grid(True, linestyle=":", alpha=0.5)
ax.legend()
ax.set_title("用导数求切线")
fig.savefig("book/public/figs/fig05_tangent_at_3.png", dpi=120, bbox_inches="tight")
plt.close(fig)

常见错误 ​

错误写法/理解原因
认为 乘法法则是 ,少一项就错
认为 商法则是
链式法则漏乘内层导数 对 求导,必须乘
认为可导和连续等价可导必连续,但连续不一定可导(如 在 0 处)
求导时把常数当变量常数导数为 0, 而不是

章末练习 ​

基础

  1. 用定义求 在 处的导数(写出完整极限过程)。
  2. 求导:、、。
  3. 求 在 处的切线方程。

提高

  1. 用乘法法则求 (设 ),并写出每一步。
  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.