← 返回报告首页

训练:右臂 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.8torch 自带,不用自己装;torchcodec 要求 cu128
显卡RTX 4090够用。注意:batch 16 在 4090 上的显存占用从来没有人实测过 —— 以前文档里那句「实测约 4.6 GB」是假的,别信。用 ③ 的 40 步基准去量
存储≥ 50 GB 卷,挂 /workspacecheckpoint 每 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_cameras3 路相机全留,正好占满 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-004240 / 16487✓ 跟实机一致右← 选它(换爪后的当前数据集)
cup-grasp-20260820-023050 / 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。