Diffusers 中的 CogVideoXDPMScheduler:为 CogVideoX 视频生成量身定制的 DPM-Solver++ 多步调度器解析
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
导读
本文围绕 🤗 Diffusers 仓库中为 CogVideoX 视频扩散模型专门实现的CogVideoXDPMScheduler(对应文档见 multistep_dpm_solver_cogvideox.md)展开,深入讲解其理论基础、完整配置参数、采样算法细节(二阶多步 DPM-Solver++)以及它与 CogVideoX 文生视频 / 图生视频 / 视频生视频三条 pipeline 的集成方式。读完本文,你将掌握该调度器的每个构造参数的作用与默认值、采样循环中step方法的调用契约,并能直接在自己的 CogVideoX 推理脚本中正确替换、配置与调优该调度器。
一、CogVideoXDPMScheduler 是什么
CogVideoXDPMScheduler是 Diffusers 库中面向 CogVideoX 视频生成模型的高阶 ODE 求解调度器,其核心实现位于 scheduling_dpm_cogvideox.py(类定义见 L134)。官方文档对其定位的说明非常明确:
CogVideoXDPMScheduler基于 DPM-Solver(DPM-Solver: A Fast ODE Solver for Diffusion Probabilistic Model Sampling in Around 10 Steps)与 DPM-Solver++(DPM-Solver++: Fast Solver for Guided Sampling of Diffusion Probabilistic Models)两篇论文,专门面向 CogVideoX 模型设计。
与通用的DPMSolverMultistepScheduler相比,该调度器针对 CogVideoX 的视频潜空间扩散任务做了三处关键定制:
- SNR 偏移(SNR shift):仿照 Stable Diffusion 3 的做法,在初始化阶段对
alphas_cumprod施加snr_shift_scale偏移,适配高分辨率视频潜空间的信噪比分布; - 二阶多步(2M)更新:采样时缓存上一步预测的原始样本
old_pred_original_sample,用 DPM-Solver++(2M) 的二阶校正公式提升少步数采样质量; - zero-SNR 支持:通过
rescale_betas_zero_snr对 beta 序列做零终端信噪比重标定。
该调度器在仓库中已注册为公开导出 API,见 schedulers/init.py 与 diffusers/init.py,可以直接通过from diffusers import CogVideoXDPMScheduler导入使用。
二、算法背景:从 DPM-Solver 到 DPM-Solver++(2M)
扩散模型的采样本质上是求解一条概率流 ODE:
dx / dt = ...DPM-Solver 系列论文的核心思想是:利用扩散模型输出的噪声估计构造关于 log-SNR(即lambda(t))的精确解形式,从而用远比 DDPM/DDIM 更少的步数完成采样。
在 DPM-Solver++ 中,模型被用来直接预测原始样本x_0(去噪结果),而不是预测噪声。二阶多步(2M)形式进一步利用上一步的模型预测结果构造二阶泰勒校正项,使每一步的截断误差更小。
CogVideoXDPMScheduler正是实现了这种"二阶多步"更新:它在采样循环外维护一个old_pred_original_sample变量,把上一时间步预测的x_0传入当前步,参与二阶校正项的计算。这一设计在 pipeline_cogvideox.py 的采样循环中有完整的体现。
三、完整配置参数详解
CogVideoXDPMScheduler的构造函数通过@register_to_config注册全部超参数(见 scheduling_dpm_cogvideox.py)。下面按功能分组列出全部参数、类型、默认值与作用:
3.1 噪声调度(beta schedule)
| 参数 | 默认值 | 说明 |
|---|---|---|
num_train_timesteps | 1000 | 训练时使用的扩散步数,决定 beta 序列长度,也是推理时间步的最大值 |
beta_start | 0.00085 | beta 起始值(注意:类 docstring 写的是 0.0001,实际构造函数默认值为 0.00085) |
beta_end | 0.0120 | beta 终止值(docstring 写 0.02,实际默认值为 0.0120) |
beta_schedule | "scaled_linear" | 支持linear、scaled_linear、squaredcos_cap_v2三种 |
trained_betas | None | 直接传入训练好的 beta 数组,传入后将绕过beta_start/beta_end |
beta_schedule的三种取值在 scheduling_dpm_cogvideox.py 中实现:
linear:torch.linspace(beta_start, beta_end, ...)线性插值;scaled_linear:对beta_start/beta_end开方后线性插值再平方,这是潜扩散模型(latent diffusion)特有的调度,也是本类的默认值;squaredcos_cap_v2:调用betas_for_alpha_bar(见 L52)生成 Glide 风格的余弦调度,底层支持cosine、exp、laplace三种alpha_transform_type。
3.2 采样稳定性相关
| 参数 | 默认值 | 说明 |
|---|---|---|
clip_sample | True | 是否将预测样本裁剪到[-clip_sample_range, clip_sample_range],保证数值稳定性 |
clip_sample_range | 1.0 | 样本裁剪的最大幅值,仅当clip_sample=True时生效 |
set_alpha_to_one | True | 最后一步无"上一步 alpha"时,将上一时刻的alphas_cumprod固定为 1(否则取第 0 步的 alpha 值),见 L239-L243 |
thresholding | False | 是否启用动态阈值(dynamic thresholding)方法;文档明确提示该方法不适用于潜空间扩散模型 |
dynamic_thresholding_ratio | 0.995 | 动态阈值比率,仅当thresholding=True时生效 |
sample_max_value | 1.0 | 动态阈值的阈值上限,仅当thresholding=True时生效 |
3.3 预测类型与时间步
| 参数 | 默认值 | 说明 |
|---|---|---|
prediction_type | "epsilon" | 模型输出类型:epsilon(预测噪声)、sample(直接预测干净样本)、v_prediction(预测速度,见 Imagen Video 论文 2.4 节) |
timestep_spacing | "leading" | 时间步缩放方式,支持leading、linspace、trailing,对应论文Common Diffusion Noise Schedules and Sample Steps are Flawed中 Table 2 的三种方案 |
steps_offset | 0 | 推理时间步的偏移量,某些模型族需要 |
rescale_betas_zero_snr | False | 是否将 beta 重标定为零终端 SNR,见下文 3.4 |
snr_shift_scale | 3.0 | SNR 偏移尺度(SNR shift),是本调度器针对 CogVideoX 的关键定制项 |
3.4 SNR 偏移与 zero-SNR 重标定
初始化阶段对噪声调度的两个后处理是理解本调度器"专为 CogVideoX 定制"的关键,实现见 scheduling_dpm_cogvideox.py:
self.alphas = 1.0 - self.betas self.alphas_cumprod = torch.cumprod(self.alphas, dim=0) # Modify: SNR shift following SD3 self.alphas_cumprod = self.alphas_cumprod / (snr_shift_scale + (1 - snr_shift_scale) * self.alphas_cumprod) # Rescale for zero SNR if rescale_betas_zero_snr: self.alphas_cumprod = rescale_zero_terminal_snr(self.alphas_cumprod)- SNR shift:将
alphas_cumprod按snr_shift_scale进行非线性缩放。SNR(信噪比)定义为alpha / (1 - alpha),偏移后整个扩散过程的信噪比曲线整体上移/变形,使模型在低噪声端也能获得足够的梯度信息,这对大分辨率、大压缩比的视频潜空间尤为重要; - zero-SNR 重标定:
rescale_zero_terminal_snr(见 L104)按论文Common Diffusion Noise Schedules and Sample Steps are Flawed的 Algorithm 1 实现:先对alphas_cumprod开方,减去末项使最后一步为零,再等比缩放回首项原始值,最后平方还原。零终端 SNR 允许模型生成极亮、极暗的样本,而不是被限制在中等亮度区间。
这两个机制决定了采样所使用的alphas_cumprod曲线,进而影响set_timesteps与step中所有alpha_prod_*的计算。
四、时间步设置:set_timesteps 的三种间距策略
set_timesteps方法(见 scheduling_dpm_cogvideox.py)负责在推理前生成离散时间步序列,其签名如下:
def set_timesteps(self, num_inference_steps: int, device: str | torch.device | None = None)有两个需要注意的约束:
- 若
num_inference_steps > num_train_timesteps(默认 1000),会直接抛出ValueError,因为模型最多只能处理训练步数对应的时间步; - 三种
timestep_spacing策略的行为(均返回降序时间步):
| 策略 | 生成方式 |
|---|---|
linspace | 在[0, num_train_timesteps-1]上均匀取num_inference_steps个点,round 后反转 |
leading | 按step_ratio = num_train_timesteps // num_inference_steps整数倍取步,并叠加steps_offset(默认策略) |
trailing | 从num_train_timesteps开始以-step_ratio步长回退取整,再整体减 1 |
生成的timesteps会移动到device上,并保存在self.timesteps中,供采样循环逐项消费。另外,采样所需的final_alpha_cumprod(最后一步无前序 alpha 时的回退值,set_alpha_to_one=True时为 1.0)与init_noise_sigma = 1.0(初始噪声标准差)都在构造函数中完成设置。
五、采样核心:step 方法中的 DPM-Solver++(2M) 更新
step方法是整个调度器的灵魂(见 scheduling_dpm_cogvideox.py)。与常规调度器不同,它的签名多出两个关键参数:
def step( self, model_output: torch.Tensor, # 当前时间步的模型输出 old_pred_original_sample: torch.Tensor, # 上一步预测的原始样本 x_0(2M 二阶校正用) timestep: int, # 当前时间步 timestep_back: int, # 需要"回看"的时间步(即上一步) sample: torch.Tensor, # 当前带噪样本 x_t eta: float = 0.0, use_clipped_model_output: bool = False, generator: torch.Generator | None = None, variance_noise: torch.Tensor | None = None, return_dict: bool = False, ) -> DDIMSchedulerOutput | tuple5.1 第一步:由预测类型还原 x_0
根据prediction_type从模型输出还原预测原始样本pred_original_sample(见 L479-L492):
epsilon:x_0 = (x_t - sqrt(1 - alpha_t) * eps) / sqrt(alpha_t)sample:模型直接输出x_0v_prediction:x_0 = sqrt(alpha_t) * x_t - sqrt(1 - alpha_t) * v
5.2 第二步:计算 log-SNR 差与乘子
get_variables(见 L331)把alphas_cumprod转换成 log-SNR 值:
lamb = log(sqrt(alpha / (1 - alpha))) # 当前步 lamb_next = log(sqrt(alpha_prev / (1 - alpha_prev))) # 前一步 h = lamb_next - lamb # log-SNR 差(步长)若提供了alpha_prod_t_back(即timestep_back对应的累积 alpha),还会额外计算上一步与上上步的 log-SNR 差比值r = h_last / h,用于二阶校正。
get_mult(见 L364)则基于这些量计算 DPM-Solver++ 的更新乘子:
mult1 = sqrt((1 - alpha_prev) / (1 - alpha)) * exp(-h) mult2 = expm1(-2h) * sqrt(alpha_prev) mult3 = 1 + 1/(2r) # 二阶校正系数(仅二阶步) mult4 = 1/(2r) # 二阶校正系数(仅二阶步)5.3 第三步:执行二阶多步更新
完成一阶项与二阶校正项的合成(见 L494-L514):
h, r, lamb, lamb_next = self.get_variables(alpha_prod_t, alpha_prod_t_prev, alpha_prod_t_back) mult = list(self.get_mult(h, r, alpha_prod_t, alpha_prod_t_prev, alpha_prod_t_back)) mult_noise = (1 - alpha_prod_t_prev) ** 0.5 * (1 - (-2 * h).exp()) ** 0.5 # 一阶项(含随机噪声项,mult_noise * noise) prev_sample = mult[0] * sample - mult[1] * pred_original_sample + mult_noise * noise if old_pred_original_sample is None or prev_timestep < 0: # 第一步或最后一步:省去一次网络评估,直接返回 return prev_sample, pred_original_sample else: # 二阶校正:用上一步的 x_0 预测修正当前步 denoised_d = mult[2] * pred_original_sample - mult[3] * old_pred_original_sample x_advanced = mult[0] * sample - mult[1] * denoised_d + mult_noise * noise prev_sample = x_advanced这段代码的注释(L455-L464)给出了符号对照:pred_original_sample -> f_theta(x_t, t),prev_sample -> x_{t-1}。注意step的返回值是(prev_sample, pred_original_sample)二元组或DDIMSchedulerOutput(其结构定义见 L32-L48,包含prev_sample与可选的pred_original_sample两个字段,return_dict=False时返回 tuple)。调用方需要把返回的pred_original_sample作为下一次调用的old_pred_original_sample传回,这是 2M 算法的必要约定。
5.4 其他辅助方法
scale_model_input(L262):恒等返回sample,用于与其他需要按时间步缩放输入的调度器保持接口一致;add_noise(L522):前向扩散加噪,x_t = sqrt(alpha_t) * x_0 + sqrt(1 - alpha_t) * noise,会先把alphas_cumprod移到与输入相同的 device/dtype,减少跨设备拷贝;get_velocity(L565):v = sqrt(alpha_t) * noise - sqrt(1 - alpha_t) * sample,供v_prediction训练使用;__len__返回num_train_timesteps。
另外需要留意类属性order = 1(L188):虽然算法本身是二阶多步,但每个时间步只调用一次网络(利用缓存的上一步预测),因此order被标记为 1,它直接参与 pipeline 中进度条与 warm-up 步数的计算(见下文第六节)。
六、与 CogVideoX Pipeline 的集成方式
CogVideoXDPMScheduler在仓库中被以下 pipeline 使用(见 pipelines/cogvideo 目录):
- pipeline_cogvideox.py(文生视频):构造函数的
scheduler参数类型标注为CogVideoXDDIMScheduler | CogVideoXDPMScheduler(L185); - pipeline_cogvideox_image2video.py(图生视频);
- pipeline_cogvideox_video2video.py(视频生视频);
- 此外
pipeline_cogview3plus.py、pipeline_consisid.py等也引用了该类。
6.1 采样循环中的特殊调用契约
在 pipeline_cogvideox.py 的去噪循环中,对 DPM 调度器与其他调度器做了分支处理:
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) with self.progress_bar(total=num_inference_steps) as progress_bar: # for DPM-solver++ old_pred_original_sample = None for i, t in enumerate(timesteps): ... # compute the previous noisy sample x_t -> x_t-1 if not isinstance(self.scheduler, CogVideoXDPMScheduler): latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] else: latents, old_pred_original_sample = self.scheduler.step( noise_pred, old_pred_original_sample, # 回传上一步的 x_0 预测 t, timesteps[i - 1] if i > 0 else None, # timestep_back latents, **extra_step_kwargs, return_dict=False, ) latents = latents.to(prompt_embeds.dtype)从源码可以清晰地看到 2M 算法的完整协作模式:
- 循环外初始化
old_pred_original_sample = None; - 每次调用
step时传入上一步的pred_original_sample与上一步的时间步timesteps[i-1](第一步传None); step内部据此决定执行一阶(第一步)还是二阶(后续步)更新,并把(prev_sample, pred_original_sample)返回;- pipeline 将返回的
pred_original_sample存回old_pred_original_sample,进入下一轮迭代。
timesteps[i-1]正是step签名中的timestep_back,用于在alphas_cumprod中索引alpha_prod_t_back,从而计算二阶校正系数r。
6.2 动态 CFG 与进度控制
采样循环中还有两点与本调度器间接相关:
- 动态引导(dynamic CFG):当
use_dynamic_cfg=True时,pipeline 会按余弦曲线在采样过程中动态调整引导系数(L742-L745),CogVideoX 的官方推理脚本(例如 image2video 示例中pipe(image, prompt, use_dynamic_cfg=True))常配合本调度器一起使用; - warm-up 步数:
num_warmup_steps的计算使用了self.scheduler.order,由于本类order=1,进度条按每步一次网络评估推进(L776)。
6.3 如何切换到该调度器
视频生视频 pipeline 的官方示例(见 pipeline_cogvideox_video2video.py)展示了标准的替换方式——保留原配置并原地换调度器:
import torch from diffusers import CogVideoXDPMScheduler, CogVideoXVideoToVideoPipeline from diffusers.utils import export_to_video, load_video pipe = CogVideoXVideoToVideoPipeline.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16) pipe.to("cuda") pipe.scheduler = CogVideoXDPMScheduler.from_config(pipe.scheduler.config) input_video = load_video("path/to/input.mp4") prompt = ( "An astronaut stands triumphantly at the peak of a towering mountain. Panorama of rugged peaks and " "valleys. Very futuristic vibe and animated aesthetic. Highlights of purple and golden colors in " "the scene. The sky is looks like an animated/cartoonish dream of galaxies, nebulae, stars, planets, " "moons, but the remainder of the scene is mostly realistic." ) video = pipe(video=input_video, prompt=prompt, strength=0.8, guidance_scale=6, num_inference_steps=50).frames[0] export_to_video(video, "output.mp4", fps=8)from_config(pipe.scheduler.config)会继承原调度器配置(num_train_timesteps、beta_*、prediction_type等),确保与已训练好的模型匹配;需要调参时只需在from_config后传入覆盖参数,例如:
pipe.scheduler = CogVideoXDPMScheduler.from_config( pipe.scheduler.config, snr_shift_scale=3.0, timestep_spacing="leading", prediction_type="v_prediction", )文生视频与图生视频 pipeline 的用法完全一致(from diffusers import CogVideoXPipeline/CogVideoXImageToVideoPipeline),官方图生视频示例(见 pipeline_cogvideox_image2video.py)中pipe(image, prompt, use_dynamic_cfg=True)即默认配合该调度器工作。
七、使用建议与注意事项
综合源码与文档,使用CogVideoXDPMScheduler时有以下几点需要留意:
- 推理步数上限:
num_inference_steps不能超过num_train_timesteps(默认 1000),否则set_timesteps会抛错(L295-L300);官方示例通常使用 50 步,这也是 DPM-Solver 系列擅长的少步数区间; - 二阶校正依赖上一步预测:不要在采样循环中破坏
old_pred_original_sample的回传链路,否则退化为低阶更新; eta参数对该调度器无效:eta(DDIM 论文中的随机噪声权重)仅对DDIMScheduler生效,pipeline 的prepare_extra_step_kwargs(L359-L368)会检测step是否接受eta参数——虽然本类step签名中声明了eta,但实现中并未使用它来引入随机性,随机性只来自mult_noise * noise项;thresholding不建议开启:动态阈值是为像素空间模型设计的,对潜空间模型(包括 CogVideoX)不适用,文档原文即有明确提示;rescale_betas_zero_snr需要配合训练一致:该开关会改变整个噪声调度曲线,只有模型在 zero-SNR 设定下训练(或与官方 checkpoint 配置一致)时才应开启;- 与 DDIM 的互换性:CogVideoX pipeline 同时支持
CogVideoXDDIMScheduler与本调度器(L185),当isinstance判断不命中 DPM 分支时走通用step调用路径,两条分支的返回契约(return_dict=False取第一个元素)保持一致,因此可以在两种调度器间平滑切换对比效果。
八、小结
CogVideoXDPMScheduler是 Diffusers 为 CogVideoX 视频生成场景定制的高阶多步 ODE 求解器:它以 DPM-Solver/DPM-Solver++ 理论为基础,通过snr_shift_scale完成针对视频潜空间的 SNR 偏移,通过"缓存上一步x_0预测"的 2M 二阶校正换取少步数下的高质量采样,并通过set_alpha_to_one、zero-SNR 重标定、clip_sample等一系列开关保证数值稳定性。其实现集中在一个文件 scheduling_dpm_cogvideox.py 中(约 600 行),采样循环的协作契约(old_pred_original_sample与timestep_back的传递)则在 pipeline_cogvideox.py 中清晰可见,是理解"调度器如何与扩散 pipeline 协作"的极佳范本。若要深入对比,可同时阅读通用多步求解器 scheduling_dpmsolver_multistep.py(其中solver_order=2配合algorithm_type="dpmsolver++"即为通用场景下的同类推荐配置),以及文档站点的调度器索引页面 index.md(若存在)获取完整调度器清单。
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考