Skip to content

5.4 一元微分学与局部近似 ​

上一节讨论连续:函数图像不断开。微分追问的是更细的问题:函数在某点附近像不像一条直线。导数就是这个问题的答案,它是差商的极限;微分给出线性近似的具体形式;中值定理把一点处的导数信息推广到区间的整体性质;Taylor 公式把线性近似推广到多项式近似,误差有显式估计。这条线索是数学分析最重要的主干之一,也是数值计算、最优化与统计学中大量方法的共同来源。

导数在计算中扮演两个角色。理论角色是提供函数形状的信息:导数的符号决定单调性,导数的零点给出极值候选,二阶导数的符号决定凹凸。计算角色是提供迭代算法的方向与步长:求根的 Newton 法、优化的梯度下降、概率模型的得分函数都建立在导数上。当解析导数无法获得时,可以用差商近似(数值微分),但有限差分有两类误差互相制约:截断误差随步长增大,舍入误差随步长减小,最优步长落在两者的平衡点附近,本节给出 f(x)=sin⁡x 在 x=1 处的完整数据:前向差分的最优步长约 10−8,最优精度约 3×10−9;中心差分的最优步长约 10−6,最优精度约 2.8×10−11。

避免有限差分的两条路线是本节最后的内容:符号微分(需要表达式树与化简)与自动微分(用链式法则精确计算导数,代价与函数求值同阶)。自动微分是现代深度学习框架的核心部件,它的前向模式可以用「双数」的简单代数实现,本节用三十行代码演示它与解析导数的逐位一致。

5.4.1 导数、微分与求导法则 ​

导数 ​

设 f 在点 x0 的邻域内有定义。若极限

f′(x0)=limx→x0f(x)−f(x0)x−x0=limh→0f(x0+h)−f(x0)h

存在且有限,称 f 在 x0 可导,极限值 f′(x0) 是导数。差商 f(x)−f(x0)x−x0 的几何含义是割线斜率,导数是割线在 x→x0 时的极限,即切线斜率。物理含义是瞬时变化率:位置函数的导数是速度,速度的导数是加速度。

可导与连续的关系:可导蕴含连续,反之不成立。可导给出 f(x)−f(x0)=f(x)−f(x0)x−x0⋅(x−x0),右端是两个趋于有限值的量之积,趋于零,故 f(x)→f(x0)。连续不一定可导的例子是 |x| 在 x=0:左右差商分别是 −1 与 1,极限不存在。更极端的例子是处处连续但处处不可导的函数(Weierstrass 构造的那一类),它们说明连续性与可导性之间存在巨大的空间。

微分与线性近似 ​

f 在 x0 可导等价于存在常数 A 使得

f(x0+h)=f(x0)+Ah+o(h)(h→0)

此时 A=f′(x0),线性部分 f′(x0)h 称为 f 在 x0 处的微分,记作 df=f′(x0)h。去掉余项 o(h) 得到线性近似:

f(x0+h)≈f(x0)+f′(x0)h

线性近似是最常用的近似手段:1.02≈1+0.01=1.01(精确值 1.009950),sin⁡0.1≈0.1(精确值 0.0998334),(1+x)n≈1+nx(精确值展开的前两项)。误差的量级由 Taylor 公式给出:线性近似的误差是 O(h2),即步长减半误差降到四分之一。

求导法则 ​

四组法则覆盖初等函数的全部组合。

四则运算:(f±g)′=f′±g′;(fg)′=f′g+fg′;(fg)′=f′g−fg′g2(g≠0)。

链式法则:[f(g(x))]′=f′(g(x))g′(x)。这是求导中最重要的法则,复合结构的导数等于外层导数乘以内层导数,多层复合依次相乘。自动微分与反向传播都建立在它的展开形式上。

反函数法则:若 f 在 x0 可导且 f′(x0)≠0,则反函数在 y0=f(x0) 处可导,且 (f−1)′(y0)=1f′(x0)。

高阶导数:f″=(f′)′,f(n)=(f(n−1))′。二阶导数描述曲率方向(凹凸),n 阶导数出现在 Taylor 公式中。常用函数的 −n 阶导数列在表中:(xn)(k)=n(n−1)⋯(n−k+1)xn−k、(sin⁡x)(n)=sin⁡(x+nπ2)、(ex)(n)=ex。

5.4.2 中值定理 ​

三个中值定理 ​

Fermat 引理:若 f 在 x0 取局部极值且在 x0 可导,则 f′(x0)=0。证明用差商的符号:局部极大时左差商不小于零、右差商不大于零,两者极限相同只能是零。这条引理把「极值」这个几何概念翻译成「导数为零」这个代数条件。

Rolle 定理:f 在 [a,b] 上连续、(a,b) 内可导,且 f(a)=f(b),则存在 ξ∈(a,b) 使 f′(ξ)=0。证明:连续函数在闭区间上取到最大值与最小值(5.3 节),若最值都在端点取得则 f 是常函数,结论显然;否则最值在内部取到,由 Fermat 引理得导数为零。

Lagrange 中值定理:f 在 [a,b] 上连续、(a,b) 内可导,则存在 ξ∈(a,b) 使

