各种发展与微调的导视

小白也能读懂的AIGC扩散(Diffusion)模型系列讲解

理解扩散模型的损失函数

Diffusion Model-5fe36152097b1c082b80d358a493f153.jpgDiffusion Model-b8add416990e6d1ee31698b86010225d.jpg

Diffusion Model-image-7.png

SD中的U-Net的结构(核心知识)

深入浅出完整解析Stable Diffusion(SD)核心基础知识

Diffusion Model-SD中的UNet.jpg

上图中包含Stable Diffusion U-Net的十四个基本模块:

  1. GSC模块:Stable Diffusion U-Net中的最小组件之一,由GroupNorm+SiLU+Conv三者组成。

  2. DownSample模块:Stable Diffusion U-Net中的下采样组件,使用了Conv(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))进行采下采样。

  3. UpSample模块:Stable Diffusion U-Net中的上采样组件,由插值算法(nearest)+Conv组成。

  4. ResNetBlock模块:借鉴ResNet模型的“残差结构”,让网络能够构建的更深的同时,将Time Embedding信息嵌入模型。

  5. CrossAttention模块:将文本的语义信息与图像的语义信息进行Attention机制,增强输入文本Prompt对生成图片的控制。

  6. SelfAttention模块:SelfAttention模块的整体结构与CrossAttention模块相同,这是输入全部都是图像信息,不再输入文本信息。

  7. FeedForward模块:Attention机制中的经典模块,由GeGlU+Dropout+Linear组成。

  8. BasicTransformer Block模块:由LayerNorm+SelfAttention+CrossAttention+FeedForward组成,是多重Attention机制的级联,并且也借鉴ResNet模型的“残差结构”。通过加深网络和多Attention机制,大幅增强模型的学习能力与图文的匹配能力。

  9. Spatial Transformer模块:由GroupNorm+Conv+BasicTransformer Block+Conv构成,ResNet模型的“残差结构”依旧没有缺席。

  10. DownBlock模块:由两个ResNetBlock模块组成。

  11. UpBlock_X模块:由X个ResNetBlock模块和一个UpSample模块组成。

  12. CrossAttnDownBlock_X模块:是Stable Diffusion U-Net中Encoder部分的主要模块,由X个(ResNetBlock模块+Spatial Transformer模块)+DownSample模块组成。

  13. CrossAttnUpBlock_X模块:是Stable Diffusion U-Net中Decoder部分的主要模块,由X个(ResNetBlock模块+Spatial Transformer模块)+UpSample模块组成。

  14. CrossAttnMidBlock模块:是Stable Diffusion U-Net中Encoder和ecoder连接的部分,由ResNetBlock+Spatial Transformer+ResNetBlock组成。

(1)时间步t是怎么嵌入到扩散模型的?

Time Embedding正是输入到ResNetBlock模块中,为U-Net引入了时间信息(时间步长T,T的大小代表了噪声扰动的强度),模拟一个随时间变化不断增加不同强度噪声扰动的过程,让SD模型能够更好地理解时间相关性。

由于在每个ResNetBlock模块中都有Time Embedding,就能告诉U-Net现在是整个迭代过程的哪一步,并及时控制U-Net够根据不同的输入特征和迭代阶段而预测不同的噪声残差。

Diffusion 模型(如 DDPM、DDIM、Stable Diffusion、DiT 等)中,时间步 tt 表示扩散过程的第几步(noise level),它控制着模型当前需要处理的噪声强度。模型必须知道 tt 才能“去噪”正确。

嵌入时间步 tt 的主要流程是:

  1. 把离散时间步 t 通过 正弦/余弦位置编码,转为连续向量(time embedding)

  2. time embedding经过一个小的 MLP(例如两层 Linear + 激活函数)映射为与特征通道数一致的向量

  3. 加到 U-Net/Transformer 的每一层(通常是残差块)中


  1. 时间步嵌入的原理

最常用的做法是 正弦/余弦位置编码(sinusoidal positional encoding),类似 Transformer 的位置编码。 公式:

Diffusion Model-image-4.png

  • 这样可以把一个标量 tt 编成 dd-维向量(通常 d=128d = 128 或 256256)。

  • 然后通过一个 MLP(两层全连接+激活)映射到模型内部通道维度(如 320、768)。


  1. 代码示例(PyTorch, 来自 DDPM/Stable Diffusion)

import torch
import torch.nn as nn
import math

class SinusoidalTimeEmbedding(nn.Module):
    def __init__(self, embedding_dim):
        super().__init__()
        self.embedding_dim = embedding_dim

    def forward(self, t):
        """
        t: [batch_size]  时间步标量
        return: [batch_size, embedding_dim]  正弦余弦编码
        """
        half_dim = self.embedding_dim // 2
        # 生成频率 [0, 1, 2, ...]
        freqs = torch.exp(
            -math.log(10000) * torch.arange(0, half_dim, dtype=torch.float32) / half_dim
        ).to(t.device)
        # [batch_size, half_dim]
        angles = t[:, None].float() * freqs[None]
        emb = torch.cat([torch.sin(angles), torch.cos(angles)], dim=-1)
        return emb  # shape: [B, embedding_dim]


