Diffusers 中的 FreeU:免训练提升图像与视频生成细节的推理期优化实战指南
2026/9/12 12:56:51 网站建设 项目流程

Diffusers 中的 FreeU:免训练提升图像与视频生成细节的推理期优化实战指南

【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers

FreeU 是一种无需额外训练的推理期图像质量优化技术,通过重新平衡 UNet 骨干网络(backbone)与跳跃连接(skip connection)的权重贡献,在不改动模型权重的前提下显著改善生成图像的细节表现。本文以 image_quality.md 为核心指南,结合 diffusers 仓库的源码实现,完整讲解enable_freeu/disable_freeu的用法、不同模型的推荐超参数,以及 FreeU 在代码层面究竟如何运作,帮助你将其直接应用于文生图、图生图和文生视频等推理任务。

为什么需要 FreeU:跳跃连接带来的"细节流失"

在 UNet 结构的扩散模型中,跳跃连接(skip connection)把编码器各阶段的特征直接拼接到解码器对应阶段。FreeU 论文指出,这种连接方式会让模型"过度依赖"跳跃特征,从而在一定程度上忽视骨干网络本身的语义信息,最终导致生成图像中出现不自然的细节。

FreeU 的核心思路是在推理时对这两类特征进行重新加权:

  • 放大骨干特征:增强骨干网络(backbone)贡献,让模型更充分地利用主干语义;
  • 衰减跳跃特征:对跳跃连接特征做低通滤波衰减,缓解去噪过程中的"过度平滑"(oversmoothing)效应。

这一技术不需要任何额外训练,可以在推理过程中即插即用,适用于 image-to-image(图生图)、text-to-video(文生视频)等各类下游任务。在 diffusers 中,你只需要调用 pipeline 上的StableDiffusionMixin.enable_freeu方法即可开启。

核心 API:enable_freeu 与 disable_freeu

FreeU 的配置入口定义在 pipeline 基类 mixin 中,源码见 src/diffusers/pipelines/pipeline_utils.py:

class StableDiffusionMixin: def enable_freeu(self, s1: float, s2: float, b1: float, b2: float): if not hasattr(self, "unet"): raise ValueError("The pipeline must have `unet` for using FreeU.") self.unet.enable_freeu(s1=s1, s2=s2, b1=b1, b2=b2) def disable_freeu(self): """Disables the FreeU mechanism if enabled.""" self.unet.disable_freeu()

四个缩放因子的含义如下:

参数类型作用
s1float第 1 阶段跳跃连接(skip features)的衰减系数,用于缓解增强去噪过程中的"过度平滑"效应
s2float第 2 阶段跳跃连接(skip features)的衰减系数,作用同上
b1float第 1 阶段骨干网络(backbone features)的放大系数
b2float第 2 阶段骨干网络(backbone features)的放大系数

参数名中的数字(1 和 2)对应 UNet 中应用该系数的阶段(stage)序号。FreeU 只会作用于 UNet 解码器的前两个上采样阶段。

需要注意两点使用前提:

  1. 调用enable_freeu的 pipeline 必须包含unet组件,否则会抛出ValueError("The pipeline must haveunetfor using FreeU.");
  2. 不同模型的推荐超参数组合不同,可以参考 FreeU 官方仓库(ChenyangSi/FreeU)中针对 Stable Diffusion v1、v2、SDXL 等模型的参数组合。

实战一:Stable Diffusion v1-5

stable-diffusion-v1-5/stable-diffusion-v1-5为例,推荐参数为s1=0.9, s2=0.2, b1=1.5, b2=1.6

import torch from diffusers import DiffusionPipeline pipeline = DiffusionPipeline.from_pretrained( "stable-diffusion-v1-5/stable-diffusion-v1-5", dtype=torch.float16, safety_checker=None ).to("cuda") # or "mps", "xpu", "cpu" pipeline.enable_freeu(s1=0.9, s2=0.2, b1=1.5, b2=1.6) generator = torch.Generator(device="cpu").manual_seed(33) prompt = "" image = pipeline(prompt, generator=generator).images[0] image

代码要点:

  • dtype=torch.float16启用半精度推理以降低显存占用;safety_checker=None跳过安全检查组件;
  • 设备支持"cuda",也可替换为"mps"(Apple Silicon)、"xpu"(Intel 独立显卡)或"cpu"
  • torch.Generator(device="cpu").manual_seed(33)固定随机种子,便于对比 FreeU 开启前后的效果差异。

实战二:Stable Diffusion v2-1

SD 2.1 的推荐参数为s1=0.9, s2=0.2, b1=1.4, b2=1.6,与 v1-5 相比b1略小:

