点击章节标题展开/折叠 | 按顺序阅读理解引导链路
核心思想:冻结一个已训好的 Diffusion Policy(会多种行为但不知道该做哪个),用一个独立训练的动态模型的梯度,在 DDIM 去噪过程中"推"动作走向期望的视觉目标。
类比:Policy 是一个会弹多首曲子的钢琴家,DynaGuide 是在他耳边轻声说"弹莫扎特"的指挥——不改变他的弹奏能力,只引导他选择弹什么。
关键限制:如果钢琴家从来没学过某首曲子,指挥再怎么说也弹不出来——这就是 DynaGuide 只能做行为选择、不能解决真正 OOD 的原因。
┌─────────────────────────────────────────────────────────────────┐ │ 推理时(单步) │ │ │ │ ┌──────────┐ ┌───────────────────────────────────────┐ │ │ │ 当前观测 │────▶│ Diffusion Policy (DDIM 10步去噪) │ │ │ │ 200×200 │ │ │ │ │ └──────────┘ │ for k in timesteps: │ │ │ │ │ for ss in range(4): │ │ │ │ │ ε = UNet(a_noisy, k, obs) │ │ │ ▼ │ grad = guidance(obs, a_noisy) │◀──┐ │ │ ┌──────────┐ │ ε -= √(1-ᾱ_k) × grad │ │ │ │ │ Dynamics │ │ a = DDIM_step(ε, k, a) │ │ │ │ │ Model │─────│───────────────────────────────────────│───┘ │ │ │ (DINOv2 │ │ 输出: action_chunk [16, 7] │ │ │ │ +Trans) │ └───────────────────────────────────────┘ │ │ └──────────┘ │ │ ▲ │ │ │ │ │ ┌──────────┐ 预计算 │ │ │ 目标demo │────────▶ good_embeddings [20, D] │ │ │ 20条hdf5 │ (DINOv2 latent of goal states) │ │ └──────────┘ │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ 训练时(完全独立) │ │ │ │ ABCD split ──▶ Dynamics Model 训练 (MSE in DINO latent space) │ │ D split ──▶ Base Policy 训练 (standard DP training) │ └─────────────────────────────────────────────────────────────────┘
文件: core/dynaguide.py → calculate_classifier_guidance()
输入: 20条 switch_on demo 的 HDF5
处理: 对每条demo取最后一帧的(obs_image, action_chunk)
→ dynamics_model.state_action_embedding(obs, action)
→ flatten → [1, D]
输出: good_embeddings [20, D] 存在GPU上
注意: 不是简单的"目标图像特征",而是"达到目标时的(状态,动作)联合编码"
这编码了"什么状态+什么动作→目标结果"的因果信息
文件: core/dynaguide.py → guidance() 闭包 输入: 当前 obs + 候选 noisy actions (来自DDIM中间状态) 处理: 1. predicted_z = model.state_action_embedding(obs, actions) # 预测未来latent 2. dist = cdist(good_embeddings, predicted_z) # [20, 1] 距离矩阵 3. energy = logsumexp(-dist / alpha) # smooth minimum 4. grad = autograd.grad(energy, actions) # 对actions求梯度 5. grad *= scale # scale=1.5 输出: gradient [1, 16, 7] — 引导方向 直觉: "如果这个动作会导致未来状态接近目标demo的ending → 梯度让actions更朝这个方向走"
文件: robomimic/.../diffusion_policy.py → _get_action_trajectory_guidance()
标准 DDIM: ε_pred = UNet(a_t, t, obs)
加引导后: ε_pred = UNet(a_t, t, obs) - √(1-ᾱ_t) × guidance_grad
这就是 Classifier Guidance 的标准公式:
score(a|goal) = score(a) + s×∇_a log p(goal|a)
对应到噪声空间: ε_guided = ε - √(1-ᾱ) × ∇_a log p(goal|a)
ss=4: 在同一个 timestep 重复4次(预测→引导→step),相当于更强的引导
但只对前几个timestep生效(后面的√(1-ᾱ)很小,引导自然衰减)
文件: core/dynamics_models.py 架构: Image → DINOv2 ViT-S/14 (冻结) → 256个patch tokens [B, 256, 384] Action chunk [B, 16, 7] → Linear embedding → [B, 16, 384] Proprio [B, 15] → Linear → [B, 1, 384] Concat: [256 + 16 + 1] = 273 tokens + Positional Embedding [1, 258, 384] → 6层 Transformer Encoder (8头, dim=384) → 取前256 tokens → z_hat [B, 256, 384] (预测的未来DINO patch embedding) 训练: Loss = MSE(z_hat, DINOv2(future_image)) + MSE(VQVAE_decode(z_hat), future_image) 第二项(reconstruction)的梯度被detach——只用来监控训练质量,不反传到主loss 关键设计: - DINOv2 完全冻结(不微调) - 只训 Transformer + ActionEmbedding + ProprioEmbedding - 参数量 ≈ 15M(轻量,单3090训24-48h) - 输入输出都在 DINO patch token 空间(384维 × 256 patches)
| 参数 | 默认值 | 实际控制什么 | 过大后果 | 过小后果 |
|---|---|---|---|---|
scale | 1.5 | 梯度的整体缩放倍数 | 动作跳出policy可执行范围→乱动 | 引导力度不够→行为仍分散 |
ss | 4 | 每个DDIM step重复引导的次数 | 计算量翻倍+后期过度干扰精修 | 引导不够持久 |
alpha | 30 | logsumexp温度:多少demo贡献引导 | 所有demo等权→信号被平均稀释 | 只看最近demo→对异常demo敏感 |
horizon | 400 | 单rollout最大步数 | — | 任务没完成就停了 |
n_rollouts | 100 | 统计样本量 | — | 统计量波动大 |
文件: train_dynaguide.py
命令:
python train_dynaguide.py --exp_dir <output> \
--train_hdf5 <calvin_ABCD_dynamics.hdf5> \
--test_hdf5 <calvin_val.hdf5> \
--cameras third_person --action_dim 7 \
--proprio_key proprio --proprio_dim 15 \
--num_epochs 6000 --action_chunk_length 16 --batch_size 16
数据格式 (MultiviewDataset):
HDF5: data/demo_X/obs/{third_person, proprio, states}, actions
每条demo被切成 (obs_t, action_chunk[t:t+16], obs_{t+16}) 三元组
action_chunk 不够长时用最后一步 padding
训练trick:
- 对 action_chunk 加高斯噪声 (--noise_augmentation)
模拟推理时DDIM中间步的noisy actions
让dynamics model对噪声actions也能给出合理预测
- 学习率: 1e-4 (Adam)
- 每40个epoch做一次validation + filmstrip可视化
数据来源:DynaGuide 论文附录(Compute Hardware 小节)。DynaGuide 的最大特点是门槛极低——单张消费级显卡即可完成全部训练与实验。
| 项 | 配置 |
|---|---|
| 训练/推理 GPU | 单张 RTX 3090(24GB VRAM)——所有 policy 与 dynamics model 都在单卡上跑 |
| Dynamics model 规模 | ≈15M 可训练参数,训练/推理时约占 4GB 显存 |
| 训练时长 | 训练至收敛约 24–48 小时(单 3090) |
| 评测 | 每 seed 每 task 约 10–20 分钟(单 3090) |
| base policy | 无需重训——DynaGuide 引导 off-the-shelf 冻结的 diffusion policy,只训练外部 dynamics model |
门槛解读:DynaGuide 是本站所有工作里资源门槛最低的之一——单张 24GB 消费卡即可完整复现(训练+评测)。原因:它不训练庞大的 policy 或 VLM,只训练一个 ~15M 的轻量 dynamics model 来"引导"已有的冻结策略。这与需要 8× A100 的 VLA、8× H200 的视频世界模型形成鲜明对比,是"推理时引导/外部小模型引导"路线省算力的典型代表。
文件: data_processing_calvin/ Step 1: calvin_to_labeled_hdf5.py 输入: CALVIN 原始 npz (episode_XXXXXXX.npz) 处理: 按 task annotations 切段,标注行为类别 输出: 分段后的 HDF5 (每段一个demo,标注行为标签) Step 2: split_behavioral_validation_datasets_calvin.py 输入: Step 1 的 validation HDF5 处理: 按行为类别拆分成单独文件 输出: switch_on_20.hdf5, button_off_20.hdf5, ... (每类取20条) Step 3: hdf5_combiner.py 输入: 单行为 HDF5 文件 处理: 合并/筛选/设定每行为条数 输出: 最终的 guidance conditions HDF5 (用于推理) 我们当前只有作者提供的 switch_on guidance (20条) 其余7个任务需要等 CALVIN 数据集下完后自行生成
| 改造内容 | 改哪个文件 | 改什么 | 难度 |
|---|---|---|---|
| VLM生成g+ | run_dynaguide.py:323 | 替换 good_dataset 来源:HDF5 → VLM生成图像 | 低 |
| 闭环目标演化 | run_dynaguide.py:rollout() | 在step循环中加失败检测→重新算good_embeddings | 中 |
| 负目标累积 | core/dynaguide.py:guidance() | 每次失败后append到bad_embeddings | 低 |
| 不确定性自适应 | core/dynaguide.py:guidance() | 加ensemble/MC-dropout估计→动态调scale | 中 |
| 迁移flow matching | diffusion_policy.py | 替换DDIM去噪为flow ODE + 修改注入公式 | 高 |
不能。它只能在 base policy 已有的动作分布内做选择。如果 policy 从未见过某个动作模式,梯度推不出分布外的动作。
不是时间维度的衰减。alpha 是 logsumexp 的温度参数,控制"对 20 条目标 demo 的距离做 smooth minimum 的程度"。时间衰减是通过 √(1-ᾱ_k) 自然实现的(DDIM 后期这个值趋近 0)。
ss=1 只在每个 timestep 引导一次就 step。ss=4 在同一 timestep 反复"引导→step"4次,相当于更深入地优化这步的动作方向。但 ss>4 收益递减(后期 DDIM step 本身变化小了)。
是。梯度 shape = [1, 16, 7],16步中的每一步动作都被引导。dynamics model 看到完整 16步 action chunk 后预测未来,所以梯度反传到每一步。
robomimic 的 env.is_success() 不知道当前任务是 switch_on(DynaGuide 通过 env_setup 设置初始条件但没定义 task)。真正的成功判据是事后从 states 数据中算 switch delta。