try ai
科普
编辑
分享
反馈
  • 前向模式自动微分

前向模式自动微分

SciencePedia玻尔百科
关键要点
  • 前向模式 AD 通过使用对偶数扩展标准算术来计算精确导数,对偶数在计算过程中同时携带函数值及其导数。
  • 该方法将链式法则机械化,无需符号操作即可自动将导数通过复杂的计算机程序(包括控制流)进行传播。
  • 对于少输入、多输出的函数,该方法效率最高,因为每次传递都会计算一个雅可比-向量积,使其成为某些灵敏度分析的理想选择。
  • 与有限差分相比,前向模式 AD 提供了更优的数值稳定性,是优化、求根以及在科学领域中对机器学习模型进行微分的核心引擎。

引言

计算函数导数是科学与工程的基石,但我们如何教会计算机为由复杂代码定义的函数执行此任务?传统方法存在显著缺陷。符号微分通过操作数学表达式来进行,但在面对真实世界程序中的循环和条件语句时常常会失败。数值微分使用小步长来近似导数,但它永远陷入截断误差和灾难性数值不稳定性之间的困境。这些局限性凸显了一个关键的空白:需要一种能够为任意代码高效、稳健地计算精确导数的方法。

本文探讨了第三种更为优雅的解决方案:自动微分(AD),并特别关注其前向模式。你将发现一种强大的技术,它既不是符号方法也不是近似方法,而是通过扩展基本算术规则来计算达到机器精度的导数。接下来的章节将引导你了解这一概念,首先解释其核心原理,然后展示其多样化的应用。第一章“原理与机制”将介绍对偶数的概念,并揭示它们如何自动执行微积分的法则。第二章“应用与跨学科联系”将展示前向模式 AD 如何成为优化、灵敏度分析和前沿科学发现的强大引擎。

原理与机制

想象一下,你希望计算机能进行微积分计算。不仅仅是将数字代入你已经推导出的公式,而是要找到一个以代码形式给出的函数的导数。你会如何教一台只真正理解算术的机器,掌握寻找变化率这门精妙的艺术呢?

一种方法是​​符号微分​​,就像一年级微积分课上学生学习的方式一样。你教计算机规则——乘法法则、除法法则、链式法则——然后它将数学表达式 x2x^2x2 处理成 2x2x2x。这种方法很强大,但出奇地脆弱。一个计算机程序不仅仅是一个清晰的数学公式;它通常是if-else语句、循环和函数调用的混乱纠缠。当面对由迭代过程或条件逻辑定义的函数时,符号方法常常会陷入停顿。

第二种更直接的方法是​​数值微分​​。这是物理学家在信封背面计算时会用的方法。我们记得导数的定义:

f′(x)=lim⁡h→0f(x+h)−f(x)hf'(x) = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}f′(x)=h→0lim​hf(x+h)−f(x)​

所以,我们只需为 hhh 选择一个非常小的数,比如 0.0010.0010.001,然后计算这个分数。这给了我们一个近似值。但它始终只是一个近似值。这种方法的误差,称为​​截断误差​​,通常与 hhh 成正比。我们可以让 hhh 更小以获得更精确的答案,但这会打开一个充满数值问题的潘多拉魔盒。如果 hhh 变得太小,f(x+h)f(x+h)f(x+h) 和 f(x)f(x)f(x) 可能会非常接近,以至于我们的有限精度计算机会将它们的差计算为零或一个被舍入误差主导的值。这种效应被称为​​灾难性抵消​​,可能导致结果严重失真,尤其是在敏感的计算中。我们陷入了截断误差的斯库拉和舍入误差的卡律布狄斯之间的两难境地。

一定有更好的方法。确实有。它被称为​​自动微分(AD)​​,它既优雅又强大。它不是符号微分,也不是数值近似。它是第三种独特而绝妙的方法,可以精确、高效地计算导数。

一种新的数

前向模式自动微分的核心思想是:如果我们在计算一个函数值的同时,能在每一步都将其导数也一并携带,会怎么样?要做到这一点,我们需要发明一种新的数。

