在上一章节《Stable Diffusion 3.5 开发指南(一):模型获取与调用》中,我们详细介绍了 Stable Diffusion 3.5 的模型获取方法、API 调用流程以及手动调用步骤。本章将深入解析 Stable Diffusion 3.5 的核心生成机制——Flow Matching,帮助开发者从根本上理解模型的训练原理与推理过程,为后续的模型优化和自定义开发奠定基础。

1 Flow Matching 的理论概述与核心思想

1.1 基本概念

Flow Matching(流匹配)是一种基于连续归一化流(Continuous Normalizing Flows, CNFs)的生成建模方法,其核心在于学习一个时间依赖的向量场 vt(x)v_t(x)vt(x),通过常微分方程(ODE)描述数据从简单分布(如噪声)到目标分布(如图像)的连续变换过程 。该方法避免了传统扩散模型中计算昂贵的模拟步骤,显著提升训练效率 。

1.2 核心思想

Flow Matching 的核心思想是将生成建模问题转化为“学习驱动数据沿连续概率路径流动的向量场”:

连续概率路径:在时间区间 t∈[0,1]t \in [0,1]t[0,1] 上定义一条从简单分布(如高斯噪声 x0x_0x0)到目标数据分布 x1x_1x1 的连续路径 xtx_txt,刻画数据的演化过程。

时间依赖向量场:通过学习向量场 vt(x)v_t(x)vt(x),描述每个时间点上数据点沿路径的变化方向和速度,即满足微分方程:

dxtdt=vt(xt,t)\frac{dx_t}{dt} = v_t(x_t, t)dtdxt=vt(xt,t)

简言之,Flow Matching 的目标是让模型学会“沿着连续路径流动”的正确向量场,以实现高效、一步生成数据的能力


2 数学推导与公式细节

2.1 流匹配(Flow Matching, FM)

条件概率路径:给定数据点 x1∼p1x_1 \sim p_1x1p1 和噪声 x0∼p0x_0 \sim p_0x0p0,条件路径定义为:
xt=t⋅x1+(1−t)⋅x0 x_t = t \cdot x_1 + (1-t) \cdot x_0 xt=tx1+(1t)x0
条件向量场:对 xtx_txt 求导得目标向量场:
v=dxtdt=x1−x0 v = \frac{dx_t}{dt} = x_1 - x_0 v=dtdxt=x1x0

2.2 损失函数

FM 的损失函数通过最小化预测向量场与目标向量场的均方误差:
LCFM=Et,x1,x0∥vθ(xt,t)−(x1−x0)∥2 \mathcal{L}_{\text{CFM}} = \mathbb{E}_{t, x_1, x_0} \left\| v_\theta(x_t, t) - (x_1 - x_0) \right\|^2 LCFM=Et,x1,x0vθ(xt,t)(x1x0)2

其中,vθv_\thetavθ就是我们要训练的模型, t∼U(0,1)t \sim \mathcal{U}(0,1)tU(0,1)x0∼N(0,I)x_0 \sim \mathcal{N} (0,I)x0N(0,I)

2.3 推理过程

在流匹配(Flow Matching)模型中,推理(或称为采样)过程是通过在时间轴上逆向执行学习到的向量场来完成的。具体而言,我们将连续的时间区间 t∈[0,1]t \in [0, 1]t[0,1] 离散化为 NNN 个等间隔的时间步,然后从先验分布(通常是标准高斯分布)中抽取初始样本,并按照学习到的向量场 vθv_\thetavθ 逐步迭代更新样本,最终生成目标数据分布的样本。

① 时间离散化

为了进行数值积分,我们将时间区间 ([0, 1]) 均匀分割为 ( N ) 份,得到一系列时间点:
t0=0<t1<t2<⋯<tN−1<tN=1 t_0 = 0 < t_1 < t_2 < \dots < t_{N-1} < t_N = 1 t0=0<t1<t2<<tN1<tN=1
其中,每个时间步的间隔为:
Δt=1N \Delta t = \frac{1}{N} Δt=N1
因此,时间点可以表示为:
tk=k⋅Δt,k=0,1,…,N t_k = k \cdot \Delta t, \quad k = 0, 1, \dots, N tk=kΔt,k=0,1,,N

