【扩散基础】基于分数的扩散模型

基于分数的扩散模型

(Score-Based Diffusion Model)

定义

高斯分布下:

噪声 ϵ 和 score 的关系

推导

这个公式的本质是:用 "噪声预测网络" 间接学习 "分数函数",从而将 "难以计算的边缘分数" 转化为 "可训练的噪声预测任务",最终实现扩散模型的反向去噪过程。

时间步

扩散模型的核心是模拟 "噪声添加(前向过程)" 和 "噪声移除(反向过程)",时间步(timestep)是这个过程的核心索引:

  • 离散扩散(如 LDM) :时间步是整数(如 0~1000),每个整数对应预定义的噪声调度参数(β/α/sigma),代表 "噪声添加的程度"(t=0 无噪声,t=1000 全噪声)。
  • 连续扩散(如 Flow Matching/Flux) :时间步是浮点数(通常∈[0,1]),描述 "从纯噪声(t=0)到真实数据(t=1)" 的连续流场过程,无预定义离散调度表,依赖分布采样。
  • 模型蒸馏:本质是 "将大模型(教师,多步)的能力压缩到小模型(学生,少步)",时间步需要适配 "学生步→教师步" 的映射。

LDM/Flow/ 蒸馏的采样差异,匹配这三种范式的底层逻辑:

  • LDM:离散时间步的均匀采样:其反向过程需要学习 "每个离散时间步的去噪函数"。为了让模型均匀学习所有噪声水平的去噪能力,必须均匀采样所有整数时间步(而非偏向某一区间)。

  • Flow 分支:连续时间步的分布采样:Flow-based Diffusion(如 Flux、Flow Matching)是连续扩散模型,核心是学习 "从噪声分布到数据分布的连续流场":

    • 时间步 t∈[0,1]:t=0 对应纯噪声,t=1 对应真实数据;

    • 时间步采样需贴合 "流场的优化目标":通常用logitnormal分布(而非均匀分布),因为 logitnormal 能让采样更偏向 t=0(高噪声)和 t=1(低噪声)区间,这两个区间是流场学习的关键。

分支 时间步类型 采样方式 底层原因
LDM 离散整数 均匀采样(分布式分组) 离散扩散需覆盖所有噪声水平,分布式分组提升训练效率
Flow 连续浮点数 分布采样 + 调度偏移 连续流场需贴合 logitnormal 分布,偏移强化复杂样本的高噪声阶段学习

SDE与ODE

正/反向SDE

采样路线

有一条结论:

给定一条 SDE,其边缘分布 pt 也可以由一条确定性的 ODE 产生 ,这条 ODE 就是probability flow ODE

区别

SDE/马尔可夫链

加噪 SDE(随机微分方程)和马尔可夫链是连续 - 离散的对应关系,核心是描述 "数据逐步加噪的过程":

flow matching

定义

不管是 diffusion 还是 flow matching,本质都是在干一件事:

从一个简单的先验分布(比如高斯)p0(x) 通过时间演化的动力系统,变成数据分布 p1(x)。

只不过:

  • Diffusion :用的是随机微分方程(SDE) / 反向 SDE / probability flow ODE ,学的是 score field

  • Flow matching :直接用一个常微分方程(ODE) ,学的是速度场(vector field),把"该往哪边流"拟合出来。

训练目标:flow matching loss

总结:flow matching 就是直接学"点在这个时间点应该朝哪个方向流动",而不是学 score。

例子

复制代码
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
 
# 超参数
dim = 2         # 数据维度(2D点:x坐标+sin(x)坐标)
num_samples = 1000  # 样本数量(生成1000个正弦曲线点)
num_steps = 50  # ODE数值求解的步数(欧拉法迭代次数)
lr = 1e-3       # 学习率(Adam优化器)
epochs = 5000   # 训练轮数
 
# 目标分布:正弦曲线上的点(x1坐标)
x1_samples = torch.rand(num_samples, 1) * 4 * torch.pi  # 0到4π的随机x坐标
y1_samples = torch.sin(x1_samples)                      # y=sin(x),对应x的正弦值
target_data = torch.cat([x1_samples, y1_samples], dim=1)  # 拼接成二维点 (x, sin(x))

# 噪声分布:高斯噪声(x0坐标)
noise_data = torch.randn(num_samples, dim) * 2  # 均值0、方差2的二维高斯噪声
 
class VectorField(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim + 1, 64),  # 输入维度: x (2维点) + t (1维时间) = 3
            nn.ReLU(),               # 非线性激活
            nn.Linear(64, dim)       # 输出维度: 2维向量(对应每个点的速度/方向)
        )

    def forward(self, x, t):
        # 拼接x(二维点)和t(时间),输入网络
        return self.net(torch.cat([x, t], dim=1))
        
model = VectorField()  # 初始化模型
optimizer = torch.optim.Adam(model.parameters(), lr=lr)  # 定义优化器
 