我们称之为​​对偶数​​。一个对偶数不仅仅是一个单一的值 aaa。它是一个数对,我们用一种可能让你想起复数的形式来书写它:a+bϵa + b\epsilona+bϵ。在这里,aaa 是“实部”,即我们变量的实际值。新的部分是 bϵb\epsilonbϵ。我们可以将 bbb 看作“对偶部”,它将持有导数。那么 ϵ\epsilonϵ 是什么呢?它是一个奇特的新对象,具有一个神奇的属性:ϵ2=0\epsilon^2 = 0ϵ2=0。

可以把 ϵ\epsilonϵ 想象成一个无穷小的量,小到它的平方完全可以忽略不计。它是一个占位符,让我们能够清晰地将一个值与其导数分离开。ϵ2=0\epsilon^2=0ϵ2=0 这条规则是解开整个机器的关键。

让我们看看它的实际效果。假设我们有一个函数 f(x)=2x3−5x2+3x+7f(x) = 2x^3 - 5x^2 + 3x + 7f(x)=2x3−5x2+3x+7,我们想求它在 x=4x=4x=4 处的值和导数。我们不再代入数字 444,而是代入对偶数 4+1ϵ4 + 1\epsilon4+1ϵ。对偶部中的 111 表示这是我们的输入变量,而 xxx 相对于自身的变化率 dxdx\frac{dx}{dx}dxdx​ 当然是 111。现在,让我们观察算术的展开过程,始终记住 ϵ2=0\epsilon^2=0ϵ2=0。

首先,让我们求 x2x^2x2: (4+1ϵ)2=42+2(4)(1ϵ)+(1ϵ)2=16+8ϵ+0=16+8ϵ(4+1\epsilon)^2 = 4^2 + 2(4)(1\epsilon) + (1\epsilon)^2 = 16 + 8\epsilon + 0 = 16 + 8\epsilon(4+1ϵ)2=42+2(4)(1ϵ)+(1ϵ)2=16+8ϵ+0=16+8ϵ 注意我们得到了什么。实部 161616 就是 424^242。对偶部 888 恰好是 x2x^2x2 在 x=4x=4x=4 处的导数,即 2x=2(4)=82x = 2(4) = 82x=2(4)=8。这并非巧合。

我们继续。对于 x3x^3x3: x3=x2⋅x=(16+8ϵ)(4+1ϵ)=16(4)+16(1ϵ)+(8ϵ)(4)+(8ϵ)(1ϵ)x^3 = x^2 \cdot x = (16 + 8\epsilon)(4 + 1\epsilon) = 16(4) + 16(1\epsilon) + (8\epsilon)(4) + (8\epsilon)(1\epsilon)x3=x2⋅x=(16+8ϵ)(4+1ϵ)=16(4)+16(1ϵ)+(8ϵ)(4)+(8ϵ)(1ϵ) =64+16ϵ+32ϵ+8ϵ2=64+48ϵ= 64 + 16\epsilon + 32\epsilon + 8\epsilon^2 = 64 + 48\epsilon=64+16ϵ+32ϵ+8ϵ2=64+48ϵ 同样,实部是 43=644^3 = 6443=64,对偶部是 x3x^3x3 在 x=4x=4x=4 处的导数,即 3x2=3(16)=483x^2 = 3(16) = 483x2=3(16)=48。

现在我们可以计算整个多项式了: f(4+1ϵ)=2(64+48ϵ)−5(16+8ϵ)+3(4+1ϵ)+7f(4+1\epsilon) = 2(64 + 48\epsilon) - 5(16 + 8\epsilon) + 3(4 + 1\epsilon) + 7f(4+1ϵ)=2(64+48ϵ)−5(16+8ϵ)+3(4+1ϵ)+7 =(128+96ϵ)−(80+40ϵ)+(12+3ϵ)+7= (128 + 96\epsilon) - (80 + 40\epsilon) + (12 + 3\epsilon) + 7=(128+96ϵ)−(80+40ϵ)+(12+3ϵ)+7 分别组合实部和对偶部: =(128−80+12+7)+(96−40+3)ϵ= (128 - 80 + 12 + 7) + (96 - 40 + 3)\epsilon=(128−80+12+7)+(96−40+3)ϵ =67+59ϵ= 67 + 59\epsilon=67+59ϵ