② 逆向采样过程

推理过程从 t=0t = 0t=0开始,到t=1t = 1t=1结束。在训练中,我们定义了从数据x1x_1x1 到噪声x0x_0x0的前向过程xt=tx1+(1−t)x0x_t = t x_1 + (1-t) x_0xt=tx1+(1t)x0,而在推理时,我们需要执行反向过程:从噪声x0x_0x0出发,逐步逼近数据x1x_1x1

初始化:从先验分布 p0=N(0,I)p_0 = \mathcal{N}(0, I)p0=N(0,I)中采样初始噪声:
xt0∼N(0,I) x_{t_0} \sim \mathcal{N}(0, I) xt0N(0,I)

迭代更新:对于 k=0,1,…,N−1k = 0, 1, \dots, N-1k=0,1,,N1,我们按照以下步骤更新样本:

  1. 计算当前时间点tkt_ktk对应的向量场输出:
    vθ(xtk,tk) v_\theta(x_{t_k}, t_k) vθ(xtk,tk)
  2. 使用欧拉方法(或其他数值积分方法)更新样本,以逼近下一个时间点 tk+1=tk+Δtt_{k+1} = t_k + \Delta ttk+1=tk+Δt 的样本:
    xtk+1=xtk+Δt⋅vθ(xtk,tk) x_{t_{k+1}} = x_{t_k} + \Delta t \cdot v_\theta(x_{t_k}, t_k) xtk+1=xtk+Δtvθ(xtk,tk)

终止:当迭代进行到k=N−1k = N-1k=N1 时,我们得到 xtN=x1x_{t_N} = x_1xtN=x1,即生成的样本。

综上所述,推理过程的算法可以描述如下:

算法:流匹配模型推理(采样)

  1. 输入

    • 训练好的向量场模型vθ(x,t)v_\theta(x, t)vθ(x,t)
    • 时间步数 NNN
    • 先验分布p0=N(0,I)p_0 = \mathcal{N}(0, I)p0=N(0,I)
  2. 初始化

    • 设置时间步长Δt=1N\Delta t = \frac{1}{N}Δt=N1
    • p0p_0p0 中采样初始噪声:x0∼N(0,I)x_0 \sim \mathcal{N}(0, I)x0N(0,I)
  3. 循环:对于k=0k = 0k=0N−1N-1N1

    • 计算当前时间 tk=k⋅Δtt_k = k \cdot \Delta ttk=kΔt
    • 计算向量场:vk=vθ(xk,tk)v_k = v_\theta(x_k, t_k)vk=vθ(xk,tk)
    • 更新样本:xk+1=xk+Δt⋅vkx_{k+1} = x_k + \Delta t \cdot v_kxk+1=xk+Δtvk
  4. 输出:生成的样本 xNx_NxN

  • 数值积分方法:上述过程使用了最简单的欧拉方法进行数值积分。在实际应用中,可以根据需要选择更高阶的方法(如Heun方法、Runge-Kutta方法等)以提高精度和稳定性。
  • 时间步数NNNNNN的选择会影响生成样本的质量和计算效率。较大的 NNN通常能产生更准确的样本,但也会增加计算成本。
  • 与训练的一致性:在训练中,我们通过最小化条件流匹配损失来学习向量场 vθv_\thetavθ,使其在任意时间点ttt和目标x1x_1x1条件下能够预测正确的方向。推理过程则是该学习结果的实际应用,通过逆向积分将噪声转化为数据。

通过这种离散化的推理过程,流匹配模型能够从简单的噪声分布出发,生成符合复杂数据分布的样本。

2.4 直观类比:高中物理的速度问题

为了帮助理解流匹配的推理过程,我们可以用一个简单的高中物理例子来类比。

① 问题设定

假设一辆小车从起点 x0x_0x0 出发,要到达终点 x1x_1x1。我们知道:

  • 起点x0=0x_0 = 0x0=0 米(初始位置)
  • 终点x1=100x_1 = 100x1=100 米(目标位置)
  • 总时间T=10T = 10T=10

② 匀速运动的情况

如果是匀速直线运动:

  • 速度v=x1−x0T=10010=10v = \frac{x_1 - x_0}{T} = \frac{100}{10} = 10v=Tx1x0=10100=10 米/秒
  • 位置随时间变化xt=x0+v⋅t=0+10tx_t = x_0 + v \cdot t = 0 + 10txt=x0+vt=0+10t

