转载自公众号:敢敢AUTOHUB
0. 简介
Nano World Models 讨论的不是一个更大的视频生成模型,而是一个更适合研究者拆开实验的世界模型代码库。过去一年里,交互式视频生成和世界模拟器进展很快,但很多系统的训练数据、模型结构、采样策略和下游任务被绑在一起,读论文能知道它“效果很强”,真正想改一个 action injection、换一个 latent space、复现实验指标时,往往要重写半条 pipeline。Nano World Models 的价值在这里:它把未来视频预测拆成可替换的配置轴,让目标函数、模型规模、动作注入、潜空间、数据集、评测和长时域 rollout 都能在同一接口下比较。
论文地址:https://arxiv.org/abs/2605.23993
GitHub主页:https://simchowitzlabpublic.github.io/nano-world-model
读这张图时不要只看“支持很多任务”,更应该看它背后的接口统一:不同环境的数据先被编码成 latent observation,未来帧预测统一走 diffusion forcing,动作条件可以通过不同机制注入,最终同一套模型接口既能做视频 rollout,也能接 MPC planning 或 Video-to-3D。这里的关键是把世界模型从“一个封闭演示系统”改造成“可控实验对象”。
1. 为什么需要 Nano World Models
1.1 世界模型研究的真正瓶颈
- • *世界模型(World Model)**的基本目标,是让模型根据历史观测和条件变量预测未来观测,进而支持生成、规划和决策。对机器人、游戏智能体和空间智能来说,这件事很自然:如果模型能在执行动作前预测未来画面,就能把自己当作内部模拟器,比较不同动作序列的后果。问题在于,现代世界模型通常牵涉视频扩散、latent 编码、动作条件、采样调度、评测指标和下游 planner,任何一个环节换掉,都可能改变结果。
这也是 Nano World Models 的出发点。论文认为,世界模型算法正在从“发明新范式”进入“理解细粒度设计选择”的阶段。换句话说,研究重点不只是提出一个更强模型,而是回答更具体的问题:-prediction、-prediction、-prediction 和 flow matching 到底差在哪里;模型从 40M 到 460M 是否稳定变好;action 是直接加到 token 上、用 AdaLN、FiLM,还是 cross-attention 更合适;VAE、DINO、V-JEPA 这类 latent space 对预测和 planning 的影响是不是一致。没有统一代码,这些问题很难严谨比较。
1.2 它和“大模型世界模拟器”的差别
工业级世界模型通常追求高真实感、长时域、实时交互或强泛化,这些方向当然重要,但它们往往代价很高:训练成本高、数据不可得、代码不可复现、实验轴线难以控制。Nano World Models 反过来选择“足够小但完整”的路线,它不是为了在视觉效果上压过闭源系统,而是为了让研究者能在普通实验流程中改配置、跑消融、看指标、保存 rollout,再把结果接到 planning 或 3D 重建工具里。
官方 README 把这个仓库描述成一个基于 diffusion-forcing 训练视频世界模型的 minimalist repository,并提供训练、推理、评测、预训练 checkpoint 和文档。当前 Hugging Face collection 中可以看到 RT-1 prediction target 消融、action injection 消融、scale 消融,以及 DINO-WM、RT-1、CSGO 等任务 checkpoint。这个开放度是文章最值得重视的地方:它不是只发布一个模型,而是把模型设计选择暴露成可复现实验。
2. 一句话理解 Nano World Models
2.1 核心思想
Nano World Models 的一句话概括是:用 diffusion forcing 把未来视频预测统一成一个条件序列生成问题,再用 Hydra 配置把目标函数、模型规模、动作注入、latent space、数据集和采样方式变成可替换实验轴。 这句话里最关键的是“统一接口”。模型并不强迫研究者为 RT-1、CSGO、PushT、Point Maze 分别写独立训练流程,而是让这些数据源暴露相同形式的 observation window、action sequence 和 metadata。
形式上,模型接收历史观测 ,条件变量 ,有动作时还接收动作序列 ,目标是生成未来观测 。实际训练时,模型通常不直接预测 RGB,而是先通过 encoder 得到 latent:
然后学习条件分布:
这里的工程判断很明确:未来视频预测不是只追求像素好看,真正有用的世界模型还要能在动作变化时产生可区分的未来。因此 NanoWM 同时评测视觉指标和 planning 指标,并把 latent space 是否“用上动作”作为重要诊断项。
2.2 它支持哪些研究轴
Nano World Models 把世界模型拆成六个主要轴线。第一是生成目标,包括 diffusion 下的 、、 prediction,以及 flow matching。第二是模型规模,论文描述 NanoWM-S、B、L、XL 四档,官方配置和 checkpoint 常用 S/B/L,CSGO 有专门的 L/2 形状。第三是 action injection,包括 additive、AdaLN、AdaLN-fuse、FiLM 和 cross-attention。第四是 latent observation space,包括 SD-VAE、Web-DINO 和 V-JEPA 2.1。第五是数据环境,包括 DINO-WM 系列控制环境、CSGO 游戏数据和 RT-1 真实机器人数据。第六是 rollout 与下游应用,包括长时域生成、MPC planning、Video-to-3D。
| 研究轴 | NanoWM 中的可替换选项 | 核心问题 |
|---|---|---|
| 生成目标 | / / / flow | 预测目标如何影响重建质量和分布质量 |
| 模型规模 | S / B / L / XL,常用 B/2 与 L/2 | 更大模型是否稳定提升 |
| 动作注入 | additive / AdaLN / AdaLN-fuse / FiLM / cross-attention | action 怎样进入视频 dynamics 更有效 |
| 潜空间 | SD-VAE / Web-DINO / V-JEPA 2.1 | 重建型 latent 与语义型 latent 是否同样适合 planning |
| 数据环境 | Point Maze / Wall / PushT / Rope / Granular / RT-1 / CSGO | 同一 pipeline 是否能覆盖不同动态复杂度 |
| 应用 | rollout / MPC / Video-to-3D | 世界模型如何变成下游工具 |
3. 方法主线:Diffusion Forcing 为什么是统一接口
3.1 从全序列扩散到逐帧噪声调度
标准视频扩散通常把整段视频作为一个样本进行加噪和去噪,这对固定长度视频生成很自然,但不太适合在线交互。在线世界模型有两个额外要求:历史帧应该作为条件保持相对干净,未来帧可以逐步生成;当 rollout 超过训练窗口时,模型还要把自己生成的帧滑入历史窗口,继续预测后面的未来。Diffusion Forcing 的关键就是允许同一条序列中不同 token 拥有不同噪声水平。
Diffusion Forcing 原论文把它定义为一种训练范式:模型对一组 token 进行去噪,但每个 token 可以有独立 noise level。放到视频世界模型里,历史帧可以被赋予较低噪声,未来帧可以被赋予较高噪声;通过改变 noise schedule,同一个模型可以表达 teacher-forced prediction、masked future prediction 和 autoregressive rollout。NanoWM 正是把这个性质做成代码接口,而不是把每种采样方式写成独立模型。
3.2 统一公式
设 latent 序列为 ,每一帧分配一个噪声索引 ,整条序列对应噪声调度:
训练时,模型看到的是被不同程度加噪后的 latent trajectory,以及当前噪声调度 。如果 是历史 context frame,可以让 很小;如果 是未来 frame,可以让 更大。这样模型学到的不是普通 next-frame regression,而是“在给定历史和噪声日程时,把未来 latent 从噪声推回合理未来”的条件生成能力。
这套抽象的好处,是目标函数和采样方式可以被配置化。对于 diffusion objective,模型可以预测 、 或 ;对于 flow matching,模型可以预测从噪声到数据路径上的 velocity field。GitHub 训练文档中也把这些目标作为同一套 experiment.diffusion.pred_name 的不同取值,而不是不同代码分支。
3.3 训练循环的代码化理解
NanoWM 的真实训练入口由 PyTorch Lightning 接管,核心逻辑在 NanoWMTrainingModule.training_step。下面是 nano-world-model/src/experiments/train_experiment.py:199-240 的源码节选,可以直接看到它怎样从 batch 取出视频和动作、用 latent codec 编码帧、按 diffusion forcing 或 full-sequence diffusion 选择 timestep 形状,再调用 self.diffusion.training_losses 计算目标函数。
def training_step(self, batch, batch_idx):
x = batch["video"].to(self.device)
video_name = batch["video_name"]
action = None
if self.args.model.use_action:
action = batch["action"].to(self.device)
with torch.no_grad():
b, _, _, _, _ = x.shape
x = rearrange(x, "b f c h w -> (b f) c h w").contiguous()
x = self._vae_encode(x)
x = rearrange(x, "(b f) c h w -> b f c h w", b=b).contiguous()
if self.args.model.extras == 78:
raise ValueError("T2V training is not supported at this moment!")
elif self.args.model.extras == 2:
model_kwargs = dict(y=video_name)
else:
model_kwargs = dict(y=None)
if self.args.model.use_action:
model_kwargs["action"] = action
diffusion_mode = self.args.experiment.diffusion.mode
if diffusion_mode == "diffusion_forcing":
t_shape = (x.shape[0], x.shape[1])
elif diffusion_mode == "full_seq_diffusion":
t_shape = (x.shape[0],)
else:
raise ValueError(f"Unknown diffusion_mode: {diffusion_mode}. Must be 'full_seq_diffusion' or 'diffusion_forcing'")
t = sample_training_timesteps(
t_shape,
self.diffusion.num_timesteps,
strategy=self.args.experiment.diffusion.timestep_sampling,
logit_normal_mean=self.args.experiment.diffusion.logit_normal_mean,
logit_normal_std=self.args.experiment.diffusion.logit_normal_std,
device=self.device,
)
loss_dict = self.diffusion.training_losses(self.model, x, t, model_kwargs)
loss = loss_dict["loss"].mean()
这段源码里最关键的不是某个变量名,而是两个设计选择。第一,diffusion_forcing 下的 t_shape=(B,T),说明每一帧可以拥有自己的 diffusion timestep;如果切到 full_seq_diffusion,timestep 只按 batch 采样。第二,目标函数并没有散落在训练循环里,而是收敛到 self.diffusion.training_losses(...),目标类型由 experiment.diffusion.pred_name、noise_schedule、zero_terminal_snr 等配置决定。这就是 NanoWM 能做 x/v/epsilon/flow 消融的原因:训练循环不变,变的是 diffusion 对象和配置。
Diffusion 对象本身在同一个模块初始化。下面是 nano-world-model/src/experiments/train_experiment.py:76-83 的源码节选,它把配置中的 prediction target、noise schedule 和 diffusion steps 传给 create_diffusion:
self.diffusion = create_diffusion(
timestep_respacing="",
noise_schedule=args.experiment.diffusion.noise_schedule,
pred_name=args.experiment.diffusion.pred_name,
diffusion_steps=args.experiment.diffusion.diffusion_steps,
snr_gamma=args.experiment.diffusion.snr_gamma,
zero_terminal_snr=args.experiment.diffusion.zero_terminal_snr,
)
如果 pred_name=v,训练目标由 diffusion 实现解释为 velocity-style parameterization;如果 pred_name=x,目标更接近 clean latent;如果切到 flow,则走 flow matching 对应路径。文章里不应该写一个仓库不存在的“目标构造函数”,因为真正承担这件事的是 diffusion.training_losses 与 create_diffusion 组合。这个细节很重要:读者照着源码查,能找到实现;照着抽象示例查,只会找不到函数。
4. 架构:latent video token + Transformer + action injection
4.1 为什么不直接预测像素
NanoWM 默认预测 encoded observation,而不是直接预测 RGB 像素。这个选择来自现代视频生成和机器人世界模型的共同经验:像素空间维度高、局部纹理噪声多,直接预测容易把容量耗在颜色和细节上;latent space 能把帧压缩成更适合生成的表示,再由 decoder 还原成 RGB,或者直接在表示空间里做 planning。SD-VAE latent 是重建导向的,可以解码成图像;Web-DINO 和 V-JEPA 2.1 是语义/预测特征导向的,不天然支持 RGB 重建。
这个选择不是实现细节,而是论文最重要的实验轴之一。直觉上,DINO 或 V-JEPA 这类语义特征更“懂世界”,似乎更适合 planning;但 NanoWM 的 PushT 结果显示,语义 latent 在相同 diffusion-forcing 接口下可能学不到可控 dynamics,表现为 ground-truth action、zero action、random action rollout 之间距离几乎不变,action embedding RMS 接近零。换句话说,语义强不等于世界模型能用动作做反事实预测。
4.2 Transformer 主干和 patch 规模
NanoWM 使用 Transformer 处理 latent video tokens。对 VAE latent,每帧 latent 被分成空间 patch,投影到 hidden dimension,再经过 interleaved spatial-temporal attention。模型命名沿用视频/图像 diffusion 社区的习惯:NanoWM-B/2 表示 Base 规模、latent patch size 为 2;NanoWM-B/4、B/8 则使用更粗 patch。官方配置文档列出常用模型:NanoWM-S/2 约 40M,NanoWM-B/2 约 160M,NanoWM-L/2 约 460M;CSGO 由于分辨率形状不同,有专门的 S/2 和 L/2 CSGO 配置。
进一步看,模型规模实验给出比较清晰的结论:在 RT-1 50K-step ablation 中,S/2 到 B/2 再到 L/2,PSNR、SSIM、LPIPS、FID 都单调改善。S/2 约 39.8M 参数,FID 54.95;B/2 约 158.6M,FID 42.27;L/2 约 460M,FID 36.31。这个结果不是说“越大越值得”,而是说明在 NanoWM 的实验范围内,还没有看到 scaling 失效;B/2 更适合作为默认质量/成本折中,L/2 更适合追求容量,S/2 更适合快速迭代。
4.3 Action injection 不只是把动作拼进去
动作条件是世界模型区别于普通视频生成的关键。如果模型只预测“自然视频下一帧”,它可以忽略 action;但在机器人或游戏里,同一个当前画面配上不同动作,未来应该不同。NanoWM 支持五种动作注入机制:additive 是把 action embedding 加到对应帧 token;AdaLN 和 AdaLN-fuse 通过 adaptive layer norm 调制特征;FiLM 通过 feature-wise modulation 改变通道;cross-attention 让 video tokens attend 到 action tokens。
官方实验给出的判断很克制:action injection 是任务相关的。RT-1 上 FiLM 的视觉指标最好,FID 40.62,略优于 additive 的 42.27;但 PushT 上 simple additive 最强,而且没有额外参数。cross-attention 在这些规模下反而弱,RT-1 FID 到 51.12。这里的工程启发很直接:不要假设更复杂的条件交互一定更好,低维动作和较短 prediction horizon 下,简单 additive 可能是更稳的默认项。
5. 代码结构:Hydra 让实验变成配置轴
5.1 环境安装和路径设置
官方 README 的 quick start 很短,核心是创建 nanowm 环境并设置数据与输出路径。LeRobot 数据加载由 lerobot==0.3.3 提供,官方还特别说明不要安装 lerobot-datasets,它是数据格式版本名,不是 PyPI 包。FID/FVD 评测需要额外下载 i3d torchscript 权重,这一点很容易漏掉。
git clone <https://github.com/simchowitzlabpublic/nano-world-model.git>
cd nano-world-model
conda env create -f environment.yml
conda activate nanowm
export DATASET_DIR=/path/to/dino_wm_data
export CSGO_DATA_DIR=/path/to/csgo
export RT1_DATA_ROOT=/path/to/rt1_fractal
export RESULTS_DIR=/path/to/results
mkdir -p pretrained_models/i3d
curl -L
"<https://www.dropbox.com/scl/fi/c5nfs6c422nlpj880jbmh/i3d_torchscript.pt?rlkey=x5xcjsrz0818i4qxyoglp5bb8&dl=1>"
-o pretrained_models/i3d/i3d_torchscript.pt
如果你只是想理解流程,最建议从 DINO-WM PushT 开始。PushT 数据相对轻,动作维度低,视觉结构清楚,训练和评测速度比 RT-1、CSGO 更适合做第一次实验。RT-1 更接近真实机器人视频,但动态和视觉复杂度高;CSGO 更适合观察长时域 autoregressive rollout 和视觉漂移。
5.2 Hydra 配置的最小理解
NanoWM 的所有训练、评测和 planning 都从 src/main.py 进入,Hydra 负责把 experiment、dataset、model、latent_codec、planning 等配置组合起来。顶层配置不是文章里临时概括出来的,nano-world-model/src/configs/config.yaml:1-9 实际写法如下:
defaults:
- model: nanowm_b2
- latent_codec: sd_vae
- dataset: dino_wm/point_maze
- experiment: default
- planning: base
路径可以来自环境变量,也可以写进 gitignored 的 src/configs/local/paths.yaml。真正使用时,常见做法是通过命令行覆盖某个轴。例如切换数据集、模型规模或动作注入,不需要改 Python 文件:
# 训练 DINO-WM PushT,NanoWM-B/2
python src/main.py
experiment=dino_wm_pusht
dataset=dino_wm/pusht
model=nanowm_b2
# 训练 CSGO,使用 CSGO 专用 L/2 模型形状
python src/main.py
experiment=csgo
dataset=game/csgo
model=nanowm_l2_csgo
# RT-1 主训练
python src/main.py
experiment=rt1
dataset=rt1/rt1
model=nanowm_b2
# 改 action injection 为 FiLM
python src/main.py
experiment=ablation_rt1
dataset=rt1/rt1
model=nanowm_b2
model.action_injection.type=film
这里最值得复用的不是某条命令,而是配置组织方式。experiment 管训练策略、学习率、step 数、diffusion 目标;dataset 管数据路径、frame interval、action dim;model 管模型规模、patch、action injection、sampling mode;planning 管 MPC horizon、CEM 样本数和目标来源。清楚这个分层后,复现实验就不再是“猜代码在哪里改”,而是知道该改哪个配置组。
6. 从训练到评测:一条完整实现路线
6.1 训练第一个模型
官方文档的默认训练例子是 DINO-WM PushT、NanoWM-B/2、pred-v、additive injection、cosine + ZTSNR。这组默认值不是随便选的:pred-v 是仓库默认,additive 是质量/参数折中最好,B/2 是默认规模,cosine + ZTSNR 是 和 prediction 常用 schedule。训练输出会写到 ${RESULTS_DIR} 对应的 run 目录,包含 .hydra 配置快照、checkpoints/latest、checkpoints/across_timesteps 和 TensorBoard 日志。
python src/main.py
experiment=dino_wm_pusht
dataset=dino_wm/pusht
model=nanowm_b2
如果要做目标函数消融,可以直接覆盖 experiment.diffusion.pred_name。当前官方 training docs 中,除了论文主表强调的 外,还列出了 flow matching 消融:flow 在该文档表中达到 23.54 PSNR 和 38.10 FID,优于默认 的 FID 42.27; 在 SSIM 和 LPIPS 上最好。这个口径和论文正文“ reconstruction 更好、 FID 更好且作为默认”略有扩展,写实验时建议标明使用的是论文表还是当前 GitHub docs。
# x-prediction 消融
python src/main.py
experiment=ablation_rt1
dataset=rt1/rt1
model=nanowm_b2
experiment.diffusion.pred_name=x
# flow matching 消融
python src/main.py
experiment=ablation_rt1
dataset=rt1/rt1
model=nanowm_b2
experiment.diffusion.pred_name=flow
experiment.diffusion.snr_gamma=0.0
experiment.diffusion.zero_terminal_snr=false
6.2 固定种子评测
NanoWM 的标准评测使用 256 个固定 validation clips,seed 为 42,默认 250 DDIM sampling steps,sequential scheduling。指标包括 PSNR、SSIM、LPIPS、FID,长视频足够时也可以算 FVD。评测输出包括 metrics.json、sample comparison MP4 和 .hydra 配置快照。这个设计很重要,因为世界模型论文常见问题是定性视频很好看,但指标难复现;固定子集和固定 seed 至少让不同 ablation 有共同比较面。
python src/main.py
experiment=evaluate_only
dataset=dino_wm/pusht
model=nanowm_b2
experiment.resume_from_checkpoint=/path/to/checkpoint.ckpt
experiment.evaluation.batch_size=1
dataset.loader.validation_fixed_subset_size=256
dataset.loader.validation_fixed_subset_seed=42
采样方式也会影响速度和质量。sequential 是默认,逐帧自回归去噪,质量通常更高;full_sequence 会联合去噪整段 clip,速度更快但质量略低。planning 场景通常要求大量 candidate rollouts,如果仍用 sequential,每次 CEM 优化都会很慢,所以官方 planning 文档建议设置 model.scheduling_mode=full_sequence。
python src/main.py
experiment=evaluate_only
dataset=dino_wm/pusht
model=nanowm_b2
experiment.resume_from_checkpoint=/path/to/checkpoint.ckpt
model.scheduling_mode=full_sequence
model.num_sampling_steps=50
7. 下游工具:从未来视频到 Planning 和 3D
7.1 长时域 rollout
官方 long rollout 脚本可以从训练 checkpoint 生成 50 帧以上视频。CSGO 示例使用 4 帧历史,生成 50 帧,总共 32 个样本,batch size 4,50 DDIM steps,sequential scheduling。--config 需要指向训练 run 里的 .hydra/config.yaml,因为 rollout 需要匹配训练时的数据、模型形状和解码设置。
python src/sample/rollout.py
--config /path/to/run/.hydra/config.yaml
--ckpt /path/to/checkpoint.ckpt
--save_path results/long_rollout/csgo_100k
--num_samples 32
--batch_size 4
--rollout_length 50
--history_length 4
--num_sampling_steps 50
--scheduling_mode sequential
--history_stabilization_level 0.02
--fps 8
长时域 rollout 的核心权衡是质量和速度。官方文档建议 50 DDIM steps 是质量/速度折中点,25 steps 可做快速预览,250 steps 适合追求更高质量。history_stabilization_level 用于给历史 latent 注入小噪声,帮助缓解 teacher-forcing brittleness;但过高会洗掉细节。这些参数不是“调参装饰”,它们直接决定模型能否从短窗口生成稳定长视频。
7.2 MPC + CEM planning
NanoWM 的 planning 接口把世界模型当作 batched rollout function。给定当前 observation、goal latent 和一批 candidate action sequences,模型并行预测未来 latent,再用目标函数打分。CEM 更新动作分布,保留 top-K elites,重复若干轮后执行最优序列的前几个动作,再重新观测和规划。这个流程不是训练 policy,而是把世界模型当作测试时 planner。
先看 nano-world-model/src/planning/cem_planner.py:90-95。CEM 并不直接访问环境未来状态,而是把每一批候选动作交给 world_model.rollout,再让 objective function 给预测 latent 打分:
with torch.no_grad():
z_obses, _ = self.world_model.rollout(
obs_0=obs_0_expanded,
act=cur_actions,
)
losses.append(self.objective_fn(z_obses, z_obs_g_expanded))
接着看 nano-world-model/src/planning/cem_planner.py:163-200。这段源码就是 CEM 的核心循环:从当前 mu/sigma 采样动作序列,计算 loss,取 top-k elites,再用 elite actions 更新分布。它比伪流程更值得放进文章,因为这里能看到 action_samples[0] = mu[b]、动作裁剪、sigma_min 这些会影响稳定性的实现细节。
for i in range(self.opt_steps):
# Optimize each instance in batch
batch_losses = []
for b in range(batch_size):
# Sample action sequences
action_samples = (
torch.randn(self.num_samples, self.horizon, self.action_dim).to(self.device)
* sigma[b]
+ mu[b]
)
action_samples[0] = mu[b] # First sample is current mean
# Clip to action space if bounds provided.
if self.action_low is not None or self.action_high is not None:
action_samples = action_samples.clamp(
min=self.action_low if self.action_low is not None else -float("inf"),
max=self.action_high if self.action_high is not None else float("inf"),
)
obs_0_single = {k: v[b:b+1] for k, v in obs_0.items()}
z_obs_g_single = {
k: v[b:b+1] if v is not None else None
for k, v in z_obs_g.items()
}
loss = self._compute_losses_for_samples(
obs_0_single=obs_0_single,
z_obs_g_single=z_obs_g_single,
action_samples=action_samples,
)
# Select top-k
topk_idx = torch.argsort(loss)[:self.topk]
topk_actions = action_samples[topk_idx]
batch_losses.append(loss[topk_idx[0]].item())
# Update distribution, flooring sigma so it doesn't collapse.
mu[b] = topk_actions.mean(dim=0)
sigma[b] = topk_actions.std(dim=0).clamp(min=self.sigma_min)
世界模型 rollout 侧也有真实接口。nano-world-model/src/planning/diffusion_world_model.py:125-140 显示,planner 调用的不是手写 dynamics,而是 dfot_sample,并把候选动作通过 model_kwargs={"action": act_chunk} 注入扩散采样:
chunk_latents = dfot_sample(
diffusion=self.diffusion,
model=self.model,
shape=shape,
context=cur_ctx,
n_context_frames=n_context,
model_kwargs={"action": act_chunk},
scheduling_mode=scheduling_mode,
num_sampling_steps=num_sampling_steps,
eta=eta,
history_stabilization_level=self.args.experiment.diffusion.history_stabilization_level,
) # [B, total_frames, C_lat, H_lat, W_lat]
# Keep only the newly generated frames (skip context)
new_latents = chunk_latents[:, n_context : n_context + chunk_len]
官方 planning 文档中,planning.horizon 默认 5,cem.num_samples 默认 100,cem.topk 默认 10,cem.opt_steps 默认 30;in-the-loop rollout 通常用较少 DDIM steps,比如 20,因为 planner 每轮要评估大量候选。PushT 使用 dataset goals 更合适,因为随机目标在 5 planner steps 内通常不可达;Point Maze 和 Wall 可以使用 random state goals。
python src/main.py
experiment=planning
model=nanowm_b2
dataset=dino_wm/pusht
ckpt_path=/path/to/checkpoint.ckpt
planning.env_name=pusht
planning.goal_source=dset
planning.goal_H=5
planning.horizon=5
planning.replan_every=5
planning.max_episode_steps=20
planning.n_evals=50
model.scheduling_mode=full_sequence
7.3 Video-to-3D
NanoWM 还支持把 rollout 视频导出到三维点云流程。官方 Video-to-3D 文档使用 Depth Anything 3 做多视角深度和相机估计,然后反投影成世界坐标点云,保存为 PLY,并可用 viser 查看。这个功能不是说 NanoWM 自己学会了三维重建,而是把“时间连续的未来视频”作为几何重建工具的输入,形成从 2D world model 到 persistent 3D scene 的轻量桥。
# CSGO rollout 原生分辨率为 150x280,长 rollout 视频可能被拉伸到训练分辨率
python src/scripts/video_to_pointcloud.py
--video results/long_rollout/csgo_100k/sample_0000_gen.mp4
--output output/csgo_scene.ply
--native_res 150 280
--visualize
这里最容易犯的错误是忽略 aspect ratio。官方文档明确提醒:对被拉伸到训练分辨率的 rollout 视频,做深度估计前要恢复 native aspect ratio,否则会破坏 Depth Anything 3 的几何估计。Video-to-3D 对世界模型研究的价值,不在于点云一定非常精确,而在于它让未来视频预测可以进入更持久的空间表示。
8. 如果你要基于它做二次研究
8.1 先做一个最小可复现实验
建议从 DINO-WM PushT 或 Point Maze 开始,不要一上来就跑 RT-1 或 CSGO。第一步复现官方命令,拿到训练日志和 evaluation metrics;第二步只改一个轴,例如 pred_name=x 或 model.action_injection.type=film;第三步检查 .hydra/config.yaml 是否只改变了你想改变的字段;第四步保存固定 seed 下的 rollout videos 和 metrics.json。这样做的目的,是把“模型不收敛”“数据路径错”“评测采样不一致”和“设计选择真的有影响”分开。
不要手写一个看似合理的实验记录,然后假装它来自仓库。更可靠的做法,是把 .hydra/config.yaml 中的关键字段抄出来,或者直接回到源配置文件确认默认值。以 DINO-WM PushT + NanoWM-B/2 为例,nano-world-model/src/configs/model/nanowm_b2.yaml:1-10 给出模型窗口、采样模式和 action injection 默认值:
arch: NanoWM-B/2
name: NanoWM-B-2
num_frames: 4
n_context_frames: 1
scheduling_mode: "sequential"
num_sampling_steps: 250
use_action: true
action_injection:
type: additive # one of: additive, adaln_fuse, adaln, film, cross_attention
causal: true # temporal self-attn and cross-attn use causal mask (frame t attends only to <= t)
数据侧的 PushT 配置来自 nano-world-model/src/configs/dataset/dino_wm/pusht.yaml:1-13,这里能看到 frame_interval=5、relative actions、action_scale=100.0 和 action 维度:
defaults:
- dino_wm/base
name: "pusht"
frame_interval: 5
loader:
data_path_train: "${dataset_dir}/pusht_noise/train"
data_path_val: "${dataset_dir}/pusht_noise/val"
use_relative_actions: True
with_velocity: True
action_scale: 100.0
normalize_action: True
spec:
action_dim: 2
训练步数则不是靠文件名猜出来的,而是 nano-world-model/src/configs/experiment/dino_wm_pusht.yaml:1-8 覆盖了 default training profile:
defaults:
- default
# 8 GPU × bs=8 → eff_bs=64; 4 epoch of ~1.98M train slices ≈ 124K steps,
# rounded down to 100K for a tidy budget (~3.2 epoch).
training:
max_steps: 100000
batch_size: 8
这种记录比“我跑了一个模型,视频看起来不错”更有价值。NanoWM 本身已经把配置快照写进 .hydra/,你要做的是在实验报告里标出这些配置来源:模型配置、数据配置、实验 profile、checkpoint、评测 subset seed 和 DDIM steps。只有这样,别人才能根据你的描述复现。
8.2 新接数据集时要注意什么
新数据集不是随便写一个自定义 PyTorch Dataset。NanoWM 的真实抽象是两层:底层 DataSource 负责按 trajectory 读 metadata 和视觉帧,上层 WorldModelDataset 负责切窗口、resize、normalize、拼 action。先看 nano-world-model/src/wm_datasets/data_source/base.py:35-70,新数据源至少要实现这些方法和属性:
class DataSource(ABC):
"""
Abstract base class for all data sources.
"""
@abstractmethod
def load_trajectory(self, index: int) -> TrajectoryData:
"""Load metadata for the trajectory at the given index."""
raise NotImplementedError
@abstractmethod
def load_visual_frames(
self,
index: int,
start: int,
end: int,
step: int = 1
) -> torch.Tensor:
"""Load visual frames for a specific trajectory and time range."""
raise NotImplementedError
@abstractmethod
def get_num_trajectories(self) -> int:
"""Return total number of trajectories in this data source."""
raise NotImplementedError
@property
@abstractmethod
def action_dim(self) -> int:
"""Action dimension."""
raise NotImplementedError
@property
@abstractmethod
def state_dim(self) -> int:
"""State dimension."""
raise NotImplementedError
然后 WorldModelDataset 会统一把这些 trajectory 切成训练 clip。nano-world-model/src/wm_datasets/world_model_dataset.py:525-539 先从 DataSource 读取 action、state 和视频帧:
# Load trajectory data (cached in DataSource)
traj_data = self.data_source.load_trajectory(slice_info.traj_idx)
actions_full = traj_data.actions[slice_info.start_frame:slice_info.end_frame]
states = traj_data.states[slice_info.start_frame:slice_info.end_frame:self.frame_interval]
states = states[:self.num_frames]
# Load visual frames (on-demand from DataSource)
# DataSource API: load_visual_frames(index, start, end, step)
video = self.data_source.load_visual_frames(
index=slice_info.traj_idx,
start=slice_info.start_frame,
end=slice_info.end_frame,
step=self.frame_interval
) # [T, C, H, W]
接着在同一个函数中,nano-world-model/src/wm_datasets/world_model_dataset.py:580-607 负责 action normalization、按 frame_interval 把多步动作拼进每帧 action token,并返回训练 batch 需要的字段:
if self.normalize_action:
# Normalize on raw per-dim stats (shape matches actions_full's last dim).
actions_full = (actions_full - self._raw_action_mean) / self._raw_action_std
if self.frame_interval > 1:
required_actions = self.num_frames * self.frame_interval
actions_full = actions_full[:required_actions]
actions = actions_full.reshape(self.num_frames, self.frame_interval, -1).reshape(self.num_frames, -1)
else:
actions = actions_full[::self.frame_interval]
actions = actions[:self.num_frames]
if self.normalize_state:
states = (states - self.state_mean) / self.state_std
# Normalize pixels to [-1, 1]
if self.normalize_pixel:
# Assume video is in [0, 1] range
video = video * 2.0 - 1.0
# Create unique video name
video_name = f"traj_{slice_info.traj_idx:04d}_start_{slice_info.start_frame:04d}"
# Prepare output
output = {
"video": video, # [T, C, H, W]
"action": actions, # [T, action_dim]
"video_name": video_name,
"meta_info": {
"traj_idx": slice_info.traj_idx,
"start_idx": slice_info.start_frame,
},
}
接入后最先做的不是大规模训练,而是三项 sanity check:第一,随机 batch 的视频顺序是否正确;第二,动作维度、尺度和帧间隔是否符合环境动态;第三,zero action、random action、ground-truth action 的 rollout 是否有可见差异。尤其是第三项,它能直接暴露模型是否真正使用动作。
9. 结论
Nano World Models 的核心贡献不是更炫的视频生成效果,而是把 diffusion forcing、latent video modeling、action conditioning、评测和下游工具统一成一个可复现实验框架。Diffusion Forcing 的价值在于允许不同帧拥有不同噪声水平,同一模型可以处理短时域预测、masked future prediction 和长时域 autoregressive rollout。实验结论比直觉更细: 更偏重重建, 是默认稳定选择,flow 在当前训练文档中表现出很强 FID;模型规模在 RT-1 上单调改善;action injection 在任务之间不一致,additive 并不弱。此外稳重认为语义 latent 不自动等于好 world model。PushT 上 Web-DINO 和 V-JEPA 2.1 的 planning 失败说明,世界模型必须学到动作可控的反事实动态,而不只是好的视觉表示。
10. 参考资料
- • Nano World Models 论文:https://arxiv.org/abs/2605.23993• Nano World Models HTML 图文版本:https://arxiv.org/html/2605.23993v2• Nano World Models GitHub:https://github.com/simchowitzlabpublic/nano-world-model• Nano World Models 项目页:https://simchowitzlabpublic.github.io/nano-world-model• Nano World Models Hugging Face 权重集合:https://huggingface.co/collections/knightnemo/nano-world-model• NanoWM Training docs:https://github.com/simchowitzlabpublic/nano-world-model/blob/main/docs/training.md• NanoWM Evaluation docs:https://github.com/simchowitzlabpublic/nano-world-model/blob/main/docs/evaluation.md• NanoWM Config docs:https://github.com/simchowitzlabpublic/nano-world-model/blob/main/docs/config_system.md• NanoWM Planning docs:https://github.com/simchowitzlabpublic/nano-world-model/blob/main/docs/applications/planning.md• NanoWM Long rollout docs:https://github.com/simchowitzlabpublic/nano-world-model/blob/main/docs/applications/long_rollout.md• NanoWM Video-to-3D docs:https://github.com/simchowitzlabpublic/nano-world-model/blob/main/docs/applications/video_to_3d.md• Diffusion Forcing: Next-token Prediction Meets Full-Sequence Diffusion:https://arxiv.org/abs/2407.01392• Flow Matching for Generative Modeling:https://arxiv.org/abs/2210.02747• DINO-WM: World Models on Pre-trained Visual Features Enable Zero-shot Planning:https://arxiv.org/abs/2411.04983• V-JEPA 2 官方介绍:https://ai.meta.com/blog/v-jepa-2-world-model-benchmarks/• RT-1 项目页:https://robotics-transformer1.github.io/
196