class TimeEmbeddingMLP(nn.Module):
    def __init__(self, time_dim, model_channels):
        super().__init__()
        self.time_mlp = nn.Sequential(
            nn.Linear(time_dim, model_channels * 4),
            nn.SiLU(),  # 或 nn.Swish()
            nn.Linear(model_channels * 4, model_channels)
        )

    def forward(self, t):
        """
        输入: t [B] 时间步
        输出: [B, model_channels]
        """
        return self.time_mlp(t)

# 例子:把时间步嵌入到 U-Net
t = torch.randint(0, 1000, (8,))  # batch_size=8
time_embed = SinusoidalTimeEmbedding(128)(t)  # [8, 128]
time_cond = TimeEmbeddingMLP(128, model_channels=320)(time_embed)  # [8, 320]

  1. 嵌入后如何用?

时间步嵌入 不会单独输入网络,而是加到每个残差块(ResBlock)的中间层

# 伪代码
class ResBlock(nn.Module):
    def __init__(self, in_channels, out_channels, time_emb_dim):
        super().__init__()
        self.time_proj = nn.Linear(time_emb_dim, out_channels)
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1)
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)

    def forward(self, x, t_emb):
        # t_emb: [B, time_emb_dim]
        h = self.conv1(x)
        # 加入时间步
        h = h + self.time_proj(t_emb)[:, :, None, None]
        h = torch.relu(h)
        h = self.conv2(h)
        return h

这样,每一层卷积都知道当前是哪个扩散步骤 tt。


  1. 其他方式(Transformer-based DiT)

DiT (Diffusion Transformer) 里,时间步 tt 会被嵌入成一个向量,然后:

  • 加到 patch embedding 上(类似位置编码)

  • 或者作为 Adaptive LayerNorm / FiLM 的调制因子


(2)多模态实现的关键:条件控制&Cross-Attention

LDM(Latent Diffusion Model)实现多模态主要通过条件控制机制,核心思想就是把所有模态的信息 编码成统一向量空间,再输入扩散网络做反向去噪。

  • 图片通过视觉编码器(Vision Encoder)转成特征向量。

  • 文本通过语言编码器(Text Encoder)转成特征向量。

这些特征向量会被注入到 U-Net(核心去噪网络)的不同层,实现条件控制。

它并不是直接在像素空间中处理图像,而是在潜空间(latent space)中进行扩散与生成,并通过条件编码器将不同模态信息(文本、图像、语音等)映射到同一个潜空间中来实现跨模态控制。 U-Net + Cross-Attention

  • LDM 多模态主要依赖 Cross-Attention 层

    • Q:来自潜空间特征(待生成图像的表示)

    • K/V:来自条件模态(如文本编码器的输出)

在Stable Diffusion中主要使用了Text Encoder部分。CLIP Text Encoder模型将输入的文本Prompt进行编码,转换成Text Embeddings(文本的语义信息),通过前面章节提到的U-Net网络的CrossAttention模块嵌入Stable Diffusion中作为Condition条件,对生成图像的内容进行一定程度上的控制与引导,目前SD模型使用的的是CLIP ViT-L/14中的Text Encoder模型。

(3)scheduler

扩散模型中的scheduler

讲解一些比较新的scheduler的一篇文章

在了解schedulers之前,首先,我们要区分Diffusion里的scheduler和pytorch里的scheduler,前者是一个采样器(samplers),用于把噪声图像还原为原始图像;后者是一个学习率策略(例如余弦退火策略,指数衰减策略等)

Diffusion的scheduler,它的功能是实现逆向扩散,直观上理解的话它就是一个采样器,循环多个step把噪声图像逐渐还原为原始图像。根据采样方式不同,scheduler也有许多版本,包括DDPM,DDIM,DPM++ 2M Karras等。

注意:我们把去噪的过程定义为采样。使用采样的方法,称之为采样器。

Diffusion Model-image-6.png

ddpm

DDPM一个最大的缺点是需要设置较长的扩散步数才能得到好的效果,这导致了生成样本的速度较慢

ddim

不再限制前向(扩散)过程必须是一个Markov链,这使得DDIM可以采用更小的采样步数来加速生成过程

哪个Schedulers最好?

目前还没有公认的最好的schedulers

  1. 如果想生成的更快,图像收敛(即种子一样,每次生成的固定),质量相对还可以的话,推荐:

  • DPM++ 2M Karras with 20-30 steps

  • UniPC with 20-30 steps

  1. 如果想要更好的图片质量,不考虑是否收敛,推荐:

  • DPM++ SDE Karras with 10-15 steps。(更慢,但是质量更好)

  • DDIM with 10-15 steps。

区分Noise scheduler

Diffusion Model-image-3.png

(4)推理步数(steps、num_inference_steps或者Sampling steps)