import torch from diffusers import DiffusionPipeline pipeline = DiffusionPipeline.from_pretrained( "stabilityai/stable-diffusion-2-1", dtype=torch.float16, safety_checker=None ).to("cuda") # or "mps", "xpu", "cpu" pipeline.enable_freeu(s1=0.9, s2=0.2, b1=1.4, b2=1.6) generator = torch.Generator(device="cpu").manual_seed(80) prompt = "A squirrel eating a burger" image = pipeline(prompt, generator=generator).images[0] image

实战三:Stable Diffusion XL

SDXL 的 UNet 结构更大,推荐参数为s1=0.9, s2=0.2, b1=1.3, b2=1.4

import torch from diffusers import DiffusionPipeline pipeline = DiffusionPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16, ).to("cuda") # or "mps", "xpu", "cpu" pipeline.enable_freeu(s1=0.9, s2=0.2, b1=1.3, b2=1.4) generator = torch.Generator(device="cpu").manual_seed(13) prompt = "A squirrel eating a burger" image = pipeline(prompt, generator=generator).images[0] image

实战四:文生视频(Zeroscope / text-to-video-ms-1.7b)

FreeU 不仅适用于图像生成,也适用于视频扩散模型。以 ZeroScope 系列使用的damo-vilab/text-to-video-ms-1.7b模型为例,推荐参数为b1=1.2, b2=1.4, s1=0.9, s2=0.2

import torch from diffusers import DiffusionPipeline from diffusers.utils import export_to_video pipeline = DiffusionPipeline.from_pretrained( "damo-vilab/text-to-video-ms-1.7b", dtype=torch.float16 ).to("cuda") # or "mps", "xpu", "cpu" # values come from https://github.com/lyn-rgb/FreeU_Diffusers#video-pipelines pipeline.enable_freeu(b1=1.2, b2=1.4, s1=0.9, s2=0.2) prompt = "Confident teddy bear surfer rides the wave in the tropics" generator = torch.Generator(device="cpu").manual_seed(47) video_frames = pipeline(prompt, generator=generator).frames[0] export_to_video(video_frames, "teddy_bear.mp4", fps=10)

视频示例中的超参数取自 FreeU_Diffusers 社区仓库的 video-pipelines 章节。文生视频 pipeline 内部的UNet3DConditionModel同样实现了enable_freeu/disable_freeu(见 src/diffusers/models/unets/unet_3d_condition.py),因此视频任务可以复用完全相同的 API。

推荐超参数速查表

模型s1s2b1b2备注
Stable Diffusion v1-50.90.21.51.6来自文档示例
Stable Diffusion v2-10.90.21.41.6来自文档示例
Stable Diffusion XL0.90.21.31.4来自文档示例
ZeroScope / text-to-video-ms-1.7b0.90.21.21.4来自 FreeU_Diffusers 视频示例

这些参数是社区验证过的起点,实际使用时可以围绕b1b2微调:骨干放大系数过高可能导致过度锐化或伪影,过低则效果不明显;s1s2控制跳跃特征的低通滤波强度。

关闭 FreeU

调用StableDiffusionMixin.disable_freeu即可随时关闭:

pipeline.disable_freeu()

关闭后 pipeline 行为与未开启 FreeU 时完全一致(可通过固定种子的输出对比验证)。

源码级解析:FreeU 在 UNet 中如何生效

1. 参数注入:挂在每个上采样块上

enable_freeu在 pipeline 层只是转发调用,真正实现位于 UNet 模型。以UNet2DConditionModel为例:

def enable_freeu(self, s1: float, s2: float, b1: float, b2: float): for i, upsample_block in enumerate(self.up_blocks): setattr(upsample_block, "s1", s1) setattr(upsample_block, "s2", s2) setattr(upsample_block, "b1", b1) setattr(upsample_block, "b2", b2) def disable_freeu(self): freeu_keys = {"s1", "s2", "b1", "b2"} for i, upsample_block in enumerate(self.up_blocks): for k in freeu_keys: if hasattr(upsample_block, k) or getattr(upsample_block, k, None) is not None: setattr(upsample_block, k, None)

可以看到,enable_freeu只是把四个系数作为属性写到每个上采样块上,不修改任何模型权重disable_freeu则把这些属性重置为None实现"卸载"。这也是 FreeU"免训练、即插即用"的根本原因。

2. 前向传播:仅作用于前两个阶段

在 UNet 解码器前向(src/diffusers/models/unets/unet_2d_blocks.py)中,首先检查四个系数是否都已设置:

