目录

Minimal-Drifting-Models

Drifting Models 的 2D 玩具实现与扩展(Mean-Shift / Sinkhorn / CF / Autoencoder)

Minimal-Drifting-Models 算法与实现说明

本文对 GitHub 项目 Algomancer/Minimal-Drifting-Models算法与实现层面的说明。该项目是论文 Generative Modeling via Drifting(Deng et al., ICML 2026)的极简 2D 实现:在训练时演化 pushforward 分布、推理时**单步前向(1-NFE)**生成,无需 score、ODE 求解器或噪声调度。



  • 论文Generative Modeling via Drifting(Deng et al., ICML 2026)。
  • 仓库Algomancer/Minimal-Drifting-Models,使用 2D 玩具数据(8 Gaussians、Checkerboard)做最小可复现实现。
  • 特点:与 diffusion/flow 不同,迭代发生在训练阶段;推理时生成器一次前向即可(1-NFE),漂移场 VV 在均衡时趋于 0(生成分布与数据分布一致)。

对比项Diffusion / FlowDrifting Models
迭代发生时机推理时多步采样训练时分布演化
推理多步 NFE(ODE/SDE 或离散步)单步前向 1-NFE
训练目标score / flow 匹配等漂移目标:f(ε)f(ε)+V(f(ε))f(\varepsilon) \to f(\varepsilon) + V(f(\varepsilon))
漂移场 VV指向数据、排斥生成样本;V0V \to 0 即均衡

直观上:漂移场 VV 告诉每个生成样本「该往哪走」才能更接近数据分布;用 VV 构造带 stop-gradient 的目标,让生成器 ff 去拟合「当前输出 + VV」,从而在训练过程中把 pushforward 分布一步步推向数据分布。收敛后 V0V \approx 0,推理时只需 zN(0,I), x=f(z)z \sim \mathcal{N}(0,I),\ x = f(z)


论文中漂移场(Algorithm 2)的紧凑形式(mean-shift 核):

