Files
roboimi/docs/experiment-guide.md
T

499 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实验操作指南
## 0. 适用范围与当前起点
这份文档面向 **当前 `main` 分支** 的通用训练 / 验证 / 评估流程,不绑定某一条历史实验分支。
当前默认 Hydra 起点:
- agent`resnet_transformer`
- data`simpe_robot_dataset`
- eval`eval`
当前仓库里常见可选 agent 配置包括:
- `resnet_transformer`
- `resnet_diffusion`
- `resnet_gr00t_dit`
- `resnet_imf_attnres`
- `resnet_imf_attnres_multitoken`
- `siglip2_imf_attnres`
- `lewm_imf_attnres`
说明:
- `experiment_suites/` 下的历史目录仍然可以作为参考,但**正文以当前 `main` 的通用流程为准**。
- 如果你只是想快速开始,优先从默认 `resnet_transformer` 配置出发,再按实验需求替换 agent。
---
## 1. 关键文件与入口
| 路径 | 作用 |
| --- | --- |
| `roboimi/demos/vla_scripts/train_vla.py` | 主训练入口;负责数据集、checkpoint、`val/loss`、held-out action MSE、训练期 rollout 验证、SwanLab |
| `roboimi/demos/vla_scripts/eval_vla.py` | 单次 rollout / 离线评估入口;支持 headless、summary、trajectory image、video artifact、多进程并行 rollout |
| `roboimi/vla/conf/config.yaml` | 全局 Hydra 训练配置 |
| `roboimi/vla/conf/data/simpe_robot_dataset.yaml` | 默认数据集配置;数据路径、相机名、图像 resize 都在这里 |
| `roboimi/vla/conf/eval/eval.yaml` | eval 默认配置;`eval.ckpt_path``eval.num_episodes`、artifact 开关、多进程 rollout 配置都在这里 |
| `roboimi/vla/conf/agent/*.yaml` | 可选 agent 配置集合 |
| `roboimi/vla/data/simpe_robot_dataset.py` | HDF5 懒加载数据集;支持 `episode_indices` 过滤与 `available_episode_indices` 元信息 |
| `roboimi/vla/scripts/calculate_stats.py` | 重算 `dataset_stats.pkl` |
| `experiment_suites/` | 历史实验目录;可参考,但不作为当前主流程的唯一事实来源 |
---
## 2. 三台机器与环境
| 机器 | GPU | Python | 常用数据集路径 | 备注 |
| --- | --- | --- | --- | --- |
| 本地 `droid-z790eagleax` | 1× RTX 5090 32GB | `/home/droid/.conda/envs/roboimi/bin/python` | `/home/droid/project/diana_sim/sim_transfer` | 适合 smoke、单条主跑、本地调参 |
| 5880 节点 `100.73.14.65` | 2× RTX 5880 Ada 48GB | `/home/droid/miniforge3/envs/roboimi/bin/python` | `/home/droid/sim_dataset/sim_transfer` | 适合 2 条并行主跑 |
| L20 节点 `100.119.99.14` | 8× NVIDIA L20 46GB | `/home/droid/miniforge3/envs/roboimi/bin/python` | `/data/simtransfer/current` | 适合大规模 grid;建议数据与 run 放 `/data` |
连接:
- 5880`ssh droid@100.73.14.65`
- L20`ssh droid@100.119.99.14`
说明:
- repo / worktree 路径可能会随当前 checkout 或同步目录变化,**以你当前机器上的实际 repo 路径为准**。
- 如果训练目录会被 Hydra 自动切走,建议显式设置 `hydra.run.dir=...` 以固定输出目录。
---
## 3. 训练流怎么走
`train_vla.py` 当前主流程如下:
1. 读取 Hydra 配置并打印完整 cfg
2. 根据配置确定数据集图像 resize:
- 默认用 `data.image_resize_shape`
- 如果 `agent.vision_backbone.dataset_image_resize_shape` 存在,则优先用 backbone 覆盖值
3. 通过 `build_train_val_datasets()` 构建 dataset / train dataset / val dataset
- 若设置 `train.val_episode_indices`:按显式 episode 切出 held-out val
- 否则按 `train.val_split` 做随机划分
4.`DataLoader` 建 train / val loader
5.`dataset_dir/dataset_stats.pkl` 读取归一化统计
6. 实例化当前指定的 agent`instantiate(cfg.agent, dataset_stats=...)`
7. 可选加载:
- `train.pretrained_ckpt`:微调起点
- `train.resume_ckpt`:断点续训(支持显式路径或 `auto`
8. 建 optimizer 与 scheduler
9. 训练循环里按 `log_freq` 记录 train loss / lr
10.`save_freq` 保存 `checkpoints/vla_model_step_*.pt`,并在有 val loader 时计算 `val/loss`
11. 若设置了显式 held-out val,并且 `train.action_mse_val_freq_epochs > 0`,则按 epoch 计算 held-out `action MSE`
12.`train.rollout_val_freq_epochs` 跑训练期 rollout 验证
13. 最后写:
- `checkpoints/vla_model_best.pt`
- `checkpoints/vla_model_final.pt`
当前 best model 选择逻辑:
- **第一次拿到 rollout reward 之前**:先用 `val_loss`(或 train loss 回退)挑 best
- **第一次 rollout 之后**:优先用 `rollout_avg_reward` 挑 best
输出目录:
- 当前代码实际输出写到 **Hydra runtime output dir**
- checkpoint 默认在:`<hydra_output_dir>/checkpoints/`
- 如果不显式指定 `hydra.run.dir`Hydra 会自动生成目录
---
## 4. 验证流怎么走
### 4.1 常规 `val/loss`
常规验证有两种来源:
1. **随机划分验证集**
-`train.val_split > 0`
- 训练脚本会在保存 checkpoint 时计算 `val/loss`
2. **显式 held-out episode 验证集**
-`train.val_episode_indices=[...]`
- 训练集 = 全部 episode - held-out episode
- 验证集 = held-out episode
- checkpoint 保存时同样会计算 `val/loss`
说明:
-`train.val_split=0.0` 且未设置 `train.val_episode_indices`,则不会有 `val/loss`
### 4.2 held-out action MSE
如果想对固定 held-out episode 做更稳定、可复现的动作预测误差验证,推荐使用:
- `train.val_split=0.0`
- `train.val_episode_indices=[100]`(或你指定的 episode 列表)
- `train.action_mse_val_freq_epochs=1`
当前 `main` 中这套逻辑会:
- 每隔 `action_mse_val_freq_epochs` 个 epoch
- 对 held-out val loader 调 `agent.predict_action_chunk(...)`
- 与 batch 中的 `action` 计算 masked MSE
- 如果存在 `action_is_pad`,会自动 mask 掉 padding 部分
日志 key
- 控制台 / `train_vla.log``held-out action MSE`
- SwanLab`val/action_mse`
重要约束:
- `train.action_mse_val_freq_epochs > 0` **必须搭配** `train.val_episode_indices`
- 如果只设了 `action_mse_val_freq_epochs` 而没有设 `val_episode_indices`,训练会直接报错
### 4.3 rollout 验证
训练内 rollout 验证由:
- `train_vla.py -> run_rollout_validation() -> eval_vla._run_eval()`
当前训练内 rollout 会强制:
- `headless=true`
- `verbose_action=false`
- `record_video=false`
- `save_trajectory_image=true`
- `trajectory_image_camera_name=front`
- `save_summary_json=true`
当前这套路径是**配置驱动**的:
- `train.rollout_device`:默认跟随 `train.device`
- `train.rollout_num_workers`:默认 `null`
- 当 rollout 设备是 CPU 时,自动退化为 `1`
- 当 rollout 设备是 CUDA 时,自动推断为 `min(train.rollout_num_episodes, 8)`
- `train.rollout_cuda_devices`:默认 `null`,等价于逻辑 GPU `[0]`
- `train.rollout_response_timeout_s`
- `train.rollout_server_startup_timeout_s`
所以现在:
- 训练在 CUDA 上时,训练期 rollout 默认也会走 GPU
- `rollout_num_workers > 1` 时,会走并行 rollout
- 可以是 **单 GPU 多 worker 共用一个 inference server**
- 也可以是 **多 GPU 多 server 分摊 worker**
训练内 rollout artifact 默认落到:
- `<hydra_output_dir>/rollout_artifacts/<checkpoint_stem>/`
常见文件:
- `rollout_summary.json`
- `rollout_front_ep01_trajectory.png` ...
日志重点看:
- `Epoch X rollout 平均奖励`
- `最佳模型已更新`
---
## 5. 数据集加载与 `val_episode_indices` 机制
### 5.1 数据集格式
`SimpleRobotDataset` 读取 `dataset_dir` 下的 `*.hdf5` / `episode_*.hdf5`,每个 episode 文件至少要有:
- `action`
- `observations/qpos`
- `observations/images/{cam_name}`
默认数据配置中的相机:
- `r_vis`
- `top`
- `front`
### 5.2 懒加载行为
`roboimi/vla/data/simpe_robot_dataset.py` 是按帧懒加载,不会一次性把整套 HDF5 全读进内存。
它会:
- 扫描目录下的 HDF5 文件
- 在 worker 内做 HDF5 文件句柄 LRU 缓存
- 根据文件名中的 `episode_XXX` 建立 `available_episode_indices`
### 5.3 `val_episode_indices` 怎么切
`build_train_val_datasets()` 的逻辑是:
1. 先 instantiate 一次完整 dataset
2. 读取 `dataset.available_episode_indices`
3. 检查 `train.val_episode_indices` 是否都存在
4.`episode_indices=` 再各 instantiate 一次:
- train dataset = 全部 episode - held-out episode
- val dataset = 只包含 held-out episode
因此:
- `train.val_episode_indices=[100]` 的意思是把 `episode_100.hdf5` 整个拿去做 held-out val
- 如果 episode 不存在,会直接报错
- 如果你把所有 episode 都塞进 `val_episode_indices`,也会直接报错,因为训练集会变空
### 5.4 图像 resize 与返回字段
dataset 侧 resize 默认来自:
- `data.image_resize_shape`
- 如果 backbone 额外覆盖,则优先 `agent.vision_backbone.dataset_image_resize_shape`
当前通用 batch 返回字段包括:
- `observation.state`
- `observation_is_pad`
- `observation.<cam>`
- `action`
- `action_is_pad`
- `task`
### 5.5 统计文件
训练和推理都默认依赖 `dataset_stats.pkl`。数据集更新后需要重算:
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/vla/scripts/calculate_stats.py \
--dataset_dir /home/droid/project/diana_sim/sim_transfer
```
远端只要把:
- Python 路径
- `--dataset_dir`
替换成对应机器上的实际路径即可。
---
## 6. SwanLab 行为
当前默认配置里:
- `train.use_swanlab=false`
如果要开启,通常显式设置:
- `train.use_swanlab=true`
- `train.swanlab_project=roboimi-vla`
- `train.swanlab_run_name=<run_name>`
`train_vla.py` 当前会记录:
- 初始化时上传 `train` / `data` / `agent` 三段 config
- 训练中:
- `train/loss`
- `train/lr`
- `train/best_loss`
- `train/step`
- checkpoint 验证时:
- `val/loss`
- held-out 数值验证时:
- `val/action_mse`
- rollout 验证时:
- `rollout/avg_reward`
- `rollout/epoch`
- 训练结束时:
- `final/checkpoint_path`
- `final/best_checkpoint_path`
训练期 rollout 生成的前视图轨迹 PNG 会 best-effort 上传到 SwanLab;上传失败只会 warning,不会让训练中断。
---
## 7. 并行 rollout 说明
### 7.1 这套能力现在在哪里
当前主仓库已经**内置**多进程并行 rollout 能力,入口就是:
- `roboimi/demos/vla_scripts/eval_vla.py`
控制参数:
- `eval.num_workers`
- `eval.cuda_devices`
语义:
- `eval.num_workers`:环境 worker 数,按 episode 切分
- `eval.cuda_devices`:推理 server 绑定到哪些逻辑 GPU
### 7.2 两种常见模式
1. **单机单卡,多 worker 共用同一张 GPU**
- 典型:5090 只有 1 卡,但想让 4 个 rollout worker 并行跑环境
- 形式:`eval.device=cuda eval.num_workers=4 'eval.cuda_devices=[0]'`
- 这时是 **1 个 CUDA inference server + 4 个 env worker**
2. **单机多卡,多 server 分摊 worker**
- 典型:5880 / L20 有多卡
- 形式:`eval.device=cuda eval.num_workers=8 'eval.cuda_devices=[0,1]'`
- worker 会按 round-robin 分到多个 server 上
### 7.3 操作约束
- 并行 rollout 依赖 **多进程 eval 路径**,不是 `train.num_workers`
- `train.num_workers` 是 DataLoader worker,和 rollout 并行不是一回事
- `eval.num_workers > 1` 时必须 `eval.headless=true`
- worker 数会自动 cap 到 `eval.num_episodes`
- 多 worker 时**不支持**同时导出:
- `eval.record_video=true`
- `eval.save_trajectory=true`
- `eval.save_trajectory_npz=true`
- `eval.save_trajectory_image=true` 可以开,适合并行 reward + 定性检查一起做
---
## 8. 常用命令模板
下面的模板都以 **当前 `main` 通用流程** 为准。远端机器只需要替换:
- Python 路径
- repo 路径
- `data.dataset_dir`
- `hydra.run.dir`
### 8.1 本地 smoke train
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/train_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda \
train.batch_size=8 \
train.max_steps=1000 \
train.num_workers=4 \
train.save_freq=200 \
hydra.run.dir=/home/droid/project/roboimi/runs/smoke-resnet-transformer
```
### 8.2 通用训练模板
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/train_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda \
train.batch_size=16 \
train.lr=0.0001 \
train.max_steps=100000 \
train.num_workers=4 \
train.save_freq=2000 \
train.use_swanlab=true \
train.swanlab_project=roboimi-vla \
train.swanlab_run_name=<run_name> \
hydra.run.dir=/home/droid/project/roboimi/runs/<run_name>
```
### 8.3 带 held-out episode 验证的训练模板
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/train_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda \
train.batch_size=16 \
train.lr=0.0001 \
train.max_steps=100000 \
train.num_workers=4 \
train.val_split=0.0 \
'train.val_episode_indices=[100]' \
train.action_mse_val_freq_epochs=1 \
train.rollout_val_freq_epochs=5 \
train.rollout_num_episodes=10 \
train.use_swanlab=true \
train.swanlab_project=roboimi-vla \
train.swanlab_run_name=<run_name> \
hydra.run.dir=/home/droid/project/roboimi/runs/<run_name>
```
### 8.4 断点续训模板
`train_vla.py` 支持通过 CLI override 使用 `train.resume_ckpt`
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/train_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda \
train.resume_ckpt=auto \
hydra.run.dir=/home/droid/project/roboimi/runs/<run_name>
```
也可以把 `train.resume_ckpt` 换成显式 checkpoint 路径。
### 8.5 单次离线评估
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/eval_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda eval.device=cuda \
eval.ckpt_path=/home/droid/project/roboimi/runs/<run_name>/checkpoints/vla_model_best.pt \
eval.num_episodes=10 \
eval.headless=true \
eval.verbose_action=false \
eval.save_summary_json=true \
eval.save_trajectory_image=true \
eval.trajectory_image_camera_name=front \
eval.artifact_dir=/tmp/roboimi_eval_front
```
### 8.6 并行离线评估
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/eval_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda eval.device=cuda \
eval.ckpt_path=/home/droid/project/roboimi/runs/<run_name>/checkpoints/vla_model_best.pt \
eval.num_episodes=10 \
eval.num_workers=4 \
'eval.cuda_devices=[0]' \
eval.headless=true \
eval.verbose_action=false \
eval.save_summary_json=true \
eval.save_trajectory_image=true \
eval.trajectory_image_camera_name=front \
eval.artifact_dir=/tmp/roboimi_parallel_eval
```
### 8.7 训练内启用并行 GPU rollout
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/demos/vla_scripts/train_vla.py \
agent=resnet_transformer \
data.dataset_dir=/home/droid/project/diana_sim/sim_transfer \
train.device=cuda \
train.batch_size=16 \
train.lr=0.0001 \
train.max_steps=100000 \
train.num_workers=4 \
train.rollout_val_freq_epochs=5 \
train.rollout_num_episodes=10 \
train.rollout_device=cuda \
train.rollout_num_workers=4 \
'train.rollout_cuda_devices=[0]' \
train.rollout_validate_on_checkpoint=false \
train.use_swanlab=true \
train.swanlab_project=roboimi-vla \
train.swanlab_run_name=<run_name> \
hydra.run.dir=/home/droid/project/roboimi/runs/<run_name>
```
### 8.8 重算统计文件
```bash
/home/droid/.conda/envs/roboimi/bin/python roboimi/vla/scripts/calculate_stats.py \
--dataset_dir /home/droid/project/diana_sim/sim_transfer
```
### 8.9 监控日志
```bash
tail -f runs/<run_name>/train_vla.log
```
如果你显式设置了 `hydra.run.dir`,优先直接 tail 对应绝对路径下的日志文件。
---
## 9. 操作建议
- **优先固定 `hydra.run.dir`**,避免输出目录分散在 Hydra 自动生成路径里
- 如果只需要常规验证,使用:
- `train.val_split > 0`
- 如果需要稳定、可复现的 held-out 指标,使用:
- `train.val_split=0.0`
- `train.val_episode_indices=[...]`
- `train.action_mse_val_freq_epochs=1`
- `train.num_workers` 是 DataLoader worker,不等于 rollout 并行度
- rollout 并行优先看:
- `train.rollout_num_workers`
- `train.rollout_cuda_devices`
- `eval.num_workers`
- `eval.cuda_devices`
- 数据集更新后记得重算 `dataset_stats.pkl`
- 若同时设置了:
- `train.pretrained_ckpt`
- `train.resume_ckpt`
当前逻辑会优先走 `resume_ckpt`
- 建议 run name 中带上关键信息:
- agent / horizon / batch size / lr / host / gpu / date
- `experiment_suites/` 下的历史目录适合作为参考资料,但不要默认把某一条旧 suite 当作当前主流程的唯一标准