扩散模型中的强化学习方法

扩散模型中的强化学习方法

本文档系统梳理四种面向扩散 / 流匹配(Flow Matching)生成模型的偏好对齐与强化学习方法:
Diffusion-DPO、Flow-GRPO、Dance-GRPO、DiffusionNFT。
每种方法均给出:动机、方法推导(关键公式)、框架图、以及完整的 Python 伪代码(标明训练流程、输入输出与全部损失函数)。

参考资料:

  • Diffusion-DPO — Diffusion Model Alignment Using Direct Preference Optimization
  • Flow-GRPO — Flow-GRPO: Training Flow Matching Models via Online RL
  • Dance-GRPO — DanceGRPO: Unleashing GRPO on Visual Generation
  • DiffusionNFT — DiffusionNFT: Online Diffusion Reinforcement with Forward Process

目录


1. 总览:四种方法的定位与对比

大语言模型(LLM)有成熟的"预训练 → 对齐(RLHF/DPO)"两阶段范式,而文生图 / 文生视频模型长期只有单阶段预训练。如何把 RLHF 搬到扩散模型与流匹配模型上,是这四篇工作共同的主线。它们分别代表了四条技术路线:

维度 Diffusion-DPO Flow-GRPO Dance-GRPO DiffusionNFT
优化范式 离线 DPO(偏好对) 在线 RL(GRPO) 在线 RL(GRPO) 在线 RL(对比监督目标,免策略梯度)
数据需求 静态偏好对 (c,xw,xl)(c, x^w, x^l) 在线采样 + 奖励模型 在线采样 + 奖励模型 在线采样 + 奖励模型
是否需要似然 需要(用 ELBO/前向过程近似) 需要(SDE 转移高斯闭式) 需要(SDE 转移高斯闭式) 不需要(likelihood-free)
是否需要 SDE 采样 否(前向加噪训练) 是(ODE→SDE 转换) 是(统一 SDE 改写) 否(rollout 用任意 ODE 求解器)
是否需要存轨迹 否 是(逐步 log_prob) 是(逐步 log_prob) 否(只存最终图像 x0x_0 与 reward)
参考模型 / KL 冻结 ref,β\beta 隐式 KL 冻结 ref,显式 KL 正则 默认省略 KL(极小 clip 代替) EMA old policy 作锚点(隐式正/负策略)
主要验证模型 SD1.5 / SDXL SD3.5-M、FLUX.1-Dev SD1.4、FLUX、HunyuanVideo、SkyReels-I2V SD3.5-M
任务范围 T2I(+图像编辑) T2I(GenEval/OCR/偏好) T2I + T2V + I2V T2I(多奖励联合)
典型组大小 G —(成对数据) 24 12 24(仅用于 reward 归一化)

一句话区分:

  • Diffusion-DPO:把 DPO 推导到扩散路径空间,得到一个"win/lose 去噪误差之差"的离线分类损失;
  • Flow-GRPO:把确定性 ODE 采样转成保边缘分布的 SDE,使 GRPO 的 ratio/KL 可闭式计算,首次在 flow 模型上跑通在线 RL;
  • Dance-GRPO:把扩散与 rectified flow 的采样统一写成 SDE 形式,用一套 GRPO 框架打通图像与视频生成;
  • DiffusionNFT:彻底跳出"反向过程 MDP + 似然"的路线,在前向加噪过程上用正/负隐式策略的对比回归目标做在线强化,无需 SDE、无需轨迹、无需似然。

2. 预备知识

2.1 扩散模型与流匹配

扩散模型前向加噪过程:

xt=αtx0+σtϵ,ϵ∼N(0,I)x_t = \alpha_t x_0 + \sigma_t \epsilon, \qquad \epsilon \sim \mathcal{N}(0, I)

训练目标为噪声预测(ϵ\epsilon-prediction):

LDM=Ex0,ϵ,t[ω(λt) ∥ϵ−ϵθ(xt,c,t)∥22],λt=αt2/σt2\mathcal{L}_{DM} = \mathbb{E}_{x_0, \epsilon, t}\left[\omega(\lambda_t)\,\|\epsilon - \epsilon_\theta(x_t, c, t)\|_2^2\right], \qquad \lambda_t = \alpha_t^2/\sigma_t^2

流匹配 / Rectified Flow 前向为数据与噪声的线性插值:

xt=(1−t) x0+t ϵ,t∈[0,1],v=ϵ−x0x_t = (1-t)\,x_0 + t\,\epsilon, \quad t \in [0,1], \qquad v = \epsilon - x_0

训练目标为速度场回归:

LFM(θ)=Et,x0,ϵ∥v−vθ(xt,c,t)∥2\mathcal{L}_{FM}(\theta) = \mathbb{E}_{t, x_0, \epsilon}\left\|v - v_\theta(x_t, c, t)\right\|^2

采样遵循确定性 ODE:dxt=vθ(xt,t) dtdx_t = v_\theta(x_t, t)\,dt。

2.2 去噪过程的 MDP 建模(DDPO 框架)

把 TT 步去噪过程建模为马尔可夫决策过程 (S,A,ρ0,P,R)(\mathcal{S}, \mathcal{A}, \rho_0, P, R):

st≜(c,t,xt),at≜xt−1,πθ(at∣st)≜pθ(xt−1∣xt,c)s_t \triangleq (c, t, x_t), \qquad a_t \triangleq x_{t-1}, \qquad \pi_\theta(a_t|s_t) \triangleq p_\theta(x_{t-1}|x_t, c)