num_inference_steps表示SD系列模型在推理过程中的去噪次数或者采样步数。一般来说,我们可以设置num_inference_steps在20-50之间,其中设置的采样步数越大,图像的生成效果越好,但同时生成所需的时间就越长。

到这里大家可能会有疑问,为什么SD系列模型在训练时设置1000的noise scheduler,在推理时却只用设置20-50的noise scheduler?

这是因为,虽然SD模型在训练时参照DDPM采样方法,但推理时可以使用DDIM这个采样方法,DDIM通过去马尔可夫化,让SD模型在推理时可以进行“跳步”,抽取短的子序列作为noise scheduler,大大减少了推理步数。

当然的,除了使用DDIM采样方法,我们也可以使用其他的采样方法,目前主流的采样方法有DPM系列、DPM++系列、Euler系列、LMS系列、Heun、UniPC、Restart等。

(5)CFG (Classifier-Free Guidance)

Classifier-Free Guidance(CFG)是一种在生成模型(尤其是扩散模型,如Stable Diffusion)中常用的技巧,用于在生成过程中控制条件信息的影响力,从而生成更符合指定条件(比如文本提示)的样本。

Diffusion Model-image-5.png

guidance_scale权重:当设置的guidance_scale越大时,文本的控制力会越强,SD模型生成的图像会和输入文本更一致。

通常guidance_scale可以设置在7-8.5之间,就会有不错的生成效果。

如果使用非常大的guidance_scale值(比如11-12),生成的图像可能会过饱和,同时多样性会降低。

CFG的核心是 在训练阶段,让模型即学会有条件生成,又学会无条件生成(在训练时以一定概率drop掉text embedding)。然后在采样阶段通过两者组合来强化条件效果。

Diffusion Model-image-2.png

其中w代表guidance_scale,当w越大时,输入文本起的作用越大,即生成的图像更和输入文本一致,当被设置为时,图像的生成是无条件的,输入文本会被忽略。

(6)Negative Prompt

我们可以使用Negative Prompt来避免生成我们不想要的内容,从而改善图像生成效果。

(7)interppolate 上采样块

1. 在图像处理中的 interpolate

最常见的应用是图像缩放/重采样

  • 上采样 (Upsampling):把图像尺寸变大(如 64×64 → 256×256)。

  • 下采样 (Downsampling):把图像尺寸变小(如 256×256 → 64×64)。

在 PyTorch 中,你可能见过:

import torch.nn.functional as F x_resized = F.interpolate(x, size=(256, 256), mode='bilinear')

这里 mode 决定插值算法:

  • nearest:最近邻插值(最简单,像素块放大)

  • bilinear:双线性插值(常用于图像)

  • bicubic:双三次插值(效果更平滑)

  • trilinear:三线性插值(3D 数据)


2. 在序列数据中的 interpolate

  • 处理时间序列缺失值:用插值方法填补缺失点。

  • 模型预测时,为了平滑曲线,也会用插值。

例如:

import torch x = torch.tensor([1., 2., 4.]) x_interp = torch.interpolate(x, ...) # 插值后得到 1, 2, 3, 4


图像可以看作高维随机变量

Diffusion Model-image-1.png

CLIP 模型

CLIP模型是一个基于对比学习的多模态模型,主要包含Text Encoder和Image Encoder两个模型。其中Text Encoder用来提取文本的特征,可以使用NLP中常用的text transformer模型作为Text Encoder;而Image Encoder主要用来提取图像的特征,可以使用CNN/Vision transformer模型(ResNet和ViT等)作为Image Encoder。与此同时,他直接使用4亿个图片与标签文本对数据集进行训练,来学习图片与本文内容的对应关系。

与U-Net的Encoder和Decoder一样,CLIP的Text Encoder和Image Encoder也能非常灵活的切换,庞大图片与标签文本数据的预训练赋予了CLIP强大的zero-shot分类能力。

CLIP在训练时,从训练集中随机取出一张图片和标签文本,接着CLIP模型的任务主要是通过Text Encoder和Image Encoder分别将标签文本和图片提取embedding向量,然后用余弦相似度(cosine similarity)来比较两个embedding向量的相似性,以判断随机抽取的标签文本和图片是否匹配,并进行梯度反向传播,不断进行优化训练

完成CLIP的训练后,输入配对的图片和标签文本,则Text Encoder和Image Encoder可以输出相似的embedding向量,计算余弦相似度就可以得到接近1的结果。同时对于不匹配的图片和标签文本,输出的embedding向量计算余弦相似度则会接近0。

就这样,CLIP成为了计算机视觉和自然语言处理这两大AI方向的“桥梁”,从此AI领域的多模态应用有了经典的基石模型。

Diffusion Model-image.png

LDM

博客1

为了避免AE压缩出的latent space过于发散(high variance),通常会把latent space的特征分布用KL散度对齐到标准正态空间。

相近的说法:

为了避免隐空间较大的方差,引入了KL正则化或者VQ正则化项。

  • 获得了通用压缩模型,其潜在空间可用于训练多个生成模型,也可用于其他下游应用,例如单图像CLIP引导合成。