30秒快速回答: 扩散模型的核心思想极其优雅——「先破坏,再重建」。训练时,给一张清晰图片逐步加噪声直到变成纯噪点;推理时,从纯噪点开始逐步去噪恢复到清晰图片。AI 在这个过程中学会了「把噪声变成图片」,这就是 Stable Diffusion 能根据文字生成画面的本质。


扩散模型的核心直觉

训练阶段(加噪声):
  清晰图 → 轻度雾化 → 中度模糊 → 重度噪点 → 纯噪点
  [注: 这是特意设计的破坏过程,教 AI 「坏掉的图长什么样」]

推理阶段(去噪声):
  纯噪点 → 猜下一帧 → 猜下一帧 → ... → 清晰图
  [AI 在做的事:每步预测一步噪声并减去,逐步恢复图像]

这个过程的数学本质是「学习从噪声分布到数据分布的转换」。


扩散模型的数学原理(极简版)

前向扩散(Forward Diffusion)

图像 x₀ 经过 T 步加噪,变成近似白噪声 x_T:

x_t = √(α_t) × x_{t-1} + √(1 - α_t) × ε

其中 ε 是高斯噪声,α_t 是随时间递减的系数

反向去噪(Reverse Denoising)

从噪声 x_T 开始,逐步去噪恢复图像:

核心任务:预测每一步的噪声 ε_t

loss = ||ε - ε_pred||²  (预测噪声与真实噪声的平方差)

训练一个 U-Net 网络来做这件事:
  输入:(加噪后的图像 x_t, 时间步 t)
  输出:预测的噪声 ε_pred

为什么预测「噪声」而不是「图像」?

这是一个巧妙的设计选择——预测噪声比直接预测清晰图像容易得多:

预测清晰图:从完全模糊重建,信息极度不足
预测噪声:每一步只预测「这一步加的噪声是什么」
→ 任务简化,训练更稳定

扩散模型的架构(U-Net)

输入: x_t (加噪图) + t (时间步)
  ↓
┌──────────────────────────────────┐
│          U-Net                    │
│                                   │
│  下采样(编码):                  │
│  输入 → Conv → Down → ... → 瓶颈  │
│                                   │
│  上采样(解码):                  │
│  瓶颈 → Up → Conv → ... → 输出    │
│                                   │
│  跳跃连接(skip-connection):     │
│  每层下采样 → 对应层上采样        │
└──────────────────────────────────┘
  ↓
输出: ε_pred (预测的噪声)

实战:从零实现一个简单扩散模型

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

# ==========================================
# 1. 扩散调度器:定义加噪过程
# ==========================================
class DiffusionScheduler:
    def __init__(self, timesteps=1000, beta_start=1e-4, beta_end=0.02):
        self.timesteps = timesteps
        self.betas = torch.linspace(beta_start, beta_end, timesteps)
        self.alphas = 1.0 - self.betas
        self.alpha_bars = torch.cumprod(self.alphas, dim=0)

    def add_noise(self, x_0, t):
        """给干净图像加噪"""
        sqrt_alpha_bar = self.alpha_bars[t].sqrt().view(-1,1,1,1)
        sqrt_one_minus_alpha_bar = (1 - self.alpha_bars[t]).sqrt().view(-1,1,1,1)
        epsilon = torch.randn_like(x_0)
        x_t = sqrt_alpha_bar * x_0 + sqrt_one_minus_alpha_bar * epsilon
        return x_t, epsilon

