【免费下载链接】DiffusionNFT
[ICLR 2026 Oral] DiffusionNFT: Online Diffusion Reinforcement with Forward Process
DiffusionNFT 是 ICLR 2026 Oral 论文开源的在线扩散强化学习(Diffusion RL)框架,它直接在前向加噪过程上对扩散模型做策略优化。这篇文章带你逐行读懂它的核心损失函数:从 advantage 加权、隐式正/负策略的构造,到自适应加权 flow-matching 损失与 KL 正则,一次看懂这个高效强化学习方案的完整推导路径。
1 先看它要解决什么问题 🎯
在 DiffusionNFT 之前,扩散模型做强化学习的主流做法是 GRPO(Flow-GRPO 路线)。它有一个硬性前提:必须保存完整的采样轨迹$x_T \to x_{T-1} \to \dots \to x_0$,然后逐步计算策略概率 $\pi_\theta(x_t|x_{t+1})$ 来做策略梯度。这带来两个痛点:
- 显存开销大:整条轨迹都要存下来;
- 采样器被绑定:只能用特定的 ODE 求解器,换高阶黑盒求解器就做不了策略梯度。
DiffusionNFT(NFT = Negative-aware Finetuning)的思路非常直接:只保留采样得到的干净图 $x_0$,训练时主动给它重新加噪,把强化信号写进标准 flow-matching 损失里。正如 README.md 中描述的三步流水线:
- Data Collection:用旧策略 $v^\text{old}$ 生成图像,奖励模型打分 $r(x_0)$;
- Conceptual Data Split:按奖励高低把图像概念上划分为正、负两个子集;
- Forward Process Optimization:在加噪图像上优化训练策略 $v_\theta$,用一个新损失函数按奖励加权"隐式正/负目标"。
对应到代码,采样阶段只存了干净潜变量,而没有存轨迹(scripts/train_nft_sd3.py):
"latents_clean": latents[:, -1], # 只保留干净图像而传统 GRPO 需要的latents全轨迹和log_probs在此完全不需要。
2 advantage 是怎么算的:按提示分组的组内标准化
强化学习里"这张图比平均好多少"由 advantage 表示。DiffusionNFT 对每个 prompt 生成一组图像(默认 24 张,见 config/nft.py 的num_image_per_prompt = 24),组内做均值-方差标准化。
核心逻辑在 flow_grpo/stat_tracking.py 的PerPromptStatTracker.update:
mean = np.mean(self.stats[prompt], axis=0, keepdims=True) std = np.std(rewards, axis=0, keepdims=True) + 1e-4 # global_std 时用全局标准差 advantages[prompts == prompt] = (prompt_rewards - mean) / std含义一目了然:reward 高于组内均值的图像 advantage 为正,低于均值的为负。训练脚本在 scripts/train_nft_sd3.py 处调用它,并把 advantage 按时间步维度广播(同一张图在所有噪声时间步共享同一个 advantage)。
拿到 advantage 后还有一层裁剪与模式选择(scripts/train_nft_sd3.py):
advantages_clip = torch.clamp(advantages, -adv_clip_max, adv_clip_max) # 默认 ±5配置项adv_mode支持多种信号强度:all(完整正负)、positive_only(只留正)、negative_only(只留负)、one_only(正负二值化)、binary(取符号)。默认配置为all(config/nft.py)。
3 核心构造:隐式正策略与隐式负策略
这是 DiffusionNFT 最有意思的地方。传统做法是"正向采样好图、负向少采样坏图",而 NFT 在一次前向预测里同时合成出两个策略:
# scripts/train_nft_sd3.py L934-L937 positive_prediction = beta * forward_prediction + (1 - beta) * old_prediction.detach() implicit_negative_prediction = (1 + beta) * old_prediction.detach() - beta * forward_prediction其中:
forward_prediction是当前训练参数 $v_\theta$ 的预测;old_prediction是旧策略 $v^\text{old}$ 的预测(训练时 detach,不回传梯度);beta是混合系数,控制"新策略"占多大权重(默认config.beta = 1.0,config/nft.py)。
直觉上:
| 隐式策略 | 公式 | 含义 |
|---|---|---|
| 正策略 $v^+$ | $\beta v_\theta + (1-\beta) v^\text{old}$ | 向当前策略方向"改进",模仿好样本 |
| 负策略 $v^-$ | $(1+\beta) v^\text{old} - \beta v_\theta$ | 把 $v_\theta$ 从 $v^\text{old}$ 上"反方向推出去",远离坏样本 |
也就是说,同一组参数、同一次前向,既能被"拉近好图",又能被"推离坏图"——正负目标被隐式地编码进了参数更新方向里。
4 损失函数逐行解析:自适应加权的 flow-matching
拿到两个隐式策略后,先各自从带噪图 $x_t$ 反推干净图预测(flow-matching 关系 $x_0 = x_t - t \cdot v$):
# scripts/train_nft_sd3.py L940-L947 x0_prediction = xt - t_expanded * positive_prediction weight_factor = abs(x0_prediction - x0).mean(...).clip(min=1e-5) # 自适应权重 positive_loss = ((x0_prediction - x0) ** 2 / weight_factor).mean(...)weight_factor是关键细节:它用该样本当前的预测误差均值做分母,把困难样本与简单样本的损失拉平(难度归一化),避免大误差样本主导梯度。负分支完全对称(scripts/train_nft_sd3.py)。
最后,advantage 在这里完成加权——这就是标题里"advantage 加权"的落点(scripts/train_nft_sd3.py):
r = clamp(advantages_clip / adv_clip_max / 2 + 0.5, 0, 1) # 归一化到 [0,1] ori_policy_loss = r * positive_loss / beta + (1 - r) * negative_loss / beta policy_loss = (ori_policy_loss * adv_clip_max).mean()拆解一下r:
- advantage = +5(极好)→ r = 1 →纯正损失,全力逼近好图;
- advantage = 0(平均水平)→ r = 0.5 → 正负各半;
- advantage = -5(极差)→ r = 0 →纯负损失,全力远离坏图。
1/beta是梯度缩放(对 $v_\theta$ 求导时 $\partial v^+/\partial\theta = \beta$,除掉它保证损失尺度稳定),* adv_clip_max则把归一化时的除回去,保持损失量级合理。
5 别忘了 KL 正则
防止强化学习把基座模型"带崩",损失里加了一项与参考模型的 KL 距离(用 flow-matching 下的 MSE 近似,scripts/train_nft_sd3.py):
kl_div_loss = ((forward_prediction - ref_forward_prediction) ** 2).mean(...) loss = policy_loss + config.train.beta * torch.mean(kl_div_loss)config.train.beta即 KL 系数,默认1e-4(config/base.py),权重很小但不可或缺。
6 训练全流程与关键配置 🛠️
把上面串起来,一个 epoch 的完整循环是:采样打分 → 算 advantage → 多次内循环重训(每次重新随机加噪)→ EMA 更新旧策略。旧策略的更新是指数衰减式(scripts/train_nft_sd3.py):
tgt_param.data.copy_(tgt_param * decay + src_param * (1.0 - decay))关键超参数一览:
| 参数 | 默认值 | 位置 |
|---|---|---|
beta(隐式策略混合系数) | 1.0(部分任务 0.1) | config/nft.py |
adv_clip_max(advantage 裁剪) | 5 | config/base.py |
train.beta(KL 系数) | 1e-4 | config/base.py |
num_image_per_prompt(组大小) | 24 | config/nft.py |
decay_type(旧策略衰减) | 1 / 2 | scripts/train_nft_sd3.py |
本地快速上手:
git clone https://gitcode.com/gh_mirrors/di/DiffusionNFT cd DiffusionNFT pip install -e . # 8 卡训练 GenEval torchrun --nproc_per_node=8 scripts/train_nft_sd3.py --config config/nft.py:sd3_geneval训练入口为 scripts/train_nft_sd3.py,多奖励配置见 config/nft.py。
7 总结:为什么这个损失值得读
- 前向一致性:强化信号直接融进标准 flow-matching 目标,不需要反向 SDE 轨迹,任何黑盒采样器都能用;
- 省显存:只存干净图 $x_0$,不存整条采样轨迹;
- 正负一体:advantage 归一化成 $r \in [0,1]$ 后,在隐式正/负两个目标间连续插值,一张前向预测同时完成"拉好图 + 推坏图";
- 工程友好:损失形式与常规扩散微调一致,可直接接入现有训练代码库。
从 advantage 组内标准化 → 隐式正负策略构造 → 自适应加权 flow-matching → KL 约束,四步环环相扣。读懂这四段代码,你就掌握了 DiffusionNFT 的全部核心。
【免费下载链接】DiffusionNFT
[ICLR 2026 Oral] DiffusionNFT: Online Diffusion Reinforcement with Forward Process
相关推荐
OSlash核心原理解析:单子定律在Python中的实现
OSlash核心原理解析:单子定律在Python中的实现 OSlash是一个专注于在Python中实现函数式编程概念的库,核心提供了Functors(函子)、A
终极指南:一文读懂YOLOv4损失函数从原理到实战的目标检测优化技巧
终极指南:一文读懂YOLOv4损失函数从原理到实战的目标检测优化技巧 YOLOv4作为当前最流行的目标检测算法之一,其损失函数设计直接影响检测精度与速度。本文将
人工智能深度学习计算机视觉如何高效理解Monodepth2:深度估计模型的损失函数与优化策略全解析
如何高效理解Monodepth2:深度估计模型的损失函数与优化策略全解析 Monodepth2是一个基于单目图像的深度估计模型,通过深度学习技术从单张图像中估计
计算机视觉深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考