halo 的技术博客

返回

先说结论:要生成逼真的金融行情,你不需要显式学出概率密度 p(x)——只需要学它的「分数」 s(x)=xlogp(x)s(x)=\nabla_x \log p(x),一个指向高密度区的向量场。 学会了分数,就能用 Langevin 动力学「顺着场往上爬」采出新样本。这套「得分匹配 + Langevin」正是今天所有扩散模型(Stable Diffusion、Sora)的数学地基。本文用纯 numpy 从零实现,把它用在金融收益的重尾+尾部联动合成上,实测生成样本能还原相关结构和下尾联动,但也踩了两个真实的坑。

真实分数场 vs DSM 学到的分数场:两个向量场都指向数据高密度区

一、为什么要绕开归一化常数 Z#

任何生成模型都想学数据分布 p(x)p(x)。参数化写法通常是

pθ(x)=1ZθeEθ(x),Zθ=eEθ(x)dxp_\theta(x) = \frac{1}{Z_\theta}\, e^{-E_\theta(x)}, \qquad Z_\theta = \int e^{-E_\theta(x)}\,dx

EθE_\theta 是能量函数(好学),ZθZ_\theta归一化常数——它要对整个空间积分,高维下根本算不出来。这是能量模型几十年的痛点。

Hyvärinen(2005)的洞察:如果我们不学 p(x)p(x) 本身,而是学它的对数梯度——分数(score)

s(x)=xlogpθ(x)=xEθ(x)xlogZθ=0s(x) = \nabla_x \log p_\theta(x) = -\nabla_x E_\theta(x) - \underbrace{\nabla_x \log Z_\theta}_{=\,0}

ZθZ_\theta 不依赖 xx,梯度为零,直接消失了! 分数只跟能量的梯度有关,彻底绕开归一化。这就是「得分匹配」的核心魔法。

分数是什么?它是一个向量场:在每个点 xxs(x)s(x) 指向「概率密度上升最快的方向」。看上面 cover 图——箭头都从低密度区指向数据簇(红点)密集的地方。左边是真实分数场(用已知混合密度解析算出),右边是 DSM 学到的,两者高度一致。

二、去噪分数匹配:把「学分数」变成「学去噪」#

原始得分匹配的目标函数含二阶导(Hessian 的迹),高维下算不动。Vincent(2011)的去噪分数匹配(DSM) 给了一个漂亮的等价形式:

  1. 给干净数据 xx 加高斯噪声:x~=x+σε, εN(0,I)\tilde x = x + \sigma\varepsilon,\ \varepsilon\sim N(0,I)
  2. 训练网络 sθ(x~)s_\theta(\tilde x) 去预测目标 xx~σ2=εσ\dfrac{x-\tilde x}{\sigma^2} = -\dfrac{\varepsilon}{\sigma}

直观理解:分数网络学的是「从含噪点指回干净数据的方向」——这正是「去噪」。Vincent 证明了:最小化这个去噪回归目标,等价于匹配加噪分布 qσ(x~)q_\sigma(\tilde x) 的分数。这就是 Tweedie 公式的实操版本。

而这个 DSM 目标的全局最优解有闭式表达(在无限模型容量下网络会收敛到它):

sσ(x)=xlogqσ(x)=iwi(x)xixσ2,wi(x)=softmax ⁣(xxi22σ2)s_\sigma(x) = \nabla_x \log q_\sigma(x) = \sum_i w_i(x)\,\frac{x_i - x}{\sigma^2}, \quad w_i(x)=\mathrm{softmax}\!\left(-\frac{\|x-x_i\|^2}{2\sigma^2}\right)

其中 qσq_\sigma 是「数据点上放高斯核」的 Parzen 窗密度。本文直接用这个闭式最优解当分数函数——它就是神经网络在理想训练下会逼近的目标,避免了拟合不稳定,让实验聚焦在方法本身。

import numpy as np
SIGMA = 0.30

def score_fn(x, data, sigma=SIGMA):
    # x:(N,2), data:(M,2) -> 分数 (N,2)
    d = x[:,None,:] - data[None,:,:]           # (N,M,2)
    logw = -(d**2).sum(-1)/(2*sigma**2)        # (N,M) 高斯核对数权重
    logw -= logw.max(1, keepdims=True)         # 数值稳定
    w = np.exp(logw); w /= w.sum(1, keepdims=True)
    # ∇log q = Σ w_i (data_i - x)/σ²
    return np.einsum("nm,nmk->nk", w, -d)/sigma**2
python

三、Langevin 动力学:顺着分数场采样#

有了分数场,怎么采出新样本?Langevin 动力学:从随机噪声出发,反复「沿分数场走一小步 + 加一点随机扰动」:

xt+1=xt+δ2sθ(xt)+δzt,ztN(0,I)x_{t+1} = x_t + \frac{\delta}{2}\, s_\theta(x_t) + \sqrt{\delta}\, z_t, \qquad z_t \sim N(0, I)

  • 漂移项 δ2sθ\frac{\delta}{2}s_\theta:把样本推向高密度区;
  • 扩散项 δz\sqrt{\delta}z:注入随机性,保证采样覆盖整个分布而不是全塌到峰值。

δ0\delta\to0、步数 \to\inftyxtx_t 的分布收敛到 p(x)p(x)

def langevin(n, steps=600, delta=0.03, data=X):
    x = np.random.randn(n, 2) * 1.5            # 从宽高斯噪声起步
    for _ in range(steps):
        s = score_fn(x, data)
        x = x + 0.5*delta*s + np.sqrt(delta)*np.random.randn(*x.shape)
    return x