# ==========================================
# 2. 简化的 U-Net 去噪网络
# ==========================================
class SimpleUNet(nn.Module):
    def __init__(self, in_channels=1, time_emb_dim=32):
        super().__init__()
        # 时间嵌入(告诉网络当前是第几步)
        self.time_mlp = nn.Sequential(
            nn.Linear(1, time_emb_dim),
            nn.ReLU(),
            nn.Linear(time_emb_dim, time_emb_dim)
        )

        # 编码器
        self.conv1 = nn.Conv2d(in_channels, 64, 3, padding=1)
        self.conv2 = nn.Conv2d(64, 128, 3, padding=1)

        # 时间调节
        self.time_proj1 = nn.Linear(time_emb_dim, 64)
        self.time_proj2 = nn.Linear(time_emb_dim, 128)

        # 解码器
        self.deconv1 = nn.ConvTranspose2d(128, 64, 3, padding=1)
        self.deconv2 = nn.ConvTranspose2d(64, in_channels, 3, padding=1)

    def forward(self, x, t):
        # 时间嵌入
        t_emb = self.time_mlp(t.float().unsqueeze(-1))

        # 编码
        h1 = F.relu(self.conv1(x) + self.time_proj1(t_emb).unsqueeze(-1).unsqueeze(-1))
        h2 = F.relu(self.conv2(h1) + self.time_proj2(t_emb).unsqueeze(-1).unsqueeze(-1))

        # 解码
        h3 = F.relu(self.deconv1(h2))
        out = self.deconv2(h3)

        return out  # 预测的噪声

# ==========================================
# 3. 训练扩散模型
# ==========================================
device = "cuda" if torch.cuda.is_available() else "cpu"
model = SimpleUNet().to(device)
scheduler = DiffusionScheduler(timesteps=1000)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(100):
    for x_0 in dataloader:  # x_0: 真实图像
        x_0 = x_0.to(device)

        # 随机采样时间步
        t = torch.randint(0, 1000, (x_0.shape[0],)).to(device)

        # 加噪
        x_t, true_noise = scheduler.add_noise(x_0, t)

        # 预测噪声
        pred_noise = model(x_t, t)

        # 噪声预测损失
        loss = F.mse_loss(pred_noise, true_noise)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f"Epoch {epoch+1}: loss = {loss.item():.4f}")

print("训练完成!")

# ==========================================
# 4. 采样生成新图像
# ==========================================
@torch.no_grad()
def sample(model, scheduler, img_size=(1, 28, 28)):
    """从纯噪声逐步去噪生成图像"""
    model.eval()
    x = torch.randn((1, *img_size)).to(device)  # 纯噪声

    for t in reversed(range(scheduler.timesteps)):
        t_batch = torch.full((1,), t, device=device)
        pred_noise = model(x, t_batch)

        # DDIM 采样公式
        alpha = scheduler.alphas[t]
        alpha_bar = scheduler.alpha_bars[t]
        beta = scheduler.betas[t]

        if t > 0:
            noise = torch.randn_like(x)
        else:
            noise = 0

        x = (1 / alpha.sqrt()) * (x - (beta / (1 - alpha_bar).sqrt()) * pred_noise) + beta.sqrt() * noise

    return x.clamp(0, 1)

generated_img = sample(model, scheduler)

主流扩散模型产品

模型 类型 参数 特点
Stable Diffusion 3 图像生成 8B 开源,社区生态最丰富
DALL-E 3 图像生成 未公开 文字理解强,与 ChatGPT 集成
Midjourney v6 图像生成 未公开 美学质量最高
FLUX.1 图像生成 12B 原 SD 团队作品,提示跟随极强
Sora 视频生成 未公开 基于 Diffusion Transformer (DiT)
Kling 2 视频生成 未公开 中文提示理解好

扩散模型 vs 其他生成模型

维度 Diffusion GAN VAE
生成质量 最高 中高
训练稳定性 稳定 不稳定 稳定
推理速度 慢(多步) 快(一步)
多样性 低(模式坍缩)
可控性

常见问题

Q: 扩散模型为什么需要这么多步推理?

因为每一步只做「微小去噪」,需要逐步迭代。这是扩散模型质量高的代价。2025-2026 年的蒸馏技术(如 SDXL-Turbo、LCM)已将步数从 50 降到 1-4 步。

Q: Diffusion Transformer (DiT) 是什么?

用 Transformer 替代 U-Net 的扩散架构。Sora 就基于 DiT——Transformer 的 scaling 能力更强,适合视频这种高维数据。

Q: 扩散模型和 Stable Diffusion 有什么关系?

Stable Diffusion 是「潜在空间扩散模型」——不在像素空间扩散,而是先用 VAE 压缩到小得多的潜在空间再扩散。这大幅降低了计算量。


下一步建议: 用 diffusers 库加载 stable-diffusion-v1-5,尝试用不同 text prompt 生成图片,体会「文字控制扩散方向」的魔法。