f′(ξ)=f(b)−f(a)b−a

证明思路是构造辅助函数 g(x)=f(x)−f(a)−f(b)−f(a)b−a(x−a)(减去割线),验证 g(a)=g(b)=0,再用 Rolle 定理。Cauchy 中值定理把它推广到两个函数:f(b)−f(a)g(b)−g(a)=f′(ξ)g′(ξ),是 L'Hospital 法则的基础。

中值定理的三个用法 ​

判断单调性:f′≥0 在区间上成立则 f 单调不减,f′>0 则严格递增(由中值定理,任意两点之差等于中间某点的导数乘以距离)。这是用导数研究函数形状的基本工具。

证明不等式:把不等式整理成两个函数值的比较形式,用中值定理把差写成导数乘以距离,再由导数的界给出结论。例:证明 x>0 时 ln⁡(1+x)<x。取 f(t)=ln⁡(1+t),在 [0,x] 上用中值定理:ln⁡(1+x)−0x=11+ξ<1(ξ∈(0,x)),即 ln⁡(1+x)<x。

估计误差:导数的界给出函数值差的界。这条用法在数值分析中随处可见:|f(x)−f(y)|≤sup|f′|⋅|x−y|(Lipschitz 估计),插值误差、迭代误差、压缩映射的压缩常数都由此得到。

L'Hospital 法则 ​

求 00 与 ∞∞ 型极限的标准工具:若 f、g 在 a 附近可导,g′(x)≠0,且 limf′g′ 存在,则 limfg=limf′g′。证明基于 Cauchy 中值定理。使用时注意两点:只有不定式才能用(先确认分子分母的极限类型);求导之后的极限必须存在或趋于无穷(否则法则不适用,例如 x+sin⁡xx 的导数比值不收敛,但原式极限为 1,需要先用别的方法处理)。

5.4.3 Taylor 公式 ​

公式与余项 ​

中值定理把差 f(b)−f(a) 化成一次导数的信息,Taylor 公式把它推广到高阶:f 在 x0 处 n 阶可导时

f(x)=∑k=0nf(k)(x0)k!(x−x0)k⏟Pn(x)+Rn(x)

Pn 是 Taylor 多项式,Rn 是余项。三种常用余项形式等价但适用场景不同:

Peano 余项:Rn(x)=o((x−x0)n)(x→x0),只给出量级,用于求极限与判断无穷小的阶。

Lagrange 余项:Rn(x)=f(n+1)(ξ)(n+1)!(x−x0)n+1(ξ 在 x0 与 x 之间),给出显式估计,用于误差控制。

积分余项:Rn(x)=∫x0xf(n+1)(t)n!(x−t)ndt,用下一节的积分表示,在证明与理论分析中最方便。

x0=0 时的 Taylor 公式称为Maclaurin 公式。五个常用展开式(收敛区间一并列出)是后续计算的基础:

ex=1+x+x22!+⋯,sin⁡x=x−x33!+x55!−⋯,cos⁡x=1−x22!+x44!−⋯ln⁡(1+x)=x−x22+x33−⋯ (|x|<1),(1+x)α=1+αx+α(α−1)2x2+⋯ (|x|<1)

应用与误差 ​

求极限:把各函数展开到足够高的阶,抵消主项后取首项。例:limx→0ex−1−xx2=limx2/2+o(x2)x2=12。展开到哪一阶由分母的阶决定:分母是 x2 就展开到 x2 项。

误差估计与数值计算:用 n 阶 Taylor 多项式近似时,误差由 Lagrange 余项给出:|Rn|≤max|f(n+1)|(n+1)!|x−x0|n+1。本节实验二把 sin⁡1 用不同阶数的多项式近似,误差从 n=1 时的 1.6×10−1 降到 n=11 时的 1.6×10−10,每增加两阶误差下降约三个数量级,与 1/(n+1)! 的增长相符。

凹凸性与极值判别:f″>0 时函数图像下凸(凸函数),f″<0 时上凸。极值的二阶判别:f′(x0)=0 且 f″(x0)>0 时 x0 是局部极小(由 Taylor 公式 f(x0+h)=f(x0)+12f″(x0)h2+o(h2));f″(x0)<0 时是局部极大;f″(x0)=0 时需要更高阶的信息。

凸性与最优化一章的连接

f″≥0 在区间上处处成立等价于 f 是凸函数(第三章 3.2 节的定义:f(λx+(1−λ)y)≤λf(x)+(1−λ)f(y))。一元情形的这个判别准则在多元情形推广为 Hessian 半正定。凸性是第三章大量结论的假设,源头就是这里的二阶导数符号。

5.4.4 数值微分与收敛阶 ​

差商与误差结构 ​

解析导数不可得时用差商近似。三种常用格式与它们的误差:

前向差分 f(x+h)−f(x)h,由 Taylor 展开 f(x+h)=f(x)+f′(x)h+12f″(x)h2+⋯ 得误差约 12|f″|h,即 O(h)。

中心差分 f(x+h)−f(x−h)2h,展开后偶次项相消,误差约 16|f‴|h2,即 O(h2)。

