halo 的技术博客

返回

普通神经网络是「离散的」:你叠 N 层,就是做 N 次变换。深度 = 层数,和参数量绑死。想要「更深」,就得加更多参数、冒过拟合和梯度消失的风险。

为什么量化研究者该在意这个?因为金融市场里大量被建模的对象本质是连续演化的:波动率微笑随时间的扩散、因子暴露的缓慢漂移、组合权重在再平衡之间的平滑过渡、甚至订单簿深度随毫秒级事件的连续变化。这些量天然是「时间 t 上的状态 h(t)」,而不是「第 1 层、第 2 层……」的离散堆叠。当你强行用固定层数的 MLP 去逼近一个其实由微分方程支配的过程,你是在用错的工具——参数学了一大堆,却没用上「它本该连续」这条免费信息。

结论先放这:Neural ODE(Chen et al. 2018)把「层数」换成「时间」——隐藏状态 h 不再靠「第几层」,而靠「积分到时刻 T」得到。 它定义一个连续变换 dh/dt = f_θ(h, t):从初值 h(0)=x 出发,用数值积分(我们用手写 RK4)把 h 积到 h(T),再读出 y = W_out·h(T)。「深度」= 积分时长 T,可以任意细分,与参数量完全解耦——这是它相对固定深度 MLP 的核心卖点。

在我们的合成任务(数据由一条已知非线性 ODE 流生成:x 采样 → 积分到 T=1 → 终点即目标 y)上:Neural ODE 测试集 R²=0.992、MSE=0.00204,相对固定深度 MLP(0.989) 改进 25.1%、相对线性(0.158) 改进 99.0%。 这个优势是「结构性的」:连续流的归纳偏置正好匹配「数据来自连续动力学」这一设定。但这不等同于 Neural ODE 在任何任务上都赢 MLP——后文五个陷阱把它讲透。关键区别:当数据真来自连续流时,Neural ODE 用更少「有效深度」达到同等甚至更好拟合;当数据本质是离散表格时,MLP 更直接。

Neural ODE 相空间:向量场 + 多条 ODE 积分轨迹,凸显连续动力学


1. 连续深度:把「第 N 层」换成「积分到 T」#

离散网络:h_{n+1} = f_θ(h_n),叠 N 次。Neural ODE 改成连续时间:

dh/dt = f_θ(h, t),   h(0) = x
h(T)  = ODESolve(f_θ, x, t=0→T)
y     = W_out · h(T)
plaintext

「深度」不再是整数 N,而是实数 T。T 越大 = 在状态空间里「流」得越久 = 变换越复杂。参数只有 f_θ 本身(一个小型 MLP)+ W_out,T 只是积分终点,不增加参数。这就是「深度与参数量解耦」。


2. 从零实现:RK4 积分 + 展开反向传播(纯 numpy)#

前向用经典四阶 Runge-Kutta:

反向传播不靠 autograd——我们把 RK4 的 K 步计算图展开,沿反向累积梯度(unrolled backprop through the integration),对 h(0)=x、f_θ 全部参数、W_out 求导。这保证「连续深度」是真端到端训练的,不是近似。下面轨迹还原图里橙线就是学到的流,真从 x 积分出来。

单点轨迹还原:真实 ODE 轨迹(青) vs 学到的 Neural ODE 轨迹(橙)


3. 数据合成:让数据真的来自一条 ODE 流#

为了公平验证「连续流的归纳偏置」,我们用一条已知非线性 ODE 生成数据:采样初值 x,积分到 T=1,终点 y 就是目标。于是「正确答案」本就是某条连续流,Neural ODE 的假设与数据同构。

def true_ode(h, t):
    """已知 swirl+收缩 流(数据生成器,测试时不知道参数)。"""
    x, y = h
    return np.array([-0.6*x + 1.2*y - 0.3*x*(x*x+y*y),
                     -1.2*x - 0.6*y - 0.3*y*(x*x+y*y)])

def make_data(n=1200):
    rng = np.random.default_rng(20260723)
    xs, ys = [], []
    for _ in range(n):
        x0 = rng.standard_normal(2) * 1.5
        hf, _ = rk4_true(x0, 0.0, 1.0, true_ode)   # 积到 T=1
        xs.append(x0); ys.append(hf)
    return np.array(xs), np.array(ys)
python

4. 实测:连续深度 vs 固定深度#

同参数量下对比三个读出:

Linear        MSE=0.20280  R²=0.158  参数量≈4
MLP(固定深度) MSE=0.00272  R²=0.989  参数量≈82
Neural ODE    MSE=0.00204  R²=0.992  参数量≈86
Neural ODE 相对 MLP 改进: 25.1%
Neural ODE 相对 Linear 改进: 99.0%
plaintext

三个诚实结论:

  • 线性(0.158) 直接出局:任务有强非线性 swirl+收缩,线性读不出。
  • MLP(0.989) 已经很好:固定深度 MLP 容量够,吃满 R²。
  • Neural ODE(0.992) 略赢 MLP 25.1%:在「数据来自连续流」的设定下,连续归纳偏置让它在同等容量下更贴合真实映射——改一点点 MSE,对金融弱信号场景这是实打实的边际增益。

测试集指标:Linear / MLP(固定深度) / Neural ODE(连续深度) 的 MSE 与 R²