在任意时刻 ttt,我们都能准确知道小车的位置。

③ 变速运动的情况(类比流匹配)

但现实中,小车可能不是匀速运动。假设我们不知道具体的运动规律,但有一个智能速度计(类比我们的模型 vθv_\thetavθ),它能够根据:

  • 当前时间 ttt
  • 当前位置 xtx_txt

告诉我们当前瞬时速度 vθ(xt,t)v_\theta(x_t, t)vθ(xt,t)

推理过程

  1. 时间离散化:把10秒分成10个时间段,每段 Δt=1\Delta t = 1Δt=1

  2. 初始状态t0=0t_0 = 0t0=0 秒,x0=0x_0 = 0x0=0

  3. 逐步更新

    • t0=0t_0 = 0t0=0 秒时,速度计显示 v0=12v_0 = 12v0=12 米/秒
      那么1秒后的位置:x1=x0+v0⋅Δt=0+12×1=12x_1 = x_0 + v_0 \cdot \Delta t = 0 + 12 \times 1 = 12x1=x0+v0Δt=0+12×1=12

    • t1=1t_1 = 1t1=1 秒时,速度计显示 v1=9v_1 = 9v1=9 米/秒
      那么2秒后的位置:x2=x1+v1⋅Δt=12+9×1=21x_2 = x_1 + v_1 \cdot \Delta t = 12 + 9 \times 1 = 21x2=x1+v1Δt=12+9×1=21

    • t2=2t_2 = 2t2=2 秒时,速度计显示 v2=11v_2 = 11v2=11 米/秒
      那么3秒后的位置:x3=x2+v2⋅Δt=21+11×1=32x_3 = x_2 + v_2 \cdot \Delta t = 21 + 11 \times 1 = 32x3=x2+v2Δt=21+11×1=32

    • … 继续这个过程…

    • t9=9t_9 = 9t9=9 秒时,速度计显示 v9=8v_9 = 8v9=8 米/秒
      那么10秒后的位置:x10=x9+v9⋅Δtx_{10} = x_9 + v_9 \cdot \Delta tx10=x9+v9Δt

  4. 最终结果:经过10次迭代,我们得到 x10≈100x_{10} \approx 100x10100

④ 与流匹配的对应关系

物理问题 流匹配模型
起点 x0x_0x0 噪声样本 x0∼N(0,I)x_0 \sim \mathcal{N}(0,I)x0N(0,I)
终点 x1x_1x1 目标数据样本 x1∼p1x_1 \sim p_1x1p1
时间 t∈[0,10]t \in [0,10]t[0,10] 时间 t∈[0,1]t \in [0,1]t[0,1]
智能速度计 v(t,xt)v(t, x_t)v(t,xt) 神经网络 vθ(xt,t)v_\theta(x_t, t)vθ(xt,t)
位置更新 xk+1=xk+vk⋅Δtx_{k+1} = x_k + v_k \cdot \Delta txk+1=xk+vkΔt 样本更新 xk+1=xk+vθ⋅Δtx_{k+1} = x_k + v_\theta \cdot \Delta txk+1=xk+vθΔt
总时间分段数 N=10N = 10N=10 时间步数 NNN

⑤ 关键理解点

  1. 模型的作用:我们的模型 vθv_\thetavθ 就像那个"智能速度计",它不需要知道整个运动轨迹,只需要在任意时刻告诉我们"现在应该往哪个方向走,走多快"。

  2. 训练的目标:在训练时,我们让模型学会在任意中间位置 xtx_txt 和时刻 ttt 时,都能正确预测"应该朝向终点的速度"。

  3. 推理的过程:就像我们一步步用速度计读数来推算小车位置一样,我们从噪声开始,一步步用模型的预测来更新样本,最终生成目标数据。

  4. 为什么有效:因为模型在训练时已经学会了整个"运动规律",所以在推理时,即使我们从完全不同的起点(噪声)开始,模型也能引导我们走到正确的终点(真实数据分布)。

这种类比让我们理解:流匹配的推理本质上是一个逐步导航的过程,模型在每个时间步提供方向指导,带领样本从简单的噪声分布"走"到复杂的数据分布