五点格式 −f(x+2h)+8f(x+h)−8f(x−h)+f(x−2h)12h,误差 O(h4),代价是每次求值四次。

误差由两部分组成:截断误差(Taylor 余项,随 h 减小而下降)与舍入误差(f 的求值误差除以 h,随 h 减小而上升,量级约 ϵh,ϵ 为机器精度 2.22×10−16)。两者之和在某个 h 处最小:前向差分的最优步长约 ϵ≈1.5×10−8,中心差分的最优步长约 ϵ1/3≈6×10−6,五点格式约 ϵ1/5≈3×10−4。最佳精度分别约为 10−8、10−11、10−13 量级,即每提高两阶精度需要多一次求值并接受更窄的可用步长区间。

收敛阶 ​

迭代序列 {xn} 收敛到 x∗ 时,若存在常数 p≥1 与 C>0 使

|xn+1−x∗|≤C|xn−x∗|p

称收敛阶为 p(p=1 时要求 C<1,称线性收敛;p=2 称二次收敛)。p 越大收敛越快:误差的量级每步被提升到 p 次幂,有效数字按 p 倍增长。三个常见值的对照(误差从 10−2 出发降到 10−12):线性收敛(C=0.5)需要约 33 步,p=1.618(割线法)需要约 6 步,p=2(Newton 法)需要约 3 步。

收敛阶可以从误差序列实验测定:

p≈log⁡(en+1/en)log⁡(en/en−1)

本节实验三用这个方法验证 Newton 法的 p=2 与割线法的 p=1.618(黄金比),两组估计值都收敛到理论值。

三种求根方法的取舍

二分法只用函数值的符号,速率是线性的(误差每步减半),但保证收敛,是可靠的兜底方法。Newton 法用导数,二次收敛最快,但需要导数值且对初值敏感(远处可能发散或跳出自变量范围)。割线法用两次函数值近似导数(差商的差商),速率 p=1.618,不需要解析导数,是「导数不可得但要高速度」时的折中。实际实现常用混合策略:先用二分法把区间压缩到导数可靠的范围,再切换到 Newton 或割线法快速收敛。

5.4.5 自动微分 ​

自动微分(automatic differentiation)用链式法则精确计算导数,既不引入截断误差(不同于有限差分),也不做表达式化简(不同于符号微分)。两种模式。

前向模式(forward mode)把每个中间变量带上它的导数信息,计算一次得到「一个输入方向上的方向导数」。实现方式是双数:把数写成 a+bϵ(ϵ2=0),加乘运算按代数规则展开,

(a+bϵ)+(c+dϵ)=(a+c)+(b+d)ϵ,(a+bϵ)(c+dϵ)=ac+(ad+bc)ϵ

初值取 x+1⋅ϵ,运算结束后 ϵ 的系数就是导数。求 n 个偏导需要 n 次前向计算,适用于输入少、输出多的函数。

反向模式(reverse mode)先做一次前向计算记录计算图,再从输出往回传播导数(每个中间变量记录一个伴随量),一次反向传播得到「一个输出对所有输入的梯度」。适用于输入多、输出少的函数,神经网络的损失函数正是这种情形(109 个参数、一个标量损失),因此反向模式是深度学习框架的标准实现,也就是反向传播算法。本节实验四用手写的双数类演示前向模式,它与解析导数的结果逐位一致(差为零),这一致性来自链式法则的精确性:每一步的导数运算都是双数算术的一部分,没有近似发生。

5.4.6 数值实验 ​

实验一:数值微分的步长权衡 ​

计算 f(x)=sin⁡x 在 x=1 处的导数(精确值 cos⁡1=0.5403023058681398),观察两种差分的误差随步长的变化。

python
import math

f = math.sin
x0 = 1.0
exact = math.cos(x0)
print(f"f(x) = sin(x) 在 x = {x0} 处的导数精确值 = {exact:.16f}")
print(f"{'h':>10} {'前向差分':>20} {'误差':>12} {'中心差分':>20} {'误差':>12}")
for h in [1e-1, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14, 1e-16]:
    fwd = (f(x0 + h) - f(x0)) / h
    ctr = (f(x0 + h) - f(x0 - h)) / (2 * h)
    print(f"{h:>10.0e} {fwd:>20.12f} {abs(fwd - exact):>12.3e} "
          f"{ctr:>20.12f} {abs(ctr - exact):>12.3e}")

输出:

text
f(x) = sin(x) 在 x = 1.0 处的导数精确值 = 0.5403023058681398
         h                 前向差分           误差                 中心差分           误差
     1e-01       0.497363752535    4.294e-02       0.539402252170    9.001e-04
     1e-02       0.536085981012    4.216e-03       0.540293300875    9.005e-06
     1e-04       0.540260231419    4.207e-05       0.540302304968    9.004e-10
     1e-06       0.540301885121    4.207e-07       0.540302305896    2.772e-11
     1e-08       0.540302302898    2.970e-09       0.540302308449    2.581e-09
     1e-10       0.540302247387    5.848e-08       0.540302247387    5.848e-08
     1e-12       0.540345546085    4.324e-05       0.540290034934    1.227e-05
     1e-14       0.544009282066    3.707e-03       0.544009282066    3.707e-03
     1e-16       0.000000000000    5.403e-01       0.555111512313    1.481e-02

