← 返回报告首页
训练:右臂 batch 16 · 断开 state 捷径
2026-09-08 · 数据集 xlerobot-team/xlerobot-right-pick-cup-20260826-0042(40 集 / 16487 帧,视角已确认跟实机一致)
★ 为什么是 batch 16 而不是 8 ★
20000 × 16 ÷ 16487 = 19.4 遍 —— 跟已有的
right-pick-b16-3cam-u20k 的 E = 19.4 完全相同。
两次训练除了 state 维度之外每一项都一样,所以斜率如果变了,只可能是 state 造成的。
用 batch 8 会同时改掉 E(9.7 遍),就变成两个变量,对照作废。
★ 千万不要加 --policy.input_features=null ★
这次靠的正是它缺席时的行为:smolvla_base 自带的 state[6] 会盖住数据集的 17 维
(policies/factory.py:312 的 if not cfg.input_features:),
而截断保留的前 6 维是左臂 —— 右臂任务里左臂全程不动,捷径就断了,模型只能去看画面。
面板「租卡训练」按钮生成的命令会自动带上这一行,还会带 --rename_map,
所以这次别用面板生成,直接用下面 ③ 那条。
①本机:把数据集推到 Hub(在 Jetson 上跑)
huggingface-cli login # 粘贴 write token
huggingface-cli upload-large-folder --repo-type=dataset \
xlerobot-team/xlerobot-right-pick-cup-20260826-0042 \
~/.cache/huggingface/lerobot/xlerobot-team/xlerobot-right-pick-cup-20260826-0042
数据集不会在录制之后自动补传 —— 只在录制结束当场传一次。先查
meta/upload.json 确认;已经在 Hub 上就跳过这步。
②租卡 + 装依赖
租什么
| 项 | 选择 | 为什么 |
| 平台 | RunPod | 之前几次训练都在这上面跑的,流程已经趟通 |
| 模板 | PyTorch 2.8 / CUDA 12.8 | torch 自带,不用自己装;torchcodec 要求 cu128 |
| 显卡 | RTX 4090 | 够用。注意:batch 16 在 4090 上的显存占用从来没有人实测过 ——
以前文档里那句「实测约 4.6 GB」是假的,别信。用 ③ 的 40 步基准去量 |
| 存储 | ≥ 50 GB 卷,挂 /workspace | checkpoint 每 2000 步存一次,加数据集缓存 |
进终端之后逐条跑
nvidia-smi
pip install -U "lerobot[dataset,training,smolvla]" huggingface_hub
hf auth login # 粘贴 HF write token
hf auth whoami # 确认能读数据集 + 能写 model 仓库(别等跑完才撞 401/403)
tmux new -s train # ★ 断线不杀训练 ★ Ctrl-B 再 D 脱离,tmux attach -t train 回来
★ 开付费 20k 之前先验证解码后端 ★
新数据集是 AV1。
torchcodec 装不上会
静默退回 pyav,训练照跑但慢几倍 ——
白烧租金。
python -c "import torch,torchcodec;print('torch',torch.__version__,'| torchcodec',torchcodec.__version__)"
ffmpeg -decoders | grep -Ei "cuvid|nvdec" # 要看到 av1_cuvid
预期
torch 2.8.0+cu128 /
torchcodec 0.7.0。看不到
av1_cuvid 就先解决它,别开正式训练。
tmux 不是可选项:家里网一断,没 tmux 训练就死了,而 Pod 照样计费。
③先 40 步基准,再开正式训练
40 步基准 — 把 --steps=20000 临时改成下面两行
--steps=40 \
--save_checkpoint=false \
| 看什么 | 正常范围 | 不对的话 |
step/s | ~4–5 | 明显更慢 → torchcodec 没生效,回 ② |
mem_gb | 这次的实测目标 | OOM → 降到 batch 8,但要在报告里记下 E 变成 9.7,对照失效 |
loss | 在降 | 不降 → 先别开 20k |
★ 正式命令 — 整段直接复制 ★
lerobot-train \
--policy.path=lerobot/smolvla_base \
--dataset.repo_id=xlerobot-team/xlerobot-right-pick-cup-20260826-0042 \
--batch_size=16 \
--num_workers=8 \
--steps=20000 \
--save_freq=2000 \
--log_freq=100 \
--policy.scheduler_decay_steps=20000 \
--dataset.eval_split=0.0 \
--dataset.image_transforms.enable=true \
--dataset.image_transforms.max_num_transforms=5 \
--dataset.video_backend=torchcodec \
--seed=1000 \
--policy.device=cuda --policy.push_to_hub=false \
--output_dir=/workspace/outputs/train/right_b16_state6 --job_name=right_b16_state6 \
2>&1 | tee /workspace/right_b16_state6.log
| 这条命令里故意没有的 | 为什么 |
--policy.input_features=null | 整个实验就靠它缺席。加上 state 就是 17 维,捷径没断,白训 |
--policy.output_features=null | 同上,面板会连着一起加 |
--rename_map | 不需要。preprocessor 两套键名都认。加了反而多一个变量 |
--policy.empty_cameras | 3 路相机全留,正好占满 SmolVLA base 的 3 个槽 |
--policy.scheduler_decay_steps=20000 必须跟 --steps 一致
—— lerobot 的默认是 30000(optim/schedulers.py:149 会自动缩放,但显式写死更省心)。
④训完第一件事:验 state 维度(别直接上机)
python -c "
import json
c=json.load(open('/workspace/outputs/train/right_b16_state6/checkpoints/020000/pretrained_model/config.json'))
s=c['input_features']['observation.state']['shape']
print('state:', s, '->', 'OK,捷径断了' if s==[6] else '★ 17 维,捷径没断,白训了 ★')
"
期望 [6]。如果是 [17],这批作废 —— 别浪费实机时间,更别浪费 Pod 时间去传它。
⑤推回 Hub — VERIFY → UPLOAD → VERIFY → STOP
# 看到 020000 且 last -> 020000 才算完成(目录名不算数)
ls -lah /workspace/outputs/train/right_b16_state6/checkpoints
hf repo create Suyang99/xlerobot-smolvla-right-b16-state6 --repo-type model --private --exist-ok
hf upload Suyang99/xlerobot-smolvla-right-b16-state6 \
/workspace/outputs/train/right_b16_state6/checkpoints/020000 checkpoints/020000 --repo-type model
hf upload Suyang99/xlerobot-smolvla-right-b16-state6 \
/workspace/right_b16_state6.log right_b16_state6.log --repo-type model # 面板 loss 曲线要用
# 多留几档(016000 / 012000)逐个按同一套实机流程试,别只留最后一档
确认上传成功之后再 Stop/Terminate Pod。
顺序是 VERIFY → UPLOAD → VERIFY → STOP。忘关会一直计费。
模型在个人账号 Suyang99 下,数据集在组织 xlerobot-team 下 —— 两者是分开的,别混。
⑥本机拉回来,测视觉斜率
hf download Suyang99/xlerobot-smolvla-right-b16-state6 \
--local-dir ~/Robotic_challenge/05-training/runs/right_b16_state6
cd ~/Robotic_challenge/03-software/scripts
python vision_sweep.py \
~/Robotic_challenge/05-training/runs/right_b16_state6/checkpoints/020000/pretrained_model \
~/Robotic_challenge/data/huggingface/lerobot/xlerobot-team/xlerobot-right-pick-cup-20260826-0042 \
"Pick up the cup with the right arm." right 20
| pan 斜率 | 结论 |
| ≥ +0.15 | 捷径断掉起作用了,值得上机(参照:会看图的两个模型是 +0.210 / +0.214) |
| +0.05 ~ +0.15 | 有效果但弱 |
| ≤ +0.01 | 跟不看图的两个(+0.004 / +0.006)一样,别上机,回头查数据 |
★ vision_sweep.py 十分钟出结果,比实机试跑准得多 —— 实机失败有五种原因,斜率只测一件事:模型到底看不看图。
为什么是这个数据集
| 数据集 | 集数 / 帧数 | 视角 | 任务臂 | 可用 |
| right-pick-cup-20260826-0042 | 40 / 16487 | ✓ 跟实机一致 | 右 | ← 选它(换爪后的当前数据集) |
| cup-grasp-20260820-0230 | 50 / 19453 | ✓ 跟实机一致 | 右 | 备选,集数多但更旧(旧爪) |
| right-pick-cup-sep2-*(4 个) | — | ✗ 视角不对 | 右 | ⛒ 不可用 |
| left-pick / left-place 全部 | — | — | 左 | ⛒ 这个方案用不了:截断保留的正是它们在动的那条臂 |
开跑前跑一遍预检
cd ~/Robotic_challenge/03-software/scripts
python check_train_cmd.py '<把上面整条命令粘进来>'
六项检查,全是踩过的坑(rename_map、input_features、E、decay_steps、数据集视角、任务臂)。
生成任何训练命令之前先跑它。
渐进训练(你提的思路)
「前几个 epoch 去掉 state、后面加回来」是真实做法,lerobot 里 groot 有现成的
state_dropout_prob(训练时按概率丢掉 state,比分阶段更常用)。
但 SmolVLA 没有这个参数,而且它的 prepare_state 直接取 batch[OBS_STATE],完全去掉会 KeyError。
所以先用上面这个「截断」方案(已被四个模型验证,斜率差 50 倍);如果它在新模型上成立,再考虑给 SmolVLA 移植 state_dropout。