P(st+1∣st,at)≜(c,t−1,xt−1) (确定性转移),R(st,at)={r(x0,c),t=00,否则P(s_{t+1}|s_t, a_t) \triangleq (c, t-1, x_{t-1}) \ \text{(确定性转移)}, \qquad R(s_t, a_t) = \begin{cases} r(x_0, c), & t = 0 \\ 0, & \text{否则} \end{cases}

奖励只在轨迹终端给出(稀疏奖励)。带 KL 正则的 RL 目标为:

max⁡πθ Eτ∼πθ[∑tR(st,at)]−β DKL(πθ ∥ πref)\max_{\pi_\theta}\ \mathbb{E}_{\tau \sim \pi_\theta}\left[\sum_{t} R(s_t, a_t)\right] - \beta\, D_{KL}\left(\pi_\theta \,\|\, \pi_{ref}\right)

核心障碍:RL 需要转移概率 pθ(xt−1∣xt,c)p_\theta(x_{t-1}|x_t,c) 可计算且采样过程有随机性,而 flow 模型的 ODE 采样是确定性的——这正是 Flow-GRPO / Dance-GRPO 要解决的问题;Diffusion-DPO 与 DiffusionNFT 则分别用"ELBO 上界"与"前向过程对比目标"绕开了这一问题。

2.3 GRPO 回顾

GRPO(Group Relative Policy Optimization,DeepSeekMath 提出)对每个 prompt cc 采样一组 GG 个输出,用组内归一化奖励代替 value network 估计优势:

A^i=ri−mean({rj}j=1G)std({rj}j=1G)\hat{A}_i = \frac{r_i - \mathrm{mean}(\{r_j\}_{j=1}^G)}{\mathrm{std}(\{r_j\}_{j=1}^G)}

并用 PPO 式 clipped 目标更新:

J=E[1G∑i=1Gmin⁡(ρiA^i, clip(ρi,1−ε,1+ε)A^i)],ρi=πθ(ai∣si)πθold(ai∣si)\mathcal{J} = \mathbb{E}\left[\frac{1}{G}\sum_{i=1}^G \min\left(\rho_i \hat{A}_i,\ \mathrm{clip}(\rho_i, 1-\varepsilon, 1+\varepsilon)\hat{A}_i\right)\right], \qquad \rho_i = \frac{\pi_\theta(a_i|s_i)}{\pi_{\theta_{old}}(a_i|s_i)}


3. Diffusion-DPO:离线偏好优化

3.1 动机

LLM 的 DPO 不显式训练奖励模型、不做在线采样,直接在偏好对数据上用分类目标优化策略。搬到扩散模型上的核心障碍是:扩散模型的似然 pθ(x0∣c)p_\theta(x_0|c) 不可解(需要对整条去噪路径 x1:Tx_{1:T} 边缘化)。此前路线的缺陷:

  • DDPO / DPOK(在线 RL):需反复完整采样轨迹,高方差、昂贵;在开放词汇 prompt 集上效果显著衰减;
  • DRaFT / AlignProp(奖励梯度反传):要求奖励可微,容易 mode collapse;
  • DOODL(推理时优化):推理开销高一个数量级。

Diffusion-DPO 用 ELBO 思路在路径空间上重新推导 DPO 目标,得到一个可微、稳定、离线的损失,首次实现开放词汇下的扩散模型偏好对齐,且不增加任何推理开销。

3.2 方法推导

第一步:DPO 回顾。 Bradley-Terry 偏好模型 p(x0w≻x0l∣c)=σ(r(c,x0w)−r(c,x0l))p(x_0^w \succ x_0^l|c) = \sigma(r(c,x_0^w) - r(c,x_0^l));KL 正则 RLHF 目标有闭式最优解 p∗(x0∣c)∝pref(x0∣c)exp⁡(r/β)p^*(x_0|c) \propto p_{ref}(x_0|c)\exp(r/\beta),奖励可重参数化为

r(c,x0)=βlog⁡pθ∗(x0∣c)pref(x0∣c)+βlog⁡Z(c)r(c, x_0) = \beta \log \frac{p_\theta^*(x_0|c)}{p_{ref}(x_0|c)} + \beta \log Z(c)

代入 BT 目标后配分函数消去,得到 LLM 版 DPO 损失。但该损失需要 pθ(x0∣c)p_\theta(x_0|c),扩散模型无法直接计算。

第二步:提升到路径空间。 定义路径级奖励 R(c,x0:T)R(c, x_{0:T}) 使 r(c,x0)=Epθ(x1:T∣x0,c)[R(c,x0:T)]r(c,x_0) = \mathbb{E}_{p_\theta(x_{1:T}|x_0,c)}[R(c,x_{0:T})],并把边际 KL 放松为联合 KL 上界:

DKL[pθ(x0∣c)∥pref(x0∣c)]≤DKL[pθ(x0:T∣c)∥pref(x0:T∣c)]D_{KL}\left[p_\theta(x_0|c) \| p_{ref}(x_0|c)\right] \le D_{KL}\left[p_\theta(x_{0:T}|c) \| p_{ref}(x_{0:T}|c)\right]

从而得到路径空间的扩散版 DPO 目标:

LDPO-Diffusion(θ)=−E(x0w,x0l)∼Dlog⁡σ(β Ex1:Tw, x1:Tl[log⁡pθ(x0:Tw)pref(x0:Tw)−log⁡pθ(x0:Tl)pref(x0:Tl)])\mathcal{L}_{DPO\text{-}Diffusion}(\theta) = -\mathbb{E}_{(x_0^w, x_0^l) \sim \mathcal{D}} \log \sigma\left( \beta\, \mathbb{E}_{x_{1:T}^w,\, x_{1:T}^l} \left[ \log \frac{p_\theta(x_{0:T}^w)}{p_{ref}(x_{0:T}^w)} - \log \frac{p_\theta(x_{0:T}^l)}{p_{ref}(x_{0:T}^l)} \right] \right)

第三步:两个近似得到可实现损失。 (i) 利用 −log⁡σ-\log\sigma 的凸性 + Jensen 不等式把期望推出 sigmoid 外(上界);(ii) 用前向过程 q(xt−1,xt∣x0)q(x_{t-1}, x_t|x_0) 近似反向条件分布(即用前向加噪采样代替跑反向链)。将高斯转移的 KL 展开后,得到最终损失——本质是"去噪误差之差":

L(θ)=−E(x0w,x0l)∼D,t∼U(0,T)xtw∼q(xtw∣x0w),xtl∼q(xtl∣x0l)log⁡σ(−βTω(λt)[∥ϵw−ϵθ(xtw,t)∥22−∥ϵw−ϵref(xtw,t)∥22⏟win:policy 相对 ref 的误差增量−(∥ϵl−ϵθ(xtl,t)∥22−∥ϵl−ϵref(xtl,t)∥22)⏟lose:同左])\boxed{\mathcal{L}(\theta) = -\mathbb{E}_{\substack{(x_0^w,x_0^l)\sim\mathcal{D},\, t\sim U(0,T) \\ x_t^w \sim q(x_t^w|x_0^w),\, x_t^l \sim q(x_t^l|x_0^l)}} \log \sigma\Big( -\beta T \omega(\lambda_t) \big[ \underbrace{\|\epsilon^w - \epsilon_\theta(x_t^w, t)\|_2^2 - \|\epsilon^w - \epsilon_{ref}(x_t^w, t)\|_2^2}_{\text{win:policy 相对 ref 的误差增量}} - \underbrace{\big(\|\epsilon^l - \epsilon_\theta(x_t^l, t)\|_2^2 - \|\epsilon^l - \epsilon_{ref}(x_t^l, t)\|_2^2\big)}_{\text{lose:同左}} \big] \Big)}

其中 xt∗=αtx0∗+σtϵ∗x_t^* = \alpha_t x_0^* + \sigma_t \epsilon^*,常数 TT 被吸收进 β\beta。直观含义:鼓励 policy 在 win 样本上比参考模型去噪得更好、在 lose 样本上去噪得更差。

理论补充:论文附录给出两个等价视角——(1) 多步 RL / soft Bellman 视角证明 Diffusion-DPO 与 DDPO 优化同一目标,但它是**离线(off-policy)**算法;(2) 噪声感知偏好模型视角给出隐式奖励估计 r(c,x0A)−r(c,x0B)=β[(SEθA−SErefA)−(SEθB−SErefB)]r(c,x_0^A) - r(c,x_0^B) = \beta[(SE_\theta^A - SE_{ref}^A) - (SE_\theta^B - SE_{ref}^B)](SE 为噪声预测误差平方),训练后的模型本身就是一个奖励模型。

3.3 框架图

flowchart LR
    subgraph 数据层["偏好数据层(静态、离线)"]
        D["Pick-a-Pic v2<br/>(851K 人类偏好对)<br/>或 AI 反馈伪标注<br/>(PickScore/HPS/CLIP 排序)"]
    end

    subgraph 训练["Diffusion-DPO 训练"]
        direction TB
        Q["前向加噪 q(x_t|x_0)<br/>t ~ U(0,T), ε ~ N(0,I)"]
        P["Policy 模型 ε_θ<br/>(可训练, SD1.5/SDXL)"]
        R["Reference 模型 ε_ref<br/>(冻结的初始副本)"]
        L["损失:<br/>-log σ(-β[(SE_θ^w - SE_ref^w)<br/>- (SE_θ^l - SE_ref^l)])"]
        Q --> P & R
        P --> L
        R --> L
    end

    D -->|"x_0^w ≻ x_0^l | c"| Q
    L -->|"梯度只更新 ε_θ"| P

    subgraph 推理["推理(无任何变化)"]
        G["标准采样器<br/>零额外开销"]
    end
    P -.->|"训练完成后"| G

组件说明:

  • Policy model ϵθ\epsilon_\theta:要训练的扩散模型(SD1.5 或 SDXL-1.0 base);
  • Reference model ϵref\epsilon_{ref}:policy 的冻结初始副本,提供 KL 锚点,损失中始终以"policy 误差 − ref 误差"形式出现;
  • 偏好数据:人类反馈(Pick-a-Pic v2)或 AI 反馈(用 PickScore/HPSv2/CLIP/Aesthetic 对生成图排序得到伪 win/lose);
  • 数据流:图像编码为 latent → 前向加噪 → 双模型四路前向(policy/ref × win/lose)→ 对比四个 MSE → 梯度只更新 policy。

3.4 伪代码(Python)

训练流程:离线训练,无需任何在线采样。每个 step 从偏好数据集取一个 batch 的 (c,x0w,x0l)(c, x_0^w, x_0^l) 三元组,前向加噪后做 4 次网络前向(policy/ref × win/lose),计算 DPO 损失并只更新 policy。

  • 输入:预训练扩散模型 model(SD1.5/SDXL)、偏好数据集 D={(c,x0w,x0l)}\mathcal{D}=\{(c, x_0^w, x_0^l)\}、超参数 beta;
  • 输出:对齐后的扩散模型 model(推理流程完全不变);
  • 损失:仅一个——DPO 偏好分类损失 LDPO\mathcal{L}_{DPO}(式 14)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import torch
import torch.nn.functional as F

def diffusion_dpo_loss(model, ref_model, x_w, x_l, c, beta):
"""
Diffusion-DPO 损失(论文 Eq.14,唯一的训练损失)

输入:
model: 可训练的扩散模型(policy),输入 (x_t, c, t) 输出噪声预测
ref_model: model 的冻结初始副本(reference),不计算梯度
x_w, x_l: (B, C, H, W) 优选 / 劣选图像 latent
c: (B,) 文本条件
beta: float,正则化强度(SD1.5 取 2000,SDXL 取 5000)
输出:
loss: 标量,DPO 偏好分类损失
"""
B = x_w.shape[0]
t = torch.randint(0, 1000, (B,)) # 均匀采样时间步 t ~ U(0, T)
noise = torch.randn_like(x_w) # win/lose 共用同一噪声 ε ~ N(0, I)

x_t_w = add_noise(x_w, noise, t) # x_t = alpha_t * x_0 + sigma_t * ε
x_t_l = add_noise(x_l, noise, t)

# ---- 四路前向:policy/ref × win/lose ----
model_w_pred = model(x_t_w, c, t) # policy 对 win 的噪声预测
model_l_pred = model(x_t_l, c, t) # policy 对 lose 的噪声预测
with torch.no_grad():
ref_w_pred = ref_model(x_t_w, c, t) # reference 对 win(冻结)
ref_l_pred = ref_model(x_t_l, c, t) # reference 对 lose(冻结)

# ---- 四个去噪误差(noise prediction squared error, SE) ----
model_w_err = (model_w_pred - noise).pow(2).flatten(1).sum(dim=1) # (B,)
model_l_err = (model_l_pred - noise).pow(2).flatten(1).sum(dim=1)
ref_w_err = (ref_w_pred - noise).pow(2).flatten(1).sum(dim=1)
ref_l_err = (ref_l_pred - noise).pow(2).flatten(1).sum(dim=1)

# ---- DPO 偏好分类损失 ----
# win 侧误差增量(越小越好);lose 侧误差增量(越大越好)
w_diff = model_w_err - ref_w_err
l_diff = model_l_err - ref_l_err
logits = -beta * (w_diff - l_diff) # 隐式奖励差
loss = -F.logsigmoid(logits).mean() # L = -E[log σ(·)]
return loss


def train_diffusion_dpo(model, ref_model, dataloader, optimizer,
beta=5000, num_steps=10000):
"""
Diffusion-DPO 训练主循环(离线,无在线采样)

输入:
model: 预训练扩散模型(SD1.5 / SDXL-1.0 base)
ref_model: model 的深拷贝,全程冻结
dataloader: 偏好数据迭代器,每个 batch 产出 (c, x_w, x_l)
optimizer: AdamW / Adafactor,lr = (2000/beta) * 2.048e-8
beta: DPO 正则化强度;num_steps: 训练步数
输出:
model: 对齐后的扩散模型(推理流程与标准采样完全一致,零额外开销)
"""
ref_model.eval().requires_grad_(False)
model.train()

for step, (c, x_w, x_l) in enumerate(dataloader):
if step >= num_steps:
break
loss = diffusion_dpo_loss(model, ref_model, x_w, x_l, c, beta)

optimizer.zero_grad()
loss.backward() # 梯度只流向 policy model
optimizer.step()
return model

3.5 实验与超参数

  • 设置:SD1.5 / SDXL-1.0 base(3.5B);Pick-a-Pic v2(851K 对,58,960 个唯一 prompt);人类评测(AMT 多数投票)+ PickScore 自动评测。
  • 主要结果:
    • DPO-SDXL vs SDXL-base:PartiPrompts 总体偏好胜率 70.0%;HPSv2 benchmark 胜率 64.7%,HPSv2 均分 28.16 登顶 leaderboard;
    • 仅 base(3.5B)vs base+refiner 完整管线(6.6B):总体偏好胜率 69%(PartiPrompts)/ 64%(HPSv2);
    • 图像编辑(SDEdit):被偏好 65% vs 24%;
    • 隐式奖励:DPO-SDXL 偏好分类准确率 72.0%,超过 PickScore(64.2)、HPS(59.3)、CLIP(57.1)等所有显式打分模型;
    • 参考模型需约 10 倍推理算力的 best-of-n 采样才能追平单次 DPO 生成。
  • 关键超参数:β=2000\beta = 2000(SD1.5)/ 50005000(SDXL);学习率 2000β×2.048×10−8\frac{2000}{\beta}\times 2.048\times 10^{-8}(与 β\beta 反比缩放);有效 batch 2048 对;t∼U(0,1000)t \sim U(0,1000)。

4. Flow-GRPO:流匹配模型上的在线 RL

4.1 动机

Flow matching 已成为图像(SD3.5、FLUX)与视频生成的主流框架,但把在线 RL(GRPO)搬过来有两大困难:

  1. ODE 确定性 vs RL 所需的随机性:flow 采样 dxt=vt dtdx_t = v_t\,dt 是确定性映射,除初始种子外没有随机性——GRPO 的 ratio 需要转移概率 pθ(xt−1∣xt,c)p_\theta(x_{t-1}|x_t,c),确定性动力学下无法直接计算;RL 的探索也无从谈起。
  2. 在线 RL 采样效率低:每步训练都要用当前策略完整采样一组轨迹(SD3.5-M 默认 40 步去噪),代价高昂。

对应解:(1) ODE-to-SDE 转换——构造一个在所有时刻与原 ODE 边缘分布完全一致的等价 SDE,注入随机性而不改变数据分布;(2) Denoising Reduction——训练只用 10 步去噪收集数据,推理仍用 40 步。

4.2 核心方法

(1) ODE-to-SDE 转换。 设 SDE 与原 ODE 边缘分布一致(Fokker–Planck 方程约束),解出漂移项后用 Anderson 反向时间 SDE 公式,得到保边缘分布的反向 SDE:

dxt=[vt(xt)−σt22∇log⁡pt(xt)]dt+σt dwˉdx_t = \Big[v_t(x_t) - \frac{\sigma_t^2}{2}\nabla\log p_t(x_t)\Big]dt + \sigma_t\, d\bar{w}

Rectified Flow 下 score 有显式表达(利用 pt∣0(xt∣x0)=N((1−t)x0, t2I)p_{t|0}(x_t|x_0)=\mathcal{N}((1-t)x_0,\, t^2 I) 与 vt(x)=E[x1−x0∣xt]v_t(x)=\mathbb{E}[x_1-x_0|x_t]):

∇log⁡pt(x)=−xt−1−ttvt(x)\nabla\log p_t(x) = -\frac{x}{t} - \frac{1-t}{t}v_t(x)

代入得到最终 SDE 及其 Euler–Maruyama 离散化更新规则(训练实际使用):

xt+Δt=xt+[vθ(xt,t)+σt22t(xt+(1−t) vθ(xt,t))]Δt+σtΔt ϵ,ϵ∼N(0,I)\boxed{x_{t+\Delta t} = x_t + \Big[v_\theta(x_t,t) + \frac{\sigma_t^2}{2t}\big(x_t + (1-t)\,v_\theta(x_t,t)\big)\Big]\Delta t + \sigma_t\sqrt{\Delta t}\ \epsilon},\qquad \epsilon\sim\mathcal{N}(0,I)

噪声调度取 σt=at1−t\sigma_t = a\sqrt{\frac{t}{1-t}},aa 为标量超参(noise level,默认 0.7)。

关键收益:策略 πθ(xt−1∣xt,c)\pi_\theta(x_{t-1}|x_t,c) 变为各向同性高斯(均值为上式确定性部分,方差 σt2Δt\sigma_t^2\Delta t),于是 ratio 与 KL 都有闭式解:

DKL(πθ∥πref)=Δt2(σt(1−t)2t+1σt)2∥vθ(xt,t)−vref(xt,t)∥2D_{KL}(\pi_\theta\|\pi_{ref}) = \frac{\Delta t}{2}\Big(\frac{\sigma_t(1-t)}{2t} + \frac{1}{\sigma_t}\Big)^2\big\|v_\theta(x_t,t)-v_{ref}(x_t,t)\big\|^2

(2) Flow-GRPO 目标。 组相对优势(同一轨迹所有时间步共享):

A^ti=R(x0i,c)−mean({R(x0i,c)}i=1G)std({R(x0i,c)}i=1G)\hat{A}_t^i = \frac{R(x_0^i,c) - \mathrm{mean}(\{R(x_0^i,c)\}_{i=1}^G)}{\mathrm{std}(\{R(x_0^i,c)\}_{i=1}^G)}

PPO 式 clipped 目标 + 闭式 KL 正则:

JFlow-GRPO(θ)=E[1G∑i=1G1T∑t=0T−1(min⁡(rti(θ)A^ti, clip(rti(θ),1−ε,1+ε)A^ti)−β DKL(πθ∥πref))]\mathcal{J}_{\text{Flow-GRPO}}(\theta) = \mathbb{E}\Big[\frac{1}{G}\sum_{i=1}^{G}\frac{1}{T}\sum_{t=0}^{T-1}\Big(\min\big(r_t^i(\theta)\hat{A}_t^i,\ \mathrm{clip}(r_t^i(\theta),1-\varepsilon,1+\varepsilon)\hat{A}_t^i\big) - \beta\, D_{KL}(\pi_\theta\|\pi_{ref})\Big)\Big]

(3) Denoising Reduction。 训练采样只用 T=10T=10 步(推理仍用 40 步),带来 >4 倍墙钟加速且最终奖励不受影响——GRPO 依赖组内相对偏好,低步数样本虽质量差但仍包含有用的相对奖励信号。

4.3 框架图

flowchart LR
    subgraph Rollout["Rollout(在线采样)"]
        C["Prompt c"] --> S["SDE 采样器 (Eq.9)<br/>T=10 步, σ_t = a√(t/(1-t))<br/>每个 prompt 采 G=24 条轨迹"]
        S --> TR["轨迹 {(x_T,...,x_0)}^G<br/>+ 每步 log_prob"]
    end

    subgraph Reward["奖励评估(黑盒,无需可微)"]
        RM["Reward Models:<br/>GenEval 规则奖励 / OCR 编辑距离 /<br/>PickScore 人类偏好"]
    end

    subgraph Update["策略更新"]
        ADV["组内归一化优势<br/>A^i = (R^i - mean)/std"]
        PPO["PPO-clip 目标<br/>+ 闭式 KL(v_θ ‖ v_ref)"]
        LORA["只更新 LoRA<br/>(r=32, α=64)"]
        ADV --> PPO --> LORA
    end

    TR -->|"x_0^i"| RM
    RM -->|"R(x_0^i, c)"| ADV
    TR -->|"逐步 log_prob"| PPO
    REF["冻结 Reference 模型"] --> PPO
    OLD["旧策略 π_θ_old<br/>(定期同步)"] --> PPO
    LORA -->|"定期同步"| OLD

    subgraph Infer["推理"]
        I["切回 40 步 ODE/SDE 采样"]
    end
    LORA -.-> I

4.4 伪代码(Python)

训练流程:每个迭代 = Rollout(SDE 采样 G 条轨迹)→ Reward 计算 → 组内优势归一化 → PPO 更新(含 KL 正则)。只更新 LoRA 参数;推理时切回 40 步 ODE/SDE 采样。

  • 输入:预训练 flow 模型 vθv_\theta(SD3.5-M)、prompt 集 C\mathcal{C}、奖励函数 R(x0,c)R(x_0, c)(黑盒,无需可微)、超参数 G,T,a,β,εG, T, a, \beta, \varepsilon;
  • 输出:对齐后的 flow 模型;
  • 损失:两部分——(1) PPO-clip 策略损失 Lclip\mathcal{L}_{clip}(含组内优势与重要性比率);(2) 闭式 KL 正则 β⋅DKL(πθ∥πref)\beta \cdot D_{KL}(\pi_\theta \| \pi_{ref})。总损失 L=Lclip+β LKL\mathcal{L} = \mathcal{L}_{clip} + \beta\,\mathcal{L}_{KL}。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import torch

# ============================================================
# 组件 1:ODE-to-SDE 采样步(论文 Eq.9),同时记录高斯转移 log_prob
# ============================================================
def sde_step(model, x_t, t, dt, c, noise_level=0.7, generator=None):
"""
Flow-GRPO 的 SDE 单步采样(保边缘分布,注入随机性)

输入: model v_θ;当前 latent x_t;时间 t(rectified flow, t∈[0,1],1→纯噪声);
步长 dt<0;文本条件 c;noise level a
输出: x_next(下一步 latent);log_prob(该步转移对数概率,old policy 记录用)
"""
v = model(x_t, c, t) # 速度场预测
sigma = noise_level * (t / (1 - t)).sqrt() # σ_t = a·sqrt(t/(1-t))
# 确定性均值:x_t + [v + (σ²/2t)(x_t + (1-t)v)]·Δt
mean = x_t + (v + sigma**2 / (2 * t) * (x_t + (1 - t) * v)) * dt
std = sigma * (-dt).sqrt() # σ_t·√|Δt|
x_next = mean + std * torch.randn(x_t.shape, generator=generator)
# 高斯转移对数概率 log π_θ(x_next | x_t, c)(闭式)
log_prob = (-(x_next - mean).pow(2) / (2 * std**2)
- torch.log(std) - 0.5 * torch.log(2 * torch.pi)).flatten(1).mean(dim=1)
return x_next, log_prob


# ============================================================
# 组件 2:组内优势归一化(GRPO 核心,无 value network)
# ============================================================
def compute_group_advantages(rewards, G):
"""
输入: rewards (B*G,) —— B 个 prompt 各采 G 张图的最终奖励 R(x_0, c)
输出: advantages (B*G,) —— Â_i = (R_i - mean_g) / std_g(全时间步共享)
"""
r = rewards.view(-1, G)
return ((r - r.mean(dim=1, keepdim=True))
/ (r.std(dim=1, keepdim=True) + 1e-8)).flatten()


# ============================================================
# 组件 3:Flow-GRPO 损失(PPO-clip 策略损失 + 闭式 KL 正则)
# ============================================================
def flow_grpo_loss(model, ref_model, latents, next_latents, timesteps, dt,
prompts, old_log_probs, advantages,
noise_level=0.7, clip_eps=0.2, kl_beta=0.04):
"""
输入:
model / ref_model: 当前 policy / 冻结 reference
latents: (B*G, T, ...) rollout 记录的每步 x_t
next_latents: (B*G, T, ...) rollout 记录的每步 x_{t+Δt}(动作)
old_log_probs: (B*G, T) rollout 时 old policy 的 log π_θ_old
advantages: (B*G,) 组内归一化优势(广播到所有时间步)
clip_eps: PPO clip 范围 ε;kl_beta: KL 系数 β
输出:
total_loss = policy_loss + kl_beta * kl_loss
"""
policy_loss, kl_loss = 0.0, 0.0
T = latents.shape[1]
for t_idx in range(T): # 对每个去噪时间步
t = timesteps[t_idx]
v = model(latents[:, t_idx], prompts, t) # 当前 policy 速度场
sigma = noise_level * (t / (1 - t)).sqrt()
mean = latents[:, t_idx] + (v + sigma**2 / (2*t)
* (latents[:, t_idx] + (1 - t) * v)) * dt
std = sigma * (-dt).sqrt()
# 当前 policy 的 log π_θ(x_{t+Δt} | x_t, c)(闭式高斯)
log_prob = (-(next_latents[:, t_idx] - mean).pow(2) / (2 * std**2)
- torch.log(std) - 0.5 * torch.log(2 * torch.pi)).flatten(1).mean(dim=1)

# ---- 损失 1:PPO-clip 策略损失 ----
ratio = torch.exp(log_prob - old_log_probs[:, t_idx]) # ρ_t^i(θ)
adv = advantages # 全时间步共享
unclipped = -adv * ratio
clipped = -adv * torch.clamp(ratio, 1 - clip_eps, 1 + clip_eps)
policy_loss = policy_loss + torch.maximum(unclipped, clipped).mean()

# ---- 损失 2:闭式 KL 正则 D_KL(π_θ ‖ π_ref)(防 reward hacking) ----
with torch.no_grad():
v_ref = ref_model(latents[:, t_idx], prompts, t)
coef = ((sigma * (1 - t) / (2 * t) + 1 / sigma)**2) * (-dt) / 2
kl_loss = kl_loss + (coef * (v - v_ref).pow(2).flatten(1).sum(dim=1)).mean()

total_loss = policy_loss / T + kl_beta * kl_loss / T
return total_loss


# ============================================================
# 训练主循环
# ============================================================
def train_flow_grpo(model, ref_model, reward_fn, prompt_set, optimizer,
G=24, T=10, T_eval=40, noise_level=0.7,
clip_eps=0.2, kl_beta=0.04, num_iters=5000):
"""
Flow-GRPO 训练主循环(在线 RL)

输入:
model: 预训练 flow 模型 v_θ(SD3.5-M,仅 LoRA 可训练, r=32, α=64)
ref_model: model 的冻结副本(KL 锚点)
reward_fn: 黑盒奖励 R(x_0, c):GenEval 规则 / OCR 编辑距离 / PickScore
prompt_set: 训练 prompt 集
G: 组大小 24;T: 训练去噪步数 10(Denoising Reduction,推理用 T_eval=40)
noise_level: a=0.7;kl_beta: 0.04(GenEval/OCR)/ 0.01(PickScore)
输出:
model: 对齐后的模型(推理切回 40 步 ODE/SDE 采样)
"""
ref_model.eval().requires_grad_(False)

for it in range(num_iters):
# ---------- 阶段 1:Rollout(old policy SDE 采样,记录轨迹与 log_prob) ----------
model.eval()
old_model = copy_and_freeze(model) # π_θ_old ← π_θ(定期同步)
prompts = sample_prompts(prompt_set) # 一批 prompt
latents, next_latents, old_log_probs, images = [], [], [], []
with torch.no_grad():
for c in prompts:
for g in range(G): # 每个 prompt 采 G 条轨迹
x = torch.randn(latent_shape) # 每条 rollout 不同初始噪声
traj, traj_next, traj_lp = [x], [], []
for t, dt in zip(sde_timesteps(T), sde_step_sizes(T)):
x_next, lp = sde_step(old_model, x, t, dt, c, noise_level)
traj_next.append(x_next); traj_lp.append(lp)
x = x_next
latents.append(traj[:-1]); next_latents.append(traj_next)
old_log_probs.append(traj_lp)
images.append(decode(x)) # 最终图像 x_0

# ---------- 阶段 2:Reward 计算 + 组内优势 ----------
rewards = reward_fn(images, prompts) # (B*G,) 黑盒奖励
advantages = compute_group_advantages(rewards, G) # Â = (R - mean_g)/std_g

# ---------- 阶段 3:PPO 更新(只更新 LoRA) ----------
model.train()
loss = flow_grpo_loss(model, ref_model, latents, next_latents,
sde_timesteps(T), dt, prompts,
old_log_probs, advantages,
noise_level, clip_eps, kl_beta)
optimizer.zero_grad()
loss.backward()
optimizer.step()
return model

4.5 实验与超参数

  • 设置:SD3.5-M(附录验证 FLUX.1-Dev),512 分辨率,24×A800,LoRA 微调。
  • 主要结果:
    • GenEval:0.63 → 0.95,超过 GPT-4o(0.84)、FLUX.1 Dev(0.66)、SD3.5-L(0.71);Position 子项 0.24→0.99、Counting 0.50→0.95;
    • OCR 文字渲染:0.59 → 0.92(w/ KL);
    • PickScore 人类偏好:21.72 → 23.31(w/ KL);
    • 质量无损:w/ KL 时 DrawBench 上 Aesthetic/DeQA/ImageReward 与基座持平;w/o KL 则显著退化(reward hacking:质量下降、多样性坍塌);
    • 基线对比:一致大幅优于 SFT / Flow-RWR / Flow-DPO / DDPO / ReFL / ORW;DDPO 后期崩溃,Flow-GRPO 稳定上升。
  • 关键超参数:G=24G=24(G=12/6G=12/6 优势估计方差大、训练坍塌);训练步数 T=10T=10;noise level a=0.7a=0.7(0.1 探索不足,过大注入噪声过多导致训练失败);KL β=0.04\beta=0.04(GenEval/OCR)/ 0.010.01(PickScore)。

5. Dance-GRPO:统一的视觉生成 GRPO 框架

5.1 动机与定位

DanceGRPO(字节跳动 Seed & HKU)把 GRPO 推广为首个统一的视觉生成 RLHF 框架,横跨两种生成范式(扩散 / rectified flow)、三类任务(T2I、T2V、I2V)、四个基座模型、五种 reward。它针对此前 RL 方法的三个未解决问题:

  1. rectified flow 采样是确定性 ODE,与 MDP 建模冲突;
  2. DDPO/DPOK 在小 prompt 集(<100 条)之外训练不稳定、易发散;
  3. 现有 RL 方法未在视频生成上验证过。

核心做法与 Flow-GRPO 同源(把采样重写成 SDE),但给出了扩散与 rectified flow 的统一 SDE 形式,并针对大规模(>10,000 prompts)、视频任务给出一套稳定性工程方案。

5.2 核心方法

(1) 统一视角。 扩散(ϵ\epsilon-prediction)与 rectified flow 是"一枚硬币的两面",采样步都可写成:

z~s=z~t+Network output⋅(ηs−ηt)\tilde{z}_s = \tilde{z}_t + \text{Network output}\cdot(\eta_s - \eta_t)

  • ϵ\epsilon-prediction:z~t=zt/αt\tilde{z}_t = z_t/\alpha_t,ηt=σt/αt\eta_t = \sigma_t/\alpha_t;
  • Rectified flow:z~t=zt\tilde{z}_t = z_t,ηt=t\eta_t = t。

(2) 统一 SDE 改写。 GRPO 需要随机探索,确定性 ODE 无法提供。

  • 扩散模型(前向 SDE dzt=ftztdt+gtdwdz_t = f_t z_t dt + g_t dw)的反向 SDE:

dzt=(ftzt−1+εt22gt2∇log⁡pt(zt))dt+εtgt dwdz_t = \left(f_t z_t - \frac{1+\varepsilon_t^2}{2} g_t^2 \nabla \log p_t(z_t)\right) dt + \varepsilon_t g_t\, dw

  • Rectified flow 引入 stochastic interpolant 形式的反向 SDE:

dzt=(ut−12εt2∇log⁡pt(zt))dt+εt dwdz_t = \left(u_t - \frac{1}{2}\varepsilon_t^2 \nabla \log p_t(z_t)\right) dt + \varepsilon_t\, dw

两者通过高斯边缘分布的 score ∇log⁡pt(zt)=−(zt−αtx)/σt2\nabla \log p_t(z_t) = -(z_t - \alpha_t x)/\sigma_t^2 统一为 MDP 中的策略分布 π(at∣st)\pi(a_t|s_t),εt\varepsilon_t 控制采样随机性。

(3) GRPO 目标与多奖励融合。 沿用 PPO-clip 目标 + 组内标准化优势,但有两个特点:

  • 默认省略 KL 正则(实验上去掉后差异很小,用极小的 clip 范围 ϵ=10−4\epsilon=10^{-4} 代替约束);
  • 多奖励在优势层面聚合而非奖励层面相加(避免量纲问题):

Ai←∑k=1Krik−μkσkA_i \leftarrow \sum_{k=1}^{K} \frac{r_i^k - \mu_k}{\sigma_k}

(4) 三个关键工程组件:

  1. 初始化噪声共享:同一 prompt 组内样本使用相同初始噪声——组内不同噪声在视频生成中必然导致 reward hacking(注意:这与 Flow-GRPO 在图像任务上"不同噪声更好"的消融结论相反,反映了图像/视频任务的差异);
  2. 时间步选择(Timestep Selection):只在随机子采样的 60% 时间步(τ=0.6\tau=0.6,即 40% dropout)上计算梯度,不损性能;
  3. Best-of-N 扩展(可选):从每 prompt N 个候选(N=16/64/256)取 top-k / bottom-k 共 16 个样本训练,显著加速收敛。

5.3 框架图

flowchart LR
    subgraph Data["Prompt 数据层(>10K prompts)"]
        P1["T2I 自构建集"]
        P2["VidProM (T2V)"]
        P3["ConsisID + FLUX 参考图 (I2V)"]
    end

    subgraph Rollout["Rollout(统一 SDE 采样器)"]
        SDE["扩散: 反向 SDE<br/>flow: stochastic interpolant SDE<br/>统一 score: ∇log p_t = -(z_t-α_t·x)/σ_t²"]
        NS["组内共享初始噪声<br/>G=12 样本/prompt"]
        SDE --- NS
    end

    subgraph Reward["Reward 评估层(黑盒 VLM)"]
        R1["HPS-v2.1 (图像美学)"]
        R2["CLIP Score (文图对齐)"]
        R3["VideoAlign VQ/MQ<br/>(视频美学/运动质量)"]
        R4["二值阈值奖励"]
    end

    subgraph Update["策略更新层"]
        AGG["多奖励优势聚合<br/>A_i = Σ_k (r_i^k-μ_k)/σ_k"]
        TS["时间步选择 τ=0.6"]
        PPO["PPO-clip (ε=1e-4)<br/>无 KL 正则"]
        AGG --> TS --> PPO
    end

    Data --> Rollout
    Rollout -->|"z_0"| Reward
    Reward --> AGG
    Rollout -->|"轨迹 + log_prob"| PPO

支持的模型与任务:

任务 基座模型 范式 Reward 模型
T2I Stable Diffusion v1.4 扩散(DDPM/DDIM) HPS-v2.1、CLIP(及二值化版本)
T2I FLUX.1-dev Rectified Flow HPS-v2.1、CLIP
T2I HunyuanVideo-T2I(latent 帧数=1) Rectified Flow HPS-v2.1
T2V HunyuanVideo Rectified Flow VideoAlign(VQ 美学 + MQ 运动质量)
I2V SkyReels-I2V Rectified Flow VideoAlign MQ

5.4 伪代码(Python)

训练流程:每个迭代 = 冻结旧策略 → 组内共享初始噪声的 SDE Rollout(G 样本/prompt)→ 多 reward 打分 → 优势层聚合 → 在随机子采样时间步上做 PPO-clip 更新(每迭代 4 次梯度更新)。评估时用确定性 ODE 采样器。

  • 输入:初始策略 πθ\pi_\theta(扩散或 flow 模型)、reward 模型集合 {Rk}k=1K\{R_k\}_{k=1}^K、prompt 数据集 D\mathcal{D}(>10K 条)、超参数 G,τ,εclip,εtG, \tau, \varepsilon_{clip}, \varepsilon_t(SDE 噪声水平);
  • 输出:优化后的策略 πθ\pi_\theta;
  • 损失:仅一个——PPO-clip 策略损失 Lclip\mathcal{L}_{clip}(无 KL 项,靠极小 clip 范围约束);与 Flow-GRPO 的差异在于多奖励在优势层聚合、时间步子采样、组内共享初始噪声。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import torch

# ============================================================
# 组件 1:统一 SDE 采样步(扩散与 rectified flow 通用)
# ============================================================
def unified_sde_step(model, z_t, t, dt, c, noise_level=0.3, model_type="flow"):
"""
DanceGRPO 统一 SDE 单步(扩散走反向 SDE;flow 走 stochastic interpolant SDE)

输入: model;当前 latent z_t;时间 t;步长 dt<0;条件 c;
noise_level ε_t=0.3;model_type ∈ {"diffusion", "flow"}
输出: z_next;log_prob(该步高斯转移对数概率)
"""
out = model(z_t, c, t) # ε-预测 或 速度场 u
if model_type == "diffusion":
f_t, g_t = sde_coefficients(t) # 前向 SDE 系数
score = -(z_t - alpha(t) * x0_pred(out, z_t, t)) / sigma(t)**2
drift = f_t * z_t - (1 + noise_level**2) / 2 * g_t**2 * score
diffusion_coef = noise_level * g_t
else: # rectified flow: stochastic interpolant SDE
score = -(z_t - alpha(t) * x0_pred(out, z_t, t)) / sigma(t)**2
drift = out - 0.5 * noise_level**2 * score # u - (ε_t²/2)·∇log p_t
diffusion_coef = noise_level
mean = z_t + drift * dt
std = diffusion_coef * (-dt).sqrt()
z_next = mean + std * torch.randn_like(z_t)
log_prob = (-(z_next - mean).pow(2) / (2 * std**2)
- torch.log(std) - 0.5 * torch.log(2 * torch.pi)).flatten(1).mean(dim=1)
return z_next, log_prob


# ============================================================
# 组件 2:多奖励优势聚合(在优势层聚合,而非 reward 层相加)
# ============================================================
def aggregate_multi_reward_advantages(rewards_dict, G):
"""
输入: rewards_dict —— {reward_name: (B*G,) 奖励值},共 K 个 reward 模型
输出: advantages (B*G,) —— A_i = Σ_k (r_i^k - μ_k) / σ_k(组内标准化后求和)
"""
adv_sum = 0.0
for k, r in rewards_dict.items():
r = r.view(-1, G)
adv_k = (r - r.mean(dim=1, keepdim=True)) / (r.std(dim=1, keepdim=True) + 1e-8)
adv_sum = adv_sum + adv_k.flatten()
return adv_sum


# ============================================================
# 组件 3:DanceGRPO 损失(纯 PPO-clip,无 KL 正则)
# ============================================================
def dance_grpo_loss(model, latents, next_latents, old_log_probs, advantages,
timesteps, dt, prompts, sub_timesteps, clip_eps=1e-4):
"""
输入:
latents / next_latents / old_log_probs: rollout 记录的轨迹与旧策略 log_prob
advantages: (B*G,) 多奖励聚合优势(广播到所有时间步)
sub_timesteps: 随机子采样的时间步索引(τ=0.6,即 40% dropout)
clip_eps: 极小 clip 范围 1e-4(代替 KL 起约束作用)
输出:
policy_loss: PPO-clip 策略损失(唯一损失,无 KL 项)
"""
policy_loss = 0.0
for t_idx in sub_timesteps: # 只在子采样时间步上更新
t = timesteps[t_idx]
mean, std = compute_transition_mean_std(model, latents[:, t_idx], t, dt, prompts)
log_prob = gaussian_log_prob(next_latents[:, t_idx], mean, std)
ratio = torch.exp(log_prob - old_log_probs[:, t_idx])
unclipped = -advantages * ratio
clipped = -advantages * torch.clamp(ratio, 1 - clip_eps, 1 + clip_eps)
policy_loss = policy_loss + torch.maximum(unclipped, clipped).mean()
return policy_loss / len(sub_timesteps)


# ============================================================
# 训练主循环(论文 Algorithm 1)
# ============================================================
def train_dance_grpo(model, reward_models, prompt_dataset, optimizer,
G=12, T=25, tau=0.6, clip_eps=1e-4, noise_level=0.3,
num_prompts_per_iter=32, num_grad_updates=4, num_iters=1000):
"""
DanceGRPO 训练主循环(在线 RL,图像/视频统一)

输入:
model: 初始策略 π_θ(SD1.4 / FLUX / HunyuanVideo / SkyReels-I2V)
reward_models: K 个黑盒 reward(HPS-v2.1 / CLIP / VideoAlign VQ&MQ / 二值阈值)
prompt_dataset: 大规模 prompt 集(>10K:自构建 / VidProM / ConsisID)
G: 12;T: 采样步数(SD 50 / FLUX、HunyuanVideo 25);tau: 时间步选择比例 0.6
noise_level: SDE 噪声水平 ε_t=0.3;num_grad_updates: 每迭代梯度更新 4 次
输出:
model: 优化后的策略(评估时用确定性 ODE 采样器)
"""
for it in range(num_iters):
prompts = sample_prompts(prompt_dataset, num_prompts_per_iter) # D_b ~ D
model.eval()
old_model = copy_and_freeze(model) # π_θ_old ← π_θ

# ---------- 阶段 1:Rollout(组内共享初始噪声 + 统一 SDE) ----------
all_latents, all_next, all_logps, all_outputs, rewards_dict = [], [], [], [], {}
with torch.no_grad():
for c in prompts:
init_noise = torch.randn(latent_shape) # ★ 组内共享初始化噪声
for g in range(G):
z = init_noise.clone() # 同一 prompt 的 G 个样本从同一 z_T 出发
traj, traj_next, traj_lp = [z], [], []
for t, dt in zip(sde_timesteps(T), sde_step_sizes(T)):
z_next, lp = unified_sde_step(old_model, z, t, dt, c, noise_level)
traj_next.append(z_next); traj_lp.append(lp)
z = z_next
all_latents.append(traj[:-1]); all_next.append(traj_next)
all_logps.append(traj_lp); all_outputs.append(decode(z))

# ---------- 阶段 2:多 reward 打分 + 优势层聚合 ----------
for k, R_k in reward_models.items():
rewards_dict[k] = R_k(all_outputs, prompts) # 每个 reward 模型独立打分
advantages = aggregate_multi_reward_advantages(rewards_dict, G)

# ---------- 阶段 3:时间步子采样 + PPO 更新 ----------
model.train()
sub_ts = random_subset(range(T), k=int(tau * T)) # τ=0.6(40% 时间步 dropout)
for _ in range(num_grad_updates): # 每迭代 4 次梯度更新
loss = dance_grpo_loss(model, all_latents, all_next, all_logps,
advantages, sde_timesteps(T), dt, prompts,
sub_ts, clip_eps)
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
return model

5.5 实验与超参数

  • 主要结果:
    • SD v1.4:HPS-v2.1 0.239 → 0.365(+53%);横向对比最优(DDPO 0.297、ReFL 0.357、DPO 0.241);
    • FLUX:HPS-v2.1 0.304 → 0.372;单用 HPS 时 GenEval 反降(0.659→0.561),联合 CLIP 后回升——多奖励聚合防 hacking;
    • HunyuanVideo T2V:视觉质量 VQ +56%、运动质量 MQ +181%(全文最大提升);
    • SkyReels-I2V:运动质量 +118%;
    • 二值(0/1 阈值)奖励下仍能稳定提升;
    • 人类评估(画师)一致偏好 RLHF 后输出。
  • 关键超参数:lr 1e-5(AdamW);G=12;每迭代 32 prompts、4 次梯度更新;clip ϵ\epsilon=1e-4;SDE 噪声水平 εt\varepsilon_t=0.3(过低探索不足,过高图像带噪点);τ=0.6\tau=0.6;SD 50 步 / FLUX、HunyuanVideo 25 步。
  • 稳定性要点:组内共享初始噪声;高保真模型(FLUX/HunyuanVideo)采样时关闭 CFG 减少梯度震荡;CFG 依赖模型(SD、SkyReels-I2V)必须联合优化条件与无条件分支且每迭代只更新 1 次;多奖励在优势层聚合;RLHF 后多样性下降是已知副作用。

6. DiffusionNFT:前向过程上的免似然在线强化

6.1 动机

Flow-GRPO / Dance-GRPO 代表的反向过程 RL 路线(把采样离散化成多步 MDP、对高斯转移求似然)存在三个根本性缺陷:

  1. 前向不一致性:只在反向过程上做 RL,破坏与前向扩散过程的一致性(违反 Fokker–Planck 约束),模型有退化为级联高斯的风险;
  2. 求解器限制:数据采集必须依赖一阶 SDE 采样器,无法使用更高效的 ODE / 高阶求解器;
  3. CFG 集成复杂:条件/无条件双模型优化方案复杂低效。

DiffusionNFT 的关键观察:一个扩散策略只对应唯一的前向(加噪)过程,但对应多个反向(去噪)过程——因此可以在前向过程而非反向过程上做扩散强化学习。由此得到:

  • 不需要 likelihood:不是 Policy Gradient 方法,而是监督学习形式的对比目标(policy-gradient-free);
  • 不需要 SDE 转换:rollout 可用任意黑盒求解器(一阶/二阶 ODE);
  • 不需要存储采样轨迹:训练只需要干净图像 x0x_0 及其 reward;
  • 原生 off-policy:采样策略与训练策略解耦,无需重要性采样,用 EMA 软更新衔接。

6.2 核心方法推导

(1) Optimality 概率与正负分布分裂。 把标量 reward r∈[0,1]r \in [0,1] 解释为 optimality 概率(RL-as-inference 视角)r(x0,c):=p(o=1∣x0,c)r(x_0,c) := p(o=1|x_0,c),按此概率把数据"想象地"分裂为正/负分布:

π+(x0∣c)∝r(x0,c) πold(x0∣c),π−(x0∣c)∝(1−r(x0,c)) πold(x0∣c)\pi^+(x_0|c) \propto r(x_0,c)\,\pi_{old}(x_0|c), \qquad \pi^-(x_0|c) \propto (1-r(x_0,c))\,\pi_{old}(x_0|c)

可证明按期望 reward 排序恒有 π+≻πold≻π−\pi^+ \succ \pi_{old} \succ \pi^-。

(2) Reinforcement Guidance(强化引导)。 定义训练目标速度场(形式类似 CFG):

v∗(xt,c,t):=vold(xt,c,t)+1β Δ(xt,c,t)v^*(x_t, c, t) := v_{old}(x_t, c, t) + \frac{1}{\beta}\, \Delta(x_t, c, t)

Theorem 3.1(改进方向):正负方向差异成正比——

Δ:=[1−α(xt)] [vold−v−]=α(xt) [v+−vold]\Delta := [1-\alpha(x_t)]\,[v_{old} - v^-] = \alpha(x_t)\,[v^+ - v_{old}]

其中 α(xt):=πt+(xt∣c)πtold(xt∣c) Eπold[r]∈[0,1]\alpha(x_t) := \frac{\pi^+_t(x_t|c)}{\pi^{old}_t(x_t|c)}\, \mathbb{E}_{\pi_{old}}[r] \in [0,1]。取 β=α(xt)\beta = \alpha(x_t) 时 v∗=v+v^* = v^+,即 π∗=π+≻πold\pi^* = \pi^+ \succ \pi_{old},保证策略改进。

(3) 正负隐式策略与对比目标(Theorem 3.2)。 不训练两个模型,而用隐式参数化在单个网络 vθv_\theta 上镜像构造正/负策略:

vθ+:=(1−β) vold+β vθ(隐式正策略)v^+_\theta := (1-\beta)\, v_{old} + \beta\, v_\theta \quad \text{(隐式正策略)}

vθ−:=(1+β) vold−β vθ(隐式负策略)v^-_\theta := (1+\beta)\, v_{old} - \beta\, v_\theta \quad \text{(隐式负策略)}

训练目标是定义在前向加噪过程上的 flow matching 对比回归损失:

L(θ)=Ec, x0∼πold, t[ r ∥vθ+(xt,c,t)−v∥22+(1−r) ∥vθ−(xt,c,t)−v∥22 ]\boxed{\mathcal{L}(\theta) = \mathbb{E}_{c,\, x_0 \sim \pi_{old},\, t}\Big[\, r\,\|v^+_\theta(x_t,c,t) - v\|_2^2 + (1-r)\,\|v^-_\theta(x_t,c,t) - v\|_2^2 \,\Big]}

其中 xt=αtx0+σtϵx_t = \alpha_t x_0 + \sigma_t\epsilon,v=α˙tx0+σ˙tϵv = \dot\alpha_t x_0 + \dot\sigma_t\epsilon。Theorem 3.2:最优解满足 vθ∗=vold+2βΔv_{\theta^*} = v_{old} + \frac{2}{\beta}\Delta——单个网络隐式学到了"old policy + 强化引导"的改进策略,无需 guided sampling。

为什么能避开似然:reward 只作为样本权重 rr / (1−r)(1-r) 进入监督损失,整个推导只涉及前向加噪核与速度场回归,不需要对反向转移核求对数似然,因此 rollout 采样器完全黑盒化。论文还证明了 Flow-GRPO 的梯度本质上是"优势加权噪声对齐速度场"的带噪估计(经 Stein 恒等式可视为 reward 反传的高方差估计),而 NFT 是确定性的 SL 目标。

(4) 实践设计:

  • Reward 归一化(沿用组内采样,但只用组内均值,不用 std):r:=12+12 clip(rraw−组内均值Zc,−1,1)r := \frac{1}{2} + \frac{1}{2}\,\mathrm{clip}\left(\frac{r_{raw} - \text{组内均值}}{Z_c}, -1, 1\right);
  • EMA 软更新:θold←ηiθold+(1−ηi)θ\theta_{old} \leftarrow \eta_i\theta_{old} + (1-\eta_i)\theta,渐进增大 ηi\eta_i(如 ηi=min⁡(0.001i,0.5)\eta_i = \min(0.001i, 0.5));η=0\eta=0(完全 on-policy)会灾难性崩溃;
  • 自适应损失加权(自归一化 x0x_0 回归):w(t)∥vθ−v∥2←∥xθ−x0∥2sg(mean∣xθ−x0∣)w(t)\|v_\theta - v\|^2 \leftarrow \frac{\|x_\theta - x_0\|^2}{\mathrm{sg}(\mathrm{mean}|x_\theta - x_0|)};
  • CFG-Free:把 CFG 解读为"离线版 reinforcement guidance",RL post-training 直接丢弃 CFG、仅以条件模型初始化。
  • 负样本必不可少:去掉负分支后 reward 几乎立刻崩溃(与 LLM 中 RFT 是强基线的现象相反)。

6.3 框架图

flowchart LR
    subgraph Rollout["Rollout(与训练解耦)"]
        C["Prompt c"] --> S["任意黑盒 ODE 求解器<br/>(二阶 ODE,CFG-free)<br/>每 prompt 采 K=24 张"]
        S --> X0["干净图 x_0 + reward r_raw"]
        X0 --> NORM["组内归一化<br/>r = 0.5 + 0.5·clip((r_raw - mean)/Z, -1, 1)"]
    end

    subgraph Train["梯度步(前向过程上的对比回归)"]
        FWD["前向加噪:<br/>x_t = α_t·x_0 + σ_t·ε<br/>v = α̇_t·x_0 + σ̇_t·ε"]
        POS["隐式正策略<br/>v⁺ = (1-β)v_old + β·v_θ"]
        NEG["隐式负策略<br/>v⁻ = (1+β)v_old - β·v_θ"]
        LOSS["L = r·‖v⁺-v‖² + (1-r)·‖v⁻-v‖²<br/>(自适应 x_0 加权)"]
        FWD --> POS & NEG
        POS --> LOSS
        NEG --> LOSS
    end

    NORM --> Train
    OLD["v_old(EMA 软更新)<br/>θ_old ← η·θ_old + (1-η)·θ"] --> POS & NEG
    LOSS -->|"梯度只更新 v_θ (LoRA)"| TH["v_θ"]
    TH -->|"EMA"| OLD
    OLD -.->|"下一轮 rollout 用 v_old"| S

与 GRPO 系方法的架构差异一目了然:rollout 黑盒化(可用高阶 ODE 求解器、不存轨迹、不算 log_prob),训练侧只是一个带正负权重的 flow matching 回归。

6.4 伪代码(Python)

训练流程:每个迭代 = Rollout(v_old 用任意 ODE 求解器采样 K 张图,只保留最终图像)→ reward 组内归一化为 optimality 概率 → 前向加噪 + 正/负隐式策略对比回归 → EMA 软更新 v_old。

  • 输入:预训练扩散/flow 策略 vrefv_{ref}、原始奖励 rraw(⋅)∈Rr_{raw}(\cdot) \in \mathbb{R}、prompt 数据集 {c}\{c\}、超参数 K,β,ηiK, \beta, \eta_i(EMA schedule);
  • 输出:优化后的策略 vθv_\theta;
  • 损失:仅一个——正/负加权的 flow matching 对比回归损失 LNFT=r Lpos+(1−r) Lneg\mathcal{L}_{NFT} = r\,\mathcal{L}_{pos} + (1-r)\,\mathcal{L}_{neg}(自适应 x0x_0 加权);无 ratio、无 KL、无 log_prob。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import torch

# ============================================================
# 组件 1:reward 归一化为 optimality 概率 r ∈ [0,1]
# ============================================================
def normalize_optimality(rewards_raw, K, Z):
"""
输入: rewards_raw (B*K,) —— 每个 prompt 组内 K 张图的原始奖励
Z —— 归一化因子(全局 reward std)
输出: r (B*K,) ∈ [0,1] —— r = 0.5 + 0.5·clip((r_raw - 组内均值)/Z, -1, 1)
(只用组内均值,不用组内 std;这是与 GRPO 优势的关键区别)
"""
r = rewards_raw.view(-1, K)
r_norm = (r - r.mean(dim=1, keepdim=True)) / Z
return (0.5 + 0.5 * torch.clamp(r_norm, -1.0, 1.0)).flatten()


# ============================================================
# 组件 2:DiffusionNFT 损失(前向过程上的正/负对比回归,唯一损失)
# ============================================================
def diffusion_nft_loss(model, old_model, x_0, r, c, beta=1.0):
"""
输入:
model: 训练策略 v_θ(仅 LoRA 可训练)
old_model: 数据采集策略 v_old(EMA 追踪 v_θ,冻结,提供"锚点")
x_0: (B, ...) rollout 得到的干净图像 latent(任意黑盒 ODE 求解器产出)
r: (B,) ∈ [0,1] optimality 概率(reward 归一化结果)
c: 文本条件;beta: guidance strength(单奖励 1,多奖励 0.1)
输出:
loss = r·L_pos + (1-r)·L_neg(自适应 x_0 加权)

推导保证(Thm 3.2):最优解 v_θ* = v_old + (2/β)·Δ,
即单网络隐式学到"old policy + reinforcement guidance"的改进策略。
"""
B = x_0.shape[0]
# ---- 前向加噪(训练完全在前向过程上定义,无需反向轨迹) ----
t = torch.sigmoid(torch.randn(B)) # logit-normal 采样 t ∈ (0,1)
eps = torch.randn_like(x_0)
x_t = (1 - t_view(t)) * x_0 + t_view(t) * eps # rectified flow 插值
v_target = eps - x_0 # 目标速度 v = ε - x_0

# ---- 隐式正/负策略(单网络镜像构造) ----
v_theta = model(x_t, c, t) # 训练策略(有梯度)
with torch.no_grad():
v_old = old_model(x_t, c, t) # EMA 旧策略(无梯度)
v_pos = (1 - beta) * v_old + beta * v_theta # 隐式正策略 v⁺
v_neg = (1 + beta) * v_old - beta * v_theta # 隐式负策略 v⁻

# ---- x_0 空间回归 + 自适应加权(stop-gradient 自归一化) ----
x0_pos = x_t - t_view(t) * v_pos # x̂_0 = x_t - t·v
x0_neg = x_t - t_view(t) * v_neg
with torch.no_grad():
w_pos = (x0_pos - x_0).abs().flatten(1).mean(dim=1).clamp(min=1e-5)
w_neg = (x0_neg - x_0).abs().flatten(1).mean(dim=1).clamp(min=1e-5)

# ---- 损失 1(正分支,权重 r)+ 损失 2(负分支,权重 1-r)----
L_pos = ((x0_pos - x_0).pow(2).flatten(1).mean(dim=1) / w_pos) # 正样本回归
L_neg = ((x0_neg - x_0).pow(2).flatten(1).mean(dim=1) / w_neg) # 负样本推离
loss = (r * L_pos + (1 - r) * L_neg).mean() / beta
return loss


# ============================================================
# 训练主循环(论文 Algorithm 1)
# ============================================================
def train_diffusion_nft(model, ref_model, reward_fn, prompt_set, optimizer,
K=24, groups_per_epoch=48, beta=1.0,
eta_max=0.5, rollout_steps=10, num_iters=1700):
"""
DiffusionNFT 训练主循环(在线 RL,rollout 与训练解耦)

输入:
model: 训练策略 v_θ(SD3.5-M 条件模型初始化,CFG-free,LoRA r=32/α=64)
ref_model: 预训练策略 v_ref;v_old 由其初始化并 EMA 追踪 v_θ
reward_fn: 黑盒原始奖励(GenEval / OCR / PickScore / HPSv2.1 / ...)
prompt_set: prompt 数据集
K: 组大小 24;beta: guidance strength;eta_max: EMA 上限
rollout_steps: rollout 采样步数(10 消融 / 40 多奖励最终模型)
输出:
model: 优化后的策略 v_θ(CFG-free 推理)
"""
old_model = copy_and_freeze(ref_model) # v_old ← v_ref
Z = estimate_global_reward_std() # 归一化因子 Z_c

for i in range(num_iters):
# ---------- 阶段 1:Rollout(黑盒 ODE 求解器,只存最终 x_0,不存轨迹/log_prob) ----------
buffer = []
with torch.no_grad():
for c in sample_prompts(prompt_set, groups_per_epoch):
x_0_group = []
for k in range(K): # 每 prompt 采 K 张干净图
x = torch.randn(latent_shape)
for t, dt in ode_schedule(rollout_steps): # 任意求解器(二阶 ODE)
x = x + old_model(x, c, t) * dt # 无需 log_prob!
x_0_group.append(x)
r_raw = reward_fn(x_0_group, c) # 黑盒奖励
r = normalize_optimality(r_raw, K, Z) # → optimality 概率 ∈ [0,1]
buffer.append((c, x_0_group, r))

# ---------- 阶段 2:梯度步(前向加噪 + 正/负对比回归) ----------
model.train()
for c, x_0_group, r in iterate_minibatches(buffer):
loss = diffusion_nft_loss(model, old_model, x_0_group, r, c, beta)
optimizer.zero_grad()
loss.backward()
optimizer.step()

# ---------- 阶段 3:EMA 软更新数据采集策略 ----------
eta = min(0.001 * (i + 1), eta_max) # η_i 渐进增大
with torch.no_grad():
for p_old, p_new in zip(old_model.parameters(), model.parameters()):
p_old.mul_(eta).add_(p_new, alpha=1 - eta) # θ_old ← ηθ_old + (1-η)θ
return model

6.5 实验与超参数

  • 设置:SD3.5-Medium(2.5B),512×512,全程 CFG-free;LoRA(r=32, α=64),lr 3e-4;组大小 K=24;rollout 用二阶 ODE。
  • 主要结果:
    • 多奖励联合训练(GenEval+OCR+PickScore+ClipScore+HPSv2.1,1.7k 迭代):8 个指标全面超过基座+CFG,并超过 SD3.5-L(8B)与 FLUX.1-Dev(12B)——GenEval 0.94、OCR 0.91、PickScore 23.80、HPSv2.1 0.331、Aesthetic 6.01;
    • 效率对比 Flow-GRPO(wall-clock):GenEval 25×(约 1k 步 0.24→0.98 vs Flow-GRPO >5k 步+CFG 到 0.95)、OCR 24×、PickScore 8×、HPSv2.1 3×,且最终分数更高;
    • 消融:负分支必不可少;ODE rollout 优于 SDE;渐增 EMA schedule 最佳;β≈1\beta \approx 1 或 0.10.1 稳定(10 不稳、0.01 太慢)。
  • 关键超参数:β\beta=1(单奖励)/ 0.1(多奖励);ηi=min⁡(0.001i,0.5)\eta_i = \min(0.001i, 0.5);K=24、每 epoch 48 组;rollout 10 步(消融)/ 40 步(多奖励最终模型)。

7. 总结与选型建议

四条路线的演进逻辑清晰:

flowchart LR
    A["Diffusion-DPO<br/>离线 DPO<br/>ELBO 上界绕开似然"] --> B["Flow-GRPO<br/>在线 GRPO<br/>ODE→SDE 转换"]
    B --> C["Dance-GRPO<br/>统一扩散/flow<br/>打通图像→视频"]
    B --> D["DiffusionNFT<br/>前向过程 RL<br/>彻底免似然/免SDE"]
    C --> D
方法 训练循环结构 损失构成
Diffusion-DPO 离线:取偏好对 → 加噪 → 四路前向 → 更新 单一 DPO 分类损失 LDPO\mathcal{L}_{DPO}
Flow-GRPO 在线:SDE rollout → reward → 组内优势 → PPO 更新 PPO-clip 损失 + 闭式 KL 正则
Dance-GRPO 在线:共享噪声 SDE rollout → 多 reward → 优势聚合 → 子采样时间步 PPO 纯 PPO-clip 损失(无 KL,clip ε=1e-4)
DiffusionNFT 在线:ODE rollout(只存 x0x_0)→ reward 归一化 → 前向对比回归 → EMA 正/负加权 FM 回归 rLpos+(1−r)Lnegr\mathcal{L}_{pos} + (1-r)\mathcal{L}_{neg}
  • 只有静态偏好对数据、追求稳定与简单 → Diffusion-DPO:离线训练、零推理开销、实现最简单,但天花板受限于数据分布,开放任务提升有限。
  • 有奖励模型、追求最大化任务指标(如 GenEval/OCR) → Flow-GRPO:在线 RL 提升幅度最大(GenEval 0.63→0.95),但必须承受 SDE 采样 + 轨迹存储 + 逐步 log_prob 的训练开销,注意 KL 正则防 reward hacking。
  • 多任务/视频生成、需要统一框架 → Dance-GRPO:扩散与 flow 统一 SDE + 多奖励优势聚合 + 一套稳定性工程(共享初始噪声、时间步 dropout),是目前视频 RLHF 的成熟方案。
  • 追求训练效率与采样自由度 → DiffusionNFT:免似然、免 SDE、rollout 可用任意高阶 ODE 求解器、不存轨迹,wall-clock 效率相对 Flow-GRPO 提升 3–25 倍,且多奖励联合训练效果最好;代价是负分支与 EMA 调度需要仔细调参。

8. 参考文献

  1. Wallace, B., et al. Diffusion Model Alignment Using Direct Preference Optimization. arXiv:2311.12908, 2023.
  2. Liu, J., et al. Flow-GRPO: Training Flow Matching Models via Online RL. arXiv:2505.05470, NeurIPS 2025.
  3. Xue, Z., et al. DanceGRPO: Unleashing GRPO on Visual Generation. arXiv:2505.07818, 2025.
  4. Chen, H., et al. DiffusionNFT: Online Diffusion Reinforcement with Forward Process. arXiv:2509.16117, ICLR 2026.
  5. Black, K., et al. Training Diffusion Models with Reinforcement Learning (DDPO). arXiv:2305.13301, 2023.
  6. Shao, Z., et al. DeepSeekMath: Pushing the Limits of Mathematical Reasoning (GRPO). arXiv:2402.03300, 2024.

扩散模型中的强化学习方法
https://huan-yin.github.io/2026/08/17/扩散模型中的强化学习方法/
作者
李相越
发布于
2026年8月17日
许可协议