3 MNIST 数据集Flow Matching 代码实战

3.1 环境和依赖配置

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import os
from tqdm import tqdm

说明

  • torch:PyTorch主库,用于深度学习计算。
  • nnnn.functional:神经网络模块与常用操作。
  • DataLoaderdatasetstransforms:数据加载与预处理。
  • matplotlib.pyplot:可视化生成样本。
  • tqdm:训练进度条显示。
  • os:文件与目录操作。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
  • 自动选择 GPU(可用时)或 CPU 进行训练和推理。

3.2 超参数配置

class Config:
    # 训练参数
    batch_size = 128
    learning_rate = 1e-3
    num_epochs = 100
    gradient_clip = 1.0

    # 模型参数
    image_size = 32
    in_channels = 1
    out_channels = 1

    # 保存路径
    save_dir = "flow_matching_mnist"
    model_save_path = "flow_matching_model.pth"

说明

  • batch_sizelearning_ratenum_epochs:训练参数。
  • image_sizein_channelsout_channels:模型输入输出尺寸。
  • save_dirmodel_save_path:保存模型和生成样本路径。
os.makedirs(config.save_dir, exist_ok=True)
  • 创建保存目录,如果已存在不会报错。

3.3 数据加载与预处理

def get_dataloaders():
    transform = transforms.Compose([
        transforms.Resize((config.image_size, config.image_size)),
        transforms.ToTensor(),
        transforms.Normalize((0.,), (1.,)),
    ])

    train_dataset = datasets.MNIST(
        root='./data',
        train=True,
        download=True,
        transform=transform
    )

    train_loader = DataLoader(
        train_dataset,
        batch_size=config.batch_size,
        shuffle=True,
        num_workers=2
    )

    return train_loader

说明

  1. Resize:调整 MNIST 图片为 32x32。
  2. ToTensor:转换为 PyTorch 张量。
  3. Normalize:将像素归一化到 [0,1](简单归一化)。
  4. DataLoader:分批加载,支持多线程。

3.4 Flow Matching 模型定义

from diffusers import UNet2DModel

class FlowMatchingModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.unet = UNet2DModel(
            sample_size=config.image_size,
            in_channels=config.in_channels,
            out_channels=config.out_channels,
            layers_per_block=2,
            block_out_channels=(64, 128, 256, 512),
            down_block_types=("DownBlock2D", "DownBlock2D", "DownBlock2D", "DownBlock2D"),
            up_block_types=("UpBlock2D", "UpBlock2D", "UpBlock2D", "UpBlock2D"),
        )

    def forward(self, x, t):
        batch_size = x.shape[0]
        t = t * torch.ones(batch_size, device=x.device)
        return self.unet(x, t).sample

说明

  • 使用 UNet 架构预测向量场 vθ(xt,t)v_\theta(x_t, t)vθ(xt,t)
  • forward 函数扩展时间步 ttt 到 batch 大小,并返回预测向量场。

3.5 Flow Matching 损失函数

def correct_flow_matching_loss(model, x1):
    batch_size = x1.shape[0]

    t = torch.rand(batch_size, device=x1.device)
    t = t.view(-1, 1, 1, 1)  # 扩展到与x相同维度

    x0 = torch.randn_like(x1)

    xt = (1 - t) * x0 + t * x1

    target_vector_field = x1 - x0

    pred_vector_field = model(xt, t.squeeze())

    loss = F.mse_loss(pred_vector_field, target_vector_field, reduction='mean')

    return loss

说明

  • 随机采样时间步 t∼U(0,1)t \sim \mathcal{U}(0,1)tU(0,1)
  • 插值:xt=(1−t)x0+tx1x_t = (1-t)x_0 + t x_1xt=(1t)x0+tx1
  • 目标向量场:ut=x1−x0u_t = x_1 - x_0ut=x1x0
  • 使用均方误差计算预测向量场与目标向量场差距。

3.6 训练函数

