简介:北京大学的一堂学术报告幻灯片,围绕“生成未必理解”这一核心命题,探讨基于扩散模型能否真正实现视觉世界模型,面向关注大模型视觉生成与理解关系的研究者、算法工程师和高年级学生。资源共一个PPTX幻灯片文件,压缩包大小约10.05MB,内容以学术讲稿形式呈现,便于直接阅读、批注与二次整理。报告先由费曼“我不能创造的,我不理解”引入,比较扩散模型与生成对抗网络在图像合成上的优劣,再用灰狼幼崽数量变化、跑步机上反向运动等典型反例,说明生成结果可能并不具备场景逻辑一致性;随后对比扩散模型与视觉理解模型在建模方式、参数规模上的差异,探讨生成与理解两条路径是否完全割裂,并提出统一多模态世界模型的可能方向。目前已有九十八人学习,读者借助这份讲稿,可以系统把握扩散模型从二零二零年至今的演进脉络,也能获得关于生成模型理解边界与未来研究路径的启发。
1. 生成未必理解:一个让视觉模型“露馅”的命题
2023 年我接过一个机器人抓取项目:用视频扩散模型预测下一帧画面,再交给机械臂执行。预测结果极其惊艳,桌面上的杯子在生成帧里平滑地“移动”,但机械臂真去抓时,十个里有九个是空的。原因很直白——扩散模型学会了像素级别的平均运动轨迹,却没有建立“杯子被挤压会碎、手伸过去会被挡住”的物理因果。这正是“2025北京大学:生成未必理解:基于扩散模型能否实现视觉世界模型”这个讲座标题背后的核心张力:生成模型的强项是分布逼近,而视觉世界模型要求的却是结构化推理。“生成”与“理解”在训练目标和评价体系上存在系统性错位。这篇文章想拆开这层错位:扩散模型走到哪一步算理解了物理世界,缺的又是什么。
2. 扩散模型和 manifold 的关系:生成为什么强,理解为什么弱
扩散模型的核心假设是数据点集中在一个远低于像素空间维度的低维流形上。加噪是把数据往流形外推,去噪则是把它拉回流形。模型学到的本质是一个从噪声分布到数据分布的确定性映射——或者更准确地说,是学到了这个流形的局部切空间结构。潜在扩散模型之所以比像素空间扩散更快、更稳,也是因为它在低维潜空间里操作,流形结构更平整,优化路径更短。
但问题在于,流形上的邻近关系并不等于语义上的因果邻近。扩散模型和 manifold 的关系更像“聚类”而非“理解”:它知道哪些样本在视觉特征上相近,却不知道一个物体被遮挡后应该以怎样的物理规律继续存在。生成质量高,说明模型的流形覆盖得好;生成结果有物理常识,才说明模型真正理解了世界结构。这两件事在当前的扩散模型框架里并没有被强制绑定。
2.1 扩散模型原理的快速重述:从 DDPM 到 Flow Matching
扩散模型原理的现代表述已经不止 DDPM 那一套离散加噪去噪。现在做视觉世界模型,我更推荐直接看 Flow Matching 或 Rectified Flow 那套连续时间公式,因为它把推理步数从几十步压到了几步,而且理论解释更干净——模型学的是从噪声分布到数据分布的常微分方程速度场,而不是每一步噪声的估计。
import torch
import torch.nn as nn
class SimpleFlowMatching(nn.Module):
def __init__(self, unet):
super().__init__()
self.unet = unet
def compute_loss(self, x_0, t, noise):
# x_0: 真实数据, 形状 (B, C, H, W)
# t: 时间步, 均匀采样自 [0, 1], 形状 (B,)
# noise: 高斯噪声, 与 x_0 同形状
x_1 = noise
# 线性插值: x_t = (1-t) * x_0 + t * x_1
x_t = (1 - t[:, None, None, None]) * x_0 + t[:, None, None, None] * x_1
# 真实速度场就是 x_1 - x_0
target = x_1 - x_0
# 模型预测速度场
v_pred = self.unet(x_t, t)
return torch.mean((v_pred - target) ** 2)
这段代码的核心是速度和位置的关系。模型不再预测噪声,而是预测从数据到噪声的直线运动方向,即速度场。训练时任意采样时间步,模型被迫学习整个插值轨迹上的运动模式,而不仅仅是最终落点。相比 DDPM 的分步去噪,它的好处是推理时可以用更粗的步长解常微分方程,采样效率高一个数量级。
2.2 生成的强项是插值,不是外推
任何扩散模型本质上都在做密度估计和插值。世界模型的最基本能力是预测——给一段过去,生成一段未来。生成一段从未出现过的物理轨迹,属于外推。外推要求模型理解“推动一个杯子,它沿桌面滑行,撞到墙停住”这类事件的内在逻辑,而不仅仅是帧间像素的连续性。当前扩散模型对外推的结构性短板集中体现在一个地方:损失函数是逐像素的 L2 或 LPIPS,它天然偏好模糊平均,而不是锐利的因果结果。我见过太多预测未来帧的项目,生成帧数越长,画面越糊,物理事件越不存在——这正是插值逻辑在时间维度上的失效。
3. 用扩散模型搭建视觉世界模型:架构、训练目标与最小实现
视觉世界模型用扩散模型做,常见做法是在视频扩散模型基础之上加一个条件接口:过去帧作为条件,未来帧作为生成目标。架构上一般选 DiT(Diffusion Transformer)而非 U-Net,因为 Transformer 的注意力机制可以自然地建模跨帧长程依赖,而 U-Net 的归纳偏置偏向局部纹理。
3.1 训练范式的选择:级联生成与联合训练
最稳妥的落地方案是“潜在空间级联”:第一步把视频压缩到潜空间,第二步在潜空间做扩散预测。直接用像素空间训练长视频,显存会很快爆炸,即使使用梯度检查点也不划算。常见实现路径是:
- 用一个预训练的 VAE(如 Stable Video Diffusion 的时空编码器)把视频帧压成潜空间 token。
- 在潜空间上训练 DiT 做条件生成。
- 生成的潜空间 token 再通过 VAE 解码回像素空间。
联合训练的优势是让压缩和生成协同优化,但实践中梯度不稳定问题极其明显。我一般会选择分开训练:先冻结 VAE,训练 DiT,等模型生成质量稳定后再解冻 VAE 低层做微调。这么做的好处是失败排查时维度清晰——生成模糊是 DiT 的问题,解码失真才是 VAE 的问题。
3.2 最小训练实现:一个视频 DiT 的核心 forward
import torch
import torch.nn as nn
from einops import rearrange
class VideoDiTBlock(nn.Module):
def __init__(self, hidden_dim, num_heads, num_frames):
super().__init__()
self.norm1 = nn.LayerNorm(hidden_dim)
self.attn = nn.MultiheadAttention(hidden_dim, num_heads, batch_first=True)
self.norm2 = nn.LayerNorm(hidden_dim)
self.ffn = nn.Sequential(
nn.Linear(hidden_dim, hidden_dim * 4),
nn.GELU(),
nn.Linear(hidden_dim * 4, hidden_dim)
)
# 时间位置编码,让模型感知当前帧在序列中的位置
self.temporal_embed = nn.Embedding(num_frames, hidden_dim)
def forward(self, x, frame_ids):
# x: (B, T, N, D) T=帧数, N=每帧token数
B, T, N, D = x.shape
# 把时间和空间合并到同一维度做注意力
x = rearrange(x, 'B T N D -> B (T N) D')
# 添加时间位置编码
t_embed = self.temporal_embed(frame_ids) # (T, D)
x = x + t_embed.unsqueeze(1)
# 空间注意力
x = x + self.attn(self.norm1(x), self.norm1(x), self.norm1(x))[0]
# 前馈网络
x = x + self.ffn(self.norm2(x))
x = rearrange(x, 'B (T N) D -> B T N D', T=T)
return x
# 使用示例
B, T, N, D = 2, 8, 1024, 768
x = torch.randn(B, T, N, D)
frame_ids = torch.arange(T).repeat(B, 1).flatten()
model = VideoDiTBlock(D, num_heads=12, num_frames=T)
out = model(x, frame_ids)
print(out.shape) # torch.Size([2, 8, 1024, 768])
这段实现有三个关键设计点。第一,注意力机制覆盖了所有帧的所有 token,模型能看到任意两帧之间任意两个位置的关联,这是建立时序因果的基础。第二,时间位置编码是必需的——没有它,模型无法区分第 2 帧和第 7 帧,生成结果会像一团混合体。第三,这里刻意没有做因果掩码,因为预测未来帧时,过去帧和未来帧的依赖关系比较复杂,全注意力让模型自己学习哪些信息该看、哪些不该看。
3.3 损失函数的三个必调参数
仅仅用 L2 损失训练是不够的,生成出的未来帧会快速退化为模糊均值。我通常在损失函数里叠加三个项,收敛效果和生成质量都明显改善:
| 损失项 | 权重 | 作用 | 典型范围 |
|---|---|---|---|
| L2 重建损失 | 1.0 | 保证像素级保真度 | 固定 1.0 |
| LPIPS 感知损失 | 0.1 | 保持细节结构和语义一致性 | 0.05 - 0.2 |
| 光流一致性损失 | 0.05 | 约束相邻帧的运动连续性,避免跳变 | 0.02 - 0.1 |
光流一致性损失是我强烈建议加的。用预训练光流模型(如 RAFT)计算相邻帧之间的光流,然后要求生成帧的光流与真实帧的光流对齐。这个损失项对物理合理性的影响远大于像素损失——它强迫模型生成“物体不是瞬移的”这一类基础物理规律。
第二个必调参数是 loss 的梯度裁剪阈值。视频扩散模型的梯度尺度波动极大,通常设为 1.0,太高会导致训练不稳定。第三个必调参数是条件帧的数量。条件越多,模型越偏向复制条件帧的纹理,生成新内容的自由度下降。常见做法是条件帧数和生成帧数保持 1:2 或 1:4 的比例,条件过多会退化成插帧模型。
4. 评测“理解”:未来帧预测之外的分水岭实验
评测是“生成未必理解”的核心战场。如果只把生成的未来帧和真实未来帧算 PSNR,扩散模型与超越均值函数几乎没有差别。心理物理感知上,人也无法仅靠一帧未来帧的高相似度来判断模型是否理解世界。因此需要设计另一类评测任务,它们要求模型具有内隐的物理一致性——这些问题无法从训练分布中直接复制。
4.1 三类评测任务和对应指标
| 评测维度 | 典型测试 | 关键指标 | 及格线参考 |
|---|---|---|---|
| 物体恒存 | 物体被遮挡后继续生成存在性 | 恒存率(透明度下降则扣分) | 大于 85% |
| 反事实推理 | 改变初始条件后重生成未来帧 | 与对照组结果差异的区分度 | 大于 0.5 |
| 时序一致性 | 生成视频倒放后运动是否合理 | 倒放置信度 | 不小于正向播放置信度的一半 |
物体恒存评测最容易实现,也是最能区分“生成”和“理解”的测试。把一段视频前 6 帧作为条件,生成未来 24 帧,其中第 10 帧起一个物体被人为遮挡。如果模型理解了物体恒存,它会在遮挡结束后继续生成该物体;如果模型仅仅是像素插值,物体大概率消失,或者出现诡异的半透明残影。这个实验不需要额外标注,成本极低,建议任何做视觉世界模型的项目先跑通它再谈别的。
4.2 过程评测:中间表征的“理解探针”
行为测试未必能覆盖全部理解能力,还应该做过程评测——检查模型内部是否已经形成了稳定的语义表征。具体操作是拿训练好的 DiT 中间层特征训练一个线性探针(linear probe),预测帧内物体的掩码。如果线性探针得分很低,说明模型虽然能生成高清视频,但它的内部表达中没有物体概念的可分离结构,更像是被蒸馏在噪声估计计算图里的视觉智能涌现,并不足以称为世界模型。
import torch
import torch.nn as nn
class ProbeHead(nn.Module):
def __init__(self, feature_dim, num_classes):
super().__init__()
self.linear = nn.Linear(feature_dim, num_classes)
def forward(self, features):
# features: (B, D),取 DiT 中间层的 pooled 特征
return self.linear(features)
# 训练线性探针
def train_probe(dit_model, data_loader, feature_layer=6, epochs=20):
# 冻结 DiT 参数,只训练探针头
for param in dit_model.parameters():
param.requires_grad = False
probe = ProbeHead(feature_dim=768, num_classes=21).cuda()
optimizer = torch.optim.AdamW(probe.parameters(), lr=1e-3)
for epoch in range(epochs):
for batch in data_loader:
x, mask = batch
with torch.no_grad():
features = dit_model.get_layer_output(x, layer=feature_layer)
loss = nn.CrossEntropyLoss()(probe(features), mask)
optimizer.zero_grad()
loss.backward()
optimizer.step()
return probe
线性探针的得分并不意味着模型“主动理解”,但它是必要而非充分条件的分水岭:如果中间层特征只是噪声,那么后续的行为理解能力无从谈起。评测结束后,要在报告里同时给出行为测试和探针测试的得分,前者反映“生成得好不好”,后者折射“内部结构是否已经编码语义”。这一点是判断这篇论文或项目是否在吹牛的关键。
4.3 评测时需要注意的三个“假阳性”
第一,反事实推理测试要严防模型偷看条件帧的纹理。如果条件帧和未来帧在浅层特征上有大量共享像素,模型只需模仿而不是真正推导,测试结果会虚高。第二,无物体遮挡的视频评测指标基本无意义——没有拓扑变化就没有物理推理。第三,生成视频如果用了 classifier-free guidance,评测时要把引导强度调到推理时的值,否则会掩盖时序一致性的真实水平。
5. 边界与进阶:扩散世界模型的三个底层限制和 15 分钟验证法
扩散模型做视觉世界模型有三个绕不开的边界:推理速度慢、因果结构缺失、输入输出对齐脆弱。
推理速度问题在实践中最明显——最先进的采样器也要 8 到 16 步才能稳定生成一帧,生成 30 帧视频意味着几百次网络前向传播,实时性差,远超一个实体世界模型该有的推理开销。这类瓶颈短期难以完全解决,常见的缓解思路是 Consistency Models 或一步生成器蒸馏,但代价是生成质量下降明显。
因果结构缺失是更深层的障碍。扩散模型的时间维度和环境的因果推进是两个概念,加噪过程中的时间步与物理时间没有对齐。而世界模型要求每一步的输出都依赖于上一步的物理状态,而扩散模型没有显式状态变量,因果推进全靠注意力机制隐式完成。这也是为什么直接拿扩散模型做机器人策略,往往需要额外接一层显式的状态估计器。
5.1 15 分钟验证法:先确认值得投资再继续
我不建议一开始就搭一个庞大的视频扩散世界模型训练流程。一个可复现的快速验证流程只需要约 15 分钟:
- 用一个 128x128 分辨率、时间窗 8 帧的视频数据集 —— 比如街景记录仪或桌面摄像头数据
- 训练一个标准的 LDM,条件为前 4 帧,生成后 4 帧
- 跑物体恒存测试,统计遮挡前后的恒存率
- 如果恒存率低于 60%,不要继续加大数据集 —— 这说明问题出在架构或损失函数上,问题不在数据规模
这套流程测的是模型是否具备理解基础,而不是生成视频的好看程度。我经历过好几次投入大量 GPU 资源之前、用这种快速验证发现架构缺陷的案例——省下的时间足够再做两个新的训练策略实验。
5.2 进阶方向:把显式物理先验注入扩散模型
如果快速验证通过,可以考虑下一步的架构升级:把显式物理先验注入扩散模型的生成过程。最轻量且有效的方式是在潜空间特征上加一个物理约束头,用简单的运动学方程(如匀加速直线运动)去约束相邻帧的潜变量偏移。具体地,在训练时对潜变量序列加一个额外损失项:
# 物理一致性正则: 保证相邻帧潜变量的二阶差分接近零(匀速运动的近似)
def physics_regularization(z_latent):
delta_1 = z_latent[:, 1:] - z_latent[:, :-1] # 一阶差分
delta_2 = delta_1[:, 1:] - delta_1[:, :-1] # 二阶差分
return torch.mean(delta_2 ** 2)
这个做法的物理假设是:短时间窗口里大多数运动近似匀速或匀加速,二阶差分惩罚会使模型天然偏向更平滑、更真实的运动轨迹。这种先验不是强加的物理引擎,而是给模型一个偏置,让它不容易跑出荒谬的物理路径。最终效果比单纯堆训练数据和网络层数强得多——后者能把生成视频做到像素上的完美,但物理上的荒谬感依然存在。
做到这一步,扩散模型作为视觉世界模型才有真正的立足点:它在像素密度估计层面提供生成能力,在潜变量层面通过物理正则获得时序一致性,在最上层通过评测体系的约束确认自身确实在“理解”,而不只是“生成”。这条路不快,但每一步都推着模型从“会画画的Transformer”走向“能预测世界的DiT”。
725



被折叠的 条评论
为什么被折叠?



