A bug on the set_timesteps function of the FlowMatchEulerDiscreteScheduler

用户在使用 FlowMatchEulerDiscreteScheduler 并手动设置推理循环(custom inference loops)时,例如参考 Write your own pipeline 教程 ,或实现 AFS(Unified Sampling Framework)等方法时发生。在

快速结论:当用户自定义 timesteps 参数传入 FlowMatchEulerDiscreteScheduler.set_timesteps() 时,如果 self.config.shift != 1.0(或其他修改 sigma 的配置),则 timesteps 与底层 sigmas 的同步关系会被破坏(即不再满足 sigmas * num_train_timesteps = timesteps)。

问题场景

用户在使用 FlowMatchEulerDiscreteScheduler 并手动设置推理循环(custom inference loops)时,例如参考 Write your own pipeline 教程,或实现 AFS(Unified Sampling Framework)等方法时发生。在 Stable Diffusion 3、Flux 等使用 flow matching 模型的自定义 Pipeline 中均可能触发。

报错原文

# 核心问题现象:
# 当 shift != 1.0 时,以下两个调用产生的 timesteps 值不同:
scheduler.set_timesteps(num_inference_steps=2)
scheduler.set_timesteps(timesteps=[1000.0, 2.99401209])  # 与上述相同的 step 对应值

# 但 sigmas 列表却完全相同。
# 导致手动设置 timesteps 时,输入到神经网络的 timestep 与实际的 noisy level 不匹配(OOD)

更严格的验证方法:

# 同步关系被破坏:断言失败
torch.testing.assert_close(scheduler.sigmas[:-1] * scheduler.config.num_train_timesteps, scheduler.timesteps)

原因分析

set_timesteps 函数内部(scheduling_flow_match_euler_discrete.py 第 341 行附近),一开始 sigmas = timesteps / self.config.num_train_timesteps 保证了两者同步。然而当 shift != 1.0(或使用 dynamic shifting、use_karras_sigmas=True 等配置)时,sigmas 在后续代码中被修改,而 self.timesteps 在第 370 行附近却直接基于原始的自定义 timesteps 创建,导致二者不同步。

可能原因:这是一个底层设计问题:当用户提供自定义 timesteps 时,代码是否应该同时修改 sigmastimesteps?当前行为会修改自定义 sigmas(例如施加 shift),这虽然是 dynamic shifting 的预期行为(如 Flux pipeline 在使用自定义 sigmas 时依赖 scheduler 自动进行 shift),但对用户手动直接提供 timesteps 的场景来说,则产生意外结果。

注意:对于蒸馏后的 LTX 2 检查点,需要设置 shift=1.0 以避免此问题导致的 sigma 序列被意外修改。

环境排查

  • Diffusers 版本:确认 diffusers.__version__,修复 PR #14011 的合并状态。
  • FlowMatchEulerDiscreteScheduler 配置:确认 shift 参数值(默认 1.0,非 1.0 时易触发)。
  • 检查是否使用了 use_karras_sigmas 或其他 sigma 调度配置。
  • 确认自定义 timesteps 的来源(是否从自动生成的 timesteps 复制而来,还是手动构造的线性/其他方式)。

解决步骤

  1. 可优先尝试:临时将 shift 显式设为 1.0
    scheduler = FlowMatchEulerDiscreteScheduler(shift=1.0)
    或直接使用默认配置。
  2. 如果必须用自定义 timesteps 且希望按原始 noisy 级别对应,只需设置 num_inference_steps 而不传 timesteps,让 scheduler 自动生成:
    scheduler.set_timesteps(num_inference_steps=10)
  3. 提供自定义 timesteps 时,建议同步检查 sigmas 是否正确(使用上述断言检查)。
  4. 等待修复 PR #14011 合并到新版 diffusers 后升级即可解决问题。
  5. 如果必须编写依赖手动 timesteps 的 Pipeline(如 Flux 自定义动态 shift),注意内部通常传入的是自定义 sigmas 而非直接传 timesteps,此时 scheduler 会正确应用 dynamic shift;应避免混用。

验证方法

运行以下代码确认同步关系恢复:

# 应在 fix 后通过(不抛出 AssertionError)
import torch
from diffusers import FlowMatchEulerDiscreteScheduler

scheduler = FlowMatchEulerDiscreteScheduler(shift=2.0)
scheduler.set_timesteps(num_inference_steps=10)
sigmas = scheduler.sigmas
custom_timesteps = scheduler.timesteps.tolist()
scheduler.set_timesteps(timesteps=custom_timesteps)

# 断言通过说明已修复
torch.testing.assert_close(scheduler.sigmas[:-1] * scheduler.config.num_train_timesteps, scheduler.timesteps)
torch.testing.assert_close(sigmas, scheduler.sigmas)

参考来源

huggingface/diffusers #14013

相关修复 PR:#14011

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 15126

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注