def train_model():
    model = FlowMatchingModel().to(device)
    optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=config.num_epochs)

    train_loader = get_dataloaders()
    losses = []
    model.train()

    for epoch in range(config.num_epochs):
        epoch_loss = 0.0
        progress_bar = tqdm(train_loader, desc=f'Epoch {epoch + 1}/{config.num_epochs}')

        for batch_idx, (data, _) in enumerate(progress_bar):
            data = data.to(device)
            loss = correct_flow_matching_loss(model, data)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            epoch_loss += loss.item()
            progress_bar.set_postfix({'Loss': f'{loss.item():.6f}'})

        scheduler.step()
        avg_loss = epoch_loss / len(train_loader)
        losses.append(avg_loss)

        print(f'Epoch {epoch + 1}/{config.num_epochs}, Average Loss: {avg_loss:.6f}, LR: {scheduler.get_last_lr()[0]:.8f}')

        if (epoch + 1) % 10 == 0:
            torch.save({
                'epoch': epoch,
                'model_state_dict': model.state_dict(),
                'optimizer_state_dict': optimizer.state_dict(),
                'loss': avg_loss,
            }, os.path.join(config.save_dir, f'model_epoch_{epoch + 1}.pth'))
            generate_samples(model, epoch + 1, num_samples=16)

    torch.save(model.state_dict(), os.path.join(config.save_dir, config.model_save_path))

    plt.figure(figsize=(10, 5))
    plt.plot(losses)
    plt.title('Training Loss')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.savefig(os.path.join(config.save_dir, 'training_loss.png'))
    plt.close()

    return model, losses

说明

  • 使用 AdamW 优化器 + CosineAnnealingLR 学习率调度。
  • 每个 epoch 循环训练数据,计算 Flow Matching 损失并反向传播。
  • 每 10 个 epoch 保存模型并生成样本。
  • 绘制训练损失曲线。

3.7 采样函数(欧拉法)

def generate_samples(model, epoch=None, num_samples=16, steps=100):
    """
    使用欧拉法采样生成图像
    参数:
        model: 训练好的 Flow Matching 模型
        epoch: 当前训练轮数(用于保存可视化图片)
        num_samples: 生成样本数量
        steps: 时间离散步数 N
    """
    model.eval()  # 推理模式,不计算梯度

    with torch.no_grad():
        # 1️⃣ 从标准正态分布初始化噪声 x0 ~ N(0,I)
        x = torch.randn(num_samples, config.in_channels, config.image_size, config.image_size, device=device)

        # 2️⃣ 离散化时间步 t ∈ [0,1]
        timesteps = torch.linspace(0.0, 1.0, steps + 1, device=device)

        # 3️⃣ 欧拉方法积分
        for i in range(steps):
            t = timesteps[i]  # 当前时间点
            dt = timesteps[i + 1] - timesteps[i]  # 时间步长 Δt

            # 4️⃣ 模型预测向量场 vθ(x_t, t)
            vector_field = model(x, t)

            # 5️⃣ 欧拉步更新: x_{t+dt} = x_t + vθ(x_t, t) * dt
            x = x + vector_field * dt

        # 6️⃣ 可视化生成样本
        fig, axes = plt.subplots(4, 4, figsize=(8, 8))
        for i, ax in enumerate(axes.flat):
            if i < num_samples:
                img = x[i].cpu().squeeze().numpy()
                ax.imshow(img, cmap='gray')
                ax.axis('off')

        title = f'Generated Samples (Epoch {epoch})' if epoch else 'Generated Samples'
        plt.suptitle(title)
        plt.tight_layout()
        filename = f'samples_epoch_{epoch}.png' if epoch else 'final_samples.png'
        plt.savefig(os.path.join(config.save_dir, filename))
        plt.close()

    return x

说明

  • 从标准正态分布初始化 x0∼N(0,I)x_0 \sim N(0,I)x0N(0,I)
  • 时间步从 0 到 1 离散化。
  • 欧拉法积分:xt+dt=xt+vθ(xt,t)⋅dtx_{t+dt} = x_t + v_\theta(x_t, t) \cdot dtxt+dt=xt+vθ(xt,t)dt
  • 生成 16 张图像进行可视化。

3.8 改进采样函数(Heun 方法)