结果出来了!在单次计算过程中,我们发现函数值为 f(4)=67f(4) = 67f(4)=67,其导数为 f′(4)=59f'(4) = 59f′(4)=59。没有极限,没有近似。导数作为 ϵ\epsilonϵ 的系数,精确而纯净地出现了。这就是前向模式 AD 的核心机制。

伪装的链式法则

那么,这种对偶数魔法到底是什么?它仅仅是针对多项式的一个小技巧吗?答案是否定的,其原因揭示了微积分结构的深层奥秘。对偶数的算术规则实际上是基本微分法则的伪装。

让我们取两个对偶数,u=uv+udϵu = u_v + u_d\epsilonu=uv​+ud​ϵ 和 v=vv+vdϵv = v_v + v_d\epsilonv=vv​+vd​ϵ,其中下标 vvv 和 ddd 分别表示值和导数部分。

  • ​​加法​​:u+v=(uv+vv)+(ud+vd)ϵu+v = (u_v + v_v) + (u_d + v_d)\epsilonu+v=(uv​+vv​)+(ud​+vd​)ϵ。这恰好是导数的加法法则:(u+v)′=u′+v′(u+v)' = u' + v'(u+v)′=u′+v′。

  • ​​乘法​​:u×v=(uvvv)+(uvvd+udvv)ϵu \times v = (u_v v_v) + (u_v v_d + u_d v_v)\epsilonu×v=(uv​vv​)+(uv​vd​+ud​vv​)ϵ。这就是乘法法则!(uv)′=uv′+u′v′(uv)' = uv' + u'v'(uv)′=uv′+u′v′。

