Skip to content

ODE

ODE 是什么

ODEOrdinary Differential Equation,中文通常叫 常微分方程

它研究的是:

一个未知函数怎样随着某个变量变化,并且这种变化率满足某个方程。

最常见的形式是:

dxdt=f(t,x).

这里,x=x(t) 是未知函数,t 是自变量,dxdt 表示 xt 的导数,也就是“变化率”。

这条式子可以读成:

在每个时刻 t,变量 x 变化得多快,由当前的 t 和当前的 x 一起决定。

比如:

dxdt=3x

表示“当前值越大,增长越快”;

dxdt=2x

表示“当前值越大,下降也越快”。

TIP

ODE 不是直接告诉你“x 是多少”,而是告诉你“x 该怎么变”。

“常微分”

关键在“常”这个字。

常微分方程里,未知函数只依赖 一个自变量。例如 x(t) 只依赖时间 t,所以导数是普通导数:

dxdt.

如果未知函数依赖多个自变量,比如 u(x,t) 同时依赖空间和时间,那么研究的就是偏微分方程 PDE,会出现偏导数:

ut,ux.

ODE 在描述什么

很多现实问题,本来就是“变化率决定下一步”的形式。

比如人口增长,如果当前人口越多,增长越快,那么会写成:

dPdt=rP.

比如牛顿第二定律,如果物体的位置是 x(t),速度是 v(t),加速度是 x(t),那么:

md2xdt2=F.

比如在 Flow Matching 和 Neural ODE 里,样本 xt 的运动由一个速度场给出:

dxtdt=vθ(xt,t).

所以 ODE 的核心思想其实非常统一:

先写出局部变化规则,再由这个局部规则推出整条轨迹。

一个 ODE 问题通常长什么样

最典型的是 初值问题(Initial Value Problem, IVP):

dxdt=f(t,x),x(t0)=x0.

这表示两件事同时给定了:

第一,变化规律是 x(t)=f(t,x)

第二,在初始时刻 t0,变量的起点是 x0

只有把这两件事放在一起,问题才算完整。因为同一个微分方程通常会有很多条不同的解曲线,而初值决定了到底选哪一条。

例如:

dxdt=x

它的通解是:

x(t)=Cet.

这里的 C 可以取很多值,所以有无穷多条解。只有再给一个初值,比如 x(0)=2,才能确定:

2=Ce0=C,

于是唯一解是:

x(t)=2et.

斜率场与向量场

把一阶 ODE

dxdt=f(t,x)

画在平面上时,f(t,x) 可以理解成:

  • 在点 (t,x) 处,解曲线应该有多大的斜率。

这就形成了一个 斜率场。每个点上都放一小段线,它的倾斜程度由 f(t,x) 决定。真正的解曲线,就是始终顺着这些局部斜率往前走的曲线。

如果把变量推广成向量 x(t)Rd,ODE 就变成:

dxdt=f(t,x),xRd.

这时 f(t,x) 是一个向量,表示“当前位置此刻应该朝哪个方向移动、多快移动”。

这就是 向量场 的视角,也是 Neural ODE、Continuous Normalizing Flow、Flow Matching 里最重要的视角。

从导数形式到积分形式

一阶初值问题

dxdt=f(t,x),x(t0)=x0

还可以改写成积分形式:

x(t)=x0+t0tf(s,x(s))ds.

这个式子特别重要,因为它把“局部变化率”变成了“累计变化量”。

为什么这个式子成立

dxdt=f(t,x)

两边对时间积分:

t0tdx(s)dsds=t0tf(s,x(s))ds.

左边根据微积分基本定理就是:

x(t)x(t0).

再用初值 x(t0)=x0,得到:

x(t)=x0+t0tf(s,x(s))ds.

这条积分形式很值得记住,因为:

  • 它是存在唯一性定理的自然起点;
  • 它也是数值方法的直觉来源;
  • 它说明 ODE 的解本质上是“把局部速度积起来”。

