Diffusers 中 Stable unCLIP 管道实战指南:基于 CLIP 图像嵌入的文生图与图像变体生成
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
Stable unCLIP 是 diffusers 提供的基于 Stable Diffusion 2.1 微调而来的图像生成管道:它额外条件化于 CLIP 图像嵌入,同时保留文本条件,从而支持「文本引导的图像变体生成」以及结合 unCLIP prior 的完整文生图能力。本文将基于 Stable unCLIP 官方文档,结合仓库源码(StableUnCLIPPipeline 实现、StableUnCLIPImg2ImgPipeline 实现)深入讲解其原理、两种加载与调用方式、noise_level等关键参数,以及常见陷阱,让读者能够直接复现文生图与图像变体两种典型应用。
Stable unCLIP 是什么:从 Stable Diffusion 2.1 微调的双条件模型
Stable unCLIP 检查点是在 Stable Diffusion 2.1 检查点基础上微调而来,核心变化是:额外以 CLIP 图像嵌入(CLIP image embeddings)作为条件输入,同时仍然保留原有的文本嵌入条件。因此它拥有两路独立的条件信号:
- 文本条件:与 Stable Diffusion 一致,通过 CLIP 文本编码器编码 prompt;
- 图像条件:CLIP 图像嵌入作为
class_labels注入 UNet 的交叉注意力过程。
由于两路条件相互独立,Stable unCLIP 可以用于:
- 文本引导的图像变体(text guided image variation)——直接给定一张图,生成保持其语义与风格、但细节发生变化的变体;
- 完整文生图(text-to-image)——与 unCLIP prior 模型串联,先用 prior 从文本生成图像嵌入,再交给 unCLIP 解码器出图。
论文摘要给出的动机是:CLIP 这类对比模型学习到的图像表征同时捕获了语义与风格。论文提出两阶段模型——prior 根据文本描述生成 CLIP 图像嵌入,decoder 再根据该嵌入生成图像。显式生成图像表征可以在几乎不损失真实感与文本相似度的前提下提升图像多样性;decoder 还能在保留语义和风格的同时改变表征中缺失的非关键细节;CLIP 的联合嵌入空间还支持零样本的语言引导图像操作。
从源码结构看,该能力在 diffusers 中由两条管道承载,均位于 src/diffusers/pipelines/stable_diffusion/ 目录:
StableUnCLIPPipeline(pipeline_stable_unclip.py):文生图管道,内置 prior 组件;StableUnCLIPImg2ImgPipeline(pipeline_stable_unclip_img2img.py):文本引导图像变体管道。
两者都继承DiffusionPipeline、StableDiffusionMixin,并混入TextualInversionLoaderMixin(支持加载 textual inversion 嵌入)与StableDiffusionLoraLoaderMixin(支持加载/保存 LoRA 权重),因此可以像标准 Stable Diffusion 管道一样使用 LoRA 与 Textual Inversion 能力。
核心机制:图像嵌入加噪与noise_level参数
文档明确指出,Stable unCLIP 在推理时会接收noise_level输入,它决定向图像嵌入中添加多少噪声:
noise_level越高,最终去噪图像中的变化(variation)越大;- 默认值为
0,即默认情况下不给图像嵌入添加任何额外噪声。
在 pipeline_stable_unclip.py 的noise_image_embeddings实现中,噪声以两种方式施加:
- 直接对嵌入施加噪声调度:通过
image_noising_scheduler.add_noise(image_embeds, timesteps=noise_level, noise=noise)完成; - 在嵌入上拼接正弦时间嵌入向量:通过
get_timestep_embedding生成与noise_level对应的正弦位置编码,拼接到加噪后的图像嵌入末尾,作为 UNet 的额外条件。
两种方式的噪声量都由同一个noise_level控制。同时,加噪前后会经过 StableUnCLIPImageNormalizer 的scale/unscale归一化——该类持有 CLIP embedder 的均值mean与标准差std(默认嵌入维度768),先对图像嵌入做标准化再加噪,加噪后再反标准化还原,保证噪声施加过程的数值稳定。
noise_level的合法取值范围在check_inputs中被校验:必须满足0 <= noise_level < image_noising_scheduler.config.num_train_timesteps,否则抛出ValueError(见 pipeline_stable_unclip.py)。由于加噪过程通过image_noising_scheduler.add_noise完成,Stable unCLIP 对图像嵌入的「噪声化」本质上是一种受控的扰动,因此在实际使用中:
noise_level = 0:保留原始图像嵌入,变体与原图最接近;noise_level逐步增大:引入更多随机扰动,生成图像在语义风格不变的前提下产生更强的细节变化。
文生图:StableUnCLIPPipeline 与 Karlo prior 的串联
管道组成与加载方式
StableUnCLIPPipeline的__init__(见 pipeline_stable_unclip.py)注册了四组组件:
| 组件分组 | 模块 | 说明 |
|---|---|---|
| prior 组件 | prior_tokenizer、prior_text_encoder(CLIPTextModelWithProjection)、prior(PriorTransformer)、prior_scheduler | 用文本生成 CLIP 图像嵌入 |
| 图像加噪组件 | image_normalizer、image_noising_scheduler | 对图像嵌入加噪/去噪 |
| 常规去噪组件 | tokenizer、text_encoder(CLIPTextModel)、unet(UNet2DConditionModel)、scheduler | 标准的文本编码与潜在空间去噪 |
| VAE | vae(AutoencoderKL) | 潜在表示与像素图像的编解码 |
该管道还定义了model_cpu_offload_seq = "text_encoder->prior_text_encoder->unet->vae",并设置_exclude_from_cpu_offload = ["prior", "image_normalizer"],说明使用顺序 CPU 卸载(sequential CPU offloading)时 prior 与归一化器不会被卸载,可在显存受限场景下启用enable_sequential_cpu_offload。
文生图需要先加载一个unCLIP prior来把文本变成图像嵌入。官方文档推荐使用 KakaoBrain 开源的 DALL-E 2 复现项目Karlo的 prior 模型(kakaobrain/karlo-v1-alpha),完整代码如下:
import torch from diffusers import UnCLIPScheduler, DDPMScheduler, StableUnCLIPPipeline from diffusers.models import PriorTransformer from transformers import CLIPTokenizer, CLIPTextModelWithProjection prior_model_id = "kakaobrain/karlo-v1-alpha" data_type = torch.float16 prior = PriorTransformer.from_pretrained(prior_model_id, subfolder="prior", dtype=data_type) prior_text_model_id = "openai/clip-vit-large-patch14" prior_tokenizer = CLIPTokenizer.from_pretrained(prior_text_model_id) prior_text_model = CLIPTextModelWithProjection.from_pretrained(prior_text_model_id, dtype=data_type) prior_scheduler = UnCLIPScheduler.from_pretrained(prior_model_id, subfolder="prior_scheduler") prior_scheduler = DDPMScheduler.from_config(prior_scheduler.config) stable_unclip_model_id = "stabilityai/stable-diffusion-2-1-unclip-small" pipe = StableUnCLIPPipeline.from_pretrained( stable_unclip_model_id, dtype=data_type, variant="fp16", prior_tokenizer=prior_tokenizer, prior_text_encoder=prior_text_model, prior=prior, prior_scheduler=prior_scheduler, ) pipe = pipe.to("cuda") # or "mps", "xpu", "cpu" wave_prompt = "dramatic wave, the Oceans roar, Strong wave spiral across the oceans as the waves unfurl into roaring crests; perfect wave form; perfect wave shape; dramatic wave shape; wave shape unbelievable; wave; wave shape spectacular" image = pipe(prompt=wave_prompt).images[0] image几个关键点值得说明:
- prior 与 unCLIP 解码器必须使用同一种 CLIP 图像嵌入:代码中 prior 使用
openai/clip-vit-large-patch14(CLIP ViT-L/14),因此解码器必须选择同样基于 ViT-L/14 训练的检查点; variant="fp16":指示加载 fp16 权重变体,配合dtype=torch.float16可显著降低显存占用;prior_scheduler的二次转换:先读取UnCLIPScheduler的配置,再通过DDPMScheduler.from_config(prior_scheduler.config)转为DDPMScheduler实例,这是因为 Karlo prior 的采样过程使用 DDPM 调度器;- 设备迁移:
pipe.to("cuda")支持cuda/mps/xpu/cpu。
为什么推荐-unclip-small变体
[!WARNING] 文生图场景应使用
stabilityai/stable-diffusion-2-1-unclip-small,因为它基于 CLIP ViT-L/14 嵌入训练,与 Karlo 模型的 prior 一致。而stabilityai/stable-diffusion-2-1-unclip基于 OpenCLIP ViT-H 训练,不推荐与 ViT-L/14 的 Karlo prior 混用。
StableUnCLIPPipeline的两个子类检查点分别对应不同 CLIP 图像编码器,混用会导致图像嵌入空间不匹配、生成质量严重劣化:
| 检查点 | 图像嵌入来源 | 适配场景 |
|---|---|---|
stabilityai/stable-diffusion-2-1-unclip-small | CLIP ViT-L/14 | 与 Karlo prior(ViT-L/14)串联做文生图 |
stabilityai/stable-diffusion-2-1-unclip | OpenCLIP ViT-H | 直接用于图像变体(img2img),无 prior 场景 |
文生图内部调用链
从__call__的源码(pipeline_stable_unclip.py)可以看到完整流程:
- 默认高宽取自
unet.config.sample_size * vae_scale_factor(vae_scale_factor由 VAE 的block_out_channels长度计算,通常为 8); check_inputs校验高宽是否可被 8 整除、noise_level是否越界等;_encode_prior_prompt用prior_text_encoder(CLIPTextModelWithProjection)编码 prompt,得到text_embeds(用于 prior 的proj_embedding)与last_hidden_state(用于encoder_hidden_states),若启用无分类器引导(CFG)会拼接空文本嵌入;- 在 prior 去噪循环中,
PriorTransformer以proj_embedding、encoder_hidden_states、attention_mask为条件预测图像嵌入,CFG 公式为uncond + prior_guidance_scale * (text - uncond)(L821-L825),随后prior_scheduler.step迭代 25 步(prior_num_inference_steps默认值),最后prior.post_process_latents输出图像嵌入; noise_image_embeddings按noise_level对图像嵌入加噪(上文已述);- 常规 UNet 去噪循环:
unet(latent_model_input, t, encoder_hidden_states=prompt_embeds, class_labels=image_embeds)——图像嵌入正是通过class_labels参数注入 UNet; - VAE 解码 +
VaeImageProcessor.postprocess输出 PIL 图像,返回ImagePipelineOutput(images=image)(return_dict=False时返回(image,)元组)。
关键推理参数
StableUnCLIPPipeline.__call__的签名(L647-L673)中,主去噪过程与 prior 过程分别拥有独立参数:
| 参数 | 默认值 | 作用 |
|---|---|---|
prompt | None | 文本提示词 |
height/width | UNet 默认 | 输出图像尺寸,必须能被 8 整除 |
num_inference_steps | 20 | UNet 去噪步数 |
guidance_scale | 10.0 | 主去噪过程的 CFG 强度,大于 1 时启用 |
negative_prompt | None | 负向提示词(guidance_scale > 1时生效) |
num_images_per_prompt | 1 | 每个 prompt 生成图像数 |
eta | 0.0 | DDIM 的 η 参数,仅对DDIMScheduler生效 |
noise_level | 0 | 图像嵌入加噪量(见上文核心机制) |
prior_num_inference_steps | 25 | prior 去噪步数 |
prior_guidance_scale | 4.0 | prior 过程的 CFG 强度 |
prior_latents | None | 预生成的 prior 噪声潜变量,用于复现 |
latents | None | 预生成的 UNet 噪声潜变量,用于复现 |
prompt_embeds/negative_prompt_embeds | None | 预计算的文本嵌入,可与prompt二选一 |
output_type | "pil" | "pil"/"np"/"latent" |
callback/callback_steps | None/1 | 推理步回调 |
clip_skip | None | 跳过 CLIP 若干层计算 prompt 嵌入(如 1 表示使用倒数第二层输出) |
文本引导的图像变体:StableUnCLIPImg2ImgPipeline
最小可运行示例
图像变体场景不需要 prior,直接加载 unCLIP 检查点即可(官方文档示例):
from diffusers import StableUnCLIPImg2ImgPipeline from diffusers.utils import load_image import torch pipe = StableUnCLIPImg2ImgPipeline.from_pretrained( "stabilityai/stable-diffusion-2-1-unclip", dtype=torch.float16, variation="fp16" ) pipe = pipe.to("cuda") # or "mps", "xpu", "cpu" url = "https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/stable_unclip/tarsila_do_amaral.png" init_image = load_image(url) images = pipe(init_image).images images[0].save("variation_image.png")可选地,也可以向pipe传入 prompt 进行文本引导:
prompt = "A fantasy landscape, trending on artstation" image = pipe(init_image, prompt=prompt).images[0] image注意文档示例中的variation="fp16"即variant="fp16"的历史别名(该写法在from_pretrained中被兼容处理),其作用是加载 fp16 权重变体以节省显存;传参时也可直接写variant="fp16"。
管道组件与图像编码流程
StableUnCLIPImg2ImgPipeline的__init__(见 pipeline_stable_unclip_img2img.py)与文生图版本相比,用图像编码组件替换了 prior 组件:
| 组件分组 | 模块 | 说明 |
|---|---|---|
| 图像编码组件 | feature_extractor(CLIPImageProcessor)、image_encoder(CLIPVisionModelWithProjection) | 预处理输入图像并编码为 CLIP 图像嵌入 |
| 图像加噪组件 | image_normalizer、image_noising_scheduler | 对图像嵌入加噪/去噪 |
| 常规去噪组件 | tokenizer、text_encoder、unet、scheduler | 文本编码与潜在空间去噪 |
| VAE | vae | 编解码 |
调用pipe(init_image, prompt=prompt)时,_encode_image会:用feature_extractor预处理图像、image_encoder输出投影后的图像嵌入,并按 batch 复制以匹配 prompt 数量,随后同样走noise_image_embeddings加噪、以class_labels注入 UNet、最后 VAE 解码。因此在图像变体场景中同样可以使用noise_level控制变化幅度——输入图固定时,noise_level越大,变体与原图的差异越大。
在 tests/pipelines/stable_unclip/test_stable_unclip_img2img.py 中可以看到官方测试同时覆盖了stable-unclip-2-1-l(ViT-L/14)与stable-unclip-2-1-h(ViT-H)两个尺寸的图像变体验证(test_stable_unclip_l_img2img/test_stable_unclip_h_img2img),说明两种检查点都支持直接做图像变体;另有test_image_embeds_none与顺序 CPU 卸载测试等,印证了_encode_image的输入分支与enable_sequential_cpu_offload兼容性。
常见问题与排查建议
- 文生图效果差/图像损坏:极大概率是 prior 与 unCLIP 解码器的 CLIP 嵌入空间不一致。请严格遵循文档建议——文生图只用
stabilityai/stable-diffusion-2-1-unclip-small+ Karlo prior;若使用stable-diffusion-2-1-unclip(ViT-H)则应更换为 ViT-H 对应的 prior。 - 显存不足(OOM):可组合使用
dtype=torch.float16+variant="fp16"加载,并在pipe.to("cuda")之后调用pipe.enable_sequential_cpu_offload()(源码已配置model_cpu_offload_seq)。注意enable_attention_slicing、enable_xformers_memory_efficient_attention等方法同样可用于降低内存占用(见文档末尾 API 清单)。 noise_level越界报错:它必须落在[0, image_noising_scheduler.config.num_train_timesteps)区间内(源码check_inputs会抛ValueError)。- 尺寸报错:
height与width必须能被 8 整除,否则check_inputs直接抛错。 - 无 prompt 变体:
pipe(init_image)时 prompt 为None,管道内部以空字符串做 CFG 编码,可正常出图;需要文本引导时再传入prompt。
延伸阅读
- 调度器选择与速度/质量权衡:Schedulers 指南,其中 Karras sigmas 重新采样噪声调度可提升细节(仅适用于以 Karras sigmas 训练的模型);
- 跨管道复用组件(如让多条管道共享同一套 VAE/文本编码器):Reusing models across pipelines;
- Stable Diffusion 2.x 背景:Stable Diffusion 2 文档;
- 底层先验模型实现:PriorTransformer、UnCLIPScheduler、以及废弃的 unCLIP 实现 pipeline_unclip.py 可作对照参考;
- 官方测试用例:test_stable_unclip.py、test_stable_unclip_img2img.py,可作为复现与结果比对的基准。
【免费下载链接】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),仅供参考