模型训练指导文档
本文件记录 IB-Robot 从数据集准备、帧检测预处理到 ACT / Diffusion Policy(DP)模型训练与蒸馏的完整命令流程,覆盖原始规模模型训练、小模型训练和小模型知识蒸馏三条路径;并在第 8 节给出 PI05(π₀.₅)VLA 模型基于预训练权重微调的流程。本文为通用训练指南,命令中的数据集与输出路径均为占位符,可按实际任务替换。
前提条件:
- 训练框架:仓库内
libs/lerobot子模块(当前基于 LeRobot v0.6.0 的定制版本,参见third_party/patches/lerobot/INDEX.yaml的active_tag),以 editable 方式安装,源码位于libs/lerobot/src。注意:v0.5.1 时期的自适应加权参数--ada_weight/--ada_weight_freq/--damping_coefficient在 v0.6.0 中已不存在,帧检测权重改由 LeRobot 原生的--sample_weighting.type=custom读取(ACT、PI05 支持,DP 不支持,见第 2 节);知识蒸馏(--policy.kd/--policy.teacher_train_config/--policy.decoder_out_dim)已移植到 v0.6.0 训练循环,ACT 与 DP 可用(见第 5、7 节)。 - 环境加载:训练前必须
source .shrc_local。该脚本激活venv/、设置PYTHONNOUSERSITE=1屏蔽用户目录下可能存在的其他 lerobot 版本,并把libs/lerobot/src前置到PYTHONPATH,确保lerobot-train与import lerobot都解析到子模块源码(详见 .shrc_local)。 - 训练机环境准备:
trainingprofile 仅支持 Ubuntu 22.04,其他平台在任何依赖安装前会报错退出(不影响full/inference的平台支持)。在 Ubuntu 22.04 上只需要 LeRobot 训练环境(不需要 ROS 2 / rosdep / colcon)时,用./scripts/setup.sh --yes --profile training初始化。该 profile 只创建 venv、应用 LeRobot 补丁栈并安装 LeRobot 与训练依赖;source .shrc_local后lerobot-train即可用(详见scripts/setup/README.md的 Setup Profile 一节)。frame_detector/bag_to_lerobot等数据集准备工具是 ROS 2 节点,需在带 ROS 2 的机器上完成(或直接拷贝已转换好的数据集到训练机)。 - 训练入口:
lerobot-train,由libs/lerobot/pyproject.toml的[project.scripts]注册,指向 lerobot_train.py:main。 - 帧检测工具:
dataset_tools包提供的 ROS 2 节点frame_detector,用于在训练前为数据集帧打上训练权重。入口为 frame_detector.py:main,注册于 dataset_tools/setup.py。 - 数据集转换工具:
dataset_tools包提供的bag_to_lerobot,把 ROS 2 bag 转换为 LeRobot v3 数据集。入口为 bag_to_lerobot.py:main,注册于 dataset_tools/setup.py。 - 设备:训练默认使用
cuda,需保证 CUDA toolkit 与 PyTorch 版本匹配;蒸馏时 teacher 与 student 同时驻留显存,且共用同一个--batch_size。 - 输出目录:每个训练任务通过
--output_dir指定独立输出路径。非--resume模式下该目录必须不存在,否则启动即报FileExistsError(train.py:240-244),不会覆盖已有结果。 - ROS 2 overlay:
frame_detector、bag_to_lerobot等dataset_tools节点通过ros2 run调用,需先构建工作区并 source overlay(source install/setup.bash)才能被找到。lerobot-train只需source .shrc_local,不依赖工作区构建。 - 数据集位置:本地数据集一律显式传
--dataset.root=<数据集根目录>;省略root时 LeRobot 会按repo_id去 HuggingFace Hub 解析,离线环境会失败。 - 所有
/path/to/xxx占位符需替换为环境中的实际绝对路径,详见文末「路径占位符说明」。
训练任务与数据集、模型的对应关系(示例,实际任务可按需调整):
| 训练任务 | 模型类型 | 是否蒸馏 | Teacher |
|---|---|---|---|
| ACT 原始规模模型 | ACT (dim_model=2048, n_decoder_layers=7) |
否 | — |
| ACT 小模型 | ACT (dim_model=1024, n_decoder_layers=2, dim_feedforward=1200) |
否 | — |
| ACT 小模型蒸馏 | ACT (dim_model=1024, n_decoder_layers=2, dim_feedforward=1200) |
是 | ACT 原始规模模型 |
| DP 原始规模模型 | Diffusion (默认 down_dims=(512,1024,2048)) |
否 | — |
| DP 小模型蒸馏 | Diffusion (down_dims=[256,512,1024]) |
是 | DP 原始规模模型 |
| PI05 微调(只训 action expert / LoRA) | PI05 (gemma_2b VLM + gemma_300m action expert,基于 lerobot/pi05_base) |
否 | — |
本文各节的训练命令(第 3-7 节、8.3、8.4)均已在单张 RTX 3090(24 GB)+ 当前 v0.6.0 补丁栈上,用 SO-101 单臂双相机数据集(top + wrist 两路 640×480 视频,6 维 state/action,80 episodes,30 fps,已执行第 2 节帧检测)实测能正常启动、完成训练步与 eval、保存 checkpoint;8.5 的全量微调超出 24 GB,未实测。各节给出的显存为该环境下的实测峰值(训练日志 mem_gb 字段),可作为调整 --batch_size 的参考。
1. 数据集准备(bag → LeRobot)
把 ROS 2 bag 转换为 LeRobot v3 数据集。转换使用的契约(关节、相机、频率)取自录制时写入的契约快照,而不是当前的 robot_config.yaml:bag 必须位于 episode_recorder 生成的录制目录 <record_root>/episodes/<bag> 下,<record_root>/dataset.yaml 中的 contract 快照即为 SSOT(bag_to_lerobot.py:_dataset_metadata_for_bag、_contract_from_dataset_metadata)。缺少快照的旧录制不再支持;一次转换选中的所有 episode 的契约快照必须完全一致。
bag_to_lerobot 的关键参数(来源:bag_to_lerobot.py:parse_args):
| 参数 | 含义 | 是否必填 |
|---|---|---|
--bag / --bags / --bags-dir |
单个 bag 目录 / 多个 bag / 包含多个 bag 的目录(三选一互斥) | 是 |
--out |
输出数据集根目录 | 是 |
--repo-id |
数据集 repo_id 元数据,默认 rosbag_v30 |
否 |
--no-videos |
存 PNG 图片而非 MP4 视频 | 否 |
--image-threads / --image-processes |
图像写入线程数 / 进程数,默认 4 / 0 | 否 |
--chunk-size / --data-mb / --video-mb |
数据/视频 chunk 大小上限 | 否 |
--video-codec |
视频编码,可选 auto/h264/hevc/libsvtav1/h264_nvenc/hevc_nvenc,默认 auto |
否 |
--timestamp |
时间基准,可选 contract/bag/header,默认 contract(按契约频率重采样) |
否 |
早期版本的 --robot-config 参数已移除,传入会直接报错(bag_to_lerobot.py:42)。
示例:
ros2 run dataset_tools bag_to_lerobot \
--bags-dir /path/to/record_root/episodes \
--out /path/to/dataset/my_dataset \
--repo-id my_dataset
转换完成后,/path/to/dataset/my_dataset 即为 LeRobot v3 数据集根目录。后续训练命令统一用 --dataset.repo_id=my_dataset --dataset.root=/path/to/dataset/my_dataset 指定:root 指向数据集根目录,repo_id 只作标识。
2. 数据集预处理(帧检测与训练权重)
在训练前对数据集执行帧检测,识别关键帧(critical frame)和冻结帧(freeze frame),并写入逐帧训练权重。关键帧是电流大、速度小的接触/抓取瞬间,权重放大;冻结帧是机器人停滞段,权重置零以抑制无效梯度。
2.1 工具入口
帧检测以 ROS 2 节点形式提供,正确调用方式是 ros2 run dataset_tools frame_detector --ros-args -p <param>:=<value>,不是 --<param>=<value> 形式。节点实现在 frame_detector.py:FrameDetectorNode。
2.2 可用参数
参数通过 self.declare_parameter(...) 声明(frame_detector.py:744-763),完整列表如下:
| ROS 参数名 | 类型 | 默认值 | 含义 |
|---|---|---|---|
dataset_path |
string | "" |
LeRobot 数据集根目录(必填) |
clip_view |
string_array | ["all"] |
处理哪些相机视角,["all"] 表示全部 |
enable_critical_detection |
bool | True |
是否检测关键帧 |
enable_freeze_detection |
bool | True |
是否检测冻结帧 |
gripper_pos |
integer_array | [-1] |
夹爪索引,[-1] 表示取最后一个关节作为夹爪 |
gripper_names |
string_array | ["*gripper*"] |
夹爪关节名匹配模式 |
critical_frame_min_current_threshold |
double | 0.5 |
关键帧最小电流阈值 |
critical_frame_max_velocity_threshold |
double | 0.01 |
关键帧最大速度阈值 |
critical_frame_training_weight |
double | 2.0 |
关键帧训练权重 |
critical_frame_min_duration |
int | 3 |
关键帧段最小持续帧数 |
n_forward_expansion |
int | 30 |
关键帧向前扩展帧数 |
n_backward_expansion |
int | 30 |
关键帧向后扩展帧数 |
freeze_head_tail_only |
bool | False |
是否只检测首尾冻结段 |
freeze_frame_max_velocity |
double | 0.1 |
冻结帧最大速度 |
freeze_frame_max_current |
double | 0.2 |
冻结帧最大电流 |
freeze_frame_training_weight |
double | 0.0 |
冻结帧训练权重,0 表示屏蔽 |
freeze_frame_min_duration |
int | 5 |
冻结段最小持续帧数 |
2.3 调用示例
ros2 run dataset_tools frame_detector --ros-args \
-p dataset_path:=/path/to/dataset/my_dataset \
-p clip_view:="['all']" \
-p enable_critical_detection:=true \
-p critical_frame_min_current_threshold:=0.5 \
-p critical_frame_max_velocity_threshold:=0.01 \
-p critical_frame_training_weight:=2.0 \
-p n_forward_expansion:=30 \
-p n_backward_expansion:=30 \
-p enable_freeze_detection:=true \
-p freeze_frame_max_velocity:=0.1 \
-p freeze_frame_max_current:=0.2 \
-p freeze_frame_training_weight:=0.0 \
-p freeze_head_tail_only:=false \
-p freeze_frame_min_duration:=5
处理完成后,逐帧权重写回数据集 parquet 的 training_weight 列(并登记到 meta/info.json 的 features),
权重分布可视化保存为数据集根目录下的 training_weights_distribution.png。后续 lerobot-train
通过 --sample_weighting.type=custom 读取该列,对每帧 loss 加权(见
sample_weighting.py:CustomWeighter)。
注意:
- 数组参数(
clip_view、gripper_pos、gripper_names)需按 ROS 2 参数语法传入,例如-p gripper_pos:="[-1]"或-p clip_view:="['all']",具体格式以 ROS 2 的参数解析器为准。 - 只有开启
--sample_weighting.type=custom的训练(ACT、PI05)依赖本步骤,否则数据集没有training_weight列。DP 不支持样本加权,知识蒸馏也不能与样本加权同时开启(见第 5、6 节),这两类训练可以跳过本步骤。 - 该节点必须先
source install/setup.bash才能被ros2 run找到。
3. ACT 原始规模模型训练
基于 LeRobot ACT 策略训练 banana 场景的原始规模 teacher 模型,作为后续 ACT 蒸馏的 teacher 候选。命令中显式覆盖架构参数,便于复现实验配置;源码默认值见下方参数表。
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=act \
--output_dir=/path/to/output/banana_act \
--job_name=act_train_banana \
--policy.push_to_hub=false \
--policy.device=cuda \
--policy.dim_model=2048 \
--policy.n_encoder_layers=4 \
--policy.n_decoder_layers=7 \
--policy.dim_feedforward=3200 \
--sample_weighting.type=custom \
--dataset.eval_split=0.1 \
--eval_steps=2000 \
--batch_size=16 \
--steps=500000 \
--log_freq=200 \
--save_freq=10000 \
--num_workers=10
实测:batch_size=16 时峰值显存约 18.9 GB,约 1.3 step/s。--sample_weighting.type=custom 读取第 2 节写入的帧权重,数据集未做帧检测时去掉该参数。--dataset.eval_split=0.1 --eval_steps=2000 按 episode 留出 10% 作验证集,每 2000 步计算一次 eval_loss,不需要时两者一起去掉(eval_steps>0 而 eval_split=0 会直接报错)。
关键参数(来源:configuration_act.py:ACTConfig):
| 参数 | 含义 | 默认值 |
|---|---|---|
--policy.dim_model |
Transformer 主隐藏维度;banana 原始规模示例覆盖为 2048 |
512 |
--policy.n_encoder_layers |
编码器层数 | 4 |
--policy.n_decoder_layers |
解码器层数;banana 原始规模示例覆盖为 7 |
1 |
--policy.dim_feedforward |
前馈网络扩展维度 | 3200 |
--steps |
总训练步数 | 100000 |
--save_freq |
每多少步保存一次 checkpoint,teacher 模型需保留可用 checkpoint | 20000 |
注意:原始规模模型训练完成后,蒸馏步骤需要 teacher 的 train_config.json。Checkpoint 目录结构由 train_utils.py:save_checkpoint 生成,train_config.json 位于 <output_dir>/checkpoints/<step>/pretrained_model/train_config.json;同时存在 <output_dir>/checkpoints/last 符号链接指向最新一次 checkpoint(见 train_utils.py:update_last_checkpoint,常量定义于 constants.py:47-50)。因此本示例中的 teacher 路径通常写为 /path/to/output/banana_act/checkpoints/last/pretrained_model/train_config.json。
4. ACT 小模型训练
使用同一份数据集训练小容量 ACT 策略,作为对照基线或轻量部署模型。
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=act \
--output_dir=/path/to/output/banana_act_small \
--job_name=act_train_banana_small \
--policy.push_to_hub=false \
--policy.device=cuda \
--policy.dim_model=1024 \
--policy.n_encoder_layers=4 \
--policy.n_decoder_layers=2 \
--policy.dim_feedforward=1200 \
--sample_weighting.type=custom \
--dataset.eval_split=0.1 \
--eval_steps=2000 \
--batch_size=16 \
--steps=500000 \
--log_freq=200 \
--save_freq=10000 \
--num_workers=10
与 banana 原始规模模型的差异在模型容量:dim_model 由 2048 降至 1024,n_decoder_layers 由 7 降至 2,dim_feedforward 由 3200 降至 1200。其余训练超参与原始规模模型一致,便于公平对比。实测 batch_size=16 时峰值显存约 7.8 GB。
5. ACT 小模型蒸馏
以 ACT 原始规模模型为 teacher 蒸馏小模型。开启 kd 后,student 在解码器输出后接一层 Linear(dim_model → decoder_out_dim) 再接动作头,并以 MSE 对齐冻结 teacher 的解码器输出(modeling_act.py:compute_kd_loss、modeling_act.py:412-413)。teacher 解码器输出宽度等于 teacher 的 dim_model,所以 decoder_out_dim 必须填 teacher 的 dim_model。
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=act \
--output_dir=/path/to/output/banana_act_kd \
--job_name=act_kd_banana \
--policy.device=cuda \
--policy.dim_model=1024 \
--policy.n_encoder_layers=4 \
--policy.n_decoder_layers=2 \
--policy.dim_feedforward=1200 \
--policy.kd=true \
--policy.teacher_train_config=/path/to/output/banana_act/checkpoints/last/pretrained_model/train_config.json \
--policy.decoder_out_dim=2048 \
--policy.push_to_hub=false \
--dataset.eval_split=0.1 \
--eval_steps=2000 \
--batch_size=16 \
--steps=500000 \
--log_freq=200 \
--save_freq=20000 \
--num_workers=10
实测:batch_size=16 时 teacher + student 峰值显存约 10.9 GB,日志中除监督 loss loss 外还会输出实际参与反传的 total_kd_loss。显存允许时可调大 batch_size(24 GB 卡上 batch_size=60 会 OOM)。
蒸馏专属参数(来源:configuration_act.py:ACTConfig):
| 参数 | 含义 | 默认值 |
|---|---|---|
--policy.kd |
开启知识蒸馏 | False |
--policy.teacher_train_config |
teacher 模型的 train_config.json 路径,用于重建 teacher 策略并加载其权重 |
None |
--policy.decoder_out_dim |
student 蒸馏投影层的输出维度,必须等于 teacher 的 dim_model(本例 teacher 为 2048) |
1024 |
注意:
--policy.kd、--policy.teacher_train_config、--policy.decoder_out_dim是策略配置(ACT/Diffusion 都有),需通过--policy.kd=true这种带前缀的形式传入。decoder_out_dim与 teacherdim_model不一致时,第一步就会报RuntimeError: The size of tensor a (1024) must match the size of tensor b (2048)。默认值1024只适用于dim_model=1024的 teacher。- teacher 加载逻辑在 lerobot_train.py:302-307:
get_teacher_model(cfg.policy.teacher_train_config, dataset, cfg.batch_size)以--config_path=<train_config.json> --resume=true重建 teacher,但喂给它的是 student 的数据集与 batch。因此 teacher 与 student 必须基于同一数据集(相同的观测 key、动作维度和归一化统计量),chunk_size也要保持一致(默认均为100),否则解码器输出无法逐元素对齐。 - 蒸馏不能与样本加权同时开启:同时传
--policy.kd=true与--sample_weighting.*会报NotImplementedError(lerobot_train.py:130),所以本命令不带--sample_weighting.type=custom。 - 本步骤
--save_freq=20000比直接训练更稀疏,蒸馏任务通常更关注最终 checkpoint。
6. DP 原始规模模型训练
使用 LeRobot 上游默认配置训练 Diffusion Policy(down_dims=(512,1024,2048)、diffusion_step_embed_dim=128、spatial_softmax_num_keypoints=32),作为后续 DP 蒸馏的 teacher 候选。命令中显式列出 crop_shape、horizon 等关键参数,其余采用默认值。
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=diffusion \
--output_dir=/path/to/output/dp_base \
--job_name=dp_train_base \
--policy.device=cuda \
--policy.crop_shape="[480,640]" \
--policy.crop_is_random=false \
--policy.push_to_hub=false \
--policy.n_obs_steps=6 \
--policy.horizon=64 \
--policy.n_action_steps=44 \
--policy.drop_n_last_frames=15 \
--dataset.eval_split=0.1 \
--eval_steps=2000 \
--batch_size=8 \
--steps=500000 \
--log_freq=200 \
--save_freq=6000 \
--num_workers=10
实测:batch_size=8 时峰值显存约 17.7 GB,约 2.2 step/s。
DP 不支持样本加权:DiffusionPolicy.forward 没有 reduction 参数,追加 --sample_weighting.type=custom 会在第一步报 TypeError: DiffusionPolicy.forward() got an unexpected keyword argument 'reduction'。因此 DP 命令不带该参数,即使数据集已做帧检测。
DP 原始规模模型关键参数(来源:configuration_diffusion.py:DiffusionConfig):
| 参数 | 含义 | 默认值 |
|---|---|---|
--policy.crop_shape |
图像裁剪尺寸 [H, W],原始规模使用 [480,640] |
None |
--policy.crop_is_random |
false 表示训练时使用中心裁剪而非随机裁剪 |
True |
--policy.n_obs_steps |
输入观测帧数 | 2 |
--policy.horizon |
扩散模型预测的动作序列长度 | 64 |
--policy.n_action_steps |
单次推理实际执行的动作步数 | 32 |
--policy.drop_n_last_frames |
丢弃末尾帧数,需满足 drop_n_last_frames = horizon - n_action_steps - n_obs_steps + 1(64-44-6+1=15) |
7 |
--policy.down_dims |
U-Net 各下采样阶段的特征维度,默认 (512,1024,2048) 即原始规模 |
(512,1024,2048) |
--policy.spatial_softmax_num_keypoints |
SpatialSoftmax 关键点数 | 32 |
--policy.diffusion_step_embed_dim |
扩散时间步嵌入维度 | 128 |
注意:horizon 必须是下采样因子 2 ** len(down_dims) 的整数倍,否则 diffusion 配置校验会报错(见 configuration_diffusion.py:209-215)。当前默认 down_dims=(512,1024,2048),下采样因子为 8,64 与 128 均满足该约束。--save_freq=6000 比 ACT 更频繁,DP 训练对 checkpoint 选取更敏感。
7. DP 小模型蒸馏
以 DP 原始规模模型为 teacher 蒸馏小模型。student 使用更小的裁剪尺寸和更小的扩散网络维度;U-Net 中间特征经对齐层投影到 teacher 的特征维度后做特征蒸馏。
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=diffusion \
--output_dir=/path/to/output/dp_kd \
--job_name=dp_kd \
--policy.device=cuda \
--policy.crop_shape="[240,320]" \
--policy.crop_is_random=false \
--policy.push_to_hub=false \
--policy.n_obs_steps=6 \
--policy.horizon=64 \
--policy.n_action_steps=44 \
--policy.drop_n_last_frames=15 \
--policy.down_dims="[256,512,1024]" \
--policy.spatial_softmax_num_keypoints=16 \
--policy.diffusion_step_embed_dim=64 \
--policy.kd=true \
--policy.teacher_train_config=/path/to/output/dp_base/checkpoints/last/pretrained_model/train_config.json \
--dataset.eval_split=0.1 \
--eval_steps=2000 \
--batch_size=8 \
--steps=500000 \
--log_freq=200 \
--save_freq=6000 \
--num_workers=10
实测:batch_size=8 时 teacher + student 峰值显存约 6.9 GB,日志输出 total_kd_loss。
DP 蒸馏与原始规模模型差异:
| 维度 | 原始规模模型 | 蒸馏小模型 | 说明 |
|---|---|---|---|
crop_shape |
[480,640] |
[240,320] |
蒸馏使用更小图像,降低视觉编码器开销;teacher 仍按自己的 crop_shape 处理同一 batch |
n_obs_steps / horizon / n_action_steps / drop_n_last_frames |
6 / 64 / 44 / 15 |
同左 | 必须与 teacher 完全一致,原因见下方注意事项 |
down_dims |
默认 (512,1024,2048) |
[256,512,1024] |
蒸馏使用更小的 U-Net 特征维度;末维必须是 1024,teacher 末维必须是 2048 |
spatial_softmax_num_keypoints |
默认 32 |
16 |
蒸馏关键点数减半 |
diffusion_step_embed_dim |
默认 128 |
64 |
蒸馏扩散时间步嵌入维度减半 |
kd |
否 | true |
蒸馏开启知识蒸馏 |
注意:
- DP 蒸馏的
--policy.teacher_train_config指向 DP 原始规模模型 checkpoint 中的train_config.json,路径形如/path/to/output/dp_base/checkpoints/last/pretrained_model/train_config.json。 - 时间窗口必须与 teacher 一致:teacher 由自己的
train_config.json重建,但吃的是 student 数据集按 student 的n_obs_steps/horizon采出的 batch,而 teacher 的compute_loss会断言horizon == self.config.horizon(modeling_diffusion.py:365)。student 用horizon=128而 teacher 为64时会直接AssertionError。如果确实需要更长的 student 时间窗口,teacher 也要用相同窗口重新训练。 - 特征维度是写死的:对齐层固定为
Conv1d(1024, 2048)、恢复层固定为ConvTranspose1d(2048, 1024)(modeling_diffusion.py:736-744),因此 studentdown_dims末维必须为1024,teacher 末维必须为2048(即默认(512,1024,2048))。DP 实现不读取--policy.decoder_out_dim,无需传入。 down_dims改为[256,512,1024]后下采样因子仍为8,horizon=64满足整除约束。- 与 ACT 相同,蒸馏不能与
--sample_weighting同时开启;DP 本身也不支持样本加权。 - teacher 与 student 的
policy.type必须一致:ACT 蒸馏用 ACT teacher,DP 蒸馏用 DP teacher,不能交叉。
8. PI05(π₀.₅)微调训练
PI05 是 PaliGemma(gemma_2b)VLM 加 gemma_300m action expert 的 flow-matching VLA 模型,总参数约 4.1B。实现位于 configuration_pi05.py:PI05Config 与 modeling_pi05.py。PI05 不从零训练,而是在预训练权重 lerobot/pi05_base 上用自采数据微调。
和 ACT/DP 相比,PI05 训练有以下几处根本差异,后文各步骤都围绕它们展开:
| 维度 | ACT / DP | PI05 |
|---|---|---|
| 初始化 | 随机初始化 | 必须加载 lerobot/pi05_base 预训练权重 |
| 语言输入 | 无 | 数据集每帧的 task 文本参与训练,部署时的 prompt 必须与之一致 |
| 状态输入 | 作为张量输入网络 | 归一化后离散成 256 档写进文本 prompt(Task: …, State: …;\nAction: ),不是网络张量输入,见 processor_pi05.py:Pi05PrepareStateTokenizerProcessorStep |
| 归一化 | MEAN_STD / MIN_MAX | STATE、ACTION 用 QUANTILES(q01/q99),数据集 stats 必须含分位数 |
| 图像 | 按 crop_shape 等配置处理 |
模型内部 resize-with-pad 到 224×224,数据集原始分辨率不必是 224 |
| 优化器/调度器 | 各策略预设 | 策略预设 AdamW(lr=2.5e-5)+ cosine(warmup 1000 / decay 30000),沿用 openpi |
| 显存 | 单卡消费级 GPU 可全量训练 | 全量微调需要大显存;24 GB 卡只能只训 action expert 或用 LoRA(见 8.3、8.4) |
8.1 前置准备
依赖:scripts/setup.sh 安装 lerobot 时已带上 pi extra(transformers、scipy,见 python_venv.sh),无需额外安装。LoRA 微调用的 peft 默认未安装,见 8.4。
预训练权重:lerobot/pi05_base 的 model.safetensors 约 14.5 GB(float32)。建议训练前先下载到 HF 缓存或本地目录,避免训练启动时阻塞下载:
source .shrc_local
# huggingface.co 不通时改走镜像
export HF_ENDPOINT=https://hf-mirror.com
hf download lerobot/pi05_base
# 或下载到固定目录,之后 --policy.pretrained_path 填该绝对路径
hf download lerobot/pi05_base --local-dir /path/to/pretrained/pi05_base
Tokenizer:PI05 的 prompt 用 PaliGemma tokenizer google/paligemma-3b-pt-224 编码(processor_pi05.py:152)。这是 gated 仓库:需要先在 Hugging Face 上同意 PaliGemma 许可,再执行 hf auth login(镜像访问同样需要 token),然后预下载:
source .shrc_local
python3 -c "from transformers import AutoTokenizer; AutoTokenizer.from_pretrained('google/paligemma-3b-pt-224')"
注意:微调时 tokenizer 配置继承自 pi05_base 的 policy_preprocessor.json,并原样写进产出 checkpoint。训练前请确认所用 pi05_base 快照的 policy_preprocessor.json 中 tokenizer_processor.tokenizer_name 为 google/paligemma-3b-pt-224。不要为了绕开 gated 下载把它改成其他 tokenizer(例如 bert-base-uncased):PaliGemma 的词表与 embedding 是绑定的,换 tokenizer 后训练能照常跑完、loss 也会下降,但语言输入已经是无意义的 token,部署后语言条件失效。
如果手头的 pi05_base 快照已被改过(tokenizer_name 不是 PaliGemma),不要直接拿它训练,也不要改共享的 HF 缓存,而是在本地目录建一份修正后的副本,再把 --policy.pretrained_path 指向该目录:
source .shrc_local
SRC=$(python3 -c "from huggingface_hub import snapshot_download; print(snapshot_download('lerobot/pi05_base'))")
DST=/path/to/pretrained/pi05_base
mkdir -p "$DST"
# 其余文件(含约 14.5 GB 的 model.safetensors)软链接到缓存,只重写 policy_preprocessor.json
for f in "$SRC"/*; do ln -sfn "$(readlink -f "$f")" "$DST/$(basename "$f")"; done
rm "$DST/policy_preprocessor.json"
python3 - "$SRC/policy_preprocessor.json" "$DST/policy_preprocessor.json" <<'PY'
import json, sys
cfg = json.load(open(sys.argv[1]))
for step in cfg["steps"]:
if step["registry_name"] == "tokenizer_processor":
step["config"]["tokenizer_name"] = "google/paligemma-3b-pt-224"
json.dump(cfg, open(sys.argv[2], "w"), indent=2)
PY
训练结束后可用 grep tokenizer_name <output_dir>/checkpoints/last/pretrained_model/policy_preprocessor.json 复核产出 checkpoint 用的是 PaliGemma tokenizer。
8.2 数据集要求
数据集仍按第 1 节由 bag_to_lerobot 生成。PI05 对数据集有以下额外要求,训练前逐项确认:
-
task 文本必须有意义且与部署一致。
bag_to_lerobot从 bag 元数据custom_data["lerobot.operator_prompt"]读取 task,缺失时回退到数据集dataset.yaml的default_task,再缺失则写空字符串(bag_to_lerobot.py:990-996)。该 prompt 在录制阶段由record_cli输入、episode_recorder写入。注意:record_cli的初始 prompt 是字面量"default_task"(record_cli.py:732),首次录制直接回车会把default_task这个字符串当作任务描述录进数据集。PI05 会把它当真实指令学习,因此录制时请输入完整的自然语言任务描述,例如Grasp the banana and put it on the plate。已经转换好的数据集不必重录,可直接用 LeRobot 自带的
lerobot-edit-dataset改写 task:source .shrc_local # modify_tasks 原地修改,先复制一份再改副本 cp -a /path/to/dataset/my_dataset /path/to/dataset/my_dataset_pi05 lerobot-edit-dataset --repo_id my_dataset_pi05 --root /path/to/dataset/my_dataset_pi05 \ --operation.type modify_tasks \ --operation.new_task "Grasp the banana and put it on the plate"modify_tasks总是原地修改--root指向的数据集:即使传了--new_repo_id/--new_root也会被忽略(lerobot_edit_dataset.py:558-564),而且会重写meta/tasks.parquet、meta/episodes/与全部data/parquet。所以一定要像上面这样先复制,再对副本操作。不同 episode 需要不同 task 时,改用--operation.episode_tasks '{"0": "Task A", "1": "Task B"}'。 -
stats 必须包含分位数。PI05 的
QUANTILES归一化需要meta/stats.json中observation.state与action的q01/q99,缺失时训练直接报错(normalize_processor.py:386-392)。当前 v0.6.0 补丁栈写数据集时默认计算q01/q10/q50/q90/q99,新转换的数据集无需处理;旧数据集需补算:source .shrc_local python libs/lerobot/src/lerobot/scripts/augment_dataset_quantile_stats.py \ --repo-id my_dataset --root /path/to/dataset/my_dataset -
观测 key 与部署契约一致。8.3 的命令会让模型
config.json的input_features直接取数据集特征。部署时inference_service要求 robot_config 契约提供input_features中的每一个 key,缺一个就拒绝启动(pipeline_policy_node.py:720-728)。因此:- 相机 key(如
observation.images.top、observation.images.wrist)必须与部署用 robot_config 的契约observations[].key同名。相机数量会固化进导出的 OM/HMM 图,每路相机对应一个静态图像输入。 - 数据集中
observation.state以外的非图像observation.*特征(例如为帧检测采集的observation.current)也会进入input_features。PI05 模型本身不使用它们,但部署契约仍须提供这些 key。数据集的契约是录制时写入dataset.yaml的快照,转换阶段无法再改,因此按下面三种方式之一处理:- 部署用的 robot_config 契约同样声明这些观测(例如
so101_single_arm.yaml就带observation.current),数据集无需改动; - 部署契约不提供这些观测时(例如
so101_pi05_ascend_310p_mock.yaml只有两路相机和observation.state),先按第 2 节完成帧检测,再用lerobot-edit-dataset生成去掉这些特征的新数据集用于训练。remove_feature输出到新目录,不改源数据集,并保留training_weight列:source .shrc_local lerobot-edit-dataset --repo_id my_dataset --root /path/to/dataset/my_dataset \ --new_repo_id my_dataset_pi05 --new_root /path/to/dataset/my_dataset_pi05 \ --operation.type remove_feature --operation.feature_names "['observation.current']" - 不需要帧检测时,可以直接用不含这些观测的 robot_config 契约录制。
- 部署用的 robot_config 契约同样声明这些观测(例如
- 契约
image.resize在采集与部署两侧保持一致。不同 resize 虽然都会被 resize-with-pad 到 224,但长宽比或画面裁切不一致会造成分布偏移。
- 相机 key(如
-
(可选)帧检测权重。按第 2 节执行
frame_detector后,数据集带training_weight列,PI05 训练可通过--sample_weighting.type=custom使用。 -
task 长度要放得进部署的 prompt 容量。PI05 会把离散化后的 state 拼进 prompt:
Task: <task>, State: <每维 0-255 的整数>;\nAction:(processor_pi05.py:83),所以 token 数等于固定开销(约 11)、state 部分(每维最多 4 个)与 task 本身之和。Ascend310P Torch-NPU 的torch-npu-fast部署把语言容量固定为 48 token,超出时直接拒绝请求,不会截断(见 8.8)。实测:6 维 SO-101 单臂在 state 最长时,空 task 已占 35 token,Grasp the banana and put it on the plate合计 45 token,task 只剩约 13 token 的余量;12 维双臂空 task 就要 59 token,只能用容量 200 的torch-npu-fidelity。可用下面的脚本按最坏情况估算:source .shrc_local python3 - <<'PY' from transformers import AutoTokenizer tok = AutoTokenizer.from_pretrained("google/paligemma-3b-pt-224") task, dims = "Grasp the banana and put it on the plate", 6 prompt = f"Task: {task}, State: {' '.join(['255'] * dims)};\nAction: " print(len(tok(prompt)["input_ids"])) # torch-npu-fast 要求 <= 48 PY
可用下面的脚本快速核对 task 与分位数:
source .shrc_local
python3 - /path/to/dataset/my_dataset <<'PY'
import json, sys
import pyarrow.parquet as pq
root = sys.argv[1]
print("tasks:", pq.read_table(f"{root}/meta/tasks.parquet").to_pydict())
stats = json.load(open(f"{root}/meta/stats.json"))
for key in ("observation.state", "action"):
print(key, "has q01/q99:", {"q01", "q99"} <= stats[key].keys())
print("features:", list(json.load(open(f"{root}/meta/info.json"))["features"]))
PY
8.3 微调命令(24 GB 显存:只训 action expert)
以下命令在 RTX 3090(24 GB)+ 当前 v0.6.0 补丁栈上实测可跑:batch_size=8 时峰值显存约 13.3 GB,约 2.0 s/step,可训练参数 693M / 总参数 4.1B。
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=pi05 \
--policy.pretrained_path=lerobot/pi05_base \
--policy.dtype=bfloat16 \
--policy.train_expert_only=true \
--policy.gradient_checkpointing=true \
--policy.device=cuda \
--policy.push_to_hub=false \
--output_dir=/path/to/output/pi05_my_task \
--job_name=pi05_my_task \
--batch_size=8 \
--steps=30000 \
--log_freq=200 \
--save_freq=5000 \
--num_workers=8
几个容易出错的地方:
- 用
--policy.type=pi05 --policy.pretrained_path=...,不要用--policy.path=lerobot/pi05_base。--policy.path会整份加载pi05_base的config.json,其中input_features是预训练数据的相机名(base_0_rgb、left_wrist_0_rgb、right_wrist_0_rgb),不会被数据集覆盖(factory.py:582)。这样训练出的模型会要求部署契约提供这三路相机,和 IB-Robot 契约对不上。用--policy.type新建配置再以pretrained_path加载权重,input_features就会取数据集的 key。同理也不建议用--rename_map把相机映射到 base 的命名上。 - 确认预训练权重确实加载成功。
PI05Policy.from_pretrained在找不到或读不了model.safetensors时只打印Returning model without loading pretrained weights,然后用随机初始化继续训练,不报错(modeling_pi05.py:1008-1011)。启动日志中必须出现✓ Loaded state dict from model.safetensors和All keys loaded successfully!。离线环境可加HF_HUB_OFFLINE=1,并把pretrained_path指向本地目录。 --dataset.repo_id+--dataset.root:本地数据集推荐用root指向数据集根目录,repo_id只作标识。- 视频解码后端:若日志出现
Could not load libtorchcodec,说明 torchcodec 与本机 FFmpeg/PyTorch 版本不匹配,可加--dataset.video_backend=pyav绕开。 --output_dir不能已存在:v0.6.0 在非--resume模式下遇到已存在目录直接报FileExistsError,不会覆盖。
需要帧检测加权时追加 --sample_weighting.type=custom。它读取数据集 training_weight 列,按帧加权 loss(sample_weighting.py:CustomWeighter)。PI05 的 forward 支持 reduction="none",已实测可用。
需要留出验证集监控 eval loss 时追加 --dataset.eval_split=0.1 --eval_steps=2000(按 episode 留出 10%,每 2000 步计算一次)。
8.4 LoRA 微调(24 GB 显存)
LoRA 冻结全部预训练权重,只训练注入的低秩 adapter(以及 full_training_modules 指定的少量模块),可以在 24 GB 卡上同时微调 VLM 与 action expert。先安装 peft(版本约束与 libs/lerobot/pyproject.toml 的 peft-dep extra 一致):
source .shrc_local
pip install "peft>=0.18.0,<1.0.0"
推荐对 VLM 语言模型与 action expert 的全部注意力/MLP 投影加 LoRA,并全量训练 action 输入输出投影与时间 MLP:
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=pi05 \
--policy.pretrained_path=lerobot/pi05_base \
--policy.dtype=bfloat16 \
--policy.gradient_checkpointing=true \
--policy.device=cuda \
--policy.push_to_hub=false \
--peft.method_type=LORA \
--peft.r=16 \
--peft.lora_alpha=32 \
--peft.target_modules='.*\.(language_model|gemma_expert)\..*\.(q_proj|k_proj|v_proj|o_proj|gate_proj|up_proj|down_proj)' \
--peft.full_training_modules='["action_in_proj","action_out_proj","time_mlp_in","time_mlp_out"]' \
--output_dir=/path/to/output/pi05_my_task_lora \
--job_name=pi05_my_task_lora \
--batch_size=8 \
--steps=30000 \
--log_freq=200 \
--save_freq=5000 \
--num_workers=8
实测:batch_size=8 时可训练参数约 29M,峰值显存约 11.4 GB,约 2.5 s/step。
说明:
- 不传
--peft.target_modules时使用 PI05 的默认目标(modeling_pi05.py:_get_default_peft_targets):只对 action expert 的q_proj/v_proj与action_in_proj/action_out_proj注入 LoRA,默认不全量训练任何模块(modules_to_save为空)。该正则沿用 PI0 的模块名,PI05 的时间 MLP(time_mlp_in/time_mlp_out)不在其中,默认完全冻结。可训练参数仅约 1.3M(实测 bs=8 约 10.7 GB)。VLM 完全不动,容量比 8.3 的只训 action expert 还小,一般不推荐;上面的推荐命令则通过--peft.full_training_modules把 action 投影与时间 MLP 设为全量训练。 - 使用 LoRA 时不要再开
--policy.train_expert_only/--policy.freeze_vision_encoder,冻结范围由 PEFT 决定。--sample_weighting.type=custom与 eval 参数用法与 8.3 相同。 - LoRA 的 checkpoint
pretrained_model/只含adapter_model.safetensors、adapter_config.json与use_peft=true的config.json,不能直接作为部署 bundle:OM 导出只在config.use_lora时重建 LoRA 层,HMM 导出以strict=True加载完整权重,直接拿 adapter checkpoint 去导出可能得到未加载微调权重的模型。部署前先把 adapter 合并回完整权重:
source .shrc_local
python3 - /path/to/output/pi05_my_task_lora/checkpoints/last/pretrained_model /path/to/output/pi05_my_task_lora_merged <<'PY'
import shutil, sys
from pathlib import Path
from peft import PeftConfig, PeftModel
from lerobot.configs.policies import PreTrainedConfig
from lerobot.policies.pi05.modeling_pi05 import PI05Policy
adapter_dir, out_dir = Path(sys.argv[1]), Path(sys.argv[2])
cfg = PreTrainedConfig.from_pretrained(adapter_dir)
cfg.use_peft = False
base_path = PeftConfig.from_pretrained(adapter_dir).base_model_name_or_path
policy = PI05Policy.from_pretrained(base_path, config=cfg)
merged = PeftModel.from_pretrained(policy, adapter_dir).merge_and_unload()
merged.save_pretrained(out_dir)
for f in adapter_dir.glob("policy_*processor*"):
shutil.copy2(f, out_dir / f.name)
print(sorted(p.name for p in out_dir.iterdir()))
PY
合并后的目录包含 use_peft=false 的 config.json、完整 model.safetensors 与 processor 文件,结构与 8.3 产出的 pretrained_model/ 相同,之后按 8.8 处理。实测合并结果可被 PI05Policy.from_pretrained 以严格模式加载(All keys loaded successfully!),并能正常输出 (1, 50, 6) 的动作 chunk。
8.5 全量 / 部分解冻微调(大显存)
只训 action expert 时 VLM 完全冻结,模型无法针对新场景调整视觉与语言表征;数据量较大或场景与预训练分布差异明显时,建议解冻更多参数:
| 方案 | 参数 | 可训练部分 | 显存需求(估算,batch 较小时) |
|---|---|---|---|
| 只训 action expert | --policy.train_expert_only=true |
action expert + 投影层(693M) | 实测 bs=8 约 13.3 GB,24 GB 卡可用 |
| LoRA(8.4) | --peft.* |
VLM + action expert 的 LoRA adapter 与 action 投影(约 29M) | 实测 bs=8 约 11.4 GB,24 GB 卡可用 |
| 冻结视觉编码器 | --policy.freeze_vision_encoder=true |
Gemma 语言模型 + action expert | bf16 下参数、梯度、Adam 状态合计已超 24 GB,需 40 GB 以上 |
| 全量微调 | 两者均为 false(默认) |
全部 4.1B 参数 | 建议 80 GB 卡(A100/H100)或多卡 |
全量微调示例(只需把 8.3 命令中的 train_expert_only 去掉,并按显存调大 batch):
source .shrc_local
lerobot-train \
--dataset.repo_id=my_dataset \
--dataset.root=/path/to/dataset/my_dataset \
--policy.type=pi05 \
--policy.pretrained_path=lerobot/pi05_base \
--policy.dtype=bfloat16 \
--policy.gradient_checkpointing=true \
--policy.device=cuda \
--policy.push_to_hub=false \
--output_dir=/path/to/output/pi05_my_task_full \
--job_name=pi05_my_task_full \
--batch_size=32 \
--steps=30000 \
--log_freq=200 \
--save_freq=5000 \
--num_workers=8
多卡训练可用 accelerate launch --multi_gpu --num_processes=<N> $(which lerobot-train) ...,此时有效 batch 为 batch_size × N。
8.6 关键参数
参数默认值来源:configuration_pi05.py:PI05Config。“部署约束”一列来自 OM/HMM 转换与运行时的实现(见 om-convert PI05 参考、hmm-convert)。修改这些参数前先确认目标部署后端支持。
| 参数 | 含义 | 默认值 | 部署约束 / 建议 |
|---|---|---|---|
--policy.pretrained_path |
预训练权重(Hub id 或本地目录) | None |
必填,lerobot/pi05_base 或其本地副本 |
--policy.dtype |
模型参数精度;bfloat16 时视觉塔与 LayerNorm 仍保持 float32 |
float32 |
训练可用 bf16 省显存;导出时统一转 fp16,不受影响 |
--policy.train_expert_only |
冻结整个 VLM,只训 action expert 与投影层 | False |
24 GB 卡必须开启 |
--policy.freeze_vision_encoder |
只冻结 SigLIP 视觉编码器 | False |
— |
--policy.gradient_checkpointing |
梯度检查点,以计算换显存 | False |
建议开启 |
--policy.chunk_size |
单次预测的动作步数 | 50 |
固化进 action expert 的噪声输入形状 (1, chunk_size, max_action_dim),改动需重新导出;建议保持 50 |
--policy.n_action_steps |
LeRobot 内单次执行步数 | 50 |
IB-Robot 编译后运行时返回整个 chunk,实际执行步数由动作执行侧控制 |
--policy.num_inference_steps |
flow matching 去噪步数 | 10 |
训练保持 10,不要修改。OM/量化 profile 按 10 步构建;Ascend310P Torch-NPU 的旧版 torch-npu 部署(无 extra_parameters)要求恰为 10;命名部署在运行时自行覆盖求解器与步数(torch-npu-fidelity 为 Euler 10 步,torch-npu-fast 为 AB2 3 步),与该字段无关 |
--policy.tokenizer_max_length |
prompt token 上限(task + 离散化 state 共用) | 200 |
OM 的 VLM 图按 200 导出;不要修改。Ascend310P Torch-NPU 命名部署另有固定的语言容量(torch-npu-fidelity 为 200,torch-npu-fast 为 48),超出直接拒绝,task 文本要按 8.2 第 5 条控制长度 |
--policy.max_state_dim / --policy.max_action_dim |
state/action 补零到的维度 | 32 / 32 |
与预训练权重绑定,不要修改 |
--policy.image_resolution |
模型输入图像尺寸 | (224, 224) |
HMM 按每图 256 token 计算 prefix 长度;不要修改 |
--policy.paligemma_variant / --policy.action_expert_variant |
模型规模 | gemma_2b / gemma_300m |
与预训练权重及 HMM/量化 profile 绑定;不要修改 |
--policy.use_relative_actions |
训练相对动作(action - state) |
False |
IB-Robot 部署链路未验证相对动作,建议保持 False |
--policy.optimizer_lr |
峰值学习率 | 2.5e-5 |
— |
--policy.scheduler_warmup_steps / --policy.scheduler_decay_steps |
cosine 调度的 warmup / decay 步数 | 1000 / 30000 |
--steps 小于 decay 步数时自动按比例缩放 |
关于状态 prompt 的一致性:PI05 的状态以离散化数字的形式进入 prompt。当前 v0.6.0 栈的 Pi05PrepareStateTokenizerProcessorStep 只离散化真实维度(SO-101 为 6 维),不补零到 max_state_dim;早期 v0.5.1 栈的 0015 补丁曾补零到 32 维,该补丁已移除,部署侧也不再兼容按旧 prompt 训练的 checkpoint。因此 PI05 必须用当前补丁栈训练,不要复用 v0.5.1 时期训练的 PI05 checkpoint。
8.7 不支持或不建议的用法
- 知识蒸馏:PI05 虽然有
--policy.kd/--policy.teacher_train_config字段,但其forward收到 teacher 后只是把普通监督 loss 记为total_kd_loss,没有任何 teacher 监督项(modeling_pi05.py:1256-1304)。开启后只会额外加载一个 teacher 占用显存,不会产生蒸馏效果。另外 KD 与--sample_weighting同时开启会直接报NotImplementedError(lerobot_train.py:129-130)。 - 直接部署 LoRA checkpoint:adapter checkpoint 必须先按 8.4 合并回完整权重再进入 8.8 的部署流程。
--policy.path/--rename_map对齐 base 相机名:原因见 8.3。
8.8 训练产物与部署衔接
PI05 checkpoint 结构与第 9 节通用结构一致,pretrained_model/ 下包含 config.json、model.safetensors(bf16 训练约 8.8 GB)、policy_preprocessor.json、policy_postprocessor.json、归一化统计量 policy_*_step_*_normalizer_processor.safetensors 和 train_config.json;training_state/ 只训 action expert 时约 2.1 GB,全量微调时显著更大。
续训:
source .shrc_local
lerobot-train \
--resume=true \
--config_path=/path/to/output/pi05_my_task/checkpoints/last/pretrained_model/train_config.json
把 checkpoints/<step>/pretrained_model 作为部署 bundle 前,还需要完成以下步骤:
- 检查
config.json:input_features的相机与状态 key 与部署 robot_config 契约逐一对应(见 8.2 第 3 条),type为pi05,num_inference_steps、chunk_size、tokenizer_max_length符合 8.6 的部署约束;task 的 token 数在目标部署的语言容量以内(8.2 第 5 条)。 - 把 tokenizer 放进 bundle:运行时只接受解析到 bundle 内部的 tokenizer 引用,OM/HMM 导出也只读本地文件,不会在线下载。需要把
google/paligemma-3b-pt-224tokenizersave_pretrained到<bundle>/tokenizer,并把policy_preprocessor.json中的tokenizer_name改为 bundle 内相对路径。具体流程见 om-convert PI05 参考。 - 注册 Torch 部署并验证:本机 CPU/CUDA 用
ros2 run model_utils package-torch-deployment --bundle-root <bundle> --devices cpu cuda注册,然后按ibrobot-inference-verifyskill 做端到端推理验证。Ascend310P 上用 Torch-NPU 原生推理时,改用ros2 run model_utils package-pi05-ascend-deployments --bundle-root <bundle>,一次注册torch-npu-fidelity(Euler 10 步,语言容量 200)和torch-npu-fast(AB2 3 步,语言容量 48)两种部署。然后在 robot_config 推理管线中用deployment: torch-npu-fast或deployment: torch-npu-fidelity选择其一,并设置runtime_options: {model_dtype: fp16}。参考配置为so101_pi05_ascend_310p_mock.yaml,默认 fast;按 8.2 第 5 条估算的 prompt 超过 48 token 时选 fidelity。详见 pi05_ascend_310p README。 - 转换板端模型:Ascend OM 走
om-convertskill(pi05-export),后摩 HMM 走hmm-convertskill。两者校准和精度对比用的 observation batch 都应从本次训练数据集生成(ros2 run model_utils observation-batch dataset --dataset-root <dataset> --policy-path <bundle> ...),不要用随机输入。 - 部署 prompt 与训练 task 一致:robot_config 推理管线的
default_task,或下发DispatchInfer时的prompt,必须与数据集meta/tasks.parquet中的 task 文本一致(大小写与措辞相同最稳妥)。多任务数据集按需下发对应 task。
9. 通用训练参数说明
以下参数在多个训练任务中复用,含义一致(来源:train.py:TrainPipelineConfig 与 default.py:DatasetConfig):
| 参数 | 含义 | 默认值 |
|---|---|---|
--dataset.repo_id |
数据集标识;本地数据集配合 --dataset.root 使用,此时只作标识 |
— |
--dataset.root |
本地数据集根目录 | None |
--dataset.video_backend |
视频解码后端。日志出现 Could not load libtorchcodec(torchcodec 与本机 FFmpeg/PyTorch 不匹配)时设为 pyav |
自动选择 |
--dataset.eval_split |
按 episode 留出作验证集的比例,[0, 1) |
0.0 |
--policy.type |
策略类型,act、diffusion 或 pi05 |
— |
--output_dir |
训练输出根目录;非 --resume 时必须不存在,否则报 FileExistsError |
— |
--job_name |
任务名,用于日志与 WandB 运行名,不影响 checkpoint 路径 | 策略类型 |
--policy.device |
训练设备,cuda / cuda:0 / cpu / mps |
— |
--policy.push_to_hub |
是否在训练末尾推送到 HuggingFace Hub。默认 True;纯本地训练请显式设 false,否则启动时会因 repo_id 缺失报错(见 train.py:263-270) |
True |
--batch_size |
训练批大小,蒸馏任务常显存压力更大,需按 GPU 显存调整 | 8 |
--steps |
总训练步数 | 100000 |
--log_freq |
每多少步打印一次训练日志 | 200 |
--eval_steps |
每多少步在验证集上计算一次 eval_loss,0 表示关闭;大于 0 时要求 --dataset.eval_split > 0 |
0 |
--max_eval_samples |
每次评估最多使用的验证样本数,0 表示全部 |
0 |
--save_freq |
每多少步保存一次 checkpoint(训练结束时总会再保存一次) | 20000 |
--sample_weighting.type |
样本加权策略;custom 读取第 2 节写入的 training_weight 列。仅 ACT、PI05 支持,且不能与蒸馏同时使用 |
不启用 |
--num_workers |
DataLoader 工作进程数 | 4 |
--resume |
续训时设为 true,并用 --config_path 指向已有 checkpoint 的 train_config.json |
False |
--seed |
训练与评估随机种子 | 1000 |
v0.6.0 的训练配置中已没有 --eval_freq(仿真环境评估频率为 --env_eval_freq,真机数据集训练用不到),也没有 --ada_weight / --ada_weight_freq / --damping_coefficient;旧命令中的这些参数需按上表替换为 --dataset.eval_split + --eval_steps 与 --sample_weighting.type=custom。
Checkpoint 目录结构(由 train_utils.py:save_checkpoint 生成):
<output_dir>/
├── checkpoints/
│ ├── <step>/ # 形如 005000
│ │ ├── pretrained_model/
│ │ │ ├── config.json # policy config
│ │ │ ├── model.safetensors # policy weights(LoRA 时为 adapter_model.safetensors + adapter_config.json)
│ │ │ ├── train_config.json # train config(teacher 蒸馏、续训用)
│ │ │ ├── policy_preprocessor.json # 预处理 pipeline
│ │ │ ├── policy_postprocessor.json
│ │ │ └── policy_*_normalizer_processor.safetensors # 归一化统计量
│ │ └── training_state/
│ │ ├── optimizer_param_groups.json
│ │ ├── optimizer_state.safetensors
│ │ ├── rng_state.safetensors
│ │ ├── scheduler_state.json # 仅在使用学习率调度器时存在(如 PI05)
│ │ └── training_step.json
│ └── last -> <step>/ # 指向最新 checkpoint 的符号链接
└── tensorboard/ # TensorBoard 日志
10. 路径占位符说明
文中所有 /path/to/xxx 需替换为环境中的实际绝对路径。常用占位符对应关系:
| 占位符 | 含义 | 示例 |
|---|---|---|
/path/to/record_root |
episode_recorder 录制目录,含 dataset.yaml 契约快照与 episodes/ 下的 bag |
~/datasets/raw/my_task |
/path/to/dataset/my_dataset |
LeRobot v3 数据集根目录(含帧权重) | ~/datasets/my_dataset |
/path/to/output/... |
训练输出根目录 | ~/training_outputs/... |
/path/to/output/banana_act/checkpoints/last/pretrained_model/train_config.json |
ACT teacher 的 train_config | ~/training_outputs/banana_act/checkpoints/last/pretrained_model/train_config.json |
/path/to/output/dp_base/checkpoints/last/pretrained_model/train_config.json |
DP teacher 的 train_config | ~/training_outputs/dp_base/checkpoints/last/pretrained_model/train_config.json |
/path/to/pretrained/pi05_base |
lerobot/pi05_base 预训练权重的本地副本(可直接用 Hub id 代替) |
~/pretrained/pi05_base |
/path/to/output/pi05_my_task |
PI05 微调输出根目录 | ~/training_outputs/pi05_banana |
/path/to/output/pi05_my_task_lora_merged |
LoRA adapter 合并后的完整权重目录 | ~/training_outputs/pi05_banana_lora_merged |
建议将训练用路径统一放在同一根目录下(如 ~/training_outputs 与 ~/datasets),并在蒸馏前确认 teacher checkpoint 路径存在且包含 train_config.json。