RLinf v0.4 基于ApxInf 在Libero上评测PI 0.5
概述
Rlinf 是一个面向 Embodied AI(具身智能) 和 Agentic AI(智能体 AI) 的开源强化学习基础框架。
ApxInf 是一个进程内的 Rust/CUDA 推理引擎。RLinf 通过 APXinf-robo 提供的 L1 bare-model 接口(load_bare_model 与 Model.infer_rgb)把它接入成 rollout 后端:环境交互、观测 batch、OpenPI 预处理与后处理、动作块执行仍由 RLinf 负责,GPU 上的模型推理交给 ApxInf。
通过百舸快速开始,您可以直接在开发机镜像内启动 LIBERO-10 上的 π₀.₅ 评测任务,并读取 eval/success_once 成功率、评测日志与 TensorBoard 指标。当前接入只支持在 LIBERO 上评测 π₀.₅,不支持训练、解耦 rollout 或非具身任务。
适用场景
- 在 LIBERO-10 上评测 π₀.₅(
lerobot/pi05_libero_base)基线成功率。 - 用进程内推理后端替代独立的 rollout 服务,观察同机评测的启动时间与显存占用。
- 排查 checkpoint 文件、
norm_stats.json、CUDA binding、EGL 渲染与轨迹预算相关的问题。 - 不适合:训练任务、解耦 rollout、π₀.₅ 以外的模型。
评测规格参考 & 环境配置
推荐评测配置如下:
| 项目 | 推荐配置 |
|---|---|
| GPU | 单卡即可;环境与 rollout 共址,显存按一份 bf16 权重加一个并行环境估算,建议单卡 ≥ 24GiB |
| CPU | 建议 16 核以上 |
| 容器内存 | 建议 64GiB 以上 |
| 存储空间 | 建议不少于 100G(checkpoint、LIBERO 资产与日志) |
项目目录配置如下表所示:
| 项目 | 推荐配置 |
|---|---|
| CUDA / 驱动 | 使用镜像自带的驱动与 CUDA,不要单独升级 |
| 工作目录 | /root/workspace/RLinf |
| Python 环境 | /root/workspace/RLinf/.venv(openpi + libero 依赖) |
| ApxInf 运行环境 | 镜像内预装 apxinf_py(CUDA binding)与 apxinf_robo(L1 封装) |
| 评测入口 | /root/workspace/RLinf/evaluations/run_eval.sh |
| 评测配置 | /root/workspace/RLinf/evaluations/libero/libero_10_apxinf_pi05_eval.yaml |
| 模型目录 | /root/workspace/models/pi05_libero_base |
| 日志目录 | /root/workspace/RLinf/logs |
配置把 env 与 rollout 都放在全部 GPU 上,每个 rollout worker 在自己的 GPU 上常驻一份 bf16 权重;单卡节点就是一份拷贝、一组并行环境。
评测产出与结果口径
单卡默认配置的评测协议是:LIBERO-10 的 10 个任务,每个任务 10 个 reset state,共 100 条轨迹(10 个并行环境 × 10 个 rollout epoch)。终端打印的 eval/success_once 就是这批轨迹的成功率。多卡节点上实际并发的环境数会变,见「轨迹预算」。
评测完成后会留下以下产物:
| 产物 | 路径 |
|---|---|
| 主评测日志 | <LOG_DIR>/eval_embodiment.log |
| 指标表 | <LOG_DIR>/metrics.log |
| TensorBoard event | <LOG_DIR>/tensorboard/ |
| 启动期完整 stdout | 启动脚本 tee 到 /root/apxinf-eval.out |
其中 <LOG_DIR> 由 run_eval.sh 生成,形如 /root/workspace/RLinf/logs/20261020-15:30:00-libero_10_apxinf_pi05_eval。配置文件里的 logger.log_path: "../results" 会被这个值覆盖,不需要也不能在配置里指定日志目录。
成功率取决于 checkpoint 权重与实际并行环境数。8 卡节点按 10 epoch × 80 环境跑满 800 条轨迹时,实测 eval/success_once=0.9525、eval/success_at_end=0.82125、eval/return=4.7625、eval/reward=0.01978,整轮评测约 12 分钟;单卡或改变环境数后数值会有差异,以本次运行写入 metrics.log 的结果为准。
准备工作
如果您所在网络环境访问 HuggingFace 较慢,建议提前配置 HuggingFace 镜像源,或配置代理加速。
huggingface 镜像配置:
1export HF_ENDPOINT=https://hf-mirror.com
2export HF_HUB_ENABLE_HF_TRANSFER=1
如果所在环境已提供该 checkpoint 的 BOS 地址,直接从 BOS 拉取到工作目录,避免依赖外部网络;本文的步骤二把 BOS 列为推荐方式。
环境说明
说明:本文中的路径均以百舸开发机内的
/root/workspace为例。实际使用时,可根据任务挂载目录调整。
当前使用的目录如下:
1/root/workspace/RLinf
2/root/workspace/RLinf/.venv
3/root/workspace/models/pi05_libero_base
4/root/workspace/RLinf/logs
APXinf-robo 与 apxinf_py 已预装在镜像内,无需执行 git clone、maturin build 或 pip install。步骤一仅做导入验证;如果导入失败,说明当前 shell 激活的不是镜像自带的那个 .venv,而不是需要重新编译。
步骤一:确认镜像内的 ApxInf 运行环境
激活镜像自带的 Python 环境,验证 binding 可以导入:
1source /root/workspace/RLinf/.venv/bin/activate
2cd /root/workspace/RLinf
3
4python -c 'import apxinf_py; print("apxinf_py", apxinf_py.__version__)'
5python -c 'import apxinf_robo; print("apxinf_robo", apxinf_robo.__version__)'
6python -c 'from apxinf_robo import load_bare_model; print("load_bare_model ok")'
三行都能正常输出,说明 ApxInf 的 CUDA binding 与 L1 封装在该环境内可用。
再确认 GPU 与驱动:
1nvidia-smi --query-gpu=index,name,driver_version,memory.total --format=csv,noheader
镜像预装的 binding 面向镜像发布时的目标 GPU 架构(例如 sm_87、sm_89、sm_101、sm_110)。换到其他架构的机器上,import 仍可能成功,但加载权重时会因为找不到对应 kernel 而失败;这种情况需要换用为目标架构构建的镜像(APXINF_CUDA_ARCH 是该 binding 的构建参数),而不是修改本文的评测命令。运行时按设备 SM 精确匹配编译目标,所以一份 binding 要覆盖多种卡,必须在构建时就把架构列全。本文实测的 binding 以 APXINF_CUDA_ARCH=sm_100,sm_103,sm_120 构建,cuobjdump --list-elf 能看到这三个 sm_ 段;B30Z(compute 10.3)命中 sm_103。
渲染后端不需要手动配置:run_eval.sh 在 MUJOCO_GL 未设置时会自动设为 egl,同时设置 PYOPENGL_PLATFORM=egl。
步骤二:准备 π₀.₅ LIBERO Checkpoint
评测需要一个包含 config.json、model.safetensors 和 norm_stats.json 的 checkpoint 目录。tokenizer.model 不是必需文件:ApxInf 用 checkpoint 自带的 tokenizer 配置加载,实测缺少该文件也能正常推理。
方式一:通过 BOS 下载(推荐)
百舸环境已提供该 checkpoint 与归一化统计的 BOS 地址,内网直连,不受 HuggingFace 出口影响。目录路径必须带结尾 /:
1bos:/aihc-models-bj/lerobot/pi05_libero_base/
2bos:/aihc-models-bj/openpi/pi05_libero/assets/physical-intelligence/libero/norm_stats.json
下载前可以先确认前缀存在:
1bcecmd bos ls bos:/aihc-models-bj/lerobot/pi05_libero_base/
bcecmd bos cp 支持一次拷贝整个前缀,权重与 norm_stats.json 分两条命令拉取;norm_stats.json 必须落在 checkpoint 根目录:
1mkdir -p /root/workspace/models/pi05_libero_base
2
3bcecmd bos cp -r \
4 bos:/aihc-models-bj/lerobot/pi05_libero_base/ \
5 /root/workspace/models/pi05_libero_base/
6
7bcecmd bos cp \
8 bos:/aihc-models-bj/openpi/pi05_libero/assets/physical-intelligence/libero/norm_stats.json \
9 /root/workspace/models/pi05_libero_base/norm_stats.json
同机实测这份 BOS 权重约 17G,通常一分钟内完成。下载量本身不大,但仍建议放进 tmux,避免 SSH 断开导致重来。
方式二:通过 HuggingFace 下载模型权重
HF 下载放在 tmux 里执行:
1cat > /tmp/download_pi05_libero.sh <<'EOF'
2#!/usr/bin/env bash
3set -euo pipefail
4export HF_ENDPOINT=${HF_ENDPOINT:-https://hf-mirror.com}
5export HF_HUB_ENABLE_HF_TRANSFER=1
6source /root/workspace/RLinf/.venv/bin/activate
7pip install "huggingface_hub<1.0"
8mkdir -p /root/workspace/models
9{
10 date
11 hf download lerobot/pi05_libero_base \
12 --local-dir /root/workspace/models/pi05_libero_base
13 date
14 echo "download done"
15} 2>&1 | tee -a /root/workspace/models/download_pi05_libero.log
16EOF
17chmod +x /tmp/download_pi05_libero.sh
18
19tmux new-session -d -s apxinf_download /tmp/download_pi05_libero.sh
不要用
pip install -U huggingface_hub:它会把版本升到 1.x,与镜像内transformers的依赖约束冲突,必须固定在<1.0。
查看下载进度:
1tmux ls 2>/dev/null | grep apxinf_download || true
2tail -20 /root/workspace/models/download_pi05_libero.log
3du -sh /root/workspace/models/pi05_libero_base 2>/dev/null || true
lerobot/pi05_libero_base 仓库不包含复现参考评测行为所需的归一化统计,norm_stats.json 必须单独下载到 checkpoint 根目录:
1curl -fL \
2 https://storage.googleapis.com/openpi-assets/checkpoints/pi05_libero/assets/physical-intelligence/libero/norm_stats.json \
3 -o /root/workspace/models/pi05_libero_base/norm_stats.json
GCS 不通时可改用方式一里的 BOS 单文件地址。
验证 checkpoint
说明:
APXINF_PI05_MODEL_DIR是评测配置中model_path的唯一来源(${oc.env:APXINF_PI05_MODEL_DIR}),必须先导出,否则 Hydra 解析配置时直接失败。
1export APXINF_PI05_MODEL_DIR=/root/workspace/models/pi05_libero_base
2
3for f in config.json model.safetensors norm_stats.json; do
4 if [ -f "${APXINF_PI05_MODEL_DIR}/${f}" ]; then
5 echo "OK ${f}"
6 else
7 echo "MISS ${f}"
8 fi
9done
10du -sh "${APXINF_PI05_MODEL_DIR}"
三行全部为 OK 再继续。
步骤三:确认评测配置
评测配置是 evaluations/libero/libero_10_apxinf_pi05_eval.yaml,核心字段如下:
1runner:
2 task_type: embodied_eval
3 only_eval: true
4 logger:
5 logger_backends: ["tensorboard"]
6
7env:
8 eval:
9 rollout_epoch: 10
10 total_num_envs: 10
11 auto_reset: false
12 max_episode_steps: 520
13 max_steps_per_rollout_epoch: 520
14
15rollout:
16 rollout_backend: "apxinf"
17 generation_backend: "apxinf"
18 pipeline_stage_num: 1
19 model:
20 model_path: ${oc.env:APXINF_PI05_MODEL_DIR}
21 num_action_chunks: 5
22 action_dim: 7
23 openpi:
24 config_name: "pi05_libero"
25 num_images_in_input: 2
26 num_steps: 5
27 noise_method: "flow_sde"
28 apxinf:
29 precision: "bf16"
30 action_horizon: 10
31 num_flow_steps: ${..openpi.num_steps}
32 noise_source: "apxinf"
这些字段在代码里有硬性校验,启动前逐条核对:
| 约束 | 位置 | 不满足时 |
|---|---|---|
runner.only_eval 必须为 true |
runner |
ApxInfRolloutWorker 初始化断言失败 |
runner.enable_decoupled_mode 必须为 false |
runner |
同上,ApxInf 不支持解耦 rollout |
openpi.config_name 必须以 pi05_ 开头 |
rollout.model.openpi |
报只支持 PI0.5 的 OpenPI 配置 |
apxinf.num_flow_steps 必须等于 openpi.num_steps |
两处 | 报两者必须一致(数值对齐要求) |
apxinf.action_horizon 必须 ≥ num_action_chunks |
rollout.model |
报 action_horizon 小于动作块长度 |
openpi.noise_method 只能是 flow_ode / flow_sde |
rollout.model.openpi |
报不支持;评测模式下 flow_sde 退化为 ODE |
apxinf.noise_source 只能是 apxinf / torch / observation |
rollout.model.apxinf |
报非法取值 |
model_path 由 APXINF_PI05_MODEL_DIR 注入 |
环境变量 | Hydra 解析配置时失败 |
noise_source: observation 只用于成对数值对齐测试,需要显式传入 [B, 10, 32] 的 noise 张量;常规评测保持 apxinf,由 ApxInf 采样初始高斯噪声。
多 epoch 评测必须关掉 auto_reset
env.eval.auto_reset 为 true 时,evaluate() 只在第 0 个 epoch 做一次 reset,之后依赖 chunk_step 自动重置;但 chunk_step 调用的是 step(..., auto_reset=False),实际不会重置。环境状态会跨 epoch 一直累积,累计步数超过 robosuite 内部 horizon(1000)后,下一次 step 抛 ValueError: executing action in terminated episode,MuJoCo 子进程以退出码 1 结束,父进程 recv() 收到 EOFError,随后整组 worker 被 ray.kill,评测以 EXIT=255 中断。
单 epoch(520 步 < 1000)不会触发这个问题,所以它只在多 epoch 的完整评测里出现。把 env.eval.auto_reset 设为 false 后,每个 epoch 开始时统一 reset,单条轨迹固定 520 步,10 个 epoch × 80 环境正好 800 条轨迹。
仓库里的配置默认是 true,启动时用 Hydra override 覆盖即可:env.eval.auto_reset=false。
轨迹预算
并行环境数必须能被 env worker 数整除。rlinf/config.py 里的 validate_embodied_cfg 断言 total_num_envs % env_world_size == 0,不满足时启动阶段直接 AssertionError 退出,不会静默截断。整除之后,每个 env worker 分到 total_num_envs // worker 数 // pipeline_stage_num 个环境。
配置里 component_placement 为 env,rollout: all,所以在 8 卡节点上 env 组有 8 个 worker,total_num_envs 必须是 8 的倍数。默认值 10 只适用于单卡(10 环境 × 10 epoch = 100 条轨迹),在 8 卡节点上会直接启动失败。
按默认协议跑满时,8 卡节点上设 env.eval.total_num_envs=80,即每个 worker 10 个环境,10 个 epoch 共 800 条轨迹。
LIBERO 的评测 reset state 池按任务交错排列后做 round-robin 分配,改变环境数会改变每个任务实际覆盖的 reset state 数量。
步骤四:启动评测
建议用 tmux 启动,避免 SSH 断开导致任务中断。启动脚本落地到 /root,评测命令只写一次:
1cd /root/workspace/RLinf
2
3cat > /root/run_apxinf_eval.sh <<'EOS'
4#!/usr/bin/env bash
5set -uo pipefail
6source /root/workspace/RLinf/.venv/bin/activate
7export APXINF_PI05_MODEL_DIR=/root/workspace/models/pi05_libero_base
8export MUJOCO_GL=egl
9export PYOPENGL_PLATFORM=egl
10cd /root/workspace/RLinf
11bash evaluations/run_eval.sh libero libero_10_apxinf_pi05_eval
12rc=$?
13echo "EXIT=${rc}"
14EOS
15chmod +x /root/run_apxinf_eval.sh
16
17if tmux has-session -t apxinf_eval 2>/dev/null; then
18 tmux kill-session -t apxinf_eval
19fi
20
21tmux new-session -d -s apxinf_eval \
22 'bash /root/run_apxinf_eval.sh 2>&1 | tee /root/apxinf-eval.out'
需要放大环境数并跑满 10 个 epoch 时,把脚本里的评测命令改成带 Hydra override 的形式。run_eval.sh 从第三个参数起把内容原样透传:
1bash evaluations/run_eval.sh libero libero_10_apxinf_pi05_eval \
2 env.eval.total_num_envs=80 \
3 env.eval.auto_reset=false
只跑单 epoch 做冒烟时,前一节提到的 auto_reset 问题不会出现,但统一带上 env.eval.auto_reset=false 可以保证多轮结果口径一致。
日志目录由 run_eval.sh 生成,不要自己指定:
1ls -dt /root/workspace/RLinf/logs/*libero_10_apxinf_pi05_eval | head -1
启动后验证点:
tmux ls中存在apxinf_eval。/root/apxinf-eval.out中出现Evaluation Mode: Standard LIBERO和Using benchmark=libero, config=libero_10_apxinf_pi05_eval。- 日志中出现
Initializing a new cluster with all connected nodes.,Ray 启动并占用预期数量的 GPU。 - 日志中出现
ApxInf policy loaded: device=cuda:N, metadata={...},每个 rollout worker 一行,metadata含action_horizon、model_action_dim、num_views、image_size。 - 出现
Evaluating Rollout Epochs进度条。 - 无
Traceback、RayOutOfMemory、worker died。
Using Python at ... 只写 stdout,不会进 eval_embodiment.log;查这一行要看 /root/apxinf-eval.out 或 tmux capture-pane。
单条轨迹最长 520 个环境步,每 num_action_chunks=5 步调用一次推理,因此单条轨迹最多 104 次 infer_rgb 调用,可以据此估算总耗时。
步骤五:监控评测
查看主日志:
1tail -f /root/workspace/RLinf/logs/<run-dir>/eval_embodiment.log
提取关键进展:
1grep -nE 'ApxInf policy loaded|Evaluating Rollout Epochs|eval/success_once|Traceback|Error|worker died' \
2 /root/workspace/RLinf/logs/<run-dir>/eval_embodiment.log | tail -50
查看 tmux 里的实时输出(含只在 stdout 的行):
1tmux capture-pane -p -t apxinf_eval | tail -40
查看 GPU:
1watch -n 2 'nvidia-smi --query-gpu=index,utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits'
查看 Ray worker:
1ps -eo pid,stat,pcpu,pmem,rss,args --sort=-pcpu | \
2 grep -E 'eval_embodied_agent.py|ApxInfRolloutWorker|ray::EnvWorker|ray::ApxInf' | head -20
步骤六:查看评测结果
评测结束(EXIT=0)后,先读指标表:
1RUN_DIR=$(ls -dt /root/workspace/RLinf/logs/*libero_10_apxinf_pi05_eval | head -1)
2cat "${RUN_DIR}/metrics.log"
指标表中每一项都带 eval/ 前缀:
| 指标 | 含义 |
|---|---|
eval/success_once |
轨迹成功率:episode 内任意时刻达成任务即计成功,是本次评测的核心结论 |
eval/reward |
每条轨迹的 return 除以成功步数(未成功则除以当前步数) |
eval/return |
每条轨迹累计回报 |
eval/episode_len |
每条轨迹的步数 |
eval/num_trajectories |
参与统计的轨迹总数,用于确认实际跑了多少条 |
eval/num_trajectories 应当等于「实际并发环境数 × rollout_epoch」。如果它小于预期,先回到「轨迹预算」核对 total_num_envs 与 worker 数。
TensorBoard(可选)
评测指标只在 step=0 写一个点,TensorBoard 里是一条单点曲线,适合与历史 run 并列对比。启动 TensorBoard:
1cd /root/workspace/RLinf
2source .venv/bin/activate
3tmux new-session -d -s tensorboard_eval \
4 'cd /root/workspace/RLinf && tensorboard --logdir /root/workspace/RLinf/logs --port 6006 --host 127.0.0.1'
本地访问:
1ssh -p <port> -L 6006:127.0.0.1:6006 root@<host>
浏览器打开:
1http://127.0.0.1:6006
如果不想起 TensorBoard,也可以直接读 event 文件:
1cd /root/workspace/RLinf
2source .venv/bin/activate
3export TF_CPP_MIN_LOG_LEVEL=3
4
5python - <<'PY'
6from pathlib import Path
7from tensorboard.backend.event_processing.event_accumulator import EventAccumulator
8
9run = Path('/root/workspace/RLinf/logs/<run-dir>')
10for event in sorted((run / 'tensorboard').glob('events.out.tfevents.*')):
11 print(f'EVENT {event.name} size={event.stat().st_size}')
12 ea = EventAccumulator(str(event), size_guidance={'scalars': 0})
13 ea.Reload()
14 for tag in sorted(ea.Tags().get('scalars', [])):
15 vals = ea.Scalars(tag)
16 if vals:
17 v = vals[-1]
18 print(f'{tag}\tcount={len(vals)}\tstep={v.step}\tvalue={v.value}')
19PY
常见问题处理
| 问题 | 解决方案 |
|---|---|
Hydra 报 APXINF_PI05_MODEL_DIR 无法解析 |
在启动脚本里 export APXINF_PI05_MODEL_DIR=... 后再调 run_eval.sh;不要只写进当前交互 shell |
报 apxinf_robo is not importable in the rollout worker |
激活的不是镜像自带的 venv,执行 source /root/workspace/RLinf/.venv/bin/activate;不要重新 pip install |
| 加载权重时报找不到 kernel 或设备非法 | 镜像内的 binding 与当前 GPU 架构不匹配;换用按该架构 APXINF_CUDA_ARCH 构建的镜像 |
报 active camera views ... but the ApxInf checkpoint expects ... |
checkpoint 的视野数与 openpi.num_images_in_input / apxinf.num_views 不一致,按 checkpoint 调整 |
| 报 norm stats 相关错误 | norm_stats.json 缺失,或没放在 checkpoint 根目录;按步骤二单独下载 |
升级 huggingface_hub 后 transformers 报依赖冲突 |
执行 pip install "huggingface_hub<1.0" 回退;镜像内 transformers 要求该版本上界 |
报 num_flow_steps must match OpenPI num_steps |
修改 openpi.num_steps 时必须同步 apxinf.num_flow_steps(配置里用 ${..openpi.num_steps} 引用即可) |
报 action_horizon must be at least num_action_chunks |
提高 apxinf.action_horizon,或降低 num_action_chunks |
启动报 total_num_envs ... divisible by the number of environment processes |
total_num_envs 不是 env worker 数(8 卡为 8)的倍数;改成 8 的倍数,例如 env.eval.total_num_envs=80 |
多 epoch 评测中途 EXIT=255,日志里是 EOFError 或 ValueError: executing action in terminated episode |
env.eval.auto_reset 为 true 导致环境跨 epoch 不重置;加上 env.eval.auto_reset=false 重跑 |
日志里找不到 Using Python at |
该行只写 stdout,看 /root/apxinf-eval.out 或 tmux capture-pane -p -t apxinf_eval |
| 任务无输出或进程消失 | 先 tmux ls、tmux capture-pane,再 grep Traceback / RayOutOfMemory / worker died,不要只看 eval_embodiment.log 尾部 |
| Ray 启动报端口或资源冲突 | 同机可能还有旧集群;先 ray status 确认,再停掉旧集群重试 |
| EGL 初始化失败 | 确认驱动与 EGL 库可用;无 EGL 的平台改为 export MUJOCO_GL=osmesa PYOPENGL_PLATFORM=osmesa |
指标表里没有 eval/success_once |
确认 runner.only_eval: true,且日志里出现过 Evaluating Rollout Epochs 与 ApxInf policy loaded |
评价此篇文章
