跳转至

# 函数逼近

函数逼近用足够接近原函数的简单函数来替代复杂函数。本文涵盖线性化、泰勒级数、多项式逼近、傅里叶级数以及通用逼近定理——这些是神经网络能够学习任意映射的理论基础。

- 我们遇到的许多函数都过于复杂，无法直接处理。例如，在纸上计算 \(e^{0.1}\)、预测卫星轨迹等，都涉及没有简单封闭形式答案的函数。

- 函数逼近用更简单的函数来替代复杂函数，使其在关心区域内"足够接近"原函数。

- 最自然的逼近是多项式。多项式只是 \(x\) 的幂次与系数的和，易于求值、微分和积分。

- 但为什么多项式作为逼近器如此有效？看看 \(x\) 的每个幂次贡献了什么。

  - 常数项 \(a_0\) 设定基准值。
  - \(a_1 x\) 项增加斜率。
  - \(a_2 x^2\) 项增加曲率。
  - 更高的幂次则捕捉函数形状的更多细节。

- 通过选择合适的系数，我们可以逐次匹配函数在某一点的值、斜率、曲率以及高阶行为。

- 当项数足够时，多项式几乎可以模仿任何光滑函数。

- 问题在于：如何找到正确的系数？

- 线性化是最简单的逼近。在点 \(x = a\) 附近，我们用函数的切线来代替它：

\[L(x) = f(a) + f'(a)(x - a)\]

- 这是一阶泰勒逼近。它的思路是：从已知值 \(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\) 的地方有效。离得稍远，逼近就失效了。为了做得更好，我们需要引入高阶项。

- 泰勒级数将函数表示为无穷多个多项式项的和，每一项都捕捉到函数在点 \(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\) 阶导数相等。

- 麦克劳林级数就是中心在 \(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\)，因此四项已经给出了三个正确的小数位。

- 并非所有泰勒级数都处处收敛。收敛半径告诉我们，在距离中心 \(a\) 多远的范围内，级数给出有效的结果。在此半径内，通过增加项数，多项式逼近可以达到任意所需的精度。超出此半径，级数发散。

- 幂级数的一般形式是：\(\sum_{n=0}^{\infty} a_n (x - c)^n\)。泰勒级数是系数由导数确定的幂级数。其他幂级数可能由其他规则定义。比值判别法用于判定收敛性：计算 \(\lim_{n \to \infty} \left|\frac{a_{n+1}}{a_n}\right|\)。如果该极限为 \(L\)，则收敛半径为 \(R = 1/L\)。

- 将泰勒级数截断到 \(n\) 项时，会产生误差。拉格朗日余项给出了这个误差的界限：

\[R_n(x) = \frac{f^{(n+1)}(c)}{(n+1)!}(x-a)^{n+1}\]

- 这里 \(c\) 是 \(a\) 和 \(x\) 之间的某个未知点。我们无法确切知道 \(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})\]

- 第一项是函数值，第二项使用梯度（向量，如我们在多元微积分中看到的），第三项使用海森矩阵（捕捉曲率）。这直接将我们的矩阵章节与微积分联系起来：海森矩阵是一个由二阶导数组成的矩阵，描述了函数表面的形状。

- 这种多变量二阶逼近是牛顿法和其他二阶优化技术的基础，我们将在下一个文件中看到。

- 除了多项式，还有其他值得了解的逼近方法：

  - 样条插值：不用单个高次多项式，而是将多个低次多项式光滑拼接在一起。这避免了高次多项式可能产生的剧烈振荡。
  - 傅里叶级数：将周期函数逼近为正弦和余弦的和。在信号处理和音频中至关重要。
  - 神经网络：通用函数逼近器。只要有足够的神经元，它们可以任意精度逼近任何连续函数。这就是深度学习的理论基础。

- 如果一个函数具有使逼近可靠的性质——连续性（无跳跃）、可微性（无尖角）、光滑性（所有阶导数都存在）和有界性（输出保持有限），我们就称其为"行为良好"的函数。

- 多项式、指数函数和三角函数都属于行为良好的函数。函数行为越好，获得良好逼近所需的泰勒项数就越少。

## 编程练习（使用 CoLab 或 Jupyter 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ˣ (精确值)")

       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} 项")

       plt.ylim(-2, 15)
       plt.legend()
       plt.title("eˣ 的泰勒逼近")
       plt.show()

2. 计算拉格朗日余项，以限定用不同数量的泰勒项逼近 \(\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"项数={n+1}  近似值={taylor:.10f}  误差={error:.2e}  界限={bound:.2e}")

3. 比较在 \(x=0\) 附近 \(\cos(x)\) 的线性化逼近与二次泰勒逼近。在同一张图上绘制两个逼近和真实函数，观察各自精确的范围。

       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="线性: 1")
       plt.plot(x, 1 - x**2/2, "--", color="#3498db", label="二次: 1 - x²/2")
       plt.plot(x, 1 - x**2/2 + x**4/24, "--", color="#27ae60", label="四阶")
       plt.ylim(-2, 2)
       plt.legend()
       plt.title("cos(x) 的泰勒逼近")
       plt.show()