函数逼近 函数逼近用「足够接近、因此足够好用」的简单函数去替代复杂函数。本文件涵盖线性化、泰勒级数、多项式逼近、傅里叶级数,以及万能逼近定理——它从理论上解释了为什么神经网络能够学习任意的映射关系。 我们遇到的很多函数都太复杂,没法直接处理。在纸上算 $e^{0.1}$、预测一颗卫星的轨迹,等等,都涉及那些没有简洁闭式解的函数。 函数逼近(function approximation)就是用一个更简单的函数去替代一个复杂的函数,要求它在我们所关心的区域里「足够接近」。 最自然的逼近工具就是多项式。多项式不过是 $x$ 的各次幂乘上系数再求和,它易于求值、求导和积分。 但为什么多项式能逼近得这么好?想想 $x$ 的每一次幂各自贡献了什么。 常数项 $a0$ 决定了基准值。
函数逼近用「足够接近、因此足够好用」的简单函数去替代复杂函数。本文件涵盖线性化、泰勒级数、多项式逼近、傅里叶级数,以及万能逼近定理——它从理论上解释了为什么神经网络能够学习任意的映射关系。
我们遇到的很多函数都太复杂,没法直接处理。在纸上算 e^{0.1}、预测一颗卫星的轨迹,等等,都涉及那些没有简洁闭式解的函数。
**函数逼近(function approximation)**就是用一个更简单的函数去替代一个复杂的函数,要求它在我们所关心的区域里「足够接近」。
最自然的逼近工具就是多项式。多项式不过是 x 的各次幂乘上系数再求和,它易于求值、求导和积分。
但为什么多项式能逼近得这么好?想想 x 的每一次幂各自贡献了什么。
只要选对系数,我们就能在某一点上依次匹配函数的值、斜率、曲率以及更高阶的行为,一项一项地逼近。
只要项数足够多,这个多项式就能模仿几乎任何光滑函数。
问题就变成了:我们该怎么找到对的系数?
**线性化(linearisation)**是最简单的逼近。在点 x = a 附近,我们用函数的切线去代替它:
这就是一阶的泰勒逼近(Taylor approximation)。它的意思是:从已知的值 f(a) 出发,再按斜率乘以到 a 的距离来修正。
例如,在 x = 0 处线性化 \sin(x):f(0) = 0,f'(0) = \cos(0) = 1,所以 L(x) = x。在零附近,\sin(x) \approx x。试试看:\sin(0.1) = 0.0998\ldots \approx 0.1。
但线性化只在非常靠近 a 的地方才好用。离得远一点,这个逼近就失效了。想要更好,我们就要引入更高阶的项。
**泰勒级数(Taylor series)**把函数表示成一个无穷的多项式之和,每一项都捕捉函数在点 a 附近更精细的行为:
每多一项,就多一个修正。第一项匹配值,第二项匹配斜率,第三项匹配曲率,依此类推。我们加的项越多,逼近有效的范围就越大。
分母里的 n! 不是随便放的。当你对 (x - a)^n 恰好求导 n 次时,会得到 n!。这个阶乘正好把它消掉,从而保证:泰勒多项式的第 n 阶导数,等于原函数在 x = a 处的第 n 阶导数。
**麦克劳林级数(Maclaurin series)**就是以 a = 0 为中心的泰勒级数:
注意 \sin x 只含奇数次幂(它是奇函数),而 \cos x 只含偶数次幂(它是偶函数)。正负号交替出现会让逼近在真实值两边来回振荡,从两侧收敛过去。
让我们用四项来逼近 e^{0.5}:1 + 0.5 + \frac{0.25}{2} + \frac{0.125}{6} = 1 + 0.5 + 0.125 + 0.02083 \approx 1.6458。真实值是 1.6487\ldots,所以仅仅四项就已经给出三位正确的小数。
并不是每个泰勒级数到处都收敛。**收敛半径(radius of convergence)**告诉我们:在离中心 a 多远的范围内,这个级数给出的结果是有效的。在这个半径之内,只要多加项,多项式逼近就可以任意精确;在它之外,级数发散。
**幂级数(power series)**是更一般的形式:\sum_{n=0}^{\infty} a_n (x - c)^n。泰勒级数是一类特殊的幂级数,其系数由各阶导数决定;而其他的幂级数可能由别的规则定义。**比值判别法(ratio test)**用来判断收敛性:计算 \lim_{n \to \infty} \left|\frac{a_{n+1}}{a_n}\right|。若这个极限是 L,则收敛半径为 R = 1/L。
当我们把泰勒级数截断到 n 项之后,就会产生误差。**拉格朗日余项(Lagrange remainder)**给出了这个误差的上界:
这里的 c 是介于 a 和 x 之间的某个未知点。我们没法精确知道 c,但通常可以估计出 |f^{(n+1)}(c)| 的上界,从而得到最坏情况下的误差。分母里的 (n+1)! 增长得极快,所以只要在收敛半径内,多加几项误差就会迅速缩小。
对于多元函数,泰勒展开还会包含混合偏导数。f(\mathbf{x}) 在点 \mathbf{a} 处的二阶逼近为:
第一项是值,第二项用到了梯度(一个向量,见多元微积分),第三项用到了海森矩阵(它刻画了曲率)。这就把我们的矩阵那一章直接和微积分联系了起来:海森矩阵是一个由二阶导数组成的矩阵,描述了函数曲面的形状。
这个多元的二阶逼近正是牛顿法及其他二阶优化方法的基础,我们会在下一个文件里见到它。
除了多项式,还有几种值得了解的逼近方法:
如果一个函数具有让逼近变得可靠的性质,我们就说它「性质良好(well-behaved)」:连续(没有跳跃)、可导(没有尖角)、光滑(各阶导数都存在)、有界(输出保持有限)。
多项式、指数函数和三角函数都是性质良好的。函数性质越好,要得到一个好的逼近,所需的泰勒项就越少。
import jax.numpy as jnp import matplotlib.pyplot as plt x = jnp.linspace(-2, 3, 300) plt.plot(x, jnp.exp(x), "k-", linewidth=2, label="eˣ (exact)") colors = ["#e74c3c", "#3498db", "#27ae60", "#9b59b6"] for n, color in zip([1, 2, 4, 8], colors): approx = sum(x**k / jnp.array(float(jnp.prod(jnp.arange(1, k+1)) if k > 0 else 1)) for k in range(n+1)) plt.plot(x, approx, color=color, linestyle="--", label=f"{n} terms") plt.ylim(-2, 15) plt.legend() plt.title("Taylor approximation of eˣ") plt.show()
import jax.numpy as jnp x = 1.0 exact = jnp.sin(x) taylor = 0.0 for n in range(8): sign = (-1)**n factorial = float(jnp.prod(jnp.arange(1, 2*n+2))) taylor += sign * x**(2*n+1) / factorial error = abs(exact - taylor) bound = x**(2*n+3) / float(jnp.prod(jnp.arange(1, 2*n+4))) print(f"terms={n+1} approx={taylor:.10f} error={error:.2e} bound={bound:.2e}")
import jax.numpy as jnp import matplotlib.pyplot as plt x = jnp.linspace(-3, 3, 300) plt.plot(x, jnp.cos(x), "k-", linewidth=2, label="cos(x)") plt.plot(x, jnp.ones_like(x), "--", color="#e74c3c", label="linear: 1") plt.plot(x, 1 - x**2/2, "--", color="#3498db", label="quadratic: 1 - x²/2") plt.plot(x, 1 - x**2/2 + x**4/24, "--", color="#27ae60", label="4th order") plt.ylim(-2, 2) plt.legend() plt.title("Taylor approximations of cos(x)") plt.show()