def generate_samples_improved(model, epoch=None, num_samples=16, steps=100, method='euler'):
    """
    改进采样函数,支持欧拉法或 Heun 方法
    参数:
        method: 'euler' 或 'heun'
    """
    model.eval()

    with torch.no_grad():
        # 1️⃣ 初始化噪声
        x = torch.randn(num_samples, config.in_channels, config.image_size, config.image_size, device=device)

        # 2️⃣ 离散化时间
        timesteps = torch.linspace(0.0, 1.0, steps + 1, device=device)

        if method == 'euler':
            # 3️⃣ 欧拉法
            for i in range(steps):
                t = timesteps[i]
                dt = timesteps[i + 1] - timesteps[i]
                vector_field = model(x, t)
                x = x + vector_field * dt

        elif method == 'heun':
            # 3️⃣ Heun 方法(二阶 Runge-Kutta)
            for i in range(steps):
                t = timesteps[i]
                dt = timesteps[i + 1] - timesteps[i]

                # 3a️⃣ 预测步 (Euler预测)
                k1 = model(x, t)
                x_pred = x + k1 * dt

                # 3b️⃣ 校正步 (计算下一个向量场)
                k2 = model(x_pred, t + dt)

                # 3c️⃣ 更新状态,取平均作为更高精度的预测
                x = x + (k1 + k2) * dt / 2

        # 4️⃣ 可视化生成样本
        fig, axes = plt.subplots(4, 4, figsize=(8, 8))
        for i, ax in enumerate(axes.flat):
            if i < num_samples:
                img = x[i].cpu().squeeze().numpy()
                ax.imshow(img, cmap='gray')
                ax.axis('off')

        title = f'Generated Samples (Epoch {epoch}, {method})' if epoch else f'Generated Samples ({method})'
        plt.suptitle(title)
        plt.tight_layout()
        filename = f'samples_epoch_{epoch}_{method}.png' if epoch else f'final_samples_{method}.png'
        plt.savefig(os.path.join(config.save_dir, filename))
        plt.close()

    return x

说明

  • 支持欧拉法和 Heun(二阶 Runge-Kutta)方法。
  • Heun 方法可以提高数值积分精度,减少误差。

3.9 模型加载函数

def load_model(model_path):
    model = FlowMatchingModel().to(device)
    model.load_state_dict(torch.load(model_path, map_location=device))
    return model
  • 加载保存的模型权重用于推理。

3.10 主函数

def main():
    model_path = os.path.join(config.save_dir, config.model_save_path)

    if os.path.exists(model_path):
        print("Loading pre-trained model...")
        model = load_model(model_path)
        print("Generating samples with pre-trained model...")
        generate_samples(model, num_samples=16)
        generate_samples_improved(model, num_samples=16, method='heun')
    else:
        print("Training new model...")
        model, losses = train_model()
        print("Generating final samples...")
        generate_samples(model, num_samples=16)
        generate_samples_improved(model, num_samples=16, method='heun')


if __name__ == "__main__":
    main()

说明

  • 检查是否存在预训练模型:

    • 有:直接加载并生成样本。
    • 无:训练新模型并生成样本。

3.11 实验结果

最终实验结果


4 总结

  • 核心思想:Flow Matching 将生成建模为从噪声到数据的连续路径,通过学习时间依赖向量场 vθ(xt,t)v_\theta(x_t, t)vθ(xt,t) 引导样本沿 ODE 运动。

  • 损失函数:最小化预测向量场与目标向量场 MSE:

  • 推理过程

    1. 从噪声 x0∼N(0,I)x_0 \sim \mathcal{N}(0,I)x0N(0,I) 出发
    2. 时间离散化 t0,…,tNt_0, \dots, t_Nt0,,tN
    3. 欧拉/Heun 更新:xt+Δt=xt+vθ(xt,t)⋅Δtx_{t+\Delta t} = x_t + v_\theta(x_t, t) \cdot \Delta txt+Δt=xt+vθ(xt,t)Δt
    4. 得到最终生成样本 x1x_1x1
  • 关键理解:模型像“智能速度计”,在每个时间步给出方向和速度,引导噪声逐步逼近目标分布。

  • 代码实现

    • 数据:MNIST,归一化 32×32
    • 模型:UNet 预测向量场
    • 训练:随机采样 (t)、MSE 损失、AdamW + CosineAnnealingLR
    • 采样:支持欧拉法和 Heun 方法,生成样本可视化
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