python

下图左边是几条 Langevin 采样轨迹——从外围噪声一路爬进数据流形;右边是最终生成样本(蓝)叠在真实样本(黑)上,簇的位置和形状都对上了:

Langevin 采样轨迹与生成样本 vs 真实样本

四、金融场景:能不能还原重尾和尾部联动#

金融收益不是高斯的——它重尾(极端行情比正态频繁)、左偏(暴跌比暴涨剧烈)、且尾部联动(危机时资产齐跌,相关性飙升)。我造了一个 2 资产混合分布来模拟:

  • 80% 平静态:小方差、弱正相关;
  • 20% 危机态:负漂移、大方差、强正相关(齐跌)。

这个混合天然产生重尾、左偏和下尾联动——正是风险管理最关心的结构。问题是:DSM 生成的样本能还原这些吗?

生成 vs 真实:边缘分布、QQ 图、依赖结构还原

实测(生成 vs 真实):

指标真实生成还原度
边缘分布形状重尾+左偏重尾+左偏QQ 图基本贴合对角线
资产间相关 ρ\rho0.5360.48691%
下尾联动 P(B跌|A跌)0.4730.44794%

结论是正面的:DSM 不仅还原了单资产的重尾边缘(左图对数坐标下两条直方图重叠得很好),更关键的是还原了尾部联动——生成样本里「A 跌时 B 也跌」的条件概率 0.45,非常接近真实的 0.47。这对压力测试、组合尾部风险聚合很有价值:你可以用它合成更多「危机情景」来增强小样本。

五、两处真实翻车#

生成结果好看,但别急着上生产。DSM + Langevin 有两个绕不过的坑:

步长两难 + 低密度区分数误差

翻车一:Langevin 步长 δ 的两难#

左图扫描了步长 δ 对生成质量的影响:

  • δ 太大(0.05→0.2):漂移步子迈太大,越过高密度区,方差爆炸——δ=0.2 时生成 std 与真实 std 的误差冲到 9.7,样本飞散;
  • δ 太小(0.005):走得太慢,固定步数内混合不充分,覆盖不到分布的远端,std 也偏。

甜点在 δ≈0.01~0.03 的窄区间。这就是为什么现代扩散模型要用退火 Langevin(annealed,多个噪声尺度从大到小)或概率流 ODE——单一步长的朴素 Langevin 太脆。

翻车二:低密度区的分数估计误差#

右图是致命伤:分数估计误差随「到最近数据的距离」线性上升,斜率 7.1。 也就是说——在数据稀疏的低密度区(恰恰是罕见的极端危机区),分数场估计得最差。

原因很直接:DSM 靠数据点周围的高斯核估分数,没数据的地方就没有可靠信号。这意味着:

  • 你最想生成的「百年一遇危机」,恰恰是模型最不会生成的——那里几乎没有训练样本支撑分数场;
  • Langevin 采样一旦飘进低密度区,就被错误的分数场带偏,可能再也回不来。

这是所有生成模型在金融尾部的通病:历史上没发生过的极端,模型学不出来。 得分匹配没有魔法能凭空造出训练集里不存在的尾部结构。

六、结果解读与适用边界#

它到底强在哪

  • 免归一化常数——这是相对能量模型/显式密度模型的根本优势,高维下才可行;
  • 还原多变量依赖结构(相关、尾部联动),比逐变量独立建模强,适合组合级情景合成;
  • ✅ 是扩散模型的地基——理解了 DSM+Langevin,就理解了 Stable Diffusion 的核心。

它的边界

  • 低密度/尾部区不可信——最需要的极端情景恰恰最不准;
  • 单尺度 Langevin 脆——生产必须用退火多尺度或概率流 ODE;
  • 不能凭空造尾部——生成分布受限于训练数据的支撑集。

五个必须拆穿的陷阱

  1. 「免归一化 = 免费午餐」:错。省掉 Z 的代价是采样要跑 Langevin(几百上千步迭代),采样成本转移到了推理端。
  2. 「单一噪声尺度够用」:错。单尺度 DSM 在低密度区分数差、Langevin 混合慢。Song-Ermon(2019)的 NCSN 用多尺度就是为了补这个洞。
  3. 「Langevin 步长随便调」:错。本文实测 δ 差一个量级,std 误差从 0.6 跳到 9.7。步长是生死参数。
  4. 「生成样本逼真 = 分布学对了」:错。相关和尾部联动还原到 90%+ 很好看,但低密度区的分数错误不会体现在这些均值级指标上——它藏在你没检验的极端区。
  5. 「可以直接拿来造行情做回测」:危险。生成行情缺失训练集之外的尾部、且不含真实的时间自相关(本文是 i.i.d. 截面演示,未建模波动聚集的时序动力学)。当情景增强工具可以,当唯一数据源做回测会严重低估尾部风险。

方法论一句话:得分匹配把「学分布」偷换成「学一个指向高密度区的向量场」,用 Langevin 顺着场采样——这是扩散模型的灵魂。它能逼真还原金融收益的重尾和尾部联动,但对最关键的极端尾部无能为力,且采样对步长极其敏感。当情景增强的利器,别当凭空造尾部的魔法。

本文所有数值来自纯 numpy 从零实现的 DSM 闭式最优解(Parzen/Tweedie 去噪器)+ Langevin 采样,在混合态重尾合成数据上跑出。合成数据非真实行情,结论用于方法演示,不构成任何投资建议。

得分匹配生成金融序列:用去噪分数匹配合成行情
https://blog.halo26812.eu.org/blog/score-matching-financial
Author halo
Published at 2026年7月24日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