REPRODUCTION REPORT • 2026-07-26
RynnWorld-4D:复现、Pipeline 与模型规模
Stage-3 EMA world model 的 50-step RGB/Depth/Flow 联合生成已跑通;报告进一步按代码和 checkpoint 解剖 14.8206B 主干、各种 fusion 模式、policy 数据流与下游开发边界。
结论先行
官方 Stage-3 + EMA,50 steps,6 路 MP4 均产生。
1×10×54 输出成功;随机 head 不能作为机器人控制策略。
World raw、EMA、DA3 与 Wan 主要分片逐文件 SHA-256 通过。
结合结构图还原端到端 Pipeline
图中其实叠放了两条计算路径:上半部是可解码的 4D world generation;下半部是只取中间 latent feature 的 action policy。两者共享 RynnWorld-4D 主干,但推理步数、加载组件和输出语义完全不同。
flowchart LR TXT["任务文本"] --> T5["UMT5-XXL
77/512 × 4096"] RGB0["首帧 RGB"] --> VAE1["Wan VAE encoder"] DEP0["首帧 Depth"] --> VAE2["同一套 VAE encoder"] FLO0["t=0 零光流"] --> VAE3["同一套 VAE encoder"] VAE1 --> ZR["RGB latent
48×21×30×40"] VAE2 --> ZD["Depth latent
48×21×30×40"] VAE3 --> ZF["Flow latent
48×21×30×40"] T5 --> DIT["30-block 三分支 DiT
50 次 flow-shifted denoise"] ZR --> DIT ZD --> DIT ZF --> DIT DIT --> DR["VAE decoder → RGB 81 帧"] DIT --> DD["VAE decoder → Depth 81 帧"] DIT --> DF["VAE decoder → Flow 81 帧"]
三个“VAE Encoder/Decoder”是同构、同权重的 Wan VAE 调用,不是三套各 705M 的独立常驻模型。噪声位于 VAE latent;Rynn block 预测三路 latent 的去噪方向。
flowchart LR CAM["当前 RGB observation"] --> VAE["VAE 编码 RGB"] CAM --> DA3["DA3 估计当前深度"] DA3 --> VAEDEP["VAE 编码 Depth"] ZERO["首帧零光流"] --> VAEF["VAE 编码 Flow"] EMB["预计算 UMT5 embedding"] --> WB["Rynn 前 20/30 blocks
单一 timestep=500"] VAE --> WB VAEDEP --> WB VAEF --> WB WB --> HOOK["Hook block 15
RGB/Depth/Flow 3×3072"] HOOK --> VF["Video Former 3D
336 latent tokens × 384"] STATE["54D proprio state"] --> FM["4+4 层 flow-matching DiT"] EMB --> FM VF --> FM FM --> ACT["4-step Euler ODE
10×54 action sequence"]
Policy 不先生成 81 帧视频:它只做一次 world-backbone 前向,在 block 15 取三模态特征,并在 block 19 后早停。因此图中从 world block 指向 policy 的箭头代表 latent feature,不是解码后的视频。
Self Attention
RGB、Depth、Flow 各自在自身时空 token 内建模;三套 Q/K/V 独立。
Cross Attention
这里是各分支对文本 embedding 的 cross-attention。三路 Q/输出独立,但文本 K/V 物理共享。
Joint Cross-Modal
RGB/Depth/Flow 互相提供 K/V;发布权重只在 0,3,…,27 共 10 个 block 激活。
代码中的融合模式与方向
| 模式 | 跨模态路径 | 每层新增 | 适用含义 |
|---|---|---|---|
| none | 无;三分支仅共享文本 K/V | 0 | 三路并行 baseline,不能交换视觉几何信息 |
| unidirectional | Depth/Flow → RGB | 2× Linear(3072,3072) | 让几何/运动辅助 RGB,不反向修改几何分支 |
| bidirectional | Depth/Flow ↔ RGB | 4× Linear(3072,3072) | 轻量线性残差融合;没有完整 token-to-token 跨模态注意力 |
| joint | 三模态 Q 对其他模态 K/V | 约 113.32M / 激活层 | 发布 Stage-3;frame-wise + 3D RoPE,共 10 层 |
unidirectional 是 Depth/Flow → RGB;joint block 的 joint_unidirectional=True 则是 RGB 只作为 K/V teacher,Depth/Flow ← RGB。两处“单向”的箭头正好相反。发布 Stage-3 的实际开关
fusion=joint、share_ffn=False、block 0–29 每 3 层插入一次 joint,共 10 次。
joint_frame_wise=True 把跨模态注意力限制在同一时间帧,避免跨时间混合造成运动模糊,并把注意力长度从 3TS 降到每帧 3S。
joint_use_rope=True 对 joint Q/K 应用与三分支对齐的 3D RoPE;joint_unidirectional=False 表示三路双向交换。
Stage-3 训练把 joint_gate_video_decay 从 1 衰减到 0:后期逐步切断 Depth/Flow 对 RGB 的回写,让 RGB 维持生成质量,同时 RGB 仍可作为 Depth/Flow 的信息源。
Checkpoint + Code 参数规模审计
以下不是按文件大小反推:safetensors 由 tensor header 逐项计数,DeepSpeed raw checkpoint 以 data pointer + offset 去重别名。特别是三路 text cross-attention 的 K/V 指向同一 Parameter,直接累加 2723 个 state_dict entry 会虚高到 15.954B。
| 组件 | 参数 | 权重驻留 |
|---|---|---|
| Rynn full joint transformer | 14.8206B | 27.606 GiB BF16 |
| Policy truncated Rynn 20/30 | 9.9490B | 18.531 GiB BF16 |
| UMT5-XXL encoder | 5.6809B | 10.582 GiB BF16 |
| Wan VAE | 704.689M | 1.313 GiB BF16 |
| DA3-LARGE | 410.942M | 1.531 GiB FP32 |
| Video Former 3D | 22.870M | 87.2 MiB FP32 |
| Flow-matching policy | 28.234M | 107.7 MiB FP32 |
Rynn 14.8206B 如何组成
- RGB/base branch:4.34264B(29.30%)
- Depth branch:4.34264B(29.30%)
- Flow branch:4.34264B(29.30%)
- 共享 text K/V:566.508M(3.82%)
- 10 个 joint block:1.13317B(7.65%)
- 共享 conditioning + 三路 I/O:约 93.0M(0.63%)
磁盘、参数和显存不是一回事
- raw 文件 29.83 GB;EMA 文件 29.45 GB。world inference 先载 raw,再用 2383 个 EMA tensor 覆盖,不会在 GPU 同时常驻两套主干。
- EMA 少 340 个 state_dict key:其中 300 个是共享 text K/V 的 depth/flow 别名,另 40 个是 raw 保留的 video-side joint output/gate/buffer。
- World 权重理论底座约 39.50 GiB(Rynn + UMT5 + BF16 VAE),实测峰值 75.15 GiB;差额来自 50-step 三分支 activations、attention 临时张量、VAE decode 与 CUDA allocator。
- Policy 跳过 UMT5、物理删除后 10 个 block、DA3 用后回 CPU,实测峰值 20.98 GiB。
运行环境
硬件
| GPU | 8× NVIDIA H20,95.1 GiB/卡 |
| Driver | 580.105.08 |
| Host RAM | 2 TiB |
| 执行卡 | GPU 3 / GPU 6 |
软件
| Python | 3.10.20 |
| PyTorch | 2.6.0+cu124 |
| diffusers | 0.35.2 |
| transformers | 4.57.1 |
| Repo revision | 2c748539849ada70d9148327e2195543ba3a921a |
下载阶段仅对单条命令临时移除本机 127.0.0.1:7890 代理;代理会截断多 GB TLS 流。运行环境本身未修改全局代理配置。
模型来源与完整性
| 发布物 | 大小 | SHA-256 | 结果 |
|---|---|---|---|
| Wan transformer 5 shards | 20.00 GB | 5 hashes | OK |
| Wan UMT5 3 shards | 11.36 GB | 3 hashes | OK |
| Wan VAE | 2.82 GB | 62cd18… | OK |
| RynnWorld raw | 29.83 GB | c69344… | OK |
| RynnWorld EMA | 29.45 GB | a08bcb… | OK |
| DA3-LARGE | 1.64 GB | eaf2ae… | OK |
World model:正式 50-step 结果
输入为官方 data/sample.json 第一个 agibot latent:左臂从篮子拿起红苹果并放入购物车。使用双向 joint attention、3D RoPE、每 3 层融合、EMA。
左到右:generated RGB / depth / optical flow。压缩预览仅用于浏览,原始 MP4 保留在结果目录。
| stream | frames | resolution | fps | file size |
|---|---|---|---|---|
| rgb | 81 | 640×480 | 16.0 | 0.27 MiB |
| depth | 81 | 640×480 | 16.0 | 0.05 MiB |
| flow | 81 | 640×480 | 16.0 | 0.16 MiB |
| gt | 81 | 640×480 | 16.0 | 0.29 MiB |
| gt_depth | 81 | 640×480 | 16.0 | 0.06 MiB |
| gt_flow | 81 | 640×480 | 16.0 | 0.26 MiB |
Action policy:官方程序链路结果
DA3 已先在 Tianji 官方样例首帧上独立前向成功(224×392 float32 depth,forward 约 0.56s),随后完整加载 world backbone、Video Former 和 flow-matching head。
随机初始化 head 的数值统计:min -3.9241,max 2.3571,mean 0.0107,std 1.0366。这些数字不可解释为控制精度。
Policy head 的 token 与层级细节
Video Former 3D · 22.870M
- 输入通道:RGB/Depth/Flow hook feature 拼接,3072×3=9216。
goal_emb:9216→768→384,占 7.374M。- 336 个 learnable latent,按 21 帧×16 token 排列;6 层逐帧 Perceiver attention + temporal attention + FFN。
- 输出
[B,336,384],把高分辨率 world token 压缩为 policy observation token。
Flow Matching DiT · 28.234M
- 条件:32 个 UMT5 goal token、336 个视觉 token、1 个 54D proprio token和 flow time embedding。
- 4 层 encoder + 4 层 decoder,hidden=384,8 heads;动作 horizon=10,action_dim=54。
- 从高斯动作噪声出发,4 次 Euler 更新得到
[B,10,54];部署step()每 10 个控制周期重规划一次。 - 公开仓库没有这 51.10M policy head 的训练 checkpoint;目前输出只是随机网络的合法张量。
基于该模型的下游开发地图
最值得利用的不是“再生成一个视频”,而是三模态对齐 latent:RGB 给语义外观,Depth 给几何,Flow 给运动。下面按落地成本区分立即可做、需要数据训练和需要改架构的方向。
冻结主干 + 小头
- block 15 三模态 feature probing:动作阶段、接触、物体状态分类。
- RGB-D-flow 检索、视频片段匹配、语言条件事件定位。
- 2D point tracking + depth lifting,构造近似 3D scene flow / 轨迹。
- 三路预测不一致度作为 OOD、异常动作和失败预警信号。
策略与规划
- 用 Tianji/LeRobot episode 训练缺失的 51.10M policy head;先冻结 world,再逐层解冻或 LoRA。
- 学习 success/reward/collision head,对候选轨迹或 action chunk 排序。
- 离线 imitation / offline RL:把 336×384 world token 当作统一 observation。
- 多任务 action tokenizer 与不同机器人 embodiment 的动作 retargeting。
闭环 World Model
- 把 action sequence 注入 world DiT,训练 action-conditioned dynamics;当前模型只有首帧+文本条件。
- 基于 latent imagination 的 MPC:采样动作→rollout→reward/risk 打分→执行首个 chunk。
- 多相机 joint attention、相机内外参 token 与跨视角 3D 对齐。
- 蒸馏/consistency/量化到实时 backbone,加入 recurrent memory 做在线闭环。
| 推荐优先级 | 最小实验 | 成功指标 | 主要风险 |
|---|---|---|---|
| P0 | 冻结 block15,训练 contact/success probe | 相对 RGB-only 的 AUC / F1 增益 | feature 对真实传感器 domain shift |
| P0 | 补齐 Tianji 54D action head 训练 | offline action MSE + rollout success | 公开权重缺失;动作归一化定义必须一致 |
| P1 | 三模态不一致度失败预测 | 提前量固定时的 failure AUROC | 生成不确定性与真实风险不完全等价 |
| P1 | action-conditioned adapter | 未来 RGB/depth/flow + state prediction | 需要动作对齐的长序列数据和高训练成本 |
| P2 | latent MPC / candidate ranking | 闭环成功率、碰撞率、规划延迟 | 50-step rollout 太慢,必须先蒸馏或单步化 |
为跑通官方发布物所需的兼容性修复
inference-sft.py补入 model card 使用、但 CLI 原本缺失的--joint_unidirectional,并传入 Stage-3 transformer。- 取消约 30 GB raw checkpoint 无条件复制到
/tmp;改为原地 mmap,并保留显式缓存目录开关。本机根分区只有 51 GB 可用。 - world example 显式使用
--per_dataset_samples 0;否则默认值会忽略sample.json,转而扫描本机不存在的数据集 JSON。 VPP_Policy构造函数补齐 YAML 已声明的 joint-attention 与 DA3 参数;原配置无法被 Hydra 正常实例化。- policy YAML 指向实际 ModelScope 目录,并清空会覆盖正确 world checkpoint 的错误
sft_ckpt_path。 - 修复 safetensors 中读取字符串
text.decode()的崩溃:safetensors 只保存 tensor,官方 bundled embedding 没有该字符串字段。 - 补齐 DA3 API 实际导入所需的
evo;gsplat只服务 3DGS rendering,不影响本次 depth inference。
可复现命令
仓库新增了三个入口:setup_runtime.sh、download_modelscope.sh、run_verified_examples.sh。正式执行命令如下。
World model
CUDA_VISIBLE_DEVICES=3 python inference-sft.py \
--model_path ./pretrained/Wan2.2-TI2V-5B-Diffusers \
--checkpoint_path ./pretrained/RynnWorld-4D \
--json_path ./data/sample.json --output_dir ./results/official-world \
--fusion_mode joint --share_ffn False \
--joint_start_layer 0 --joint_end_layer 30 --joint_every_n_layers 3 \
--joint_frame_wise True --joint_use_rope True \
--joint_unidirectional False --zero_fusion False --use_ema True \
--num_inference_steps 50 --guidance_scale 1.0 \
--per_dataset_samples 0 --max_samples 1 --keep_on_gpu True
Action policy program smoke
cd rynnworld4d_policy
CUDA_VISIBLE_DEVICES=6 PYTHONPATH=. python inference_with_embeddings.py \
--embedding_path ./text_embeddings/pick_up.safetensors \
--checkpoint_path "" --output_dir ../results/official-policy \
--device cuda --seed 42
原始证据与来源
World inference log(节选)
✅ [Monkey Patch Applied] `WanTimeTextImageEmbedding.forward` has been replaced to ensure float32 stability during mixed-precision training.
✅ [Monkey Patch Applied] `WanTimeTextImageEmbedding.forward` has been replaced to ensure float32 stability during mixed-precision training.
✅ [Monkey Patch Applied] `WanTransformer3DModel.forward` has been added control video latent.
Joint attention ENABLED at inference. Output dir suffixed: ./results/official-world_joint-on
sample.json: selected 1 samples
Total samples to process: 1
Loading pipeline ...
Loading checkpoint shards: 0%| | 0/5 [00:00<?, ?it/s]
Loading checkpoint shards: 20%|██ | 1/5 [00:00<00:00, 5.88it/s]
Loading checkpoint shards: 40%|████ | 2/5 [00:00<00:00, 6.06it/s]
Loading checkpoint shards: 60%|██████ | 3/5 [00:00<00:00, 6.20it/s]
Loading checkpoint shards: 80%|████████ | 4/5 [00:00<00:00, 6.25it/s]
Loading checkpoint shards: 100%|██████████| 5/5 [00:00<00:00, 7.50it/s]
Joint attention config: frame_wise=True, use_rope=True, unidirectional=False, layers=[0, 30) every 3.
Loading pipeline components...: 0%| | 0/5 [00:00<?, ?it/s]The config attributes {'clip_output': False} were passed to AutoencoderKLWan, but are not expected and will be ignored. Please verify your config.json configuration file.
Loading pipeline components...: 40%|████ | 2/5 [00:00<00:00, 13.24it/s]`torch_dtype` is deprecated! Use `dtype` instead!
Loading checkpoint shards: 0%| | 0/3 [00:00<?, ?it/s]
Loading checkpoint shards: 100%|██████████| 3/3 [00:00<00:00, 154.32it/s]
Loading pipeline components...: 80%|████████ | 4/5 [00:00<00:00, 7.07it/s]
Loading pipeline components...: 100%|██████████| 5/5 [00:00<00:00, 9.49it/s]
Loading SFT checkpoint from ./pretrained/RynnWorld-4D/pytorch_model/mp_rank_00_model_states.pt ...
Memory-mapping checkpoint in place.
Checkpoint has 2723 keys.
SFT checkpoint loaded incrementally. Loaded: 2723, Missing: 0.
EMA weights loaded from ./pretrained/RynnWorld-4D/ema_weights.pt (2383 params overridden).
Scheduler flow_shift = 5.0 (flow-shifted sigma).
Skipping fusion layer zeroing — using trained fusion weights.
VAE tiling DISABLED (workaround for diffusers tiled_decode shape bug).
Pipeline ready (kept entirely on GPU; faster but more VRAM).
Initial VRAM: 72.07 GB
[1/1] [sample] The robot uses its left arm to pick up a red apple from a basket and places it i
0%| | 0/50 [00:00<?, ?it/s]
2%|▏ | 1/50 [00:11<09:06, 11.15s/it]
4%|▍ | 2/50 [00:22<08:50, 11.06s/it]
6%|▌ | 3/50 [00:33<08:38, 11.04s/it]
8%|▊ | 4/50 [00:44<08:26, 11.02s/it]
10%|█ | 5/50 [00:55<08:15, 11.01s/it]
…
32%|███▏ | 16/50 [02:56<06:13, 10.99s/it]
34%|███▍ | 17/50 [03:07<06:02, 10.99s/it]
36%|███▌ | 18/50 [03:17<05:51, 10.99s/it]
38%|███▊ | 19/50 [03:28<05:40, 10.99s/it]
40%|████ | 20/50 [03:39<05:29, 10.99s/it]
42%|████▏ | 21/50 [03:50<05:18, 10.99s/it]
44%|████▍ | 22/50 [04:01<05:07, 10.99s/it]
46%|████▌ | 23/50 [04:12<04:56, 10.99s/it]
48%|████▊ | 24/50 [04:23<04:45, 10.99s/it]
50%|█████ | 25/50 [04:34<04:34, 10.99s/it]
52%|█████▏ | 26/50 [04:45<04:23, 10.99s/it]
54%|█████▍ | 27/50 [04:56<04:12, 10.99s/it]
56%|█████▌ | 28/50 [05:07<04:01, 10.99s/it]
58%|█████▊ | 29/50 [05:18<03:50, 10.99s/it]
60%|██████ | 30/50 [05:29<03:39, 10.99s/it]
62%|██████▏ | 31/50 [05:40<03:28, 10.99s/it]
64%|██████▍ | 32/50 [05:51<03:17, 10.99s/it]
66%|██████▌ | 33/50 [06:02<03:06, 10.99s/it]
68%|██████▊ | 34/50 [06:13<02:55, 10.99s/it]
70%|███████ | 35/50 [06:24<02:44, 10.99s/it]
72%|███████▏ | 36/50 [06:35<02:33, 10.99s/it]
74%|███████▍ | 37/50 [06:46<02:22, 10.99s/it]
76%|███████▌ | 38/50 [06:57<02:11, 10.99s/it]
78%|███████▊ | 39/50 [07:08<02:00, 10.99s/it]
80%|████████ | 40/50 [07:19<01:49, 10.99s/it]
82%|████████▏ | 41/50 [07:30<01:38, 10.99s/it]
84%|████████▍ | 42/50 [07:41<01:27, 10.99s/it]
86%|████████▌ | 43/50 [07:52<01:16, 10.99s/it]
88%|████████▊ | 44/50 [08:03<01:05, 10.99s/it]
90%|█████████ | 45/50 [08:14<00:54, 10.99s/it]
92%|█████████▏| 46/50 [08:25<00:43, 10.99s/it]
94%|█████████▍| 47/50 [08:36<00:32, 10.99s/it]
96%|█████████▌| 48/50 [08:47<00:21, 10.99s/it]
98%|█████████▊| 49/50 [08:58<00:10, 10.99s/it]
100%|██████████| 50/50 [09:09<00:00, 10.99s/it]
100%|██████████| 50/50 [09:09<00:00, 10.99s/it]
Saved ./results/official-world_joint-on/sample_0_rgb.mp4
Saved ./results/official-world_joint-on/sample_0_gt.mp4
Saved ./results/official-world_joint-on/sample_0_depth.mp4
Saved ./results/official-world_joint-on/sample_0_flow.mp4
Saved ./results/official-world_joint-on/sample_0_gt_depth.mp4
Saved ./results/official-world_joint-on/sample_0_gt_flow.mp4
VRAM after cleanup: 72.12 GB
All done.Policy inference log(节选)
/nvme3/xinhai/micromamba/envs/rynnworld4d/lib/python3.10/site-packages/hydra/_internal/defaults_list.py:251: UserWarning: In 'train_config': Defaults list is missing `_self_`. See https://hydra.cc/docs/1.2/upgrades/1.0_to_1.1/default_composition_order for more information
warnings.warn(msg, UserWarning)
Loaded text embedding: 'unknown' -> torch.Size([1, 77, 4096])
Loading rynnworld4d-policy...
✅ [Monkey Patch Applied] `WanTimeTextImageEmbedding.forward` has been replaced to ensure float32 stability during mixed-precision training.
✅ [Monkey Patch Applied] `WanTimeTextImageEmbedding.forward` has been replaced to ensure float32 stability during mixed-precision training.
✅ [Monkey Patch Applied] `WanTransformer3DModel.forward` has been added control video latent.
Loading checkpoint shards: 0%| | 0/5 [00:00<?, ?it/s]
Loading checkpoint shards: 20%|██ | 1/5 [00:00<00:00, 5.77it/s]
Loading checkpoint shards: 40%|████ | 2/5 [00:00<00:00, 6.01it/s]
Loading checkpoint shards: 60%|██████ | 3/5 [00:00<00:00, 6.17it/s]
Loading checkpoint shards: 80%|████████ | 4/5 [00:00<00:00, 6.15it/s]
Loading checkpoint shards: 100%|██████████| 5/5 [00:00<00:00, 7.40it/s]
[WanFeatureExtractor] No safetensors index, falling back to DeepSpeed loading
Loaded: 2723 keys, missing=0, unexpected=0
Loading pipeline components...: 0%| | 0/4 [00:00<?, ?it/s]The config attributes {'clip_output': False} were passed to AutoencoderKLWan, but are not expected and will be ignored. Please verify your config.json configuration file.
Loading pipeline components...: 75%|███████▌ | 3/4 [00:00<00:00, 15.43it/s]
Loading pipeline components...: 100%|██████████| 4/4 [00:00<00:00, 7.43it/s]
[WanFeatureExtractor] Early-exit enabled: running 20/30 transformer blocks
[WanFeatureExtractor] Dropped 10 trailing transformer blocks (not loaded to GPU)
obs dim: 384, goal_dim: 4096, action_dim: 54, proprio_dim: 54
seq_size: 378
WARNING: no action-policy checkpoint supplied; the policy head is randomly initialized. This run validates execution and tensor shapes only.
Running inference...
[WARN ] Dependency `gsplat` is required for rendering 3DGS. Install via: pip install git+https://github.com/nerfstudio-project/gsplat.git@0b4dddf04cb687367602c01196913cde6a743d70
[WanFeatureExtractor] Loading DA3 from ../pretrained/da3
[INFO ] using MLP layer as FFN
Loading weights from local directory
[INFO ] Processed Images Done taking 0.0093841552734375 seconds. Shape: torch.Size([1, 3, 392, 392])
[INFO ] Model Forward Pass Done. Time: 0.25443601608276367 seconds
[INFO ] Conversion to Prediction Done. Time: 0.00030612945556640625 seconds
Action sequence shape: torch.Size([1, 10, 54])
Current action shape: torch.Size([54])
First action: [-0.6341481 -1.9253476 -1.0167533 -1.6653363 -0.1967295] ...
Saved action to ../results/official-policy/action.pt