is_freeu_enabled = ( getattr(self, "s1", None) and getattr(self, "s2", None) and getattr(self, "b1", None) and getattr(self, "b2", None) ) for resnet, attn in zip(self.resnets, self.attentions): res_hidden_states = res_hidden_states_tuple[-1] res_hidden_states_tuple = res_hidden_states_tuple[:-1] # FreeU: Only operate on the first two stages if is_freeu_enabled: hidden_states, res_hidden_states = apply_freeu( self.resolution_idx, hidden_states, res_hidden_states, s1=self.s1, s2=self.s2, b1=self.b1, b2=self.b2, ) hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1)

代码注释明确写道"FreeU: Only operate on the first two stages"——这正是b1/b2s1/s2中数字 1、2 的来源:分别作用于解码器的第 1、2 个阶段(resolution_idx为 0 和 1 的块)。

3. 核心算子:骨干放大 + 跳跃特征 FFT 低通滤波

apply_freeufourier_filter的实现位于 src/diffusers/utils/torch_utils.py:

def apply_freeu(resolution_idx, hidden_states, res_hidden_states, **freeu_kwargs): if resolution_idx == 0: num_half_channels = hidden_states.shape[1] // 2 hidden_states[:, :num_half_channels] = hidden_states[:, :num_half_channels] * freeu_kwargs["b1"] res_hidden_states = fourier_filter(res_hidden_states, threshold=1, scale=freeu_kwargs["s1"]) if resolution_idx == 1: num_half_channels = hidden_states.shape[1] // 2 hidden_states[:, :num_half_channels] = hidden_states[:, :num_half_channels] * freeu_kwargs["b2"] res_hidden_states = fourier_filter(res_hidden_states, threshold=1, scale=freeu_kwargs["s2"]) return hidden_states, res_hidden_states

每一步操作都对应论文的核心思想:

  • 骨干放大:取骨干特征通道的前一半(num_half_channels = hidden_states.shape[1] // 2),整体乘以b1b2,其余通道保持不变;
  • 跳跃特征滤波:对跳跃特征做频域低通处理——fourier_filter对特征执行 FFT 变换,构造一个中心2×2邻域区域(threshold=1)的频域掩码,将低频区域乘以scale(即s1/s2),高频成分被相对抑制,再通过 IFFT 还原到空间域,达到衰减跳跃特征中高频"噪声细节"的目的。

此外,fourier_filter内部会自动处理精度问题:非 2 的幂尺寸或 float16/bfloat16 输入会先升到 float32 再做 FFT(fftn不支持 bfloat16,float16 会产生数值不稳定的 ComplexHalf),计算完成后恢复原始 dtype——因此 FreeU 可以安全地与dtype=torch.float16的推理搭配使用。

4. 支持范围:哪些模型带 FreeU 接口

从源码结构看,以下模型/组件均实现了enable_freeu/disable_freeu,FreeU 的适用范围远不止 Stable Diffusion 系列:

  • UNet2DConditionModel:SD 1.x / 2.x / SDXL 等 2D 文生图、图生图模型;
  • UNet3DConditionModel:文生视频模型(如 text-to-video-ms-1.7b);
  • UNetMotionModel:AnimateDiff 等运动模块;
  • UNetI2VGenXL:I2VGen-XL 图生视频模型;
  • ControlNetXSModel:ControlNet-XS 也提供 FreeU 支持。

如何验证 FreeU 是否生效

diffusers 的测试套件提供了现成的验证思路。通用 pipeline 测试中的test_freeu覆盖了三条断言:

  1. 开启后结果必须不同enable_freeu之后生成的输出与默认输出不近似(not np.allclose(...));
  2. 关闭后属性复位disable_freeu后,UNet 每个上采样块的s1/s2/b1/b2属性都应被置为None
  3. 关闭后行为还原disable_freeu之后的输出与默认 pipeline 输出高度一致(np.allclose(output, output_no_freeu, atol=1e-2))。

针对 Stable Diffusion 的专项测试(tests/pipelines/stable_diffusion/test_stable_diffusion.py)也以enable_freeu(s1=0.9, s2=0.2, b1=1.2, b2=1.4)为固定参数,验证了"开启改变结果、关闭恢复原样"的行为闭环。

实际使用时,你可以用相同种子分别跑一次enable_freeu前后的推理,对比两张图的细节差异来确认效果;若发现某个参数的组合导致画面失真,直接调用disable_freeu()即可恢复默认行为,无需重载模型。

小结

FreeU 为扩散模型推理提供了一条"零成本"的画质优化路径:通过pipeline.enable_freeu(s1, s2, b1, b2)一行代码,即可在 UNet 前两个阶段对骨干特征做放大、对跳跃特征做频域低通滤波,从而恢复被跳跃连接掩盖的细节。结合本文给出的 SD v1-5、SD v2-1、SDXL 与文生视频四组推荐参数,你可以在文生图、图生图与文生视频任务中直接上手,并通过disable_freeu()随时回退。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询