最简单的例子:指数增长与衰减

考虑:

dxdt=ax,x(0)=x0.

这是最经典的一阶 ODE。

为什么它能分离变量

把所有和 x 有关的项放到一边,和 t 有关的项放到另一边:

1xdx=adt.

两边积分:

1xdx=adt.

得到:

ln|x|=at+C.

两边取指数:

|x|=eCeat.

把常数重新记成 C,就有:

x(t)=Ceat.

再代入初值 x(0)=x0

x0=Ce0=C,

所以:

x(t)=x0eat.

这个解怎么理解

如果 a>0,那就是指数增长;

如果 a<0,那就是指数衰减;

如果 a=0,那就是常数不变。

它之所以重要,是因为很多更复杂系统在局部线性化以后,都会长得像这个样子。

一阶线性 ODE:积分因子法

一个非常重要的类型是:

dxdt+a(t)x=b(t).

它叫 一阶线性微分方程

这类方程最经典的解法是 积分因子法

想法从哪里来

我们希望把左边写成某个乘积的导数,也就是写成:

ddt(μ(t)x(t)).

因为根据乘法求导法则:

ddt(μx)=μx+μx.

而原方程左边是:

x+a(t)x.

如果我们先把方程两边都乘上一个函数 μ(t),得到:

μx+μa(t)x=μb(t),

那么只要希望

μ=μa(t),

就能让左边正好变成:

μx+μx=ddt(μx).

于是只需要解:

μμ=a(t).

两边积分:

lnμ(t)=a(t)dt,

所以可以取:

μ(t)=exp(a(t)dt).

这就是 积分因子

代回去以后怎么解

方程变成:

ddt(μx)=μb(t).

两边积分:

μ(t)x(t)=μ(t)b(t)dt+C.

于是:

x(t)=1μ(t)(μ(t)b(t)dt+C).

也就是:

x(t)=ea(t)dt(ea(t)dtb(t)dt+C).

这就是一阶线性 ODE 的标准解法。

二阶 ODE:为什么会出现二阶导数

如果一阶导数表示“速度”,二阶导数就表示“加速度”。

所以二阶 ODE 经常来自力学系统。例如:

md2xdt2=F(x,x˙,t).

最典型的例子是简谐振子:

d2xdt2+ω2x=0.

它描述的是“位置偏离平衡点越远,拉回去的力越大”。

为什么可以猜指数解

对常系数线性方程,常用试探解:

x(t)=ert.

因为导数之后仍然是指数函数本身,代进去很方便。

对上式有:

x(t)=rert,x(t)=r2ert.

代回:

r2ert+ω2ert=0.

因为 ert0,所以必须有:

r2+ω2=0.

这就是特征方程。

解得:

r=±iω.

于是实数解可以写成:

x(t)=Acos(ωt)+Bsin(ωt).

这说明简谐振子不会指数发散,而会周期振荡。

为什么高阶 ODE 常常先改写成一阶系统

在机器学习和动力系统里,大家更喜欢写成一阶向量形式:

dzdt=f(t,z).

因为高阶 ODE 都可以改写成一阶系统。

例如简谐振子:

x+ω2x=0

设:

z1=x,z2=x.

那么:

z1=z2,z2=ω2z1.

于是就变成:

ddt(z1z2)=(z2ω2z1).

这就是一个一阶系统。

这一步非常重要,因为 Neural ODE、CNF、Flow Matching 的标准形式都是这种一阶向量 ODE。

什么叫 autonomous ODE

如果方程里不显式出现时间 t,只依赖状态本身:

dxdt=f(x),

它就叫 自治系统(autonomous ODE)。

这类系统的直觉尤其清楚:

  • 当前状态 x 决定下一刻怎么动;
  • 运动规则本身不会随着“绝对时间”变化。

很多动力系统理论,比如平衡点、相图、稳定性分析,都是围绕自治系统展开的。

Neural ODE 里常见的是 fθ(x,t),也就是非自治系统;但如果网络不显式输入时间,也可以看成自治系统。

解一定存在且唯一吗

不是所有 ODE 都会“自动有唯一解”。

对于初值问题

dxdt=f(t,x),x(t0)=x0,

一个很重要的基本结论是:

  • 如果 f 连续,通常可以保证 局部存在解
  • 如果 fx 还满足局部 Lipschitz 条件,通常可以进一步保证 局部唯一解

你可以把 “Lipschitz” 粗略理解成:

右边函数不能对 x 太尖、太爆、太不稳定。

一个非唯一的经典例子

看方程:

dxdt=|x|,x(0)=0.

至少有一个解是:

x(t)=0.

因为代回去两边都为 0。

但还有别的解。比如对任意 c0,定义:

x(t)={0,tc,(tc)24,t>c.

你可以验证它也满足同一个初值问题。

这说明:

如果右边对 x 不够“规整”,同一个起点可能长出不止一条轨迹。

这件事在动力系统和数值分析里都很重要,因为它告诉我们:不是写下 ODE 就自然有一个稳定、明确的未来。

为什么很多 ODE 解不出闭式公式

虽然教科书里常见的例子能手算,但大多数真实 ODE 并没有漂亮的解析解。

这时就要用 数值方法

最基本的是 Euler 方法。设:

dxdt=f(t,x),x(t0)=x0.

取步长 h,令 tn+1=tn+h。用泰勒展开:

x(tn+h)=x(tn)+hx(tn)+O(h2).

因为 x(tn)=f(tn,x(tn)),所以:

x(tn+h)=x(tn)+hf(tn,x(tn))+O(h2).

把高阶误差先忽略,就得到 Euler 更新:

xn+1=xn+hf(tn,xn).

这个式子怎么理解

它的含义就是:

当前先看一下瞬时速度,再假装这段很短的时间里速度几乎不变,于是往前走一步。

这正是“导数 = 局部线性近似”的直接体现。

更高级的数值方法,比如 Runge-Kutta,会在一步里多看几次斜率,从而更准确。

ODE、轨迹与“流”

一旦有了向量场

dxtdt=v(xt,t),

从不同初值出发,你就会得到很多条轨迹。把所有这些轨迹一起看,就形成了一个 flow

也就是说,ODE 不只是给出一条曲线,而是在说:

空间中每个点如果放一个粒子,它以后会怎么运动。

如果初始点本身来自某个概率分布 p0,那么这些粒子整体形成的密度也会随时间流动。它满足连续性方程:

tpt(x)+(pt(x)vt(x))=0.

这条式子在 Flow Matching 里非常关键,因为:

  • 单个粒子的轨迹由 ODE 决定;
  • 一整群粒子的概率密度由连续性方程决定。

所以 ODE 解决的是“一个点怎么走”,而 continuity equation 解决的是“整团分布怎么流”。


ODE 和 Neural ODE / Flow Matching 的关系

在 Neural ODE 里,神经网络直接参数化速度场:

dxtdt=fθ(xt,t).

这时模型不再是一层一层离散叠加,而是被看成一个连续深度系统。

在 Flow Matching 里,模型同样学习一个速度场:

dxtdt=vθ(xt,t).

只不过训练目标不是普通监督学习,而是让模型输出的速度去匹配某条目标概率路径对应的“正确速度”。

所以如果把 Flow Matching 看懂,背后其实已经默认你接受了 ODE 这套语言:

  • 状态是连续时间变化的;
  • 每个时刻都由一个速度场决定下一瞬间怎么走;
  • 生成样本时,本质上是在解一条 ODE。

总结

ODE 研究的是“变化率决定轨迹”的方程。

初值问题:

dxdt=f(t,x),x(t0)=x0.

积分形式:

x(t)=x0+t0tf(s,x(s))ds.

前者说明“局部怎么变”,后者说明“整体怎么积出来”。