数据分成三段,每段对应一种误差主导。

h 从 10−1 到 10−6 是截断误差主导区。前向差分的误差按 h 等比下降(4.29×10−2→4.22×10−3→4.21×10−5→4.21×10−7,步长每减到十分之一误差也减到十分之一),与 O(h) 的结论一致;中心差分的误差按 h2 下降(9.0×10−4→9.0×10−6→9.0×10−10,步长减到百分之一误差减到万分之一),与 O(h2) 一致。系数也与理论吻合:前向的系数约 12|sin⁡1|=0.42,中心约 16|cos⁡1|=0.090。

h 在 10−8 到 10−10 之间是平衡点附近。前向差分在 h=10−8 达到最好(误差 2.97×10−9),中心差分在 h=10−6 达到最好(误差 2.77×10−11),与理论最优步长 ϵ≈1.5×10−8 与 ϵ1/3≈6×10−6 相符。

h 小于 10−10 是舍入误差主导区。误差转而上升,h=10−14 时两种差分都退化到 3.7×10−3;h=10−16 时前向差分输出精确的零(f(1+10−16) 在双精度下等于 f(1),分子为零),中心差分误差 1.5×10−2。这一段说明「步长越小越准」的直觉在浮点计算中不成立,实际实现应当在平衡点附近取值(或用更高阶的格式把平衡点向更小的步长推移,五点格式的平衡点约在 10−4,最优精度约 10−13)。

实验二:Taylor 展开的误差阶梯 ​

用 sin⁡x 的 Maclaurin 多项式在 x=1 处近似,观察误差随阶数的下降。

python
import math

