🤗 Diffusers 中的 Perturbed-Attention Guidance (PAG):免训练提升扩散模型采样质量
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
导读
Perturbed-Attention Guidance(PAG,扰动注意力引导)是一种全新的扩散模型采样引导技术,由论文《Self-Rectifying Diffusion Sampling with Perturbed-Attention Guidance》(论文编号 2403.17377)提出。它的核心思路是:在去噪过程中,将选定自注意力层的注意力图替换为单位矩阵(identity matrix),从而构造出"结构被破坏"的中间样本,再引导去噪过程远离这些退化样本,以此逐步增强样本的结构性。PAG 最大的特点是不需要额外训练、不需要接入外部模块,且在条件生成(如文本引导)与无条件生成场景下都能提升质量。本文以 🤗 Diffusers 仓库中的官方文档(docs/source/en/api/pipelines/pag.md)为主体,结合仓库源码(src/diffusers/pipelines/pag/与src/diffusers/models/attention_processor.py),完整讲解如何在 SDXL 文生图、图生图、修复(Inpainting)、ControlNet、IP-Adapter 等场景中启用 PAG,以及pag_applied_layers、pag_scale等核心参数的作用与底层实现原理。
PAG 技术原理:为什么"扰动注意力"能提升质量
论文核心思想
根据论文摘要,PAG 的设计动机是:现有的采样引导技术(如分类器引导 CG、无分类器引导 CFG)往往依赖类别标签或文本条件,在无条件生成以及图像复原等下游任务(如修复、去模糊)中难以充分发挥作用。而 PAG 观察到:自注意力机制天然能够捕获样本的结构信息。因此:
- 在去噪过程中,把选定的自注意力图替换为单位矩阵,相当于让每个 token 只关注自身,从而得到一个"结构退化"的中间样本;
- 用这个退化样本作为引导基准,让去噪过程远离退化样本,即引导预测结果向结构更完整的方向偏移。
论文在 ADM(扩散模型骨干)和 Stable Diffusion 上均验证了 PAG 在条件生成乃至无条件生成场景下的质量提升,并显著改善了 ControlNet 空提示(empty prompt)、修复、去模糊等下游任务的表现。
Diffusers 中的实现:两个专用的注意力处理器
在仓库的 src/diffusers/models/attention_processor.py 中,PAG 通过两个继承自标准注意力处理器的专用 Processor 实现:
PAGIdentitySelfAttnProcessor2_0(attention_processor.py#L5043):用于未启用 CFG的场景。它将输入的 hidden states 沿 batch 维切成两份:hidden_states_org走正常的 scaled dot-product attention(F.scaled_dot_product_attention)路径;hidden_states_ptb则走"扰动路径"——跳过to_q、to_k,只经过to_v线性投影(hidden_states_ptb = attn.to_v(hidden_states_ptb)),这等价于用单位矩阵作为注意力权重,即每个位置只聚合自身的信息,从而破坏结构特征。两条路径的输出再torch.cat拼接回 batch 维。PAGCFGIdentitySelfAttnProcessor2_0(attention_processor.py#L5142):用于启用 CFG的场景。输入被切成三份:hidden_states_uncond(无条件分支)、hidden_states_org(文本条件分支,走正常注意力)、hidden_states_ptb(扰动分支,仅过to_v)。其中无条件分支与正常分支拼接后一起走scaled_dot_product_attention,扰动分支单独处理。
注意:这两个 Processor 都要求 PyTorch 2.0 及以上版本(因为依赖
F.scaled_dot_product_attention),代码中会在初始化时显式检查并抛出ImportError。
PAGMixin:引导公式与自适应缩放
PAG 的核心逻辑封装在 src/diffusers/pipelines/pag/pag_utils.py 的PAGMixin中,所有 PAG pipeline 都通过继承该 Mixin 获得能力:
_apply_perturbed_attention_guidance(pag_utils.py#L100)实现引导合成公式:- 启用 CFG 时,噪声预测被切为三份
noise_pred_uncond / noise_pred_text / noise_pred_perturb,最终预测为:uncond + guidance_scale * (text - uncond) + pag_scale * (text - perturb),即 CFG 项与 PAG 项线性叠加; - 未启用 CFG 时(如无条件生成、ControlNet 空提示),则退化为
text + pag_scale * (text - perturb),此时 PAG 成为唯一引导来源。
- 启用 CFG 时,噪声预测被切为三份
_get_pag_scale(pag_utils.py#L87)实现自适应缩放:若启用了pag_adaptive_scale,则按pag_scale - pag_adaptive_scale * (1000 - t)随时间步t动态衰减(小于 0 时截断为 0);do_pag_adaptive_scaling属性要求pag_adaptive_scale > 0 且 pag_scale > 0 且已配置 pag_applied_layers(pag_utils.py#L206)。_set_pag_attn_processor(pag_utils.py#L35)遍历unet(或transformer)的全部子模块,找到所有自注意力(isinstance(module, Attention) and not module.is_cross_attention)且模块名与pag_applied_layers中标识匹配的层,为其挂上上述 PAG 处理器;若某个标识找不到任何匹配层,会抛出ValueError。
从源码结构看,PAG 不修改任何网络权重,只通过替换 attention processor 与改动__call__中的噪声合成逻辑生效,这正是"免训练、免外挂模块"的落地方式。
安装与启用方式总览
PAG 通过AutoPipeline家族集成,启用方式是在from_pretrained时传入enable_pag=True,并可通过pag_applied_layers指定要应用 PAG 的层。在仓库的 src/diffusers/pipelines/auto_pipeline.py 中可以看到完整的映射关系:
- 文生图:
stable-diffusion-pag、stable-diffusion-xl-pag、stable-diffusion-xl-controlnet-pag、stable-diffusion-3-pag、hunyuan-pag、sana-pag、pixart-sigma-pag等(auto_pipeline.py#L152-L174); - 图生图:
stable-diffusion-pag、stable-diffusion-xl-pag、stable-diffusion-xl-controlnet-pag、stable-diffusion-3-pag等(auto_pipeline.py#L219-L229); - 修复:
stable-diffusion-controlnet-pag、stable-diffusion-xl-pag、stable-diffusion-pag等(auto_pipeline.py#L254-L262)。
from_pretrained与from_pipe内部会根据enable_pag标志自动把普通 pipeline 类替换为对应的*PAGPipeline类(参见 auto_pipeline.py#L544-L547 等处的类名替换逻辑),对使用者透明。
⚠️ 当前仓库支持 PAG 的 pipeline 有限,主要包括选定的 SDXL 系列 pipeline、Stable Diffusion 系列、Stable Diffusion 3 系列、PixArtSigma、Sana、HunyuanDiT、Kolors与AnimateDiff等(完整类清单见下文"支持的 Pipeline 类"一节)。使用前请确认你的模型架构在支持列表内。
通用任务:文生图 / 图生图 / 修复
Text-to-image(文生图)
用AutoPipelineForText2Image加载 SDXL 并启用 PAG:
from diffusers import AutoPipelineForText2Image from diffusers.utils import load_image import torch pipeline = AutoPipelineForText2Image.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", enable_pag=True, pag_applied_layers=["mid"], dtype=torch.float16 ) pipeline.enable_model_cpu_offload()如果你已经有一个创建好的 pipeline,也可以用from_pipeAPI 在不额外占用内存的情况下"升级"为 PAG pipeline(两者会同时加载、随时可用):
pipeline_sdxl = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16) pipeline = AutoPipelineForText2Image.from_pipe(pipeline_sdxl, enable_pag=True)生成时传入pag_scale。pag_scale越大,图像结构越连贯、伪影越少;但过大的引导强度(与 CFG 类似)会使纹理变平滑、出现轻微过饱和。官方 demo 中pag_scale=3.0在多数场景表现良好,pag_scale=0时 PAG 被关闭。示例:
prompt = "an insect robot preparing a delicious meal, anime style" for pag_scale in [0.0, 3.0]: generator = torch.Generator(device="cpu").manual_seed(0) images = pipeline( prompt=prompt, num_inference_steps=25, guidance_scale=7.0, generator=generator, pag_scale=pag_scale, ).imagesImage-to-image(图生图)
from diffusers import AutoPipelineForImage2Image from diffusers.utils import load_image import torch pipeline = AutoPipelineForImage2Image.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", enable_pag=True, pag_applied_layers=["mid"], dtype=torch.float16 ) pipeline.enable_model_cpu_offload()已有图生图 pipeline 时启用 PAG:
pipeline_t2i = AutoPipelineForImage2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16) pipeline = AutoPipelineForImage2Image.from_pipe(pipeline_t2i, enable_pag=True)也可以直接从文生图 pipeline 切换到启用 PAG 的图生图 pipeline:
pipeline_pag = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16) pipeline = AutoPipelineForImage2Image.from_pipe(pipeline_t2i, enable_pag=True)如果已有 pipeline 本身就启用了 PAG,切换到图生图时 PAG 会自动保持启用:
pipeline_pag = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", enable_pag=True, dtype=torch.float16) pipeline = AutoPipelineForImage2Image.from_pipe(pipeline_t2i)生成示例(注意strength控制重绘强度):
pag_scale = 4.0 guidance_scale = 7.0 url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/sdxl-text2img.png" init_image = load_image(url) prompt = "a dog catching a frisbee in the jungle" generator = torch.Generator(device="cpu").manual_seed(0) image = pipeline( prompt, image=init_image, strength=0.8, guidance_scale=guidance_scale, pag_scale=pag_scale, generator=generator).images[0]Inpainting(修复)
from diffusers import AutoPipelineForInpainting from diffusers.utils import load_image import torch pipeline = AutoPipelineForInpainting.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", enable_pag=True, dtype=torch.float16 ) pipeline.enable_model_cpu_offload()在已有修复 pipeline 上启用 PAG,或从其他任务(如文生图)的 pipeline 转换:
pipeline_inpaint = AutoPipelineForInpainting.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16) pipeline = AutoPipelineForInpainting.from_pipe(pipeline_inpaint, enable_pag=True) # 或从文生图 pipeline 转换 pipeline_t2i = AutoPipelineForText2Image.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16) pipeline = AutoPipelineForInpainting.from_pipe(pipeline_t2i, enable_pag=True)生成示例:
img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png" mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png" init_image = load_image(img_url).convert("RGB") mask_image = load_image(mask_url).convert("RGB") prompt = "A majestic tiger sitting on a bench" pag_scale = 3.0 guidance_scale = 7.5 generator = torch.Generator(device="cpu").manual_seed(1) images = pipeline( prompt=prompt, image=init_image, mask_image=mask_image, strength=0.8, num_inference_steps=50, guidance_scale=guidance_scale, generator=generator, pag_scale=pag_scale, ).images images[0]PAG 与 ControlNet 结合:空提示下的结构控制
PAG 的一大亮点是:在ControlNet 搭配空提示(无 prompt)的场景下,传统 CFG 无法生效,而 PAG 依然可以显著提升生成质量。先创建ControlNetModel,再连同 PAG 参数一起传入:
from diffusers import AutoPipelineForText2Image, ControlNetModel import torch controlnet = ControlNetModel.from_pretrained( "diffusers/controlnet-canny-sdxl-1.0", dtype=torch.float16 ) pipeline = AutoPipelineForText2Image.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", controlnet=controlnet, enable_pag=True, pag_applied_layers="mid", dtype=torch.float16 ) pipeline.enable_model_cpu_offload()已有 ControlNet pipeline 时可用AutoPipelineForText2Image.from_pipe(pipeline_controlnet, enable_pag=True)直接启用。
使用方式与普通 ControlNet 一致,仅需额外传入pag_scale。下面的例子使用空 prompt 生成(guidance_scale=0,完全依赖 PAG 与 ControlNet 条件):
from diffusers.utils import load_image canny_image = load_image( "https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/pag_control_input.png" ) for pag_scale in [0.0, 3.0]: generator = torch.Generator(device="cpu").manual_seed(1) images = pipeline( prompt="", controlnet_conditioning_scale=controlnet_conditioning_scale, image=canny_image, num_inference_steps=50, guidance_scale=0, generator=generator, pag_scale=pag_scale, ).images images[0]参考仓库测试 tests/pipelines/pag/test_pag_controlnet_sdxl.py 与 tests/pipelines/pag/test_pag_controlnet_sd.py,可以了解 ControlNet+PAG 组合在 SDXL 与 SD 上的调用与校验方式。
PAG 与 IP-Adapter 结合:图像提示 + 结构增强
IP-Adapter 可在不修改底层模型的前提下为扩散模型注入图像提示(image prompting)。PAG 与 IP-Adapter 可以同时启用,用于减少伪影、改善整体构图:
from diffusers import AutoPipelineForText2Image from diffusers.utils import load_image from transformers import CLIPVisionModelWithProjection import torch image_encoder = CLIPVisionModelWithProjection.from_pretrained( "h94/IP-Adapter", subfolder="models/image_encoder", dtype=torch.float16 ) pipeline = AutoPipelineForText2Image.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", image_encoder=image_encoder, enable_pag=True, dtype=torch.float16 ).to("cuda") # 或 "mps"、"xpu"、"cpu" pipeline.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter-plus_sdxl_vit-h.bin") pag_scale = 5.0 ip_adapter_scale = 0.8 image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/ip_adapter_diner.png") pipeline.set_ip_adapter_scale(ip_adapter_scale) generator = torch.Generator(device="cpu").manual_seed(0) images = pipeline( prompt="a polar bear sitting in a chair drinking a milkshake", ip_adapter_image=image, negative_prompt="deformed, ugly, wrong proportion, low res, bad anatomy, worst quality, low quality", num_inference_steps=25, guidance_scale=3.0, generator=generator, pag_scale=pag_scale, ).images images[0]从源码看,SDXL 系列 PAG pipeline 继承了IPAdapterMixin(见 pipeline_pag_sd_xl.py#L28-L33),因此 IP-Adapter 与 PAG 的叠加是原生支持的;测试 tests/pipelines/pag/test_pag_sdxl.py 中也通过IPAdapterTesterMixin对组合能力做了覆盖。
核心参数详解:pag_applied_layers 与 pag_scale
pag_applied_layers:选择 PAG 作用的层
pag_applied_layers是实例化 PAG pipeline 时的关键参数,接受单个字符串或字符串列表,每个字符串可以是唯一的层标识符或正则表达式:
- 完整标识符(普通字符串):
down_blocks.2.attentions.0.transformer_blocks.0.attn1.processor - 完整标识符(正则):
down_blocks.2.(attentions|motion_modules).0.transformer_blocks.0.attn1.processor - 部分标识符(正则):
down_blocks.2或attn1 - 标识符列表(字符串与正则混用):
["blocks.1", "blocks.(14|20)", r"down_blocks\.(2,3)"]
⚠️官方警告:由于支持用正则匹配层标识符,使用不当可能引发非预期行为。官方推荐只使用
blocks.{layer_index}与blocks.({layer_index_1|layer_index_2|...})两种形式;其他写法虽然可行,但可能绕过基础校验并产生意外结果。
该参数在源码中的处理逻辑(见 pag_utils.py#L151-L193)为:若非列表则包装为单元素列表,逐项校验类型必须为字符串,随后保存到self.pag_applied_layers。匹配时(_set_pag_attn_processor)会遍历 denoiser 的全部子模块,用re.search(layer_id, name)做正则匹配,并通过is_fake_integral_match防止blocks.1误匹配到blocks.10这类"假整数匹配"(pag_utils.py#L58-L61)。
默认值:只应用于 mid blocks(中间块)。修改此设置会显著影响输出,因此官方建议在创建 pipeline 后使用set_pag_applied_layers方法动态调整,寻找最适合你模型的层组合。示例:
prompt = "an insect robot preparing a delicious meal, anime style" pipeline.set_pag_applied_layers(pag_layers) generator = torch.Generator(device="cpu").manual_seed(0) images = pipeline( prompt=prompt, num_inference_steps=25, guidance_scale=guidance_scale, generator=generator, pag_scale=pag_scale, ).images images[0]官方文档给出两组对照:["down.block_2"]与["down.block_2", "up.block_1.attentions_0"],说明叠加更多层会影响结构与细节的平衡,需要针对模型与任务调优。
pag_scale:引导强度
pag_scale是调用 pipeline 时传入的引导强度:
pag_scale=0时 PAG 完全关闭;- 增大
pag_scale,图像获得更连贯的语义结构、伪影更少; - 过大时(类似 CFG 的行为)纹理变平滑、轻微过饱和;
- 官方 demo 常用
pag_scale=3.0,可根据需求实验调整。
其底层作用由_apply_perturbed_attention_guidance中的公式体现(见上文"PAGMixin"小节):PAG 项pag_scale * (noise_pred_text - noise_pred_perturb)把预测结果从"结构退化样本"方向拉开。此外还有pag_adaptive_scale(自适应缩放系数),启用后引导强度随去噪进程递减,避免后期过度引导(pag_utils.py#L87-L98)。在 SDXL 的 PAG pipeline 测试配置中,__call__签名要求包含pag_scale与pag_adaptive_scale两个参数(见 test_pag_sdxl.py#L57-L59)。
支持的 Pipeline 类一览
除通过AutoPipeline使用外,也可以直接实例化以下 PAG pipeline(均位于 src/diffusers/pipelines/pag/):
| Pipeline 类 | 对应源文件 |
|---|---|
StableDiffusionPAGPipeline/StableDiffusionPAGImg2ImgPipeline/StableDiffusionPAGInpaintPipeline | pipeline_pag_sd.py 等 |
StableDiffusionControlNetPAGPipeline/StableDiffusionControlNetPAGInpaintPipeline | pipeline_pag_controlnet_sd.py 等 |
StableDiffusionXLPAGPipeline/StableDiffusionXLPAGImg2ImgPipeline/StableDiffusionXLPAGInpaintPipeline | pipeline_pag_sd_xl.py 等 |
StableDiffusionXLControlNetPAGPipeline/StableDiffusionXLControlNetPAGImg2ImgPipeline | pipeline_pag_controlnet_sd_xl.py 等 |
StableDiffusion3PAGPipeline/StableDiffusion3PAGImg2ImgPipeline | pipeline_pag_sd_3.py 等 |
PixArtSigmaPAGPipeline | pipeline_pag_pixart_sigma.py |
SanaPAGPipeline | pipeline_pag_sana.py |
HunyuanDiTPAGPipeline | pipeline_pag_hunyuandit.py |
KolorsPAGPipeline | pipeline_pag_kolors.py |
AnimateDiffPAGPipeline | pipeline_pag_sd_animatediff.py |
对应的测试覆盖位于 tests/pipelines/pag/,其中 testing_utils.py 提供共享的PAGPipelineTesterMixin,各模型测试(如test_pag_sdxl.py、test_pag_sd3.py、test_pag_animatediff.py等)据此验证 PAG 的启用、参数传递与确定性输出。
总结与调参建议
PAG 为扩散模型提供了一条"零成本"的质量提升路径:
- 免训练、免外挂模块:仅通过替换自注意力处理器与调整噪声合成公式生效,权重保持不变;
- CFG 失效场景的救星:无条件生成、ControlNet 空提示、图像复原(修复/去模糊)等场景中,PAG 是少数能直接发挥作用的引导手段;
- 与现有生态无缝集成:通过
AutoPipeline+enable_pag=True一行启用,支持from_pipe复用已有 pipeline,且可与 ControlNet、IP-Adapter 叠加使用; - 调参要点:先固定
pag_scale(可从 3.0 起步),再用set_pag_applied_layers尝试不同的层组合(从默认的mid开始,逐步叠加down/up块),最后微调pag_scale与pag_adaptive_scale平衡结构强度与纹理细节。
使用前请确认你的 PyTorch 版本不低于 2.0(PAG 处理器依赖scaled_dot_product_attention),并确认目标模型架构在当前仓库支持的 PAG pipeline 列表内。
【免费下载链接】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),仅供参考