diffusers学习--stable diffusion的管线解析

1. 核心组件加载

一个完整的 Stable Diffusion 潜在扩散模型主要由以下 5 个核心组件构成:

  1. VAE (Variational Autoencoder) : 一个图像自编码器,负责在像素空间 (我们看到的图片)和潜空间(UNet 处理的低维数据)之间进行转换。
  2. Tokenizer: 文本分词器,负责将人类语言(提示词)转换为模型能够理解的数字 ID(Tokens)。
  3. Text Encoder: 文本编码器(通常是 CLIP),负责将 Token ID 转换为包含丰富语义信息的词嵌入向量(Embeddings)。
  4. UNet: 模型的核心,一个在潜空间中运行的噪声预测模型。它在每一步根据文本编码器的指导,预测并移除噪声。
  5. Scheduler: 调度器,负责定义去噪的时间步长和策略。它根据 UNet 的预测结果,计算出下一个时间步的潜空间状态。
python 复制代码
# 加载所有组件,并指定 subfolder
repo_id="CompVis/stable-diffusion-v1-4"
vae = AutoencoderKL.from_pretrained(repo_id, subfolder="vae", ...)
tokenizer = CLIPTokenizer.from_pretrained(repo_id, subfolder="tokenizer")
text_encoder = CLIPTextModel.from_pretrained(repo_id, subfolder="text_encoder", ...)
unet = UNet2DConditionModel.from_pretrained(repo_id, subfolder="unet", ...) # 注意:这里原代码有误,应使用 UNet2DConditionModel
scheduler = UniPCMultistepScheduler.from_pretrained(repo_id, subfolder="scheduler")

2. 提示词处理

这是将文本指令转换为 UNet "指导"信息的过程。

  1. 分词与编码 :将正面提示词(如"宇航员骑马")转换为词嵌入向量 text_emb

  2. 准备无条件/负面提示词 :为了实现 CFG,我们还需要一个"无指导"的基准。这通常是通过编码一个空字符串 "" 来实现的,它代表了模型的"自由创作"状态,在应用上等同于一个负面提示词。这里只把提示词嵌入,还有负面提示词的嵌入,这里就不写负面提示词了,就用 ""

  3. 拼接 :将无条件嵌入 uncond_embeddings 和有条件嵌入 text_emb 拼接在一起,形成一个 (2, 77, 768) 的张量,为 CFG 的并行计算做准备。

python 复制代码
# 正面提示词
text_input = tokenizer(prompt, ...)
text_emb = text_encoder(text_input.input_ids.to(device))[0]

# 负面/无条件提示词 (空字符串)
uncond_input = tokenizer([""], ...)
uncond_embeddings = text_encoder(uncond_input.input_ids.to(device))[0]

# 拼接成一个批次为 2 的张量
text_embeddings = torch.cat([uncond_embeddings, text_emb])

3. 潜空间初始化

我们不直接生成图片,而是在潜空间中创建初始的随机噪声。

  1. 确定尺寸 :由于 VAE 的下采样因子为 8,一个 512x512 的图像对应的潜空间尺寸是 64x64。因此,初始噪声的尺寸是 height // 8width // 8
  2. 调整噪声尺度 :使用 torch.randn 创建的是标准正态分布噪声(强度为 1)。但像 UniPCMultistepScheduler 这样的高级调度器,其数学模型要求一个特定的初始噪声强度,这个值存储在 scheduler.init_noise_sigma 中。因此,我们需要将标准噪声乘以这个值,进行缩放。
python 复制代码
# 创建潜空间噪声
latents = torch.randn((batch_size, unet.config.in_channels, height // 8, width // 8), ...)

# 根据调度器要求进行缩放
latents = latents * scheduler.init_noise_sigma

4. 核心:去噪循环

这是从纯噪声生成图像潜空间表示的核心迭代过程。

python 复制代码
scheduler.set_timesteps(num_inference_steps) # 使用正确的函数调用
for i, timestep in enumerate(scheduler.timesteps):
    # ... 循环体 ...

循环中的每一步都包含以下关键操作:

  1. 复制 Latents: 这里只创建了正面提示词的随机噪声,负面提示词的直接复制一份正面的,对齐词向量嵌入

    python 复制代码
    latent_model_input = torch.cat([latents] * 2)
  2. UNet 预测 : 将复制后的 latents 和拼接好的 text_embeddings 一同送入 UNet,UNet 会并行地预测出两个噪声:noise_uncond (无指导的) 和 noise_text (有指导的)。

  3. CFG 计算 : 应用公式 noise = noise_uncond + guidance_scale * (noise_text - noise_uncond),计算出最终的、被强力引导的噪声。

  4. Scheduler 步进 : 调用 scheduler.step(),根据最终的噪声,计算出下一个时间步的、噪声更少的 latents

5. VAE 解码与保存

循环结束后,我们得到了最终的图像潜空间表示,需要将其解码成像素图像。

  1. 逆向缩放 : 在送入 VAE 解码器之前,需要用 1 / vae.config.scaling_factor (即 1 / 0.18215) 对 latents 进行一次逆向缩放,以匹配 VAE 解码器的输入尺度。
  2. 解码 : 调用 vae.decode(latents) 将潜空间表示转换回像素图像。
  3. 后处理与保存 : 将图像的数值范围从 [-1, 1] 转换到 [0, 255],并保存为图片文件。
相关推荐
重启的码农2 小时前
ggml 介绍(4) 计算图 (ggml_cgraph)
c++·人工智能
重启的码农2 小时前
ggml 介绍(5) GGUF 上下文 (gguf_context)
c++·人工智能·神经网络
R-G-B2 小时前
OpenCV Python——报错AttributeError: module ‘cv2‘ has no attribute ‘bgsegm‘,解决办法
人工智能·python·opencv·opencv python·attributeerror·module ‘cv2‘·no attribute
数据知道3 小时前
机器翻译:模型微调(Fine-tuning)与调优详解
人工智能·自然语言处理·机器翻译
沫儿笙3 小时前
焊接机器人保护气体效率优化
人工智能·机器人
青岛前景互联信息技术有限公司4 小时前
应急救援智能接处警系统——科技赋能应急,筑牢安全防线
人工智能·物联网·智慧城市
楚韵天工4 小时前
基于多分类的工业异常声检测及应用
人工智能·深度学习·神经网络·目标检测·机器学习·分类·数据挖掘
爱分享的飘哥4 小时前
第六十五章:AI的“精良食材”:图像标注、视频帧抽帧与字幕提取技巧
人工智能·语音识别·ai训练·视频处理·数据预处理·图像标注·字幕提取
技术老金4 小时前
给你的AI应用“降本增效”:吃透模型级联、智能缓存等三大成本优化策略
人工智能·架构