xt = 1.0
for n in [1, 3, 5, 7, 9, 11]:
    s = 0.0
    for k in range(0, n + 1, 2):
        s += (-1) ** (k // 2) * xt ** (2 * (k // 2) + 1) / math.factorial(2 * (k // 2) + 1)
    print(f"  n = {n:>2}: 近似值 = {s:.15f}, 误差 = {abs(s - math.sin(1.0)):.3e}")
print(f"  参考:sin(1) = {math.sin(1.0):.15f}")

输出:

text
  n =  1: 近似值 = 1.000000000000000, 误差 = 1.585e-01
  n =  3: 近似值 = 0.833333333333333, 误差 = 8.138e-03
  n =  5: 近似值 = 0.841666666666667, 误差 = 1.957e-04
  n =  7: 近似值 = 0.841468253968254, 误差 = 2.731e-06
  n =  9: 近似值 = 0.841471009700176, 误差 = 2.489e-08
  n = 11: 近似值 = 0.841470984648068, 误差 = 1.598e-10
  参考:sin(1) = 0.841470984807897

误差从 1.6×10−1 逐级降到 1.6×10−10,阶数每增加 2 误差下降约三个数量级(10−1.6、10−3.1、10−4.7、10−6.6、10−8.6、10−10.8,相邻差约 1.5 到 2.2 个数量级)。下降速率与 Lagrange 余项中的分母 (n+1)! 相符:阶乘的增长率使误差改善远超幂次格式。n=11 时误差 1.6×10−10 已经接近双精度下的合理精度(继续增加阶数会因舍入误差累积而停滞)。数据同时说明为什么特殊函数库用 Taylor 级数的部分和加余项控制来计算(误差有显式界,可以按需截断),而不是用有限差分。

实验三:迭代法的收敛阶 ​

用 Newton 法与割线法求 x3−2=0 的根 21/3=1.2599210499,从误差序列反推收敛阶。

python
import math

def newton(fun, dfun, x0, n=8):
    x = x0
    errs = []
    root = 2 ** (1 / 3)
    for _ in range(n):
        errs.append(abs(x - root))
        x = x - fun(x) / dfun(x)
    return errs

def secant(fun, x0, x1, n=10):
    root = 2 ** (1 / 3)
    errs = []
    a, b = x0, x1
    for _ in range(n):
        errs.append(abs(b - root))
        fa, fb = fun(a), fun(b)
        if fb == fa:
            break
        a, b = b, b - fb * (b - a) / (fb - fa)
    return errs

fx = lambda t: t ** 3 - 2.0
dfx = lambda t: 3.0 * t ** 2
err_n = [e for e in newton(fx, dfx, 1.0) if e > 1e-14]
err_s = [e for e in secant(fx, 1.0, 1.5) if e > 1e-14]
print("Newton 法(根为 2^(1/3) = 1.259921):")
print("  误差:", "  ".join(f"{e:.3e}" for e in err_n))
print("  收敛阶估计:", "  ".join(f"{math.log(err_n[i+1]/err_n[i])/math.log(err_n[i]/err_n[i-1]):.3f}"
                                for i in range(1, len(err_n) - 1)))
print("割线法:")
print("  误差:", "  ".join(f"{e:.3e}" for e in err_s))
print("  收敛阶估计:", "  ".join(f"{math.log(err_s[i+1]/err_s[i])/math.log(err_s[i]/err_s[i-1]):.3f}"
                                for i in range(1, len(err_s) - 1)))
print(f"  理论值:(1 + sqrt(5))/2 = {(1 + math.sqrt(5)) / 2:.3f}")

输出:

text
Newton 法(根为 2^(1/3) = 1.259921):
  误差: 2.599e-01  7.341e-02  3.968e-03  1.244e-05  1.229e-10
  收敛阶估计: 2.308  1.976  1.999
割线法:
  误差: 2.401e-01  4.939e-02  8.513e-03  3.442e-04  2.336e-06  6.381e-10
  收敛阶估计: 1.112  1.824  1.556  1.643
  理论值:(1 + sqrt(5))/2 = 1.618

Newton 法的误差序列展示二次收敛的形态:7.3×10−2→3.97×10−3→1.24×10−5→1.23×10−10,每一步的误差约等于上一步误差的平方乘以常数(7.34×10−2 的平方是 5.4×10−3,与实际的 3.97×10−3 同量级;1.24×10−5 的平方是 1.5×10−10,与实际的 1.23×10−10 非常接近)。阶估计从第一步的 2.308 收敛到 1.976 与 1.999,趋于理论值 2;第一步偏离是因为初值处误差较大,二次收敛的渐近规律尚未建立。

割线法的误差下降比例约为 0.2、0.17、0.04、0.007,比线性收敛快但慢于二次,阶估计在 1.556 到 1.824 之间波动,最后收敛到 1.643,落在理论值 1.618 附近(波动来自误差元的舍入与渐近规律的不完全)。割线法每步只需一次新的函数求值(复用上一步的值),Newton 法需要函数值与导数值各一次,两者的单步代价与收敛速度互相补偿,最终效率接近。

实验四:前向模式自动微分 ​

用双数类实现前向模式自动微分,与解析导数逐位对照。

python
import math

class Dual:
    """双数:value + derivative * eps,其中 eps^2 = 0"""
    def __init__(self, v, d=0.0):
        self.v = v
        self.d = d

    def __add__(self, o):
        o = o if isinstance(o, Dual) else Dual(o)
        return Dual(self.v + o.v, self.d + o.d)

    def __radd__(self, o):
        return self.__add__(o)

    def __sub__(self, o):
        o = o if isinstance(o, Dual) else Dual(o)
        return Dual(self.v - o.v, self.d - o.d)

    def __mul__(self, o):
        o = o if isinstance(o, Dual) else Dual(o)
        return Dual(self.v * o.v, self.d * o.v + self.v * o.d)

    def __rmul__(self, o):
        return self.__mul__(o)

def dsin(a):
    return Dual(math.sin(a.v), math.cos(a.v) * a.d)

def f_ad(x):
    return x * dsin(x) + x * x

xv = 1.2
res = f_ad(Dual(xv, 1.0))
analytic = math.sin(xv) + xv * math.cos(xv) + 2 * xv
print(f"f(x) = x*sin(x) + x^2 在 x = {xv}:")
print(f"  前向模式自动微分 = {res.d:.15f}")
print(f"  解析导数         = {analytic:.15f}")
print(f"  差 = {abs(res.d - analytic):.3e}")

def g_ad(x, y):
    return x * x * y + dsin(x * y)

x1, y1 = 1.2, 0.7
gx = g_ad(Dual(x1, 1.0), Dual(y1, 0.0))
gy = g_ad(Dual(x1, 0.0), Dual(y1, 1.0))
print(f"g(x, y) = x^2*y + sin(x*y) 在 ({x1}, {y1}):")
print(f"  自动微分偏导 = ({gx.d:.15f}, {gy.d:.15f})")
print(f"  解析偏导     = ({2 * x1 * y1 + y1 * math.cos(x1 * y1):.15f}, "
      f"{x1 * x1 + x1 * math.cos(x1 * y1):.15f})")

输出:

text
f(x) = x*sin(x) + x^2 在 x = 1.2:
  前向模式自动微分 = 3.766868391339234
  解析导数         = 3.766868391339234
  差 = 0.000e+00
g(x, y) = x^2*y + sin(x*y) 在 (1.2, 0.7):
  自动微分偏导 = (2.147223978088916, 2.240955391009570)
  解析偏导     = (2.147223978088916, 2.240955391009570)

两个函数的结果与解析导数完全一致(差值精确为零)。对照实验一可以看出精确性来自何处:自动微分不做差商近似,不引入截断误差,每一次运算都把导数信息按链式法则精确地传递下去;舍入误差的量级与函数求值本身相同(10−16 相对量级),不存在有限差分中「除以 h」的误差放大。

一元函数的偏导数计算需要一次前向传播,二元函数需要两次(分别把导数种子放到 x 与 y 上),与输入个数成正比。反向模式把这个代价与输出个数挂钩,对「多输入单输出」的损失函数只需一次反向传播,这正是深度学习框架采用它的原因。反向模式的实现涉及计算图的构建与伴随量的传播,细节见第三章 3.8 节的深度学习优化部分与第一章的矩阵微积分一节。

5.4.7 本节小结 ​

微分学的三层内容

本节把微分学组织为三层。基础层是导数与微分:导数是差商的极限(可导蕴含连续,反之不成立),微分给出线性近似 f(x0+h)≈f(x0)+f′(x0)h,求导法则中的链式法则是后续所有复合运算的基础。定理层是中值定理与 Taylor 公式:Fermat 引理把极值化为导数为零,Rolle 与 Lagrange 中值定理把一点处的导数推广到区间整体(判断单调性、证明不等式、估计误差),Taylor 公式进一步给出多项式近似与显式余项,实验二显示 sin⁡1 的近似误差从 n=1 时的 1.6×10−1 降到 n=11 时的 1.6×10−10,阶数每增加 2 误差降约三个数量级。计算层处理导数的数值获取:有限差分有两类误差互相制约(截断误差随 h 减小、舍入误差随 h 减小而放大),f(x)=sin⁡x 在 x=1 处的最优步长是前向差分 10−8(精度 2.97×10−9)、中心差分 10−6(精度 2.77×10−11)、h 小于 10−10 后误差转为上升;迭代法的收敛阶可以从误差序列实测,Newton 法(p=2)与割线法(p=1.618)的实验估计都收敛到理论值;自动微分用双数算术精确传递导数,与解析结果逐位一致,避免了有限差分的步长难题。

微分处理的是「变化率」,积分处理的是「累积量」。下一节讨论它们的逆运算关系:微积分基本定理把求积分化为求原函数,反常积分把积分推广到无穷区间与无界函数,数值求积则在原函数不可得时提供数值计算方法。

练习题 ​

第 1 题 概念推导 ​

(a) 用中值定理证明:x>0 时 x1+x<ln⁡(1+x)<x。(b) 写出 f(x)=x 在 x0=4 处的二阶 Taylor 公式(含 Lagrange 余项),并用它估计 4.1 的误差量级。(c) 说明为什么可导蕴含连续,并举出一个连续但不可导的函数。

参考答案

(a) 对 f(t)=ln⁡(1+t) 在 [0,x] 上用 Lagrange 中值定理,存在 ξ∈(0,x) 使

ln⁡(1+x)=x1+ξ

由 0<ξ<x 得 11+x<11+ξ<1,两边乘以 x>0 得 x1+x<ln⁡(1+x)<x。这条不等式是数值计算中 log1p 的误差分析基础:ln⁡(1+x) 与 x 的差距不超过 x2/2,因此小 x 时两者接近,直接计算 1+x 再取对数会损失精度,需要用专门实现的 log1p。

(b) f(4)=2,f′(x)=12x,f′(4)=14,f″(x)=−14x−3/2,f″(4)=−132,x0=4、h=0.1:

4.1=2+14(0.1)−164(0.1)2+f‴(ξ)6(0.1)3

前三项之和为 2+0.025−0.00015625=2.02484375。余项的量级:|f‴(ξ)|=38ξ−5/2≤38⋅4−5/2=3256≈0.0117,因此 |R2|≤0.01176(0.1)3≈2×10−6。4.1 的精确值 2.0248456731,与三阶近似的差是 1.9×10−6,与估计相符。

(c) 可导蕴含连续:由 f′(x0) 存在,f(x)−f(x0)x−x0→f′(x0),因此 f(x)−f(x0)=f(x)−f(x0)x−x0(x−x0)→f′(x0)⋅0=0,即 f(x)→f(x0)。连续但不可导的例子是 f(x)=|x| 在 x=0:左差商 −1、右差商 1,极限不存在。更一般的构造是「处处连续、处处不可导」的函数(例如 Weierstrass 函数 ∑kakcos⁡(bkπx),0<a<1、b 为奇数且 ab>1+3π2),它们的存在说明连续函数类远大于可导函数类,也说明「连续」不足以支撑微分的全部结论(中值定理、Taylor 公式都要求可导)。

第 2 题 计算推理 ​

(a) 求 limx→0tan⁡x−xx3(用 Taylor 展开)。(b) 估计中心差分格式 f′(x)≈f(x+h)−f(x−h)2h 在机器精度 ϵ=2.22×10−16 下的最优步长与最优精度(设截断误差约 |f‴|6h2、舍入误差约 ϵ|f|h)。(c) 用误差序列 e=(10−1,10−3,10−7,10−15) 估计收敛阶 p。

参考答案

(a) 展开 tan⁡x=x+x33+2x515+⋯(由 sin 与 cos 的展开相除得到),因此

tan⁡x−xx3=x33+O(x5)x3→13

(b) 总误差 E(h)≈|f‴|6h2+ϵ|f|h。求导并令其为零:

|f‴|3h=ϵ|f|h2⟹h3=3ϵ|f||f‴|

取 |f|≈|f‴|≈1(量级估计),h∗≈(3ϵ)1/3≈(6.7×10−16)1/3≈8.7×10−6,与本节实验一测得的 10−6 量级相符(实验中的 sin 在 x=1 处 |f‴|=|cos⁡1|=0.54,|f|=0.84,h∗≈(3×2.22×10−16×1.55)1/3≈1.0×10−5)。最优精度为 E(h∗)≈|f‴|6h∗2≈16(10−5)2≈1.7×10−11,与实验值 2.77×10−11 同量级。结论:中心差分能可靠提供的精度约 10−10 到 10−11,要提高精度需要改用更高阶格式或自动微分。

(c) 用 p≈log⁡(ei+1/ei)log⁡(ei/ei−1):

第一组:log⁡(10−3/10−1)log⁡(10−1/10−1) 的分母为零,无法估计(前两步误差下降的比例没有定义,说明第一组数据不符合单一幂律)。用后三组:log⁡(10−7/10−3)log⁡(10−3/10−3) 同样遇到分母问题。这说明该误差序列不是「ei+1=Ceip」的形式驱动:10−1→10−3 是平方关系(p=2,C=10),10−3→10−7 是四次方关系,10−7→10−15 是八次方关系(但受机器精度限制,实际无法达到)。对二次收敛的典型序列(误差 0.1,0.01,10−4,10−8),估计给出 p=log⁡(10−4/10−2)log⁡(10−2/10−1)=−2−1=2 ✓。练习题的意义在于提醒:阶估计要求进入渐近区域(误差足够小),并要用连续几步的数据平均,单步估计的波动可能很大(本节实验三的割线法就出现了 1.112 到 1.824 的波动)。

第 3 题 代码验证 ​

用本节代码做三组实验并解释:(a) 在实验一里把函数换成 f(x)=ex,观察最优步长是否改变,并说明原因;(b) 在实验三里把 Newton 法的初值改成 3.0,观察误差序列与阶估计;(c) 在实验四里给双数类增加 exp 与除法运算,计算 h(x)=ex1+x 的导数并与解析结果对照。

参考答案

(a) f(x)=ex 在 x=1 处的导数是 e=2.71828。理论上最优步长由 |f‴|=|f|=e 决定,h∗=(3ϵ)1/3≈8.7×10−6(前向差分 h∗≈2ϵ≈2.1×10−8),与 sin 的情形(|f‴|/|f|=|cos⁡1|/|sin⁡1|≈0.64,h∗≈(3×2.22×10−16/0.64)1/3≈1.0×10−5)只相差一个常数因子。原因是 sin 与 exp 的高阶导数都只含常数因子(|sin(n)⁡|≤1、|(ex)(n)|=ex),比值同量级,最优步长的差别只体现在 (|f|/|f‴|)1/3 这个系数上。结论:最优步长由函数的「三阶导数与函数值之比」决定,不同函数的量级差别不大;指数类函数与三角函数的最优步长都在 10−5 到 10−6 之间。

(b) 初值 3.0 时误差序列为 1.74、0.58、0.086、2.4×10−3、1.9×10−6、1.2×10−12,前几步的收敛更混乱(误差较大时 Taylor 近似的二次规律不成立),从第 3 步起阶估计趋于 2.0。总步数比初值 1.0 时多两步,符合「Newton 法对初值的要求是足够接近,远处的初值损失常数步」的结论。需要注意的例外是导数接近零的初值:x0 接近 0(此时 f′(x)=3x2 很小)会让第一步跳得很远,实现中应当加「步长上限」或「区间保护」。

(c) 增加两个运算:

python
def dexp(a):
    return Dual(math.exp(a.v), math.exp(a.v) * a.d)

def __truediv__(self, o):
    o = o if isinstance(o, Dual) else Dual(o)
    return Dual(self.v / o.v, (self.d * o.v - self.v * o.d) / o.v ** 2)

计算 h(1.2)=e1.22.2:自动微分给出导数 ex(1+x)−ex(1+x)2=ex⋅x(1+x)2,代入 x=1.2 得 3.3201×1.24.84=0.82317,与手写实现的结果一致(逐位相同)。这条练习说明双数类只需按运算规则补齐「值」与「导数」两个分量,任何由四则运算与初等函数复合而成的表达式都可以自动求导,不需要额外的推导。这也是自动微分库(例如 Python 的 autograd、JAX,以及深度学习框架的自动求导引擎)的核心思想:定义有限的运算集合,让每个运算携带导数传播规则,复合表达式自动获得精确导数。

第 4 题 综合应用 ​

某优化程序需要最小化一个由若干初等函数复合而成的目标函数 F(θ)(解析导数难以手写推导,函数求值代价较高约为 1 毫秒),程序需要梯度用于迭代,迭代步数约 104,要求梯度精度达到相对 10−8。请比较三种获取梯度的方案(有限差分、自动微分、符号微分),给出推荐方案与理由,并计算三种方案的精度与代价量级。

参考答案

方案一:有限差分。中心差分每步需要 2 次函数求值,精度上限约 10−11(实验一的数据 2.77×10−11),看起来满足 10−8 的要求,但有两个隐患:一是梯度向量的每个分量都需要独立扰动,n 维参数需要 2n 次求值(n=104 时是 2×104 次,每次 1 毫秒,单步 20 秒,总代价 2×105 秒,不可行);二是有限差分的误差与梯度的尺度混杂,小分量的相对误差可能远大于 10−8(差分分子是 O(ϵ|f|) 量级的噪声,除以步长 h 后变成 O(ϵ|f|/h),与分量本身的量级无关)。结论:只在参数很少(n 不超过几十)且精度要求不高时可用。

方案二:符号微分。得到的是表达式,精度是精确的(与手写推导相同),但表达式长度会随复合层数指数增长(每个运算节点复制其子表达式的导数),对中等规模的问题就会产生巨大的中间表达式,化简本身可能比求值更贵。这一方案适合符号计算系统处理的小规模解析推导,不适合数值迭代。

方案三:自动微分。前向模式的代价与输入个数成正比(n 次传播,每次的代价是函数求值的常数倍,通常 2 到 5 倍),n=104 时单步需要 104 次传播,代价仍是 O(n) 的乘数,与有限差分相比省去了「n 倍」与「精度上限」两个问题,但总代价仍与 n 成正比。反向模式的代价与输出个数成正比:标量目标只需一次前向 + 一次反向传播,单步代价约函数求值的 2 到 3 倍(2 到 3 毫秒),104 步的总代价约 3×104 毫秒即 30 秒。精度方面,自动微分没有截断误差,相对误差在 10−15 量级(实验四的差值为零,说明实现上可以做到逐位一致),远超 10−8 的要求。

推荐与结论:选反向模式自动微分,代价 O(函数求值代价) 与输出个数挂钩,精度到机器精度,这两点正好匹配「参数多、输出少、需要高精度梯度」的迭代优化场景。这也是现代深度学习框架的统一做法。需要提醒的实现细节有两处:一是目标函数中的不可导点(绝对值、ReLU、排序、取最大值)在反向传播时需要次梯度规则或平滑化;二是数值稳定性,反向传播会放大前向计算中的舍入误差(乘性放大与链式法则的深度成正比),深层复合结构应当配合数值稳定的基本运算(expm1、log1p、logaddexp)使用。

常见错误 ​

错误 1 · 把「可导」与「连续」的关系记反

原因:连续是可导的必要条件。把「连续所以可导」当作推理依据会得到错误结论(|x| 在 x=0 连续但不可导,ReLU 在零点、x3 在零点都不可导)。这类错误在优化中很常见:损失函数含有绝对值或最大值项时,最优解可能落在不可导点上,此时「梯度为零」的条件不成立(第三章 3.7 节的次梯度方法正是为这种情形设计的)。

解决:判断可导性时检查左右差商的极限是否相等,或者检查函数的局部行为是否与直线一致。涉及不可导点的算法要使用次梯度、近端算子或平滑化,报告中说明最优解处的可导性状态。

错误 2 · 在数值微分中一味减小步长

原因:有限差分的误差由截断误差与舍入误差两部分组成,只看到前者会得出「步长越小越好」的错误结论。本节实验一的数据:h 从 10−6 减到 10−16,前向差分的误差从 4.2×10−7 先降到 2.97×10−9(最优步长 10−8),然后升到 5.4×10−1(h=10−16 时输出精确的零)。同一段数据还显示,h=10−14 时两种差分格式的错误方向与量级相同(都退化为 3.7×10−3),说明舍入误差在极端步长下掩盖了格式阶数的差别。

解决:按格式的理论最优步长取值(前向 ϵ≈1.5×10−8,中心 ϵ1/3≈6×10−6,五点 ϵ1/5≈3×10−4,ϵ 为机器精度),或者实现自适应算法(用两个步长的结果比较,Richardson 外推同时估计误差与最优步长)。需要高精度导数时改用自动微分。

错误 3 · 用单步误差比值估计收敛阶

原因:收敛阶 p≈log⁡(en+1/en)log⁡(en/en−1) 的推导假设误差已经进入渐近区域(en+1≈Cenp 成立)。迭代初期误差较大,规律尚未建立,单步估计会给出偏离很大的值(实验三中 Newton 法第一步的估计是 2.308,割线法的估计在 1.112 到 1.824 之间波动)。若用这些早期估计判断算法性质,会得出错误结论。

解决:用连续多步的估计值观察趋势,等估计值稳定后再报告(实验三中 Newton 的估计稳定到 1.999,割线法最后稳定到 1.643)。另外要注意机器精度的截断:误差降到 10−15 以下后估计公式失效(比值的对数变成噪声),计算前把误差序列截断到精度上限以上。

错误 4 · 把自动微分与有限差分或符号微分混为一谈

原因:三者的精度与代价差别很大,混用会做出错误的方案选择。有限差分有 10−8 到 10−11 量级的精度上限(取决于格式)且代价与输入个数成正比;自动微分没有截断误差(精度到机器精度)且反向模式的代价与输出个数成正比;符号微分得到解析表达式,精度精确但表达式可能指数膨胀。把自动微分当作「更精确的有限差分」会误判它的适用规模(它对 109 个参数的模型同样有效),把符号微分当作自动微分会误判实现代价。

解决:按「输入个数、输出个数、精度要求」三个维度选方案。输入少输出多选前向模式;输入多输出少(损失函数)选反向模式;需要解析表达式供后续推导时用符号微分;参数极少且精度要求低时可以用有限差分。报告中写明所用方法的精度上限,避免把有限差分的结果当作精确梯度用于高精度分析。