函数逼近


文档摘要

函数逼近 函数逼近用「足够接近、因此足够好用」的简单函数去替代复杂函数。本文件涵盖线性化、泰勒级数、多项式逼近、傅里叶级数,以及万能逼近定理——它从理论上解释了为什么神经网络能够学习任意的映射关系。 我们遇到的很多函数都太复杂,没法直接处理。在纸上算 $e^{0.1}$、预测一颗卫星的轨迹,等等,都涉及那些没有简洁闭式解的函数。 函数逼近(function approximation)就是用一个更简单的函数去替代一个复杂的函数,要求它在我们所关心的区域里「足够接近」。 最自然的逼近工具就是多项式。多项式不过是 $x$ 的各次幂乘上系数再求和,它易于求值、求导和积分。 但为什么多项式能逼近得这么好?想想 $x$ 的每一次幂各自贡献了什么。 常数项 $a0$ 决定了基准值。

函数逼近

函数逼近用「足够接近、因此足够好用」的简单函数去替代复杂函数。本文件涵盖线性化、泰勒级数、多项式逼近、傅里叶级数,以及万能逼近定理——它从理论上解释了为什么神经网络能够学习任意的映射关系。

  • 我们遇到的很多函数都太复杂,没法直接处理。在纸上算 e^{0.1}、预测一颗卫星的轨迹,等等,都涉及那些没有简洁闭式解的函数。

  • **函数逼近(function approximation)**就是用一个更简单的函数去替代一个复杂的函数,要求它在我们所关心的区域里「足够接近」。

  • 最自然的逼近工具就是多项式。多项式不过是 x 的各次幂乘上系数再求和,它易于求值、求导和积分。

  • 但为什么多项式能逼近得这么好?想想 x 的每一次幂各自贡献了什么。

    • 常数项 a_0 决定了基准值。
    • a_1 x 项贡献了斜率。
    • a_2 x^2 项贡献了曲率。
    • 每更高一次幂,就捕捉到函数形状更精细的细节。

多项式的每一项都为逼近叠加一层新的细节

  • 只要选对系数,我们就能在某一点上依次匹配函数的值、斜率、曲率以及更高阶的行为,一项一项地逼近。

  • 只要项数足够多,这个多项式就能模仿几乎任何光滑函数。

  • 问题就变成了:我们该怎么找到对的系数?

  • **线性化(linearisation)**是最简单的逼近。在点 x = a 附近,我们用函数的切线去代替它:

L(x) = f(a) + f'(a)(x - a)
  • 这就是一阶的泰勒逼近(Taylor approximation)。它的意思是:从已知的值 f(a) 出发,再按斜率乘以到 a 的距离来修正。

  • 例如,在 x = 0 处线性化 \sin(x)f(0) = 0f'(0) = \cos(0) = 1,所以 L(x) = x。在零附近,\sin(x) \approx x。试试看:\sin(0.1) = 0.0998\ldots \approx 0.1

  • 但线性化只在非常靠近 a 的地方才好用。离得远一点,这个逼近就失效了。想要更好,我们就要引入更高阶的项。

  • **泰勒级数(Taylor series)**把函数表示成一个无穷的多项式之和,每一项都捕捉函数在点 a 附近更精细的行为:

f(x) = \sum_{n=0}^{\infty} \frac{f^{(n)}(a)}{n!}(x - a)^n = f(a) + f'(a)(x-a) + \frac{f''(a)}{2!}(x-a)^2 + \frac{f'''(a)}{3!}(x-a)^3 + \cdots

泰勒级数:加的项越多,逼近就越好

  • 每多一项,就多一个修正。第一项匹配值,第二项匹配斜率,第三项匹配曲率,依此类推。我们加的项越多,逼近有效的范围就越大。

  • 分母里的 n! 不是随便放的。当你对 (x - a)^n 恰好求导 n 次时,会得到 n!。这个阶乘正好把它消掉,从而保证:泰勒多项式的第 n 阶导数,等于原函数在 x = a 处的第 n 阶导数。

  • **麦克劳林级数(Maclaurin series)**就是以 a = 0 为中心的泰勒级数:

f(x) = \sum_{n=0}^{\infty} \frac{f^{(n)}(0)}{n!} x^n
  • 一些著名的麦克劳林级数:
e^x = 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \cdots
\sin x = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \cdots
\cos x = 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + \cdots
  • 注意 \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)**给出了这个误差的上界:

R_n(x) = \frac{f^{(n+1)}(c)}{(n+1)!}(x-a)^{n+1}
  • 这里的 c 是介于 ax 之间的某个未知点。我们没法精确知道 c,但通常可以估计出 |f^{(n+1)}(c)| 的上界,从而得到最坏情况下的误差。分母里的 (n+1)! 增长得极快,所以只要在收敛半径内,多加几项误差就会迅速缩小。

  • 对于多元函数,泰勒展开还会包含混合偏导数。f(\mathbf{x}) 在点 \mathbf{a} 处的二阶逼近为:

f(\mathbf{x}) \approx f(\mathbf{a}) + \nabla f(\mathbf{a})^T (\mathbf{x} - \mathbf{a}) + \frac{1}{2} (\mathbf{x} - \mathbf{a})^T H(\mathbf{a}) (\mathbf{x} - \mathbf{a})
  • 第一项是值,第二项用到了梯度(一个向量,见多元微积分),第三项用到了海森矩阵(它刻画了曲率)。这就把我们的矩阵那一章直接和微积分联系了起来:海森矩阵是一个由二阶导数组成的矩阵,描述了函数曲面的形状。

  • 这个多元的二阶逼近正是牛顿法及其他二阶优化方法的基础,我们会在下一个文件里见到它。

  • 除了多项式,还有几种值得了解的逼近方法:

    • 样条插值(spline interpolation):不用一个高次多项式,而是把很多个低次多项式平滑地拼接起来。这样可以避免高次多项式可能出现的那种剧烈振荡。
    • 傅里叶级数(Fourier series):把周期函数逼近成一系列正弦和余弦的和。在信号处理和音频里至关重要。
    • 神经网络(neural networks):万能函数逼近器。只要神经元足够多,它就能以任意精度逼近任何连续函数。这正是深度学习在理论上站得住脚的理由。
  • 如果一个函数具有让逼近变得可靠的性质,我们就说它「性质良好(well-behaved)」:连续(没有跳跃)、可导(没有尖角)、光滑(各阶导数都存在)、有界(输出保持有限)。

  • 多项式、指数函数和三角函数都是性质良好的。函数性质越好,要得到一个好的逼近,所需的泰勒项就越少。

编程练习(使用 CoLab 或 notebook)

  1. 用越来越多的泰勒项来逼近 e^x,并可视化逼近效果是如何改善的。
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()
  1. 计算拉格朗日余项,为用不同项数的泰勒展开逼近 \sin(1) 时所产生的误差给出上界。
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}")
  1. 对比 \cos(x)x = 0 附近的线性化与二阶泰勒逼近。把两种逼近和真实函数画在一起,观察各自在多大范围内是准确的。
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()

发布者: 作者: HenryNdubuaku 转发
评论区 (0)
U