V(x)=1ZpZqEy+p,yq[k(x,y+)k(x,y)(y+y)] V(x) = \frac{1}{Z_p Z_q} \mathbb{E}_{y^+ \sim p,\, y^- \sim q}\big[ k(x,y^+) k(x,y^-) (y^+ - y^-) \big]
  • pp:数据分布(正样本 y+y^+
  • qq:当前生成分布(负样本 yy^-,通常取为当前 batch 的生成点)
  • k(x,y)k(x,y):核,实现中为 k(x,y)=exp(xy/τ)k(x,y) = \exp(-\Vert x-y\Vert/\tau)(或与距离相关的 logits),τ\tau 为温度 temp

含义:V(x)V(x) 是「被拉向数据 y+y^+、被推离生成 yy^-」的加权平均方向;权重由 k(x,y+)k(x,y^+)k(x,y)k(x,y^-) 共同决定。
实现时用 doubly-normalized affinity(对 x 和 y 两维做 softmax 后取几何平均)或 Sinkhorn 双随机归一化,再分解成 Wposy+WnegyW_{\mathrm{pos}} y^+ - W_{\mathrm{neg}} y^-,避免显式 O(N×Npos×Nneg)O(N\times N_{\mathrm{pos}}\times N_{\mathrm{neg}})


损失为带 stop-gradient 的 MSE

L=E[f(ε)sg(f(ε)+V(f(ε)))2] \mathcal{L} = \mathbb{E}\Big[ \big\Vert f(\varepsilon) - \mathrm{sg}\big(f(\varepsilon) + V(f(\varepsilon))\big) \big\Vert^2 \Big]
  • εN(0,I)\varepsilon \sim \mathcal{N}(0,I)ff 为生成器。
  • sg\mathrm{sg} 表示 stop-gradient:梯度只通过 f(ε)f(\varepsilon),不通过 f(ε)+Vf(\varepsilon)+V
  • 目标 = 当前生成点 + 漂移量;损失值等价于 V2\Vert V\Vert^2

这样,优化器是在「把 f(ε)f(\varepsilon)f(ε)+Vf(\varepsilon)+V 移动」,即沿 VV 的方向更新生成分布。


文件作用
drifting.py基础 Drifting Model:mean-shift 核 + 几何平均双归一化 affinity,8 Gaussians / Checkerboard 训练与漂移场可视化。
sinkhorn_drifting.pySinkhorn(log 域 5 轮)得到双随机 affinity,替代几何平均 softmax。
cf_drifting.py特征函数(CF)距离驱动的漂移:VV 取为 CF 距离的泛函梯度,复杂度 O(NF)O(N\cdot F)FF 为频率向量数。
drifting_autoencoder.py漂移自编码器:编码器 data→latent 向先验漂移,解码器 latent→data 向数据漂移;编码/解码均为 1-NFE。

  • gen_data(n):2D 8 Gaussians(圆周排列)。
  • gen_checkerboard(n):2D Checkerboard(4 块)。
  • Net:MLP,noise_dim → hidden_dim(4 层 SELU)→ 2;输入为噪声 zz,输出为 2D 样本。
  • x:查询点(当前生成)[N, D]
  • y_pos:数据 [N_pos, D]
  • y_neg:负样本(通常 = 当前生成 x[N_neg, D]
  • torch.cdistdist_posdist_neg,当 yneg=xy_{\mathrm{neg}}=x 时屏蔽自交互(dist_neg 对角线加一大数)。
  • logits = -dist / temp,在最后一维做 softmax 得 ArowA_{\mathrm{row}},在倒数第二维做 softmax 得 AcolA_{\mathrm{col}}A=ArowAcolA = \sqrt{A_{\mathrm{row}} \cdot A_{\mathrm{col}}}
  • 拆成 AposA_{\mathrm{pos}}AnegA_{\mathrm{neg}},因子化权重:
    • Wpos=AposkAneg[i,k]W_{\mathrm{pos}} = A_{\mathrm{pos}} \cdot \sum_k A_{\mathrm{neg}}[i,k]
    • Wneg=AnegjApos[i,j]W_{\mathrm{neg}} = A_{\mathrm{neg}} \cdot \sum_j A_{\mathrm{pos}}[i,j]
  • V=Wpos@yposWneg@ynegV = W_{\mathrm{pos}} @ y_{\mathrm{pos}} - W_{\mathrm{neg}} @ y_{\mathrm{neg}}
  • gen:当前生成样本(保留梯度)。
  • torch.no_grad() 下算 V=compute_drift(gen,pos,gen,temp)V = \mathrm{compute\_drift}(\mathrm{gen}, \mathrm{pos}, \mathrm{gen}, \mathrm{temp})
  • target = (gen + V).detach(),loss = (gen - target).pow(2).sum(dim=-1)(每样本 V2\Vert V\Vert^2)。
  • DriftingModel.forward(pos, n_gen):采样 n_genzz,生成 gen,返回 drifting_loss(gen, pos, temp)
  • DriftingModel.generate(n)torch.no_grad()zN(0,I)z \sim \mathcal{N}(0,I)x=net(z)x = \mathrm{net}(z),即 1-NFE。

drifting.py 的差异仅在 affinity 的归一化方式

  • 同一套 logits(-dist_pos/temp-dist_neg/temp 拼接)。
  • 不用「行 softmax × 列 softmax 的几何平均」,而用 Sinkhorn:在 log 域交替按行、按列做 logsumexp 归一化(代码中 5 轮),再 exp 得到双随机矩阵 AA(行和、列和均为 1)。
  • 之后同样拆成 Apos/AnegA_{\mathrm{pos}}/A_{\mathrm{neg}}、因子化权重、V=Wpos@yposWneg@ynegV = W_{\mathrm{pos}}@y_{\mathrm{pos}} - W_{\mathrm{neg}}@y_{\mathrm{neg}}

双随机约束比几何平均更严格,有时对分布匹配更稳定。


这里不再用 mean-shift 核,而是用特征函数(CF)距离的泛函梯度作为漂移方向。

  • 采样 FF 个频率向量 fN(0,σ2I)f \sim \mathcal{N}(0, \sigma^2 I)
  • 经验 CF:ϕμ(f)=1Njeifxj\phi_\mu(f) = \frac{1}{N}\sum_j e^{i f^\top x_j},实部 Cμ(f)=1Njcos(fxj)C_\mu(f)=\frac{1}{N}\sum_j \cos(f^\top x_j),虚部 Sμ(f)=1Njsin(fxj)S_\mu(f)=\frac{1}{N}\sum_j \sin(f^\top x_j)
  • 距离:D=1Ffϕμ(f)ϕν(f)2D = \frac{1}{F}\sum_f |\phi_\mu(f) - \phi_\nu(f)|^2μ\mu=生成,ν\nu=数据)。

对样本 xkx_kDD 的梯度并取负(梯度下降方向),得到:

V(xk)=2NFf[ΔCsin(fxk)+ΔScos(fxk)]f V(x_k) = -\frac{2}{NF} \sum_f \big[ -\Delta C \sin(f^\top x_k) + \Delta S \cos(f^\top x_k) \big] f

其中 ΔC=CμCν\Delta C = C_\mu - C_\nuΔS=SμSν\Delta S = S_\mu - S_\nu。实现里会对 VV 做方差归一化(normalize_drift),再用同一套 drifting loss:target = (x + V).detach(),loss = (x - target).pow(2).sum(dim=-1)

O(NF)O(N \cdot F):每个 batch 对 NN 个样本、FF 个频率算内积与三角函数即可,无需 N×MN\times M 的核矩阵。


  • 编码器:data → latent;用 drifting_loss(encoded, prior) 让 latent 分布向先验 N(0,I)\mathcal{N}(0,I) 漂移(prior 每步重采样)。
  • 解码器:latent → data;用 L1 重建损失 decoded vs data(代码中未对解码端再做一次向数据的漂移,而是直接重建)。
  • 特征归一化(Sec A.6):在算漂移前对特征做零均值、单位方差,再缩放使平均成对距离约 D\sqrt{D},便于温度与尺度解耦;漂移算在归一化空间,再映射回原空间并做方差归一化。
  • 编码/解码均为 1-NFE;latent 在训练时 detach,编码器与解码器通过不同损失分别更新。

  • 基础训练(8 Gaussians + Checkerboard,漂移场与样本对比图):
    python drifting.py
  • Sinkhorn 版python sinkhorn_drifting.py,流程同 drifting.py,仅 affinity 为 Sinkhorn。
  • CF 漂移python cf_drifting.py,使用 CFDriftGenerator,可调 num_freqfreq_std
  • 漂移自编码器python drifting_autoencoder.py,输出 data / encoded / generated / reconstructed 四宫格。

生成图片包括:数据分布、训练中/收敛后生成分布对比、漂移场 quiver 图(数据点、生成点、VV 箭头)。


  • Minimal-Drifting-Models 用 2D 玩具数据实现了 Deng et al. 的 Drifting Models:训练时用漂移场 VV 演化 pushforward 分布,推理时 1-NFE 生成。
  • 漂移场:mean-shift 形式 V=Wposy+WnegyV = W_{\mathrm{pos}}y^+ - W_{\mathrm{neg}}y^-,由核亲和度双归一化(或 Sinkhorn)得到权重;另有 CF 距离版本,复杂度 O(NF)O(N\cdot F)
  • 损失:MSE(f(ε)f(\varepsilon), sg(f(ε)+V)\mathrm{sg}(f(\varepsilon)+V)),梯度只过 ff,等价于最小化 V2\Vert V\Vert^2
  • 扩展包括:Sinkhorn 双随机、CF 漂移、漂移自编码器(编码向先验、解码向数据/重建)。

相关内容