- 环境概览
- 部署步骤
- 1. 拉取 Docker 镜像
- 2. 启动容器
- 3. 硬件可用性检查
- 4. 创建虚拟环境
- 5. 准备数据集
- 6. 系统依赖补充
- 7. 关键环境变量
- 一、背景
- 二、Step 1 — Compute Returns(通过)
- 三、Step 2 — Value Model SFT(8 卡 FSDP 训练)
- 3.1 transfer_to_npu 的 Segfault
- 3.2 LeRobot Dataset 离线加载
- 3.3 Ray Timer Crash
- 3.4 训练指标
- 四、已完成修改清单
- 五、Step 3 — Compute Advantages(8 卡推理)
- batch_size 调优
- 运行结果
- 结果统计(与 batch_size 无关)
- compute_advantages.py 修复
- 六、Step 4 — CFG Training(8 卡 FSDP 训练)
- 修复清单
- norm_stats.json 生成
- 显存与 batch size
- 快速验证运行(15 步)
- Eval 验证
- 数据集说明
- 已知问题
- 七、上游回馈计划
环境概览
| 组件 | 版本/说明 |
|---|---|
| 硬件 | 8 × Ascend 910B (aarch64) |
| 基础镜像 | `rlinf/rlinf:agentic-rlinf0.2-libero-cann9.0` |
| Python | 3.11(自建 .venv) |
| PyTorch | 2.6.0+cpu + torch-npu 2.6.0 |
| CANN | 9.0.0 |
| transformers | 4.57.x |
| Ray | 2.x(分布式调度) |
| 数据集 | RECAP-Libero10-Task0-48succ-Data |
| 分布式 | 8 卡 FSDP(hccl 通信后端) |
部署步骤
1. 拉取 Docker 镜像
RLinf 官方提供了预置 CANN 9.0 环境的 Docker 镜像,镜像内包含 PyTorch 2.6、torch-npu、Ray 等核心依赖:
docker pull rlinf/rlinf:agentic-rlinf0.2-libero-cann9.0
> 注意:该镜像带 CANN 9.0 驱动栈,需确保宿主机已安装对应版本的 NPU 驱动和固件。
2. 启动容器
docker run -itd \
--name rlinf-recap \
--network host \
--ipc=host \
--privileged \
--device=/dev/davinci0 \
--device=/dev/davinci1 \
--device=/dev/davinci2 \
--device=/dev/davinci3 \
--device=/dev/davinci4 \
--device=/dev/davinci5 \
--device=/dev/davinci6 \
--device=/dev/davinci7 \
--device=/dev/davinci_manager \
--device=/dev/hisi_hdc \
-v /data:/data \
rlinf/rlinf:agentic-rlinf0.2-libero-cann9.0 \
/bin/bash
docker run -itd \
--name rlinf-recap \
--network host \
--ipc=host \
--privileged \
--device=/dev/davinci0 \
--device=/dev/davinci1 \
--device=/dev/davinci2 \
--device=/dev/davinci3 \
--device=/dev/davinci4 \
--device=/dev/davinci5 \
--device=/dev/davinci6 \
--device=/dev/davinci7 \
--device=/dev/davinci_manager \
--device=/dev/hisi_hdc \
-v /data:/data \
rlinf/rlinf:agentic-rlinf0.2-libero-cann9.0 \
/bin/bash关键参数说明:
--network host+--ipc=host:Ray 分布式通信必需--privileged+ 设备映射:NPU 设备直通-v /data:/data:挂载数据目录,存放数据集和模型权重
3. 硬件可用性检查
进入容器后验证 NPU 设备:
docker exec -it rlinf-recap bash
npu-smi info
预期输出 8 卡均为 OK 状态:
NPU-ID Name Health Power(Tot) Temp(C)
0 910B OK 65.0W 42
1 910B OK 65.0W 41
...(8 卡均 OK)
4. 创建虚拟环境
RLinf 使用自建 .venv 而非镜像内置 conda 环境,通过 install.sh 安装依赖:
cd /RLinf
bash install.sh embodied --model openpi --env libero --platform ascend
该命令会:
- 创建
.venv(Python 3.11) - 安装 torch 2.6.0+cpu + torch-npu 2.6.0
- 安装 transformers、diffusers、accelerate、lerobot 等依赖
- 配置 hccl 分布式通信后端
验证环境:
source .venv/bin/activate
python -c "import torch; import torch_npu; print('NPU count:', torch.npu.device_count())"
python -c "import ray; print('Ray version:', ray.__version__)"
5. 准备数据集
从 HuggingFace 下载 RECAP-Libero10 数据集:
pip install huggingface-hub
# 下载数据集到 /data 目录
huggingface-cli download \
--repo-type dataset RLinf/RECAP-Libero10-Task0-48succ-Data \
--local-dir /data/RECAP-Libero10-Task0-48succ-Data
数据集目录结构:
/data/RECAP-Libero10-Task0-48succ-Data/
├── libero10_task0_sft/ # 48 条成功轨迹(8005 帧),Step 2 训练集
│ ├── data/ # 图像帧 + 动作数据
│ ├── meta/ # episodes 元信息
│ └── info.json # 数据集版本信息
├── libero10_task0_train/ # 4096 条成败混合 rollout
├── libero10_task0_eval/ # 验证集
6. 系统依赖补充
RLinf 推理/评估阶段可能需要额外依赖:
apt update && apt install -y \
libavcodec-dev libavformat-dev libavutil-dev \
libswscale-dev libswresample-dev ffmpeg
7. 关键环境变量
训练前设置以下环境变量:
# NPU 可见设备(8 卡全用)
export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
# hccl 通信超时(8 卡建连需要更长时间)
export HCCL_CONNECT_TIMEOUT=600
# 关闭信号处理(避免 Ray fork 时信号冲突)
export TORCHELASTIC_ENABLE_FILE_TIMER=0
# torch-npu 库路径(确保 hccl so 可找到)
export LD_LIBRARY_PATH=/RLinf/.venv/lib/python3.11/site-packages/torch/lib:\
/RLinf/.venv/lib/python3.11/site-packages/torch_npu/lib:$LD_LIBRARY_PATH
# tf32 关闭(NPU 的 tf32 实现精度与 CUDA 不同)
export ALLOW_TF32=0
一、背景
RLinf 是一个基于 Ray 的机器人强化学习框架。其核心算法 RECAP 通过四阶段流水线实现 classifier-free guidance (CFG) 策略优化:先计算轨迹回报(Step 1),再训练价值模型(Step 2),然后计算优势标签(Step 3),最后 CFG 训练策略模型(Step 4)。整个过程涉及 VLM backbone(SigLIP + Gemma3)、8 卡 FSDP 分布式训练,目标是把这套 pipeline 完整跑在华为昇腾 910B 上。
数据集路径:
libero10_task0_sft:48 条成功轨迹(8005 帧),Step 2 训练集libero10_task0_train:4096 条成败混合的 rollout,Step 2 eval + Step 1 输入libero10_task0_eval:验证集
二、Step 1 — Compute Returns(通过)
纯 CPU 计算,无 NPU 依赖。对 8005 帧 SFT 数据和 4096 条 rollout 轨迹分别计算折扣回报(G_t = r_t + γ·G_{t+1},γ=1.0),生成 meta/returns.parquet。全程顺畅。
python examples/recap/process/compute_returns.py --config-name=compute_returns
三、Step 2 — Value Model SFT(8 卡 FSDP 训练)
这是整个适配过程中踩坑最密集的阶段。
3.1 transfer_to_npu 的 Segfault
torch_npu.contrib.transfer_to_npu 是 torch-npu 的 monkey-patch 模块,作用是把所有 torch.cuda.* 调用自动替换为 torch.npu.*。但它在遍历 torch 模块树时触发了 transformers 的 lazy import→image_transforms→tensorflow→pywrap_tensorflow.py→self_check.py:63,TF 的 C 扩展在 NumPy 2.x 下直接 segfault。
修复:注释掉 train_value.py 里的 from torch_npu.contrib import transfer_to_npu,然后 pip uninstall tensorflow。
但实际上 Ray worker 文件 rlinf/workers/sft/fsdp_value_sft_worker.py 也 import 了它,所以每个 NPU worker fork 时还是会触发。关键是要确保环境中没有 tensorflow。
3.2 LeRobot Dataset 离线加载
Step 2 的 config 里指定了 eval_data_paths 指向本地 libero10_task0_train。LeRobot 的 LeRobotDataset.__init__ 会调用 get_safe_version(),该方法内部 get_repo_versions() 去 hf-mirror 查数据集版本信息,但这个本地数据集不在 HuggingFace 上——404。
修复:直接 patch LeRobot 源码 lerobot/common/datasets/utils.py:
# get_repo_versions — 捕捉异常返回空列表
def get_repo_versions(repo_id: str):
try:
return _get_repo_versions_impl(repo_id)
except Exception:
return []
# get_safe_version — hub_versions 为空时直接用本地版本
def get_safe_version(repo_id, version):
hub_versions = get_repo_versions(repo_id)
if not hub_versions:
return f"v{target_version}" # 用本地 info.json 的 _version_
...
.pth 文件和入口脚本 monkey-patch 都不生效(Ray workers 独立 fork,不继承父进程 monkey-patch),必须改源文件。
3.3 Ray Timer Crash
训练跑 4 步没问题,第 5 步触发 save+eval 时崩:
ValueError: Timer 'run_eval' has not been recorded.
根因:transfer_to_npu monkey-patch 了 torch.distributed.init_process_group(替换 backend 为 hccl),干扰了 Ray 内部 @Worker.timer 装饰器的计时机制。
修复:把 pop_execution_time 的 crash 改成 no-op:
# rlinf/scheduler/worker/worker.py:1295
if tag not in self._timer_metrics:
return 0.0 # patched: Ray timer broken by transfer_to_npu
3.4 训练指标
8 卡 FSDP,micro_batch=16×8=128 global_batch,8005 样本,约 62 步/epoch。
前 4 步训练指标(模型尚未收敛,正常):
- loss ~6.0,MAE ~0.43
- predicted_value_std ~0.01(模型输出近乎常数,预期中)
- value_spearman ~-0.3 逐步改善(step 4 到 -0.02)
四、已完成修改清单
| 文件 | 修改 | 原因 |
|---|---|---|
| `examples/recap/value/train_value.py:26` | import transfer_to_npu(保留) | NPU cuda→npu 映射 |
| `rlinf/workers/sft/fsdp_value_sft_worker.py:30` | import transfer_to_npu(保留) | Ray worker 需要 |
| `rlinf/scheduler/worker/worker.py:1295` | ValueError → return 0.0 | Ray timer crash |
| `lerobot/utils.py:get_repo_versions` | try/except 包裹 | 离线数据集 404 |
| `lerobot/utils.py:get_safe_version` | hub_versions 空时用本地版 | 同上 |
| `examples/recap/value/config/libero_sft_value.yaml` | val_check_interval=50, save_interval=50 | 控制验证频率 |
备份目录:/RLinf/backups_adapt/20260630_101234/
五、Step 3 — Compute Advantages(8 卡推理)
batch_size 调优
Value Model 推理显存只用到 7G(910B 共 61G),初始 batch_size=16 时 GPU busytime 仅 30%,数据加载(avg_fetch=1088ms)远超推理(avg_infer=456ms)。调大到 batch_size=128 后 batch 数从 64 降到 8,avg_infer=2831ms,GPU busy 提升至 19% — 数据加载仍是瓶颈但吞吐有改善。
# compute_advantages_npu.yaml
advantage:
batch_size: 128 # 16 → 128
运行结果
指标 batch_size=16 batch_size=128 批次数 64 8 总耗时 ~104s ~122s avg_infer/批 456ms 2831ms GPU busy 30% 19% samples/s 37 23
| 指标 | batch_size=16 | batch_size=128 |
|---|---|---|
| 批次数 | 64 | 8 |
| 总耗时 | ~104s | ~122s |
| avg_infer/批 | 456ms | 2831ms |
| GPU busy | 30% | 19% |
| samples/s | 37 | 23 |
> 注意:batch_size 翻 8 倍后总耗时反而略增(104s→122s),瓶颈在 avg_fetch=12s 的视频解码 + 预处理,不在 NPU 推理。进一步优化需提高 dataloader workers 或预缓存预处理结果。
结果统计(与 batch_size 无关)
指标 值 推理样本 8005 samples, 30 episodes V(o_t) mean -0.4851 Advantage mean -0.0033 Advantage range [-0.0435, 0.5022] Positive rate 30.0% (2402/8005) SFT 模式 全部标记为 True 输出 `advantages.parquet`, `mixture_config.yaml`
compute_advantages.py 修复
- 添加
hccl 后端 override(原代码只检测 nccl) - 添加
torch.npu.set_device() 兼容(torch.cuda.set_device 在 NPU 上会报错)
六、Step 4 — CFG Training(8 卡 FSDP 训练)
修复清单
文件 修复内容 `rlinf/workers/sft/fsdp_cfg_worker.py` NPU device 初始化:`torch.cuda.set_device` → `torch.npu.set_device` `examples/recap/cfg/config/libero_cfg_openpi_npu.yaml` 新建 NPU 配置,指向 `/data/pi05_base` + 实际数据集路径 `/data/pi05_base/physical-intelligence/libero/norm_stats.json` 从数据集生成 LIBERO action/state 归一化统计(mean/std/q01/q99) NPU OOM 修复 `micro_batch_size` 32→4, `global_batch_size` 512→256
norm_stats.json 生成
| 指标 | 值 |
|---|---|
| 推理样本 | 8005 samples, 30 episodes |
| V(o_t) mean | -0.4851 |
| Advantage mean | -0.0033 |
| Advantage range | [-0.0435, 0.5022] |
| Positive rate | 30.0% (2402/8005) |
| SFT 模式 | 全部标记为 True |
| 输出 | `advantages.parquet`, `mixture_config.yaml` |
- 添加
hccl后端 override(原代码只检测 nccl) - 添加
torch.npu.set_device()兼容(torch.cuda.set_device在 NPU 上会报错)
六、Step 4 — CFG Training(8 卡 FSDP 训练)
修复清单
文件 修复内容 `rlinf/workers/sft/fsdp_cfg_worker.py` NPU device 初始化:`torch.cuda.set_device` → `torch.npu.set_device` `examples/recap/cfg/config/libero_cfg_openpi_npu.yaml` 新建 NPU 配置,指向 `/data/pi05_base` + 实际数据集路径 `/data/pi05_base/physical-intelligence/libero/norm_stats.json` 从数据集生成 LIBERO action/state 归一化统计(mean/std/q01/q99) NPU OOM 修复 `micro_batch_size` 32→4, `global_batch_size` 512→256
norm_stats.json 生成
| 文件 | 修复内容 |
|---|---|
| `rlinf/workers/sft/fsdp_cfg_worker.py` | NPU device 初始化:`torch.cuda.set_device` → `torch.npu.set_device` |
| `examples/recap/cfg/config/libero_cfg_openpi_npu.yaml` | 新建 NPU 配置,指向 `/data/pi05_base` + 实际数据集路径 |
| `/data/pi05_base/physical-intelligence/libero/norm_stats.json` | 从数据集生成 LIBERO action/state 归一化统计(mean/std/q01/q99) |
| NPU OOM 修复 | `micro_batch_size` 32→4, `global_batch_size` 512→256 |
pi05 通用模型缺少 LIBERO 环境专属的 norm_stats.json。从数据集的 8005 样本中提取 action(7维) 和 state(8维) 的统计信息,写入 physical-intelligence/libero/norm_stats.json:
# 从 parquet 文件读取
for f in parquet_files:
df = pd.read_parquet(f)
actions = np.stack(df["actions"].values) # (N, 7)
states = np.stack(df["state"].values) # (N, 8)
# 计算 mean/std/q01/q99 → norm_stats.json
显存与 batch size
pi05 14B 模型在 sharding_strategy: no_shard 下每卡完整加载模型。初始 micro_batch_size=32 时前向已占 56G/61G,backward 时 OOM:
RuntimeError: NPU out of memory. Tried to allocate 970.00 MiB
(NPU 0; 60.96 GiB total; 56.37 GiB already allocated)
减小 micro_batch_size=4 后顺利运行。gradient accumulation 自动补偿(256/(4×8)=8 步累积)。
快速验证运行(15 步)
# libero_cfg_openpi_npu_validate.yaml
runner:
max_steps: 15 # 仅 15 步验证
save_interval: 5 # 每 5 步保存
val_check_interval: -1 # 关闭校验(CFG worker 不支持 eval)
actor:
micro_batch_size: 4
global_batch_size: 256
optim:
lr_warmup_steps: 3
total_training_steps: 15
指标 值 速度 ~13-23s/it Loss 0.0703 → 0.0433(15 步稳定下降) Grad Norm 0.815 → 0.336(收敛) Checkpoints global_step_5, _10, _15 ✅ 显存 ~56G/61G(no_shard 模式)
Eval 验证
# libero_cfg_openpi_npu_validate.yaml
runner:
max_steps: 15 # 仅 15 步验证
save_interval: 5 # 每 5 步保存
val_check_interval: -1 # 关闭校验(CFG worker 不支持 eval)
actor:
micro_batch_size: 4
global_batch_size: 256
optim:
lr_warmup_steps: 3
total_training_steps: 15| 指标 | 值 |
|---|---|
| 速度 | ~13-23s/it |
| Loss | 0.0703 → 0.0433(15 步稳定下降) |
| Grad Norm | 0.815 → 0.336(收敛) |
| Checkpoints | global_step_5, _10, _15 ✅ |
| 显存 | ~56G/61G(no_shard 模式) |
fsdp_cfg_worker.py 硬编码 self.eval_data_loader = None,未实现 eval 逻辑(不像 fsdp_sft_worker.py 会从 val_data_paths 构建 eval dataloader)。如需 validation,需在 CFG worker 中添加 eval dataloader 支持。快速验证阶段暂时关闭校验(val_check_interval: -1)。
数据集说明
数据集 Episodes Frames 用途 `libero10_task0_sft` 30 8,005 🟢 训练集(Step 2/3/4) `libero10_task0_train` 4,096 1,561,874 🟡 备用训练/验证集 `libero10_task0_eval` 64 25,493 🔵 纯验证集
已知问题
aten::_sample_dirichlet NPU 不支持,fallback CPUNO_SHARD 策略 PyTorch 已标记 deprecated,后续可改 shard_grad_op- 速度 ~13s/it 偏慢(pi05 14B + no_shard + 非融合算子)
- CFG worker 不支持 eval dataloader
七、上游回馈计划
| 数据集 | Episodes | Frames | 用途 |
|---|---|---|---|
| `libero10_task0_sft` | 30 | 8,005 | 🟢 训练集(Step 2/3/4) |
| `libero10_task0_train` | 4,096 | 1,561,874 | 🟡 备用训练/验证集 |
| `libero10_task0_eval` | 64 | 25,493 | 🔵 纯验证集 |
aten::_sample_dirichletNPU 不支持,fallback CPUNO_SHARD策略 PyTorch 已标记 deprecated,后续可改shard_grad_op- 速度 ~13s/it 偏慢(pi05 14B + no_shard + 非融合算子)
- CFG worker 不支持 eval dataloader
七、上游回馈计划
适配过程中积累的改动计划回馈 RLinf 主仓库:
| 优先级 | 内容 | 说明 |
|---|---|---|
| P0 | `torch.npu.set_device()` 兼容 | 三个 worker + compute_advantages,用 try/except 包裹,不影响 CUDA |
| P0 | FSDP device 修复 | `cls_embedding.weight.device` → buffer device,通用修复 |
| P1 | `drop_last=True` 修复 | eval 死锁,对 CUDA 也有益 |
| P1 | HCCL backend 自动检测 | `compute_advantages.py` 的分布式后端 |
| P2 | NPU 配置模板 | `compute_advantages_npu.yaml`, `libero_cfg_openpi_npu.yaml` |
| P2 | CFG eval dataloader | 补充 `val_data_paths` → `eval_data_loader` 构建逻辑 |
完整 patch: RLinf-ascend-910b.patch,使用指南: RLinf-ascend-910b-guide.md。
简记。








COMMENTS | NOTHING