我做算法开发和带人的这些年被问得最多的不是“这个模型叫什么”而是“这个公式到底在干什么”。尤其是讲到梯度下降、反向传播、损失函数这些概念时最后几乎都会绕回到同一个东西泰勒公式和麦克劳林公式。我发现很多学人工智能的同学不是不努力而是卡在了一个很要命的地方——把泰勒公式当成高数期末考的一个考点背完了就扔完全不知道它跟神经网络有什么关系。这篇文章就从我的视角把它彻底讲透不绕弯子直接告诉你这两个公式在AI里到底藏在哪里、怎么用、为什么绕不开。先说结论梯度下降本质是一阶泰勒展开的一次截断牛顿法是二阶泰勒展开的求极值softmax里的数值稳定处理要用到对数展开激活函数的近似实现也离不开多项式逼近。你不需要把这些公式当定理去背只需要把它们当成“用导数信息重建函数”的工具很多算法设计的直觉会一下子通。这篇适合两类人一类是刚接触人工智能、被数学劝退的初学者另一类是已经在用深度学习框架写代码但心里总觉得底层原理是黑盒的开发者。1. 从一张AI算法图说起泰勒公式和麦克劳林公式到底藏在哪里1.1 一个让很多学习者后知后觉的发现我第一次真正意识到泰勒公式的价值不是在课堂上而是工作后重新去推梯度下降的更新公式时。当时我在纸上写下损失函数的泰勒展开突然发现梯度下降不过是在做一件极其朴素的事用当前位置的切线方向代替整个函数然后沿着切线往下走一步。后来看的东西越多这个感觉越强烈。反向传播里的“局部梯度”本质就是函数在某一点的线性系数牛顿法里的“二阶导”是泰勒展开第二项的系数甚至很多滤波算法、卡尔曼滤波、高斯过程底层都离不开局部多项式展开的思想。可以说泰勒公式是沟通“导数”和“函数值”之间的那座桥而机器学习算法百分之八九十都在反复用这座桥。所以我想先把这个公式的直觉讲清楚。如果你能理解它到底在做什么后面所有的应用都会变得顺理成章。1.2 泰勒公式到底在做什么用手里的导数信息还原函数的样子假设我们现在只知道一个函数在某个点 (x_0) 处的取值和若干阶导数值但不知道函数的完整解析式能不能把附近的值猜出来最简单的想法是用切线。如果 (f(x)) 在 (x_0) 附近变化平缓那 (f(x)) 约等于 (f(x_0)f(x_0)(x-x_0))。这是一阶近似也是一个直线方程。但直线只能反映“当前的倾斜程度”如果函数是弯曲的直线就会偏。怎么办再加一个二阶项变成抛物线让它匹配上 (x_0) 处的“弯曲程度”。如果还不够就加三阶、四阶项不断修正。这其实就是泰勒公式[ f(x)f(x_0)f(x_0)(x-x_0)\frac{f(x_0)}{2!}(x-x_0)^2\cdots\frac{f^{(n)}(x_0)}{n!}(x-x_0)^nR_n(x) ]注意每一项前面的系数为什么带阶乘原因很简单如果你用一个多项式去逼近 (f(x))为了保证这个多项式在 (x_0) 处的各阶导数和原函数完全一致第 (n) 项的系数就必须被 (n!) 除一次。我当年学的时候经常忘记这个分母直到自己动手对多项式求了一次 (n) 阶导才彻底记住。我比较喜欢用“天气预报”来类比只知道此刻的温度、温度变化速度和变化的加速度你就能推测几小时后的温度。导数的阶数越多相当于掌握的变化规律越细预测也就越准。泰勒公式就是这个预测过程的标准答案。1.3 麦克劳林公式不是新公式而是泰勒在原点处的特例麦克劳林公式就是把泰勒公式的展开点 (x_0) 固定为 0[ f(x)f(0)f(0)x\frac{f(0)}{2!}x^2\cdots\frac{f^{(n)}(0)}{n!}x^n\cdots ]数学上它没有任何新东西就是个特例。但在工程里这个特例出现得实在太多了。为什么因为很多算法的默认状态就建立在“当前点离零点不远”的假设上。权重初始化取零附近的小随机数特征会做标准化让数据落在零附近概率模型里也经常围绕“事件不发生”的状态做小扰动分析。举例来说(e^x) 在 0 点的麦克劳林展开是[ e^x1x\frac{x^2}{2!}\frac{x^3}{3!}\cdots ]这个展开式在计算指数、softmax、交叉熵这些函数时频繁出现。某种意义上你不需要去记忆麦克劳林公式的“独立概念”只需要记住以后看到“在 0 附近的泰勒展开”那就是麦克劳林公式的工作现场。2. 梯度下降和牛顿法都是泰勒公式截断的不同阶数2.1 一阶截断就是梯度下降它不过是在走切线很多教程讲梯度下降时会直接抛出一个更新公式[ \theta_{t1}\theta_t-\eta \nabla L(\theta_t) ]然后解释“这是沿着负梯度方向下降”。但这只是描述了操作没有解释为什么。回头用泰勒公式一看逻辑非常简单。把损失函数 (L(\theta)) 在当前位置 (\theta) 处做一阶泰勒展开[ L(\theta\Delta\theta)\approx L(\theta)\nabla L(\theta)^T\Delta\theta ]我们希望找到一个小步长 (\Delta\theta)让移动之后函数值变小。上式中第二项是 (L(\theta)) 的变化量。要让变化量为负(\Delta\theta) 就必须和梯度的方向大致相反。于是取[ \Delta\theta-\eta \nabla L(\theta) ](-\eta) 里的负号就是“朝反方向走”(\eta) 控制走多远。这个推导吸引了我的地方在于它同时解释了为什么学习率不能太离谱。一阶泰勒展开只在当前点的邻域内成立(\Delta\theta) 越大线性近似越失真。你把学习率调到 10相当于拿着一个点上的切线去预测十万八千里外的函数值不爆炸才怪。所以深度学习训练里学习率通常要从小开始调不是工程惯例而是数学上的一阶近似边界决定的。2.2 二阶截断就是牛顿法把弯曲也考虑进去如果我们不甘心只用到一阶信息把泰勒展开多写一项[ L(\theta\Delta\theta)\approx L(\theta)\nabla L(\theta)^T\Delta\theta\frac12\Delta\theta^T H(\theta)\Delta\theta ]其中 (H(\theta)) 是损失函数的 Hessian 矩阵也就是二阶导组成的矩阵。对 (\Delta\theta) 求导并令导数为零可以得到使这个二次近似最小的步长[ \Delta\theta-H^{-1}\nabla L(\theta) ]这就是牛顿法的更新公式。它比梯度下降多用了二阶信息所以对“弯曲”的函数适应更快理论上收敛步数少很多。代价也很明显要计算和求逆一个 (n\times n) 的 Hessian 矩阵当参数是百万级别时这是不可能完成的任务。所以标准牛顿法在高维深度学习中基本用不了但在低维最优化、内点法和一些数值计算场景里仍然非常有用。到这里你应该能看出梯度下降和牛顿法不是两个割裂的算法它们是同一个泰勒公式截断到不同阶数的结果。一阶截断得到的是直来直往的梯度下降二阶截断得到的是考虑曲率的牛顿法。2.3 实际算法里的“阶数经济学”深度学习里还有个常见的算法家族叫拟牛顿法比如 BFGS、L-BFGS它们的思路很巧妙不直接算 Hessian而是用梯度变化的序列去近似 Hessian 的逆。这本质上是在“一阶信息里挤出二阶效果”。这些细节如果你不了解泰勒公式理解起来会非常费力。下面这张表是我平时给新同学讲优化器时常用的直接拿过来参考优化算法使用的泰勒展开阶数实际代价应用场景SGD / Adam一阶每次只需算梯度内存开销小深度学习大模型训练牛顿法二阶需要 Hessian 及其逆计算量巨大低维强凸优化L-BFGS近似二阶用历史梯度近似 Hessian省内存中小规模模型、无约束优化高斯牛顿法二阶近似利用残差结构简化 Hessian非线性最小二乘比如 SLAM、拟合问题我自己的体会是一旦理解了“阶数”这个维度看优化器就有一种俯视的感觉。Adam 再怎么加动量、加自适应学习率底层仍然是一阶策略的加强版而 牛顿法之所以在某些问题上收敛得飞快是因为它主动去拟合了函数的弯曲。不是 Adam 更聪明是它没用到二阶信息。2.4 一个让我想明白“学习率”的场景有一次我用牛顿法求一个非常简单的二维函数最小值从同一个初始点出发梯度下降学了五六步才收敛牛顿法一步就跳到了中心。当时我还没有那么强烈的感觉后来在纸上把两个更新方向画出来才发现梯度下降是用切线一步一步摸着走牛顿法则直接根据当前点的抛物线近似猜到了下一步落点应该在哪。这个对比让我明白了一件事学习率为什么是深度学习里最重要的超参数之一因为一阶近似是“短视”的它只告诉你当前最陡的方向却不知道走了这一步之后函数会不会突然抬升。学习率本质上是在补偿一阶近似没有“远见”的那个缺口。没有泰勒公式这层视角你永远只能靠调参经验去猜。3. 常见展开式、余项误差与收敛半径数学细节不能跳过3.1 常用的几个麦克劳林展开式最好形成肌肉记忆做AI算法有几个展开式出现频率极高我建议不要只记结论要自己动手推一遍。推导套路完全一致在 0 点求各阶导数然后代入麦克劳林公式。下面是我整理的一个小表建议收藏函数麦克劳林展开收敛半径说明(e^x)(1x\frac{x^2}{2!}\frac{x^3}{3!}\cdots)对所有实数收敛(\sin x)(x-\frac{x^3}{3!}\frac{x^5}{5!}-\cdots)对所有实数收敛(\cos x)(1-\frac{x^2}{2!}\frac{x^4}{4!}-\cdots)对所有实数收敛(\ln(1x))(x-\frac{x^2}{2}\frac{x^3}{3}-\cdots)(-1 x \le 1)(\frac{1}{1-x})(1xx^2x^3\cdots)(|x|1)((1x)^\alpha)(1\alpha x\frac{\alpha(\alpha-1)}{2!}x^2\cdots)(|x|1)(\alpha) 不同时边界情况不同我个人的记忆方法是不要死背每一项系数而是记住“每多一项就是让展开多项式在 0 点的第 n 阶导数和原函数相等”。比如 (\sin x) 的一阶展开就是 (x)因为 (\sin 00)、(\sin(0)1)二阶项系数是 (f(0)/20/20)所以没有二次项。理解了这个产生过程系数是自然涌现的根本不用背。3.2 余项展开到第 n 阶误差到底有多大用泰勒公式最怕“只取几项然后假装精确”。实际工程里误差分析跟结果本身一样重要。泰勒展开的余项最常用的是拉格朗日余项[ R_n(x)\frac{f^{(n1)}(\xi)}{(n1)!}(x-x_0)^{n1} ]其中 (\xi) 落在 (x_0) 和 (x) 之间。这个式子给的是一个误差上界的估计方法虽然我们不知道 (\xi) 具体是多少但能根据 (f^{(n1)}) 的最大值估算出误差范围。举个我用过的例子在数值计算里用一阶泰勒替代 (\ln(1x))当 (x0.1) 时如果只取一阶项 (x)真实值和近似值的差是 (\ln(1.1)-0.1\approx -0.00469)。拉格朗日余项给出的上界是 (\frac{1}{2(1\xi)^2}\times0.01)因为 (\xi0)分母大于 1上界小于 (0.005)跟实际误差在同一量级。这说明余项公式不只是理论它真的能用来判断你的近似干不靠谱。在机器学习里我们经常会在损失函数上做近似比如用代理损失、用采样估计梯度。这时候心里要有一根弦你丢掉的余项可能正在主导梯度的方向。3.3 收敛半径展开不是万能钥匙还有一个特别容易踩的坑泰勒展开有适用范围。看 (\frac{1}{1-x}) 的麦克劳林展开它只在 (|x|1) 时收敛。如果你拿来近似 (x1.5)得到的级数会越来越大完全崩掉。原因在于 (x1) 是函数的“奇点”——函数在这一点取无穷大Taylor多项式在一个带奇点的区域里无论如何都跨不过去。这个观点在特征标准化里特别有意思。当我们在训练前把特征归一化到 0 附近很多人以为只是为了让梯度下降更平稳、学习率更容易调。从泰勒公式的视角看你其实是在让模型依赖的那些非线性变换都工作在“展开点附近”。一旦特征分布偏移比如出现极端值很多非线性映射的局部近似就会失真模型预测自然变差。这里我还有一个个人经验处理长尾特征时与其直接对原始值做线性归一化不如先做对数变换或幂变换让数值分布更紧地围绕在 0 附近。背后的数学原因其实就是这些非线性函数在 0 点附近最容易用低阶多项式拟合。这种认知在特征工程里非常实用远比“跟着经验走”可靠。4. 神经网络里那些随处可见的泰勒影子4.1 softmax 和 logsumexp数值稳定性本质也是展开问题写过 softmax 的人都知道如果直接算 (e^{x_i})当 (x_i) 特别大的时候很容易溢出。标准解法是把所有数减去最大值 (m)[ \text{softmax}(x_i)\frac{e^{x_i-m}}{\sum_j e^{x_j-m}} ]这背后的 logsumexp 变换长这样[ \ln(\sum_i e^{x_i})m\ln(1\sum_{i\ne \text{max}} e^{x_i-m}) ]注意后面那个 (\ln(1\sum e^{x_i-m}))当其余项很小时它正好可以用 (\ln(1u)) 的麦克劳林展开来理解(u0) 时值为 0一阶项是 (u)。这保证了即使 (x_i) 很小我们也能得到一个稳定且足够精确的对数概率。虽然实际代码里不会手动展开这一项但其设计思想跟泰勒展开是一致的让计算尽量围绕数据实际分布的中心展开而不是在最容易溢出的极端状态里硬闯。4.2 激活函数为什么神经网络需要非线性很多人问过一个问题为什么激活函数一定要是非线性的如果去掉 ReLU、sigmoid 这些神经网络就变成了多个线性变换的复合最后还是线性变换。换句话说网络的表达能力来自于非线性。那非线性能力从哪来泰勒公式给了我们一个视角任意光滑函数都可以用多项式逐阶逼近。一个神经元如果用 sigmoid 激活从局部看它输出的是一系列高阶项的叠加层与层之间不断复合这些高阶项就能逼近极其复杂的函数。反过来说如果所有激活都是线性的你连二次函数都拟合不出来。更有意思的是 sigmoid 在 0 附近的麦克劳林展开[ \sigma(x)0.50.25x-\frac{x^3}{48}\cdots ]前两项其实是线性的高次项很微弱。这意味着如果权重初始化得特别小输入的预激活接近 0网络在一开始几乎就是线性的。这就解释了为什么初始化尺度会显著影响训练行为初始化越大非线性越早被激活梯度信息也越不稳定初始化太小网络又退化成线性模型。理解这点后你对“初始化为什么重要”就不再停留在别人告诉你“要 Xavier 初始化”的层面了。4.3 泰勒展开和过拟合项数并不是越多越好用泰勒公式做插值展开阶数越高局部拟合越精确但离开展开点后常常剧烈震荡。这个过程跟机器学习里的过拟合几乎是同一个模型。可以拿多项式回归来感受一下。给一组点用一次多项式拟合欠拟合用五次多项式训练集误差很低但测试点稍微远一点就飞了。这和高阶泰勒展开的“局部准、全局飘”一模一样。深度学习中模型参数就是多项式的系数每一层隐藏单元相当于引入了更多“高阶项”当数据不够时这些高阶项拟合的是噪声而不是真实规律所以泛化差。我也建议你在项目里遇到反常的模型抖动时想一想是不是特征分布离“展开中心”太远、或者模型的非线性容量超过了你拥有的数据量。很多时候一个简单的正则化手段比如给权重加 L2 约束本质上就是在压缩那些高阶项的系数幅度让函数不至于在采样点之外疯狂震荡。泰勒公式的视角给了我们一个很朴素的解释复杂度带来的收益有一个边界过了这个边界误差不会停止增长公共社区的经典曲线会呈 U 型这个 U 型在数学上跟余项的增长是同构的。5. 我用代码验证泰勒公式的几个实验5.1 实验一麦克劳林展开逼近 e 的 x 次方理解归理解还是要写点代码让感知落地。我用 Python 写了麦克劳林展开逼近 (e^x) 的函数然后对比不同阶数在 (x0.5) 和 (x2) 时的误差。import math def exp_approx(x, n): result 0.0 term 1.0 for k in range(n 1): if k 0: term term * x / k result term return result for x in [0.5, 1.0, 2.0]: true_val math.exp(x) print(fx{x}, 真值{true_val:.10f}) for n in [1, 3, 5, 8, 12]: approx exp_approx(x, n) err abs(approx - true_val) print(f n{n:2d}, 近似{approx:.10f}, 误差{err:.2e})我跑出来的结果大概是这样的规律当 (x0.5) 时(n5) 误差已经到 (1e-7) 以下当 (x2) 时同样取 5 项误差还在 (1e-2) 量级。这说明同一个展开式展开点离得越远需要的阶数越多。这正对应之前说的局部性是一切泰勒近似的前提你用几个导数信息重建一个局部函数覆盖范围是有限度的。5.2 实验二从一阶泰勒推导出的梯度下降做最优化为了展示一阶泰勒近似的实际使用我写了一个非常简单的二维最优化问题。目标函数是[ L(\theta_1,\theta_2)(\theta_1-3)^2(\theta_21)^2 ]它的最小值在 ((3,-1))。从原点出发用固定学习率的梯度下降迭代 30 次打印路径上几个关键点。import numpy as np theta np.array([0.0, 0.0]) eta 0.15 def loss(t): return (t[0] - 3) ** 2 (t[1] 1) ** 2 def grad(t): return np.array([2 * (t[0] - 3), 2 * (t[1] 1)]) for i in range(1, 31): theta theta - eta * grad(theta) if i 5 or i % 10 0: print(fstep{i:2d}, theta{theta}, loss{loss(theta):.6f})这里每一步都相当于在做一个一阶泰勒截断决策用当前点的梯度方向走一小步。学习率 (0.15) 时能平滑收敛如果把 (\eta) 改成 (0.3)可能开始震荡改成大于 0.5 甚至直接发散。你可以亲手改一下感受一阶近似在步长过大时如何失效。这种用“失败”来建立直觉的过程比看十遍公式都有用。5.3 实验三不同阶数展开对 sin 函数逼近效果的对比最后一个实验用来观察“阶数增加带来的边际收益”。我们知道 (\sin x) 的麦克劳林展开只含奇次项[ \sin x \approx x-\frac{x^3}{6}\frac{x^5}{120} ]我这里分别用一阶、三阶、五阶去逼近 (\sin x)并统计在区间 ([-\pi,\pi]) 上的最大误差。import numpy as np def sin_approx(x, n_order): if n_order 1: return x if n_order 3: return x - x ** 3 / 6 if n_order 5: return x - x ** 3 / 6 x ** 5 / 120 xs np.linspace(-np.pi, np.pi, 1000) for order in [1, 3, 5]: approx np.array([sin_approx(x, order) for x in xs]) err np.max(np.abs(approx - np.sin(xs))) print(forder{order}, 最大误差{err:.4f})一阶展开在 (\pi) 附近的最大误差能达到 3 量级三阶降到 0.5 左右五阶只剩 0.08 左右。阶数越多越准这是显然的。但我做这个实验更想看的其实是在展开点附近 0.5 范围一阶展开的误差已经非常小在远离展开点的地方哪怕加到五阶也谈不上完美。这又一次说明了“局部精确、全局有限”这个主题。实际工程里这种“阶数-范围”之间的平衡会直接体现在你要不要对输入做标准化、要不要限制特征的极端值范围上。没有这个直觉很容易在数据预处理环节犯懒结果模型效果一直上不去。说了这么多我最想留给你的一句话是不要再去背泰勒公式的证明过程把它当成一个“用导数重建函数”的工具就好。每看到一个机器学习算法先问一句它是多少阶的近似展开点选在了哪里丢掉的余项可能在什么情况下变坏。带着这三个问题去读优化器、读激活函数、读损失函数你会觉得数学和代码之间的墙一下子矮了很多。我自己也是踩过无数次数值震荡、过拟合、不收敛的坑之后才慢慢建立起这套认知的。希望这篇文章帮你把这条路走快一点。
阅读完成 · 觉得有帮助?