4.3.2 导数:变化率的直觉

- 直觉理解导数 = 切线斜率 = 变化速度
- 用生活场景(速度、股价)理解导数
- 掌握常用求导规则
- 用 Python 进行数值求导和可视化
先说一个很重要的学习预期
Section titled “先说一个很重要的学习预期”这一节不是为了让你先变成“会推导一切导数的人”, 而是为了让你先真的理解:
- 导数到底在描述什么
- 为什么它会直接关系到模型怎么更新参数
如果你现在读完一遍,还不能熟练做大量求导题,这完全正常。 更重要的标准是:
- 你能不能把导数说成“变化率”
- 你能不能把它和后面的损失变化、参数更新连起来
先建立一张地图
Section titled “先建立一张地图”这一节最好先把它放回整章里理解:

所以这一节不是单独的数学知识点,而是在给后面整个优化主线打地基。
一、导数是什么?
Section titled “一、导数是什么?”生活中的”变化率”
Section titled “生活中的”变化率””| 场景 | 变量 | 变化率(导数) |
|---|---|---|
| 开车 | 距离随时间变化 | 速度(km/h) |
| 股票 | 股价随时间变化 | 涨跌速度 |
| 学习 | 分数随练习时间变化 | 学习效率 |
| AI 训练 | 损失值随训练步数变化 | 收敛速度 |
导数 = 某个量在某一瞬间的变化速度。
一个更适合新人的类比
Section titled “一个更适合新人的类比”如果你总觉得“切线斜率”还是有点抽象,可以先把导数想成:
- 仪表盘上的“当前速度”
比如开车时:
- 总路程是累计值
- 速度是你此刻变化得有多快
所以导数不是在问“总共变了多少”, 而是在问:
现在这一刻,变化到底有多快。
几何直觉:切线斜率
Section titled “几何直觉:切线斜率”import numpy as npimport matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']plt.rcParams['axes.unicode_minus'] = False
# 函数 f(x) = x²def f(x): return x ** 2
# 在 x=1 处的切线x0 = 1slope = 2 * x0 # f'(x) = 2x → f'(1) = 2
x = np.linspace(-1, 3, 200)tangent = slope * (x - x0) + f(x0)
plt.figure(figsize=(8, 6))plt.plot(x, f(x), 'steelblue', linewidth=2, label='f(x) = x²')plt.plot(x, tangent, 'r--', linewidth=2, label=f'切线(斜率 = {slope})')plt.plot(x0, f(x0), 'ro', markersize=10, zorder=5)plt.annotate(f'x={x0}, 斜率={slope}', xy=(x0, f(x0)), xytext=(x0+0.5, f(x0)+1.5), fontsize=12, arrowprops=dict(arrowstyle='->', color='gray'))plt.xlim(-1, 3)plt.ylim(-1, 8)plt.xlabel('x')plt.ylabel('f(x)')plt.title('导数 = 切线斜率')plt.legend()plt.grid(True, alpha=0.3)plt.show()解读:f(x) = x² 在 x=1 处的导数是 2,意思是”当 x 在 1 附近每增加一点点,f(x) 大约增加 2 倍那么多”。
数值求导——用 Python “近似”计算
Section titled “数值求导——用 Python “近似”计算”不需要知道公式,只要能算函数值就能算导数:
f’(x) ≈ (f(x + h) - f(x - h)) / (2h) (h 取很小的数)
def numerical_derivative(f, x, h=1e-7): """用中心差分法计算数值导数""" return (f(x + h) - f(x - h)) / (2 * h)
# 测试:f(x) = x² 的导数应该是 2xf = lambda x: x ** 2
for x0 in [0, 1, 2, 3]: approx = numerical_derivative(f, x0) exact = 2 * x0 print(f"x={x0}: 数值导数={approx:.6f}, 精确导数={exact}")二、常用求导规则
Section titled “二、常用求导规则”你不需要记住所有规则,只需要熟悉最常见的几个:
基本规则速查表
Section titled “基本规则速查表”| 函数 | 导数 | 例子 |
|---|---|---|
| 常数 c | 0 | (5)’ = 0 |
| x 的 n 次方 | n × x 的 (n-1) 次方 | (x³)’ = 3x² |
| e 的 x 次方 | e 的 x 次方 | (eˣ)’ = eˣ |
| ln(x) | 1/x | (ln x)’ = 1/x |
| sin(x) | cos(x) | (sin x)’ = cos x |
用 Python 验证
Section titled “用 Python 验证”# 验证常用导数规则functions = [ ("x³", lambda x: x**3, lambda x: 3*x**2), ("eˣ", lambda x: np.exp(x), lambda x: np.exp(x)), ("ln(x)", lambda x: np.log(x), lambda x: 1/x), ("sin(x)", lambda x: np.sin(x), lambda x: np.cos(x)),]
print(f"{'函数':<10} {'x':<5} {'数值导数':<15} {'解析导数':<15} {'误差':<15}")print("-" * 60)
for name, f, f_prime in functions: x0 = 1.0 numerical = numerical_derivative(f, x0) analytical = f_prime(x0) error = abs(numerical - analytical) print(f"{name:<10} {x0:<5} {numerical:<15.8f} {analytical:<15.8f} {error:<15.2e}")可视化:函数及其导数
Section titled “可视化:函数及其导数”fig, axes = plt.subplots(2, 2, figsize=(14, 10))
cases = [ ('f(x) = x²', lambda x: x**2, lambda x: 2*x), ('f(x) = x³', lambda x: x**3, lambda x: 3*x**2), ('f(x) = sin(x)', np.sin, np.cos), ('f(x) = eˣ', np.exp, np.exp),]
for ax, (name, f, f_prime) in zip(axes.flat, cases): x = np.linspace(-2, 2, 200) ax.plot(x, f(x), 'steelblue', linewidth=2, label='f(x)') ax.plot(x, f_prime(x), 'coral', linewidth=2, linestyle='--', label="f'(x)") ax.axhline(y=0, color='gray', linewidth=0.5) ax.set_title(name, fontsize=12) ax.legend() ax.grid(True, alpha=0.3)
plt.suptitle('函数(蓝)和导数(红)', fontsize=14)plt.tight_layout()plt.show()三、导数在 AI 中的角色
Section titled “三、导数在 AI 中的角色”损失函数的导数 = 优化方向
Section titled “损失函数的导数 = 优化方向”flowchart LR L["损失函数 L(w)"] --> D["计算导数 dL/dw"] D --> U["更新参数<br/>w = w - lr × dL/dw"] U --> L
style L fill:#ffebee,stroke:#c62828,color:#333 style D fill:#e3f2fd,stroke:#1565c0,color:#333 style U fill:#e8f5e9,stroke:#2e7d32,color:#333导数告诉你:参数应该往哪个方向调,才能让损失变小。 这就是梯度下降的核心思想(下下节详细讲)。
AI 中常见函数的导数
Section titled “AI 中常见函数的导数”# Sigmoid 函数及其导数def sigmoid(x): return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x): s = sigmoid(x) return s * (1 - s)
# ReLU 函数及其导数def relu(x): return np.maximum(0, x)
def relu_derivative(x): return (x > 0).astype(float)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))x = np.linspace(-5, 5, 200)
# Sigmoidaxes[0].plot(x, sigmoid(x), 'steelblue', linewidth=2, label='sigmoid(x)')axes[0].plot(x, sigmoid_derivative(x), 'coral', linewidth=2, linestyle='--', label="sigmoid'(x)")axes[0].set_title('Sigmoid 及其导数')axes[0].legend()axes[0].grid(True, alpha=0.3)
# ReLUaxes[1].plot(x, relu(x), 'steelblue', linewidth=2, label='ReLU(x)')axes[1].plot(x, relu_derivative(x), 'coral', linewidth=2, linestyle='--', label="ReLU'(x)")axes[1].set_title('ReLU 及其导数')axes[1].legend()axes[1].grid(True, alpha=0.3)
plt.tight_layout()plt.show()Sigmoid 导数的问题:在 x 远离 0 时,导数趋近 0(“梯度消失”),这就是为什么深度网络更常用 ReLU。
学到这里,下一节该带着什么问题走?
Section titled “学到这里,下一节该带着什么问题走?”看完导数以后,最值得带去下一节的问题是:
- 如果函数不只一个变量,变化率该怎么表示?
- 如果参数很多,模型到底该往哪个方向一起改?
- 为什么“一个变量的导数”会自然升级成“多变量的梯度”?
这三个问题,正好会把你自然带到:
学完这一页,至少保留这张证据卡:
- 函数
- 目标函数、损失、导数、梯度或链式法则表达式
- 计算
- 数值导数、梯度步长或反向传播轨迹
- 输出
- 斜率、梯度向量、更新后的参数,或损失变化
- 失败检查
- 符号错误、学习率过大、局部斜率理解错误或链式法则出错
- 期望产出
- 展示参数如何变化的计算轨迹
| 概念 | 直觉 | Python 实现 |
|---|---|---|
| 导数 | 函数在某点的变化速度 | (f(x+h) - f(x-h)) / (2h) |
| 切线斜率 | 导数的几何含义 | 画切线可视化 |
| 常用规则 | 幂函数、指数、对数、三角函数 | 用数值导数验证 |
| AI 中的角色 | 导数指示优化方向 | 梯度下降的基础 |
这节最该带走什么
Section titled “这节最该带走什么”- 导数最重要的直觉是“当前变化率”
- 数值求导在帮你先看见变化,而不是先逼你背推导
- 导数在 AI 里最关键的作用,是告诉模型参数应该往哪边调
练习 1:数值求导
Section titled “练习 1:数值求导”用 numerical_derivative 函数计算以下函数在 x=2 处的导数,和精确值对比:
- f(x) = 3x² + 2x - 1
- f(x) = 1/x
- f(x) = x × sin(x)
练习 2:画导数图
Section titled “练习 2:画导数图”画出 f(x) = x³ - 3x 和它的导数 f’(x) = 3x² - 3 在 [-3, 3] 范围内的图形。观察:f’(x) = 0 的地方(x = ±1),对应 f(x) 的什么特征?
练习 3:Sigmoid 梯度消失
Section titled “练习 3:Sigmoid 梯度消失”画出 sigmoid 的导数图,找出导数最大值是多少,在什么位置。解释为什么这会导致”梯度消失”问题。
参考实现与讲解
- 在
x=2,3x^2+2x-1的导数是14,1/x的导数是-0.25,x sin(x)的导数是sin(2)+2cos(2)≈0.0770。 - 对
f(x)=x^3-3x,导数在x=-1和x=1为 0,分别对应曲线上的局部最大值和局部最小值。 - Sigmoid 导数在
x=0最大,值为0.25。离 0 很远时导数接近 0,导致基于梯度的更新非常小。