Diffusers 中的 Kandinsky 3 文生图与图生图流水线实战指南
【免费下载链接】diffusers🤗 Diffusers: State-of-the-art diffusion models for image, video, and audio generation in PyTorch.项目地址: https://gitcode.com/GitHub_Trending/di/diffusers
Kandinsky 3 是开源的文本到图像扩散模型家族成员,由俄罗斯 AI 团队(ai-forever)在 Kandinsky 2.x 基础上演进而来,在文本理解与视觉质量两个维度均有显著提升。本文基于本仓库 Kandinsky 3 官方 API 文档,结合 Kandinsky3Pipeline 源码 与 Kandinsky3Img2ImgPipeline 源码 以及对应测试用例,系统讲解其架构组成、推理调用方式、核心参数含义与底层实现原理。读完本文,你将能够在 Diffusers 中直接运行 Kandinsky 3 完成文生图与图生图任务,并掌握 prompt 编码、无分类器引导(CFG)、上下文截断等关键实现细节。
Kandinsky 3 概览:架构与设计要点
Kandinsky 3.0 是一个开源的文本到图像扩散模型,构建于 Kandinsky 2-x 模型家族之上。与前辈相比,其改进集中在两点:增大文本编码器规模以强化文本理解能力,以及增大 Diffusion U-Net 规模以提升视觉质量(依据官方文档描述)。
整个模型架构由 3 个核心组件构成:
- FLAN-UL2 文本编码器:一个基于 T5 架构的 encoder-decoder 模型,负责把自然语言 prompt 编码为条件向量,是模型文本理解能力大幅增强的关键。
- 全新 U-Net 架构:采用BigGAN-deep 风格的残差块,在保持参数总量不变的前提下使网络深度翻倍,从而获得更强的去噪建模能力。
- Sber-MoVQGAN 解码器:将去噪得到的潜在表示还原为图像,在图像重建质量上被证明优于同类方案。
从源码层面看,上述三部分在 Diffusers 中分别对应T5EncoderModel(文本编码)、Kandinsky3UNet(去噪骨干,实现在 unet_kandinsky3.py)与VQModel(MoVQGAN 式 VQ 解码器),并由DDPMScheduler负责调度去噪时间步。三者以模块方式注册进流水线(见 pipeline_kandinsky3.py 的__init__),因此可以按需替换或复用组件。
[!TIP] 官方模型权重由 kandinsky-community 组织在 Hub 上发布,覆盖文生图、图生图与图像修复等任务。
[!TIP] 在使用之前,建议先阅读 调度器使用指南 了解不同 scheduler 在速度与质量之间的取舍,并参考 跨流水线复用组件 了解如何高效地在多个流水线之间共享同一批模型权重。
快速上手:文生图(Kandinsky3Pipeline)
Kandinsky3Pipeline是 Kandinsky 3 的文本到图像流水线。官方在源码中给出了开箱即用的示例(pipeline_kandinsky3.py),借助自动流水线工厂AutoPipelineForText2Image即可加载:
from diffusers import AutoPipelineForText2Image import torch pipe = AutoPipelineForText2Image.from_pretrained( "kandinsky-community/kandinsky-3", variant="fp16", torch_dtype=torch.float16 ) pipe.enable_model_cpu_offload() prompt = "A photograph of the inside of a subway train. There are raccoons sitting on the seats. One of them is reading a newspaper. The window shows the city in the background." generator = torch.Generator(device="cpu").manual_seed(0) image = pipe(prompt, num_inference_steps=25, generator=generator).images[0]要点说明:
variant="fp16"会加载 fp16 精度的权重,torch_dtype=torch.float16将模型运行精度设为半精度,以降低显存占用。enable_model_cpu_offload()按text_encoder -> unet -> movq的顺序(即源码中的model_cpu_offload_seq,见 pipeline_kandinsky3.py)把组件逐个搬到 GPU 执行再卸载回 CPU,从而显著压缩峰值显存。num_inference_steps=25是推荐的默认去噪步数,更多步数通常带来更高画质但更慢的推理。- 固定
generator的随机种子可以复现相同结果。
对应地,仓库中的慢速集成测试 test_kandinsky3.py 使用完全相同的方式加载kandinsky-community/kandinsky-3(fp16、CPU offload、seed 0),以 5 步生成并断言输出尺寸为(1024, 1024),同时与 Hub 上的参考图逐像素对比(容差atol=5e-2),印证了上述调用路径的可行性。
快速上手:图生图(Kandinsky3Img2ImgPipeline)
Kandinsky3Img2ImgPipeline以一张输入图片为起点,按给定强度注入噪声后再去噪,实现图生图 / 风格迁移。官方示例(pipeline_kandinsky3_img2img.py):
from diffusers import AutoPipelineForImage2Image from diffusers.utils import load_image import torch pipe = AutoPipelineForImage2Image.from_pretrained( "kandinsky-community/kandinsky-3", variant="fp16", torch_dtype=torch.float16 ) pipe.enable_model_cpu_offload() prompt = "A painting of the inside of a subway train with tiny raccoons." image = load_image( "https://huggingface.co/datasets/hf-internal-testing/diffusers-images/resolve/main/kandinsky3/t2i.png" ) generator = torch.Generator(device="cpu").manual_seed(0) image = pipe(prompt, image=image, strength=0.75, num_inference_steps=25, generator=generator).images[0]与文生图相比,图生图新增了两个关键参数:
image:输入图像,支持torch.Tensor、PIL.Image.Image或二者构成的列表。流水线内部先经image_processor.preprocess预处理,再交给movq.encode得到潜在表示(见 pipeline_kandinsky3_img2img.py)。strength(默认 0.3):表示对参考图的改造程度,取值 0~1。数值越大,向原图注入的噪声越多、与原文偏离越大;取 1 时加噪量最大,去噪过程将跑满num_inference_steps全部步数,相当于几乎完全忽略原图。
strength的实际生效逻辑在get_timesteps方法中(pipeline_kandinsky3_img2img.py):先计算init_timestep = min(int(num_inference_steps * strength), num_inference_steps),再从调度器时间步序列的t_start = max(num_inference_steps - init_timestep, 0)处开始切片,即实际去噪步数随强度收缩。对应集成测试 test_kandinsky3.py 以strength=0.75、5 步验证输出尺寸为(512, 512)并与参考图对比。
__call__核心参数详解
两个流水线的__call__签名高度一致(差异仅在图生图多出image与strength),下表汇总了官方 docstring 中定义的关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
prompt | None | 正向提示词,str或list[str](批量生成);与prompt_embeds二选一 |
num_inference_steps | 25 | 去噪步数,越多画质越好但推理越慢 |
guidance_scale | 3.0 | 无分类器引导强度,即 Imagen 论文公式中的w;设为> 1启用 CFG,越高图像越贴近 prompt,但通常牺牲多样性 |
negative_prompt | None | 负向提示词,忽略引导时不生效(guidance_scale <= 1) |
num_images_per_prompt | 1 | 每个 prompt 生成的图片数 |
height/width | 1024 | 输出图像尺寸(像素) |
generator | None | torch.Generator或列表,用于复现性生成 |
prompt_embeds | None | 预生成的文本嵌入,可用于 prompt 加权等精细化控制;传入时必须同时提供attention_mask |
negative_prompt_embeds | None | 预生成的负向嵌入;传入时必须同时提供negative_attention_mask |
attention_mask/negative_attention_mask | None | 与嵌入配套的注意力掩码,直接传入嵌入时必填 |
output_type | "pil" | 输出格式,可选"pil"、"np"、"pt"与"latent" |
return_dict | True | 返回ImagePipelineOutput命名元组还是普通 tuple |
latents | None | 预生成的噪声潜在张量,可用于以同一批噪声配合不同 prompt 做对照 |
callback_on_step_end | None | 每步去噪结束时的回调,接收(pipeline, step, timestep, callback_kwargs) |
callback_on_step_end_tensor_inputs | ["latents"] | 传入回调的张量名列表,可选值受_callback_tensor_inputs约束 |
关于校验规则:源码中的check_inputs(pipeline_kandinsky3.py)会拒绝prompt与prompt_embeds同时传入、要求嵌入与掩码形状一致、并校验callback_steps为正整数;旧的callback/callback_steps参数已被标记为弃用(计划在 1.0.0 移除),应改用callback_on_step_end。
源码级原理:prompt 编码与上下文截断
encode_prompt(pipeline_kandinsky3.py)是理解整个流水线的钥匙,其核心流程如下:
- Tokenize:使用 T5 tokenizer 将 prompt 编码为
max_length=128的输入序列(padding="max_length"且truncation=True),同时产出attention_mask。 - 文本编码:
T5EncoderModel前向得到隐藏状态,取[0]作为嵌入。 - 上下文截断(cut_context):
process_embeds会把被 mask 掉的填充位嵌入清零,并按max_seq_length = attention_mask.sum(-1).max() + 1把序列裁短(pipeline_kandinsky3.py)。这避免了 U-Net 在 128 token 全长度上进行昂贵的交叉注意力计算,是 Kandinsky 3 推理效率的关键优化。 - Mask 相乘:
prompt_embeds = prompt_embeds * attention_mask.unsqueeze(2),用掩码显式屏蔽填充 token。 - CFG 分支:当
guidance_scale > 1(即do_classifier_free_guidance为真)时,若未提供负向嵌入,则对negative_prompt(缺省为空串"")执行同样的编码流程,或直接以全零张量作为无条件嵌入。 - 重复扩展:按
num_images_per_prompt用repeat+view的方式扩展 batch(注释指出这是为了兼容 MPS 设备)。
在__call__中,正负向嵌入被torch.cat拼接成单个 batch 一次前向(pipeline_kandinsky3.py),从而避免两次 U-Net 前向;随后在去噪循环中拆分并应用CFG 组合公式:
noise_pred = (guidance_scale + 1.0) * noise_pred_text - guidance_scale * noise_pred_uncond注意此处与常见的noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)在数学上等价(源码注释中保留了该对照写法),公式中guidance_scale + 1.0的形式对应 Imagen 论文的w定义。
源码级原理:U-Net 与 MoVQGAN 解码
Kandinsky3UNet 的 BigGAN-deep 结构
Kandinsky3UNet(unet_kandinsky3.py)的默认配置为:in_channels=4、time_embedding_dim=1536、groups=32、attention_head_dim=64、layers_per_block=3、block_out_channels=(384, 768, 1536, 3072)、cross_attention_dim=4096、encoder_hid_dim=4096。从结构看可以确认以下设计:
- BigGAN-deep 风格残差块:
Kandinsky3ResNetBlock内部串联 4 个子块,卷积核尺寸依次为[1, 3, 3, 1],中间以compression_ratio=2压缩通道(unet_kandinsky3.py)——这正是"深度翻倍而参数不涨"的实现载体。 - 条件化 GroupNorm:
Kandinsky3ConditionalGroupNorm将时间步嵌入映射为逐通道 scale/shift(初始化时置零,保证训练初期等价于普通归一化),实现 FiLM 式的条件注入(unet_kandinsky3.py)。 - 文本条件注入:文本嵌入先经
Kandinsky3EncoderProj(线性投影 + LayerNorm)对齐到交叉注意力维度;时间步嵌入再通过Kandinsky3AttentionPooling与文本上下文做一次注意力池化融合(unet_kandinsky3.py)。 - 下采样/上采样块:每个层级由自注意力块、若干 ResNet + 交叉注意力块组合而成,
add_cross_attention与add_self_attention均为(False, True, True, True),即最底层不做注意力,其余层级同时具备自注意力与文本交叉注意力(unet_kandinsky3.py)。
在流水线前向中,U-Net 接收encoder_hidden_states与encoder_attention_mask,后者会在模型内部被转换为(1 - mask) * -10000.0的负无穷大掩码再注入注意力(unet_kandinsky3.py)。
MoVQGAN 解码与尺寸规整
- 尺寸对齐:
downscale_height_and_width(pipeline_kandinsky3.py)会把height/width规整为 64 的整数倍(scale_factor=8,二次方后为 64),防止解码时出现非整除尺寸。 - 潜在空间:
prepare_latents从标准正态采样噪声,并乘以scheduler.init_noise_sigma完成初始化;潜在张量通道数为 4。 - 解码:去噪结束后调用
self.movq.decode(latents, force_not_quantize=True)["sample"](pipeline_kandinsky3.py),其中force_not_quantize=True表示跳过 VQ 量化直出连续潜在。随后对np/pil输出执行image * 0.5 + 0.5的反归一化与clamp(0, 1)。
图生图的潜在加噪路径
图生图流水线的prepare_latents(pipeline_kandinsky3_img2img.py)先经movq.encode得到初始潜在并乘scaling_factor,再用scheduler.add_noise(init_latents, noise, timestep)按首个有效时间步注入噪声,得到带噪起点后进入标准去噪循环。这也是strength直接控制"从哪个时间步切入"的底层原因。另外,图生图流水线的model_cpu_offload_seq为"text_encoder->movq->unet->movq"(pipeline_kandinsky3_img2img.py),因为编码原图也需要先加载 MoVQGAN。
LoRA 支持、组件复用与扩展能力
两个流水线都继承自StableDiffusionLoraLoaderMixin(pipeline_kandinsky3.py、pipeline_kandinsky3_img2img.py),这意味着你可以直接使用load_lora_weights/save_lora_weights等接口为 Kandinsky 3 加载或导出 LoRA 权重,对 U-Net 等组件做轻量化定制(这也是官方文档徽章中标注 LoRA 支持的原因)。
此外还有两个值得关注的仓库配套能力:
- 权重转换脚本:仓库在 pipelines/kandinsky3/convert_kandinsky3_unet.py 提供了将原始 Kandinsky 3 checkpoint 转换为 Diffusers 格式的脚本,方便自行转换非官方发布的权重。
- 组件复用:由于 tokenizer / text_encoder / unet / scheduler / movq 是独立注册的模块,可参考 跨流水线复用组件 将 Kandinsky 3 的文本编码器或 MoVQGAN 解码器复用到其他任务流水线中;调度器方面则可根据 调度器指南 替换
DDPMScheduler以探索速度与质量的权衡(默认配置为squaredcos_cap_v2的 beta schedule,见测试用例 test_kandinsky3.py)。
测试验证与工程保障
仓库为 Kandinsky 3 提供了完整的测试覆盖(tests/pipelines/kandinsky3):
- 单元级:
test_kandinsky3.py中的TestKandinsky3Pipeline使用微型随机组件(tiny-random-t5文本编码器、小型 U-Net 与 VQModel)验证流水线可端到端产出正确形状的张量(输出(1, 3, 16, 16)),并通过assert_tensors_close与预期切片对比(容差atol=1e-1),同时覆盖test_inference_batch_single_identical批量一致性检查;MemoryTesterMixin则覆盖 CPU offload、group offload 等显存优化路径。 - 集成级:
TestKandinsky3PipelineIntegration标记为@slow且要求 GPU 加速器,直接加载真实权重kandinsky-community/kandinsky-3,对文生图(1024×1024)与图生图(512×512)分别与 Hub 参考图比对,容差atol=5e-2。 - 图生图侧另有独立的 test_kandinsky3_img2img.py 负责对应单元测试。
这些测试既是对上文调用方式的直接验证,也为读者在本地复现、或在自定义权重上做回归测试提供了模板。整体而言,Kandinsky 3 在 Diffusers 中的实现呈现出"标准 DiffusionPipeline 骨架 + T5 编码 + BigGAN-deep U-Net + MoVQGAN 解码"的清晰分层:理解encode_prompt的上下文截断与 CFG 组合方式,是掌握其推理行为与显存占用的关键所在。
【免费下载链接】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),仅供参考