当我们将函数复合时,其真正的美才得以展现。考虑计算 h(x)=f(g(x))h(x) = f(g(x))h(x)=f(g(x))。AD 过程遵循计算本身。

  1. 首先,我们用输入 x0+1ϵx_0 + 1\epsilonx0​+1ϵ 来计算内部函数 g(x)g(x)g(x)。根据我们之前的发现,这将产生一个新的对偶数:g(x0)+g′(x0)ϵg(x_0) + g'(x_0)\epsilong(x0​)+g′(x0​)ϵ。我们称这个中间结果为 udualu_{dual}udual​。

  2. 接下来,我们使用 udualu_{dual}udual​ 作为输入来计算外部函数 fff。也就是说,我们计算 f(udual)=f(g(x0)+g′(x0)ϵ)f(u_{dual}) = f(g(x_0) + g'(x_0)\epsilon)f(udual​)=f(g(x0​)+g′(x0​)ϵ)。

应用 fff 的泰勒展开(这正是在基本函数上进行对偶数求值所做的),我们得到: f(a+bϵ)=f(a)+f′(a)⋅bϵf(a + b\epsilon) = f(a) + f'(a) \cdot b\epsilonf(a+bϵ)=f(a)+f′(a)⋅bϵ 代入 a=g(x0)a=g(x_0)a=g(x0​) 和 b=g′(x0)b=g'(x_0)b=g′(x0​),结果是: f(g(x0))+f′(g(x0))g′(x0)ϵf(g(x_0)) + f'(g(x_0)) g'(x_0) \epsilonf(g(x0​))+f′(g(x0​))g′(x0​)ϵ 看看 ϵ\epsilonϵ 的系数。它正是 f′(g(x0))g′(x0)f'(g(x_0)) g'(x_0)f′(g(x0​))g′(x0​),这恰好是 h(x)h(x)h(x) 导数的​​链式法则​​!我们从未编写过链式法则。我们只为对偶数定义了基本的算术运算。链式法则从函数的一步步求值中自动产生。AD 将链式法则机械化了。这种从简单的局部规则构建复杂真理的原则,在物理学和数学中是一个反复出现的主题。

通过定义一个 DualNumber 类并重载标准的算术运算符(+、* 等)和数学函数(sin、exp 等),这种机制可以在现代编程语言中优雅地实现。当你在代码中写 f(x) 时,如果 x 是一个对偶数,重载的运算符会自动将导数在整个计算过程中向前传播。

从单变量到多变量

世界很少能用单变量函数来描述。如果我们有一个函数 f(x,y)f(x, y)f(x,y),并且想计算一个偏导数,比如 ∂f∂x\frac{\partial f}{\partial x}∂x∂f​,该怎么办呢?

这个逻辑可以自然地扩展。偏导数问的是当我们改变一个输入(xxx)而保持所有其他输入(yyy)不变时,函数如何变化。我们可以用对偶数完美地表达这个想法。为了在 (x0,y0)(x_0, y_0)(x0​,y0​) 处求 ∂f∂x\frac{\partial f}{\partial x}∂x∂f​,我们“播种”我们的输入以反映这个问题:

  • 输入 xxx 成为对偶数 ⟨x0,1⟩\langle x_0, 1 \rangle⟨x0​,1⟩,因为 xxx 相对于自身的变化率为 1。
  • 输入 yyy 成为对偶数 ⟨y0,0⟩\langle y_0, 0 \rangle⟨y0​,0⟩,因为 yyy 相对于 xxx 被视为常数。

然后我们使用与之前相同的规则计算函数 f(⟨x0,1⟩,⟨y0,0⟩)f(\langle x_0, 1 \rangle, \langle y_0, 0 \rangle)f(⟨x0​,1⟩,⟨y0​,0⟩)。最终结果的对偶部分将是 ∂f∂x\frac{\partial f}{\partial x}∂x∂f​ 在 (x0,y0)(x_0, y_0)(x0​,y0​) 处的精确值。

这个思想可以进一步推广到计算​​方向导数​​。如果我们想知道 fff 在 (x0,y0)(x_0, y_0)(x0​,y0​) 处,当我们沿着由向量 v=(vx,vy)\mathbf{v} = (v_x, v_y)v=(vx​,vy​) 给出的特定方向移动时如何变化,我们只需用这个方向来“播种”输入:x→⟨x0,vx⟩x \rightarrow \langle x_0, v_x \ranglex→⟨x0​,vx​⟩ 和 y→⟨y0,vy⟩y \rightarrow \langle y_0, v_y \rangley→⟨y0​,vy​⟩。计算过程与之前完全相同,最终的对偶部分给出了在该特定方向上的变化率。前向模式 AD 的每一次传递都会计算一个​​雅可比-向量积​​。

知识的成本:何时使用前向模式

所以,前向模式 AD 是精确、自动且通用的。但它总是最合适的工具吗?答案在于理解其计算成本。

要计算一个偏导数(或一个方向导数),我们需要运行一次函数求值,尽管使用对偶数会增加一个小的常数开销。如果我们的函数有 nnn 个输入,f:Rn→Rmf: \mathbb{R}^n \to \mathbb{R}^mf:Rn→Rm,并且我们想要找到整个梯度(单个输出的所有 nnn 个偏导数)或完整的雅可比矩阵(所有 n×mn \times mn×m 个偏导数),我们必须执行 nnn 次独立的前向模式 AD 传递。总成本大约是评估原始函数成本的 nnn 倍。

这揭示了一个关键的权衡。考虑一个机器学习模型,它有数百万个输入参数(nnn 很大)和一个单一的输出损失函数(m=1m=1m=1)。计算梯度将需要数百万次前向传递,这是极其昂贵的。在这种“多输入,少输出”的情况下,一种称为​​反向模式 AD​​(以其著名的反向传播算法而闻名)的方法效率要高得多,因为它可以在一次前向加一次反向传递中计算整个梯度。

然而,对于“少输入,多输出”的问题(n≪mn \ll mn≪m),情况就反过来了。想象一下模拟一个神经回路,其中 101010 个输入参数控制 250025002500 个输出神经元的活动。要找出所有输出对所有输入变化的响应(即完整的雅可比矩阵),前向模式只需要 n=10n=10n=10 次传递。而反向模式则需要 m=2500m=2500m=2500 次传递。在这里,前向模式无疑是效率的冠军。工具的选择完全取决于问题的形态。

推动微分的边界

对一个计算方法的真正考验是它在面对现实世界复杂性时的稳健性。这正是 AD 真正脱颖而出的地方。

  • ​​控制流​​:与那些难以处理代码分支的符号方法不同,AD 能够轻松处理 if-else 语句。程序只执行条件的一个分支。对偶数被路由到那个活动分支,微积分的规则只应用于实际执行的操作。导数是针对所采取的路径计算的。

  • ​​非光滑函数​​:许多重要的函数,如 max(x, y) 函数或神经网络中的 ReLU 激活函数,都不是光滑的。它们有“扭结”或角点,在这些点上导数没有正式定义。AD 可以通过实现​​次梯度​​(导数的一种泛化)的规则来处理这些情况。对于 max(v1, v2),规则很简单:梯度对于“获胜”的输入(较大的那个)是 1,对于“失败”的那个是 0。这使得 AD 能够将导数传播到对现代优化至关重要的一大类函数中。

  • ​​数值稳定性​​:也许最微妙的是,AD 在数值上可能比分别计算函数及其导数更稳定。考虑函数 q(T)=1−cos⁡(T)T2q(T) = \frac{1 - \cos(T)}{T^2}q(T)=T21−cos(T)​ 在 TTT 很小时的情况。直接的有限差分计算会因 cos⁡(T)\cos(T)cos(T) 非常接近 1 而遭受严重的灾难性抵消。然而,前向 AD 并不计算 q(T+h)−q(T)q(T+h)-q(T)q(T+h)−q(T)。相反,它将除法法则和链式法则应用于计算 q(T)q(T)q(T) 的*算法*。这个过程有效地使用其解析上稳定的形式来计算导数,从而避开了原始表达式的数值不稳定性。它计算的是程序的导数,而不仅仅是其输出的近似值。

从一个简单、近乎有趣的代数技巧——ϵ2=0\epsilon^2 = 0ϵ2=0——诞生了一个强大的计算框架。它将微积分的规则统一到一个单一的自动化过程中,在近似法失效的地方提供精确的导数,并优雅地处理真实计算机程序的复杂性。这证明了有时最优雅的解决方案来自于以全新的方式看待数字。

应用与跨学科联系

既然我们已经探究了前向模式自动微分的引擎,并看到了它的齿轮——对偶数——是如何啮合在一起的,我们就可以退后一步,欣赏这台机器的运作。这个工具的真正美妙之处不仅在于其巧妙的内部机制,还在于它让我们能够探索和解决的广阔而多样的问题领域。这就像拥有一个通用的探针,可以测量任何计算过程的“如果……会怎样”。如果我们轻推这个输入,最终的输出会发生什么?这个问题是科学和工程的核心,而 AD 为我们提供了一种精确而优雅地回答它的方法。

灵敏度分析的艺术:从电路到星辰

从本质上讲,导数是灵敏度的度量。前向模式 AD 为我们提供了一种机械地、精确地计算任何我们可以编写成计算机程序的函数的这些灵敏度的方法。想象你是一位设计复杂信号处理组件的工程师。输出功率通过一系列放大、偏移和非线性变换依赖于输入信号。一个关键问题是:最终的功率输出对输入信号的微小波动有多敏感?使用前向模式 AD,我们可以追踪输入端一个微小扰动在计算的每一步中传播的影响,并在一次传递中得到输出对输入的精确导数。无需近似或猜测;微积分的机器为我们完成了工作。

这个思想可以完美地扩展到随时间演化的系统。考虑一个化学反应或人口模型的简单模拟,由一个常微分方程(ODE)dydt=f(y,p)\frac{dy}{dt} = f(y, p)dtdy​=f(y,p) 控制,其中 ppp 是一个参数,如反应速率。我们可能使用一个简单的数值方法,如前向欧拉法,来将系统随时间推进:yn+1=yn+h⋅f(yn,p)y_{n+1} = y_n + h \cdot f(y_n, p)yn+1​=yn​+h⋅f(yn​,p)。但如果我们对参数 ppp 的值有些不确定呢?这种不确定性对我们预测的 y1y_1y1​ 有多大影响?通过用关于 ppp 的导数来“播种”我们的计算,前向模式 AD 不仅可以计算出新的状态 y1y_1y1​,还可以同时计算出灵敏度 ∂y1∂p\frac{\partial y_1}{\partial p}∂p∂y1​​。我们可以继续这个过程,将状态及其灵敏度从一个时间步传播到下一个时间步,从而完整地了解参数不确定性如何影响我们模拟的整个轨迹。

也许在这个领域最精妙和强大的应用是理解处于平衡状态的系统。想象一个微处理器,其最终工作温度是其产生的热量和散发的热量之间的平衡。这个稳态温度 T∗T^*T∗ 是一个不动点方程 T∗=g(T∗,p)T^* = g(T^*, p)T∗=g(T∗,p) 的解,其中 ppp 是计算负载。一个模拟可能需要数千次迭代步骤才能收敛到这个平衡点。如果我们想知道这个最终温度对负载变化的灵敏度 dT∗dp\frac{dT^*}{dp}dpdT∗​,为稍微不同的 ppp 重新运行整个模拟将是非常低效的。在这里,AD 展现了它的魔力。通过将链式法则应用于不动点方程本身,我们可以推导出收敛解灵敏度的直接关系,而无需展开找到它的过程。这是一个深刻的飞跃:我们通过分析动态景观在该不动点处的局部几何形状,来推断方程解的性质。

优化与科学模拟的引擎

除了仅仅询问“如果……会怎样”,导数还是我们最强大的数值算法背后的驱动力,尤其是在优化和模拟中。典型的例子是用于寻找函数 f(x)=0f(x)=0f(x)=0 根的牛顿法。迭代更新式 xk+1=xk−f(xk)f′(xk)x_{k+1} = x_k - \frac{f(x_k)}{f'(x_k)}xk+1​=xk​−f′(xk​)f(xk​)​ 在每一步都需要函数的值及其导数。前向模式 AD 非常适合这项任务。在一次计算传递中,它同时提供 f(xk)f(x_k)f(xk​) 和 f′(xk)f'(x_k)f′(xk​),正好为迈向解的下一步提供了所需的两种成分。

当我们从单个方程转向方程组 F(x)=0F(x) = 0F(x)=0(其中 F:Rn→RmF: \mathbb{R}^n \to \mathbb{R}^mF:Rn→Rm)时,导数变成了雅可比矩阵 JFJ_FJF​。我们如何计算这个矩阵?一个关键的洞见是,一次前向模式 AD 传递不仅仅计算单个导数;它计算一个​​雅可比-向量积(JVP)​​,JF⋅vJ_F \cdot vJF​⋅v。通过选择“种子”向量 vvv 为基向量,如 (1,0,…,0)(1, 0, \dots, 0)(1,0,…,0),我们可以计算出雅可比矩阵的第一列。通过对所有基向量重复此操作,我们可以逐列构建整个雅可比矩阵。对于一个从 Rn\mathbb{R}^nRn 到 Rm\mathbb{R}^mRm 的函数,这需要 nnn 次前向传递。这也暗示了一种友好的竞争关系:另一种技术,反向模式 AD,在 mmm 次传递中逐行构建雅可比矩阵。对于一个多输入少输出(nnn 大,mmm 小)的函数,反向模式获胜。对于一个少输入多输出(nnn 小,mmm 大)的函数,前向模式是冠军。对于一个方阵雅可比(n=mn=mn=m),选择取决于实现的常数因子。

高效计算 JVP 的能力不仅仅是一个花招;它是现代大规模科学计算的基石。考虑模拟一个复杂的物理系统——比如机翼上的气流或蛋白质的折叠——由一个庞大的微分方程组描述。求解这些方程通常需要隐式时间步进法,其中每一步都必须求解一个大型非线性系统,通常有数百万个变量。在这样的系统上使用牛顿法将需要求解一个涉及巨大雅可比矩阵的线性系统。形成、存储和求逆这样一个矩阵是完全不可行的。但这里有一个美妙的联系:许多现代线性求解器,称为克雷洛夫子空间方法,是“无矩阵”的。它们不需要矩阵本身;它们只需要知道矩阵对一个向量的作用。它们只需要一个可以计算雅可比-向量积的函数。而这正是前向模式 AD 所能提供的,高效且无需显式形成矩阵。数值线性代数和自动微分之间的这种协同作用,使得模拟的规模和复杂性达到了否则难以想象的水平。

超越斜率:探索曲率与高阶世界

有时候,仅仅知道一个景观的斜率是不够的。要理解稳定性,我们需要知道我们是在谷底(正曲率)还是在山顶(负曲率)。这需要二阶导数。例如,在分子动力学中,分子的势能面 f(x)f(x)f(x) 是其原子位置 xxx 的函数。原子受到的力由负梯度 −∇f(x)-\nabla f(x)−∇f(x) 给出。这个表面的曲率,由二阶导数的 Hessian 矩阵 Hf(x)H_f(x)Hf​(x) 描述,决定了分子的振动频率和其结构的稳定性。

我们的 AD 机制能处理这个吗?当然!关键在于认识到函数的导数本身也是一个函数。我们可以递归地应用 AD。要找到沿向量 vvv 的二阶方向导数,它探测该方向的曲率,我们可以首先使用一次前向传递来定义一个新函数 g(x)=∇vf(x)g(x) = \nabla_v f(x)g(x)=∇v​f(x),即一阶方向导数。然后,我们可以应用第二次前向传递来计算 g(x)g(x)g(x) 沿同一方向 vvv 的方向导数。这种 AD 的“前向迭加前向”应用为我们提供了所需的二阶信息 ∇v(∇vf)(x)\nabla_v (\nabla_v f)(x)∇v​(∇v​f)(x),使我们能够探测复杂函数的精细几何结构。

跨学科前沿:更智能的算法与科学人工智能

一个基本思想的真正力量在于它与其他思想结合创造出新事物时得以显现。AD 是这种思想交叉融合的典型例子。

一个美丽的例子是 AD 与图论的结合。在计算一个我们知道大部分元素为零的大型雅可比矩阵——一个稀疏矩阵——时,一种朴素的方法仍然需要 nnn 次前向传递。但我们可以做得更好。如果雅可比矩阵的两列,比如对应变量 xix_ixi​ 和 xjx_jxj​ 的两列,没有重叠的非零项(意味着没有单个输出分量同时依赖于 xix_ixi​ 和 xjx_jxj​),那么我们可以在一次 AD 传递中同时计算这两列。寻找最优的列分组以最小化传递次数的问题,结果证明等价于图论中的一个经典问题:图着色。通过构建一个图,其中变量是节点,任何出现在同一计算中的两个变量之间都有一条边,我们可以使用着色算法来找到所需的最少传递次数。这是一个惊人的综合:一个微积分问题通过离散数学的算法得到解决,从而带来了巨大的效率提升。

也许今天 AD 最令人兴奋的前沿是它作为科学机器学习引擎的角色。神经网络本质上是非常复杂的、高维的、可微的函数。训练它们的过程,即反向传播,无非是对一个标量损失函数应用反向模式 AD——这是一个美丽的例子,展示了反向模式在单输出函数上的效率优势。

但这种联系更为深刻。科学家们现在正在训练神经网络来表示物理量,例如分子系统的势能(神经网络势能面,或 NN-PES)。一旦网络被训练好,它就成为一个远比昂贵的量子力学计算更经济的替代品。要在分子动力学模拟中使用这个 NN-PES,我们需要原子上的力。力是能量的负梯度——这对于单次反向模式 AD 传递来说是完美的工作。如果我们需要分析振动模式,我们需要 Hessian-向量积。这可以通过结合前向和反向模式传递来完成。AD 提供了神经网络输出对其输入的精确导数,达到机器精度,并且效率极高。这种对机器学习模型进行微分的能力,正是将人工智能世界与严谨的、基于导数的物理定律联系起来的桥梁,开启了一个由人工智能驱动的科学发现新时代。

从工程师的工作台到计算化学家的模拟,从优化的基础到人工智能的前沿,前向模式自动微分这个简单而优雅的思想,被证明是贯穿现代科学结构的一条线索。它证明了有时候,最深刻的工具诞生于最简单的思想。