for epoch in range(epochs):
    # 随机采样噪声点和目标点(打乱顺序,避免数据顺序影响)
    idx = torch.randperm(num_samples)
    x0 = noise_data[idx]  # 起点:噪声点(t=0)
    x1 = target_data[idx] # 终点:正弦曲线点(t=1)

    # 随机采样时间t(形状:(batch_size, 1)),t∈[0,1]
    t = torch.rand(x0.size(0), 1)  

    # 线性插值生成中间点xt:xt = (1-t)*x0 + t*x1
    xt = (1 - t) * x0 + t * x1

    # 模型预测当前(xt, t)处的向量场
    vt_pred = model(xt, t)  

    # 目标向量场:vt_target = x1 - x0(线性插值的导数,xt对t求导)
    vt_target = x1 - x0

    # 损失函数:MSE(预测向量场 vs 真实向量场)
    loss = torch.mean((vt_pred - vt_target)**2)

    # 反向传播+参数更新
    optimizer.zero_grad()  # 清空梯度
    loss.backward()        # 计算梯度
    optimizer.step()       # 更新参数
 
x = noise_data[0:1]  # 选第一个噪声点作为初始点
trajectory = [x.detach().numpy()]  # 记录轨迹的列表

# 数值求解ODE(欧拉法)
t = 0
delta_t = 1 / num_steps  # 每步的时间增量(1/50)
with torch.no_grad():  # 禁用梯度计算(推理阶段)
    for i in range(num_steps):
        # 预测当前t时刻x点的向量场(速度)
        vt = model(x, torch.tensor([[t]], dtype=torch.float32))
        t += delta_t  # 时间步进
        x = x + vt * delta_t  # 欧拉法更新位置:x(t+Δt) = x(t) + v(t)*Δt
        trajectory.append(x.detach().numpy())  # 记录新位置

# 整理轨迹格式(去除冗余维度)
trajectory = torch.tensor(trajectory).squeeze()

# 打印最终点的x坐标(辅助验证)
print(trajectory[-1] / (torch.pi / 10 * 4))

# 绘制向量场和生成轨迹
plt.figure(figsize=(10, 5))
plt.scatter(target_data[:,0], target_data[:,1], c='blue', label='Target (sin(x))')  # 目标分布(正弦)
plt.scatter(noise_data[:,0], noise_data[:,1], c='red', alpha=0.3, label='Noise')    # 初始噪声
plt.plot(trajectory[:,0], trajectory[:,1], 'g-', linewidth=2, label='Generated Path')  # 生成轨迹
plt.legend()
plt.title("Flow Matching: From Noise to Target Distribution")
plt.show()

步骤 1:线性插值路径

定义从初始点 x0(t=0)到目标点 x1(t=1)的线性插值:xt​=(1−t)x0​+tx1​

  • t=0 → xt = x0(噪声点);

  • t=1 → xt = x1(正弦点);

  • t∈(0,1) → 中间插值点。

步骤 2:目标向量场(真实速度)

对插值路径求导,得到真实的向量场:dt/dxt​​=x1​−x0​:这就是 vt_target 的由来 ------ 线性插值的速度是恒定的(与 t 无关)。

步骤 3:训练目标

让神经网络 model 学习预测这个真实向量场:

  • 输入:插值点 xt + 时间 t

  • 输出:预测的向量场 vt_pred

  • 损失:MSE(vt_pred - vt_target),即让模型的预测尽可能接近真实速度。

步骤 4:优化

用 Adam 优化器最小化损失,让模型学会在任意时间 t、任意插值点 xt 处,输出正确的移动方向 / 速度。

运行结果:

看起来好像不太准,尝试把epochs增大到50000后:可以看出来生成的路径是从一个随机noise点往target曲线上移动的。

推理(采样)

连续性方程

某点的概率密度随时间变多/变少 = 从周围流入/流出的概率质量。写成数学就是:本地变化 + 净流出 = 0。

依据:总概率守恒,不会凭空生出概率,也不会凭空消失,只能流动。

对比diffusion

相关推荐
AI生成未来1 天前
ICCV 2025 | 北大王选所推出AnyPortal:像素级操控视频背景,前景细节100%保留!
人工智能·扩散模型·视频编辑·视频生成
m0_650108242 天前
DiffusionDrive:面向端到端自动驾驶的截断扩散模型
论文阅读·扩散模型·端到端自动驾驶·阶段扩散策略·高级联扩散解码器·cvpr2025
AI生成未来2 天前
南洋理工&腾讯最新Rolling Forcing解决流视频生成长期误差累积,连贯如一且长达数分钟!
aigc·扩散模型·视频生成·长视频
m0_650108242 天前
DiffVLA:视觉语言引导的扩散规划在自动驾驶中的创新与实践
自动驾驶·扩散模型·多模态融合·端到端规划·混合稀疏-稠密感知模块·vlm命令引导·截断扩散
人邮异步社区4 天前
想要系统地学习扩散模型,应该怎么去做?
人工智能·学习·程序员·扩散模型
PixelMind20 天前
【超分辨率专题】HYPIR:扩散模型先验与 GAN 对抗训练相结合的新型图像复原框架
人工智能·生成对抗网络·扩散模型·图像复原
m0_650108241 个月前
【论文精读】AVID:基于扩散模型的任意长度视频修复
人工智能·扩散模型·论文精读·视频修复·时序一致性·任意时长·结构引导
m0_650108241 个月前
【论文精读】DreamVideo:定制化主体与动作的视频生成技术
扩散模型·论文精读·定制化视频生成·主体学习与动作学习·轻量级适配器·文本引导生成·阿里千问实验室
m0_650108241 个月前
【论文精读】EMO:弱条件下基于 Audio2Video 扩散模型的表情肖像视频生成
aigc·扩散模型·论文精读·音频到视频生成·会说话的头部·弱条件