5. 五个真实陷阱(必须说清)#

陷阱 1:Neural ODE 比 MLP 强不是普适的。 我们赢,是因为数据生成器和 Neural ODE 同构(都来自连续流)。若换成离散表格数据(如「市盈率+市值→下季收益」这种无连续动力学含义的特征),MLP 往往更直接、Neural ODE 的「连续」假设反成多余约束。论文里的优势是「归纳偏置匹配」的结果,不是「ODE 永远更优」。

陷阱 2:连续深度不是免费的。 T 越大,RK4 积分步数 K 通常也要增大(否则积分误差爆炸),前向 + 反向都是 K 倍计算。我们的 K=20 已够,但真实长 T 任务上 Neural ODE 的单次前向可能比同等容量 MLP 慢一个数量级——它省的是「参数」,不是「算力」。

陷阱 3:反向传播靠「展开积分计算图」,内存随 K 涨。 我们 unrolled backprop 把 K 步全存下来再反向,内存 O(K·state)。K 大或 state 维高时显存吃紧。PyTorch 的 torchdiffeq 用 adjoint method 把内存降回 O(1),但数值更复杂——生产别手写展开。

陷阱 4:数值积分有误差,且误差进梯度。 RK4 是四阶,但步长不够仍会偏。积分误差会顺梯度传进参数,训练初期 f_θ 乱、积分轨迹乱飞时尤其明显。真实实现要监控积分误差、必要时自适应步长(如 Dopri5),我们固定 K 只为可控演示。

陷阱 5:小数据下 f_θ 不可辨识。 连续动力学参数多、流形状灵活,样本少时容易「用奇怪的流绕路拟合」,过拟合且物理解释性差。金融数据普遍样本稀缺,直接上 Neural ODE 风险高——先用 MLP 探底,确认连续结构有边际收益再上。


6. 它和 RNN / 残差网络 / 物理模型怎么选#

结构深度含义参数量归纳偏置适用
MLP固定层数 N随层涨无(万能逼近)离散表格
ResNetN 个残差块随块涨局部残差图像/离散
RNN时间步递推固定循环序列
Neural ODE积分时长 T与 T 解耦连续流连续动力学数据
  • 连续动力学数据(物理量、微分方程生成的序列、平滑演化的状态):Neural ODE 归纳偏置最匹配。
  • 离散表格 / 无连续含义特征:MLP 更直接,别硬上 ODE。
  • 序列预测:RNN/TCN/Mamba 更对口,Neural ODE 也可做连续时间 RNN 变体。
  • 要可解释物理:直接辨识参数化 ODE,比黑箱 f_θ 更干净。

一句话:Neural ODE 是「把网络深度变成连续时间,让深度与参数解耦」,代价是积分计算贵、且只在数据真连续时占便宜。


7. 金融里它到底解决什么问题#

把上面抽象一点看,Neural ODE 给量化研究者三件具体的事:

  1. 连续时间插值:日频因子序列想补出「盘中演化」?直接积一段,不必另训一个高维时序模型。
  2. 平滑状态演化:把因子暴露 / 组合权重当成连续流,预测其演化路径而不是单点收益,天然带平滑先验(流不能瞬间跳变),对噪声样本更稳。
  3. 深度即正则:连续深度 T 本身是一种隐式正则——流被约束成「不太陡」,等价于对映射复杂度加惩罚,缓解过拟合。

但要再强调一次:只有当你的目标真有连续动力学结构时,这些才成立。把「市盈率、市值、换手率」这种本无时间演化含义的截面特征喂进去,Neural ODE 既不会更好,还多花几倍算力。它是对「连续」建模的工具,不是对「表格」建模的工具。


8. 落地路径#

  1. 连续时间插值:用 Neural ODE 对稀疏采样(如日频但想知道盘中演化)做连续时间补全。
  2. 状态演化建模:把组合/因子状态当成连续流,预测其平滑演化路径而非单点。
  3. 正则化深度:用「连续深度」天然平滑的特性当隐式正则(流不能太陡)。
  4. 真要做端到端:上 torchdiffeqodeint(adjoint 反向),别手写展开(内存爆)。

9. 结论#

Neural ODE 的精髓一句话:把隐藏状态写成微分方程 dh/dt = f_θ(h,t),从 h(0)=x 积到 h(T),「深度」= 积分时长 T,与参数量解耦。

我们纯 numpy 从零实现 RK4 积分 + 展开反向传播证明:在「数据来自已知 ODE 流」的合成任务上,Neural ODE 测试集 R²=0.992、MSE=0.00204,相对固定深度 MLP(0.989) 改进 25.1%——因为连续流的归纳偏置匹配了「数据来自连续动力学」。但这优势是结构性的、非普适的:离散表格数据上 MLP 更直接(陷阱 1);连续深度省参数不省算力(陷阱 2);反向传播靠展开积分图、内存随步数涨(陷阱 3);数值积分误差进梯度(陷阱 4);小数据下不可辨识(陷阱 5)。

深度不必是一层层叠起来的整数——它可以是一段被积出来的连续时间。前提是,你的数据真的在连续地流。

Neural ODE 连续时间建模:把隐藏状态写成微分方程
https://blog.halo26812.eu.org/blog/neural-ode-financial
Author halo
Published at 2026年7月23日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