Modules

UNet

1️⃣ UNetModel 代码结构解析(in ldm/modules/openaimodel.py
UNetModel 是一个标准的 U-Net 结构,包含:
  • 编码器(input_blocks)
  • 瓶颈层(middle_block)
  • 解码器(output_blocks)
  • 最终输出层(self.out)
📌 主要流程
1. input_blocks(编码器)
通过多个 ResBlock(残差块) 和 Downsample 逐层提取特征
attention_resolutions 控制是否加入注意力机制
特征 h 被存入 hs,用于后续跳跃连接
2. middle_block(瓶颈层)
通过 ResBlock + AttentionBlock 进一步处理特征
3. output_blocks(解码器)
通过 Upsample 层恢复分辨率
核心:与 hs 进行跳跃连接(skip connection)
如果有注意力机制,加入 AttentionBlock
4. self.out(最终输出层)
归一化 + SiLU 激活
通过 conv_nd 生成最终输出

2️⃣ ControlledUnetModel 代码解析
ControlledUnetModel 继承 UNetModel,并 增加了 control 变量,用于外部引导 U-Net 处理特定任务。
📌 主要改进点
1. 编码器部分(input_blocks)
  • 加速优化:使用 torch.no_grad(),避免计算梯度,提高推理速度
  • hs.append(h):存储编码器输出,用于跳跃连接
2. 中间层(middle_block)
  • control 的第一次作用:如果提供了 control,则 在 middle_block 之后 进行相加 h += control.pop()
  • 这里 control.pop() 代表 取出控制变量的最后一层
3. 解码器部分(output_blocks)
  • 加入 control 的方式
  • 如果 only_mid_control=True,则 control 仅在 middle_block 作用,后续解码过程正常否则,在解码过程中不断加入 control.pop()
  • h = torch.cat([h, hs.pop() + control.pop()], dim=1)
    • h 是当前特征图
    • hs.pop() 是来自编码器的跳跃连接
    • control.pop() 是控制信号(用于影响解码过程)

ControlNet

ControlNet 解析
ControlNet 是一种改进的 U-Net 结构,它通过引入额外的“控制”信息(如 hint 输入)来 引导 神经网络的生成过程,使其能够更加精准地遵循某些约束或先验信息。在扩散模型(Diffusion Models)或图像生成任务中,ControlNet 可以用于特定任务,如边缘检测、深度信息控制、姿态引导等。

🔹 ControlNet 相较于标准 U-Net 的主要改进
1️⃣ 额外的 hint 信息输入
  • 传统的 U-Net 主要依赖输入 x 进行图像生成,而 ControlNet 额外引入 hint 作为辅助信息,提供某种先验引导。
  • hint 通过 input_hint_block 进行处理,这个模块是一个深度卷积网络(CNN),它将 hint 逐步下采样并映射到 model_channels 维度,使其与主 U-Net 结构兼容。
2️⃣ zero_convs 额外控制分支
  • zero_convs 是一系列 零初始化卷积层,用于让 ControlNet 直接在各个层级学习额外的偏差信息。
  • 这意味着 ControlNet 不是直接干预主 U-Net 的计算,而是以 残差(Residual) 方式调整输出,这样可以在保持预训练 U-Net 结构的同时,让 ControlNet 提供新的信息。
notion image
3️⃣ 编码器 input_blocks 中引入 guided_hint
  • 在前向传播过程中,ControlNet 逐层融合 hint 信息
    • 1. hint 经过 input_hint_block 处理后,变成 guided_hint
      2. 在编码阶段的 第一层 input_blocks,guided_hint 直接加到 h 上,使得 ControlNet 受 hint 影响
      3. 后续层不会重复加 guided_hint

🔹 代码解析
1️⃣ __init__ 构造函数
💡 新增的 hint 处理模块
  • 这个 input_hint_block 负责 处理 hint 数据,它是一个深度卷积网络:
  • 从 hint_channels 开始,逐步增加通道数,并进行三次 下采样 (stride=2),最终与 U-Net 主网络的 model_channels 维度匹配。
  • 这样可以确保 hint 信息可以直接用于调整 U-Net 的隐藏层特征。

2️⃣ forward() 计算流程
  • 这个 forward() 接受 4 个主要输入
1. x - 原始图像
2. hint - 额外的控制信号
3. timesteps - 扩散模型的时间步
4. context - 额外的上下文信息(如文本)
💡 计算时间嵌入
  • 时间步 timesteps 先经过 timestep_embedding() 变换,再经过 self.time_embed 提取时间特征。
💡 处理 hint 额外信息
  • hint 经过 input_hint_block 处理,生成 guided_hint,它将在 ControlNet 结构中 融合到 U-Net 的编码过程中
💡 编码时融合 guided_hint
  • 逐层遍历 input_blocks 进行编码:
    • 第一层(guided_hint 仍然存在)
      • 计算 h = module(h, emb, context)(标准 U-Net 计算)
      • 直接 h += guided_hint,融合额外控制信息
      • 然后 guided_hint = None,确保后续层 不会 继续加 hint
  • 其他层正常执行 h = module(h, emb, context)。

3️⃣ middle_block 处理
  • 瓶颈层 middle_block 继续处理,并存入 outs 作为最终控制信号。

🔹 ControlNet 主要作用
  • 与 UNet 主要区别:
    • ControlNet 增加了 hint 作为额外控制信号,用于引导扩散过程。
    • 额外的 zero_convs 让 ControlNet 以残差方式调整特征,不会破坏 U-Net 原始权重。
    • 只在编码器 input_blocks 第一层添加 guided_hint,避免后续层过度干预。

🔹 ControlNet VS ControlledUnetModel
模型
核心机制
是否使用 hint
影响位置
作用
UNetModel
经典 U-Net
仅依赖输入 x
ControlledUnetModel
允许 control 影响解码
✅ (可选)
仅影响 output_blocks
受控扩散模型,风格迁移
ControlNet
hint 直接影响编码器
✅ (必须)
仅影响 input_blocks 第一层
条件生成(草图,深度图)

DDIMSampler

DDIMSampler 主要用于 扩散模型(Diffusion Model)高效采样,采用 DDIM (Denoising Diffusion Implicit Models) 进行降噪,相比于传统 DDPM (Denoising Diffusion Probabilistic Model) 具有更快的采样速度,同时可以调整 eta 来控制生成样本的多样性。

🔹 DDIMSampler 主要结构
1️⃣ 初始化
  • 绑定 扩散模型 model,并读取 总时间步长 ddpm_num_timesteps。
  • schedule="linear" 代表时间步(timestep)的调度方式。

2️⃣ 预计算采样公式
  • 该函数预计算 DDIM 采样所需的时间步、方差、累计 α (alphas_cumprod) 等参数。
  • ddim_eta=0.0 对应 确定性采样(即 DDIM 采样)。
  • ddim_eta>0.0 时会引入 随机性(向 DDPM 采样靠近)。
关键参数
  • 计算 累计噪声方差 sqrt_one_minus_alphas_cumprod,用于后续 从噪声恢复图像

3️⃣ 主要采样流程
🟢 sample() 入口
  • S: 采样步数(通常 S << self.ddpm_num_timesteps,即少量步数生成高质量图像)。
  • conditioning: 控制信号(如文本、深度图等)。
  • x_T: 初始噪声(如果 None,默认 torch.randn(shape))。
  • unconditional_guidance_scale: 文本控制权重(如 CLIP 指导)。
🟢 ddim_sampling() 采样核心
  • x_T=None 时,使用随机噪声 torch.randn(shape, device=device) 作为初始图像。
  • 时间步 timesteps 递减(由 T 逐步减少到 0)。
  • self.p_sample_ddim() 进行逐步去噪

4️⃣ 单步降噪 p_sample_ddim()
  • 计算预测噪声
  • 计算去噪后的 x_0:
  • 计算下一步 x_prev
其中:
  • dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t 计算噪声方向
  • noise = sigma_t * noise_like(...) 加权随机噪声(当 eta=0 时无噪声)。
eta 影响采样方程
DDIM 采样公式 中:
其中:
表示噪声尺度,由 eta 控制
  • 当 eta=0.0 时,,完全确定性采样。
  • 当 eta>0.0 时,,加入随机噪声 ,增加多样性。

🔹 DDIMSampler 的作用
1. 更快的采样
  • S << T,可使用 10~50 步采样,而 DDPM 需要 1000+ 步
2. 可调控的生成
  • eta=0.0:完全确定性采样(一致的结果)。
  • eta>0.0:增加随机性(提升多样性)。
3. 支持文本引导(Guidance)
  • unconditional_guidance_scale 控制文本影响程度。

🔹 结论
DDIM 采样器扩散模型中高效的采样方法,可以大幅提升推理速度,并允许调整多样性,使得 扩散模型生成更加灵活高效

Models

ControlNet里面模型的继承思路是DDPM→ LDM→其他。

DDPM

DDPM (Denoising Diffusion Probabilistic Models) 解析
DDPM(去噪扩散概率模型),它是一种基于扩散过程的生成模型。DDPM 通过向数据添加噪声进行训练,然后通过学习逆过程去噪,从随机噪声生成清晰的图像。

代码核心结构
1️⃣ 初始化 (__init__)
作用:初始化 DDPM 关键参数,并定义模型结构。
  • unet_config:U-Net 结构的配置文件,扩散模型通常基于 U-Net 进行训练。
  • timesteps=1000:扩散过程的时间步数(T),表示扩散链的长度。
  • beta_schedule="linear":用于控制扩散过程的噪声增加方式,常见的有线性 (linear) 或余弦 (cosine) 。
  • loss_type="l2":损失函数选择,默认为 L2损失(均方误差 MSE)
  • parameterization="eps":指示模型的预测目标:
  • "eps":预测加噪的噪声 ε(最常用)。
  • "x0":直接预测无噪声图像 x₀ 。
  • "v":预测 x₀ 和 ε 的加权组合。

2️⃣ 噪声调度 (register_schedule)
作用:定义噪声的扩散调度,即 β 和 α 的计算方式。
关键变量:
  • betas:表示每个时间步添加的噪声量(βₜ)。
  • alphas = 1 - betas:表示保持原始数据的部分。
  • alphas_cumprod = np.cumprod(alphas, axis=0):累乘所有 αₜ,用于计算 q(x_t | x_0) 的均值。
这些变量决定了扩散过程的噪声模式,使模型能够从噪声数据中恢复出原始数据。

3️⃣ 前向传播 (forward)
作用:计算训练过程中的损失。
  • t = torch.randint(0, self.num_timesteps, (x.shape[0],))
  • 这行代码随机选择 t,表示在扩散链中某个时间步对图像 x 进行加噪。
  • self.p_losses(x, t, *args, **kwargs):
  • 计算训练损失,核心方法如下:
其中:
  • x_noisy = self.q_sample(x_start, t, noise):生成加噪后的数据 x_t。
  • model_out = self.model(x_noisy, t):让 U-Net 预测噪声(或 x0)。
计算损失
如果 parameterization == "eps",那么 model_out 代表的是噪声 ε,损失函数就是:
计算 L2 Loss:

4️⃣ 前向扩散过程 (q_sample)
作用:在训练过程中,把真实数据 x_0 逐步加噪,生成 x_t。
  • x_t = sqrt(alpha_cumprod) * x_0 + sqrt(1 - alpha_cumprod) * noise
    • 这个公式来自于 前向扩散公式
  • sqrt(alphas_cumprod) 控制 x_0 贡献的部分
  • sqrt(1 - alphas_cumprod) 控制噪声的比例。

5️⃣ 反向去噪 (p_sample)
作用:在推理阶段,通过去噪逐步恢复数据。
  • model_mean:当前 x_t 估计出的均值 μ_t。
  • model_log_variance:估计出的方差 Σ_t。
  • noise:采样的高斯噪声。
  • 计算:
其中:
  • μ_t = model_mean
  • σ_t = exp(0.5 * model_log_variance)
  • ε = noise

6️⃣ 图像生成 (p_sample_loop)
作用:从纯噪声开始,通过多步去噪生成样本。
  • 输入:随机噪声 img = torch.randn(shape, device=device)。
  • 去噪:逐步从 x_T 逆扩散到 x_0。
  • 返回:最终去噪后的 img。

总结
📌 DDPM 的核心流程
1. 训练阶段
  • 从 x_0 开始,逐步加噪 x_t = sqrt(alphas_cumprod) * x_0 + sqrt(1 - alphas_cumprod) * noise。
  • 让 U-Net 学习 ,即预测 x_0 或 ε。
2. 推理阶段
  • 从 x_T(纯噪声)开始,逆扩散回 x_0。
  • 逐步去噪 x_t = model_mean + sigma * noise,最终生成清晰图像。

LDM

LatentDiffusion 继承自 DDPM(Denoising Diffusion Probabilistic Models),是 稳定扩散模型(Stable Diffusion) 及类似框架的核心。它结合了扩散模型与 VAE(变分自编码器),在潜空间(latent space)中进行扩散,从而降低计算成本并提高生成质量。

📌 1. 核心思想
与标准 DDPM 直接在像素空间进行扩散不同,LatentDiffusion:
1. 首先使用 VAE(Autoencoder)将图像编码到潜变量 z,然后在潜变量上进行扩散。
2. 扩散过程 仍然遵循 DDPM 逻辑,但在 更低维的潜空间 中进行,减少计算复杂度。
3. 反扩散后,VAE 将 z 解码回图像,生成最终结果。
这个和Dreamer是一个思路

📌 2. 代码结构
① 初始化 (__init__)
主要参数:
  • first_stage_config:定义 VAE 编码器/解码器 的参数。
  • cond_stage_config:定义 条件模型(文本/图像等) 的参数。
  • num_timesteps_cond:定义 条件调度 的时间步数。
  • scale_factor:用于缩放潜变量 z,控制图像的动态范围。
初始化过程中:
1. 加载 DDPM 的扩散调度
2. 创建 VAE (first_stage_model) 和条件模型 (cond_stage_model)
3. 设置 conditioning_key,确定 U-Net 采用 拼接 (concat) 还是 交叉注意力 (cross-attention) 作为条件方式。

② 训练前预处理
作用:
  • 第一个训练 batch 之前,如果 scale_by_std=True,则:
  • 计算 z 的标准差,并 动态缩放 scale_factor 以标准化分布。

③ VAE 编码 & 解码
作用:
  • encode_first_stage(x):将 x 通过 VAE 编码 得到 z。
  • decode_first_stage(z):将 z 解码回图像,最终生成。

④ 训练输入 (get_input)
作用:
1. 获取 x 并编码为 z
2. 获取条件信息 c
  • 若 conditioning_key = "crossattn",则使用文本编码 c_crossattn。
  • 若 conditioning_key = "concat",则使用额外的图像 c_concat 作为拼接条件。

⑤ 计算损失 (p_losses)
扩散过程:
1. 从 x_start 生成 x_noisy,模拟 t 时刻的噪声图像。
2. U-Net 预测 ε 或 x0,从 x_noisy 还原干净图像。
3. 计算损失
  • 如果 parameterization = "eps",则 预测噪声 ε,损失函数为 MSE(ε_pred, ε)。
  • 如果 parameterization = "x0",则 直接预测 x0,损失为 MSE(x0_pred, x0)。

⑥ 采样 (p_sample_loop & sample)
作用
  • 通过 p_sample_loop() 从纯噪声 x_T 逆扩散回 x_0,最终生成图像。

⑦ 采样过程 (p_sample)
核心方程
其中:
  • μ_t 由 model_mean 计算。
  • σ_t 由 model_log_variance 计算。
  • ε 为高斯噪声。

⑧ 额外特性
💡 1. 无条件采样 (get_unconditional_conditioning)
  • 用于 classifier-free guidance(无分类标签引导)。
  • 生成 空白条件 以提高采样质量。
💡 2. 进阶采样 (progressive_denoising)
  • 逐步去噪并 记录中间状态,可用于可视化整个扩散过程。

📌 3. 总结
💡 LatentDiffusion(LDM) 结合了 扩散模型(DDPM)+ VAE(变分自编码器)+ 条件输入(文本/图像)
  • 通过 VAE 降维:在潜变量 z 上运行扩散,提高效率。
  • U-Net 预测 ε 或 x0:指导采样过程。
  • 支持文本 & 图像条件
    • c_crossattn(文本条件)
    • c_concat(额外图像条件)
  • 最终生成高质量图像

LDM 是 Stable Diffusion 的核心框架! 🚀
  • 比 DDPM 更高效(在潜变量上运行)。
  • 支持文本-图像扩散(如 Stable Diffusion)。
  • 采样方式丰富(DDIM、progressive_denoising)。

ControledLDM

ControlLDM 是一个基于 Latent Diffusion Model (LDM)受控扩散模型,它在标准 LDM 基础上加入了 ControlNet 控制机制,用于在扩散过程中施加额外的引导(control hints),以便更好地控制生成图像的结构和内容。

🔹 ControlLDM 的主要改进
1. 加入 ControlNet
  • 额外增加 control_model,用于处理控制信息(例如边缘检测、深度图等)。
  • 通过 control_key 指定输入数据中的控制信息字段。
  • only_mid_control 控制是否仅在中间层施加控制。
2. 自定义输入处理
  • 在 get_input 方法中,额外提取 control hints(如边缘检测、深度信息)。
    • 这些控制数据被转换为 通道优先格式(b c h w),并送入 ControlNet。
3. 修改 apply_model 方法
  • apply_model 负责模型前向传播。
  • ControlNet 生成 控制向量 并施加到 diffusion_model 之中。
  • 计算方式:
  • 这样,ControlNet 影响 LDM 的采样过程,使得生成结果更符合控制信息。
4. 日志记录 log_images
  • 记录重建图像控制条件去噪过程等信息,以便可视化 LDM 的行为。
  • 计算 unconditional guidance,用于 CFG 采样(Classifier-Free Guidance)。
5. 优化器 configure_optimizers
  • 只优化 control_model,保持 LDM 其他部分不变(sd_locked 控制是否锁定 Stable Diffusion 组件)。
  • 使用 AdamW 进行优化。
6. 低显存模式 low_vram_shift
  • 在推理时释放部分显存,将不必要的组件移到 CPU,提高大模型的可用性。

🔹 代码解析
组件
作用
control_model
额外的 ControlNet,提供额外的控制信号
get_input
解析输入,并提取 control 作为额外的条件
apply_model
结合 ControlNet 的输出,影响扩散模型
log_images
记录生成过程中的各个阶段,便于调试
configure_optimizers
只优化 ControlNet,不影响 LDM 主体
low_vram_shift
在推理时减少显存占用,提高效率
简明机器学习 Easy Machine Learning时间序列分析 Time Series Analysis
Loading...