OpenPI_RLinf Supervised Fine-Tuning#
This page explains how to run supervised fine-tuning (SFT) of the
self-contained OpenPI_RLinf Pi0.5 flow-matching VLA on the
BEHAVIOR-1K task with RLinf. The model is a pure-PyTorch reimplementation
of the Pi0.5 architecture (dual-expert Gemma + SigLIP with a flow-matching
action head), registered in RLinf under model_type: openpi_rlinf. SFT is
typically the first stage before reinforcement learning: the model imitates
high-quality demonstrations so that RL can continue optimization from a strong
prior. This page also describes use of the same JAX-aligned implementation for
Pi0 + RoboTwin.
For SFT and eval-only deployment of PyTorch OpenPI Pi0.5 on a real dual-Franka setup, see the Dual-Franka PyTorch OpenPI guide.
Contents#
The OpenPI_RLinf SFT flow and its configuration
The FSDP optimizer and mixed-precision contract
BEHAVIOR streaming-loader fields and norm-stat/tokenizer handling
Launching training and converting checkpoints for evaluation
The official OpenPI/LeRobot data path, training, and evaluation for Pi0 RoboTwin
What it is#
The openpi_rlinf model is a self-contained PyTorch port of the Pi0.5
flow-matching VLA. It is worth emphasizing that the PyTorch implementation
shipped in the official OpenPI repository is not numerically aligned with its
JAX reference, whereas this port is numerically aligned with the JAX
implementation. Unlike the JAX/LeRobot-backed OpenPI path (see
OpenPI Supervised Fine-Tuning), it builds the model shape directly from a small set of
config fields (no config.json is read at construction time) and is wired
for BEHAVIOR-1K out of the box. During SFT, the policy predicts 32-step,
23-dimensional action chunks for the dual-arm R1 Pro robot from BEHAVIOR
demonstrations using the flow-matching denoising objective.
Pi0.5 + BEHAVIOR-1K#
Configuration#
The example is split into a reusable, path-free model template and an experiment config that supplies filesystem paths:
Experiment config:
examples/sft/config/behavior_pi05_vla.yamlModel template:
examples/sft/config/model/pi0_5_rlinf.yaml
The experiment config imports the model template through Hydra defaults:
defaults:
- model/pi0_5_rlinf@actor.model
- hybrid_engines/fsdp@actor.fsdp_config
- override hydra/job_logging: stdout
Precision contract#
The OpenPI_RLinf SFT configuration deliberately separates the load dtype from the compute dtype:
The model template sets
actor.model.precisiontofp32(inpi0_5_rlinf.yaml). fp32 weights are loaded as the FSDP optimizer master, preventing small warmup-LR updates from being lost to bf16 rounding.FSDP
MixedPrecisioncomputes in bf16 while keeping gradient all-reduce and buffers in fp32:actor: fsdp_config: gradient_checkpointing: True mixed_precision: param_dtype: bf16 # FSDP compute dtype reduce_dtype: fp32 # gradient all-reduce stays fp32
param_dtypeis the FSDP compute dtype and is explicitly set to bf16, rather than interpolated fromactor.model.precision. The load-dtype selector and compute dtype are independent, so an fp32-master load still computes in bf16.Gradient checkpointing is enabled on the dual-expert Gemma + SigLIP backbone through
actor.fsdp_config.gradient_checkpointing: Trueto reduce activation memory.The learning-rate schedule uses the reference warmup + cosine decay through
actor.optim.lr_scheduler: openpi_cosine. Warmup begins atpeak / (warmup + 1)and the schedule cosine-decays tomin_lrovertotal_training_steps.
Streaming data loader#
The BEHAVIOR streaming loader reads all parameters directly from the data:
section; there are no hidden defaults:
data:
train_data_paths: /path/to/2025-challenge-demos
behavior_dataset_root: /path/to/2025-challenge-demos
repo_id: "behavior-1k/2025-challenge-demos"
modalities: ["rgb"]
num_workers: 8
fine_grained_level: 0
tolerance_s: 1.0e-4
tasks: ["turning_on_radio"]
use_skill: false
task_subtasks:
turning_on_radio:
- "move to radio"
- "pick up radio from coffee table"
- "press radio"
- "place radio on coffee table"
Key data fields:
train_data_paths/behavior_dataset_root: root of the BEHAVIOR dataset (the latter defaults to the former).repo_id: BEHAVIOR demonstration repo id (behavior-1k/2025-challenge-demos).modalities: input modalities consumed by the loader, such as["rgb"].num_workers: number of data-loader worker processes.fine_grained_levelandtolerance_s: time-alignment controls for the streaming reader.tasks: the BEHAVIOR task or tasks to train on.use_skill: whenfalse, train on the main-task text; whentrue, train on the per-frame REFERENCE skill text selected fromtask_subtasks.task_subtasks: ordered per-task skill labels used to build the index-to-label mapping whenuse_skill: true.
Norm stats and tokenizer#
Normalization-statistics paths are configured under actor.model.openpi:
actor:
model:
model_path: /path/to/pi05_base_pytorch_new
openpi:
assets_dir: /path/to/assets
asset_id: "behavior-1k/2025-challenge-demos"
assets_dir: directory holding quantile-normalization statistics.asset_id: sub-path underassets_dirfor the task statistics.
Norm stats resolve at {assets_dir}/{asset_id}/norm_stats.json. The
PaliGemma tokenizer is loaded from the base-model configuration by OpenPI’s
ModelTransformFactory when constructing the input transform, so the
openpi_rlinf SFT YAML does not need a separate SentencePiece tokenizer
path.
Filesystem paths#
All filesystem paths are written as /path/to/... placeholders in
examples/sft/config/behavior_pi05_vla.yaml. Replace them with your staged
resources:
data.train_data_paths/data.behavior_dataset_root: root of the BEHAVIOR streaming dataset.actor.model.model_path: new-format fp32 base checkpoint loaded by the trainer.actor.model.openpi.assets_dir: normalization-statistics directory.
Pi0 + RoboTwin#
Pi0 RoboTwin uses the official OpenPI/LeRobot map-style data loader:
Experiment config:
examples/sft/config/robotwin_sft_openpi_rlinf.yamlModel template:
examples/sft/config/model/pi0_rlinf.yamlOpenPI data config:
pi0_aloha_robotwin
Replace the dataset, base checkpoint, and task-specific normalization-statistics paths with your local paths:
data:
train_data_paths: /path/to/robotwin-data
num_workers: 4
actor:
model:
model_path: /path/to/pi0_base_pytorch_new
num_action_chunks: 50
action_dim: 14
openpi:
config_name: "pi0_aloha_robotwin"
assets_dir: ${actor.model.model_path}
asset_id: "physical-intelligence/robotwin/adjust_bottle"
num_images_in_input: 3
openpi_data:
norm_stats_path: ${actor.model.openpi.assets_dir}/${actor.model.openpi.asset_id}/norm_stats.json
RoboTwin uses 14-dimensional ALOHA actions and three input images. OpenPI pads
actions to the 32-dimensional model action space. Set asset_id to the
statistics directory for the selected task, such as adjust_bottle above.
openpi_data.norm_stats_path explicitly selects that task’s
norm_stats.json, ensuring SFT and evaluation use the same statistics.
The Pi0 RoboTwin recipe also uses fp32 master weights, bf16 FSDP computation,
and fp32 gradient reduction. Its experiment YAML defaults to
actor.optim.lr_scheduler: openpi_cosine. This schedule starts warmup at
peak / (warmup + 1) and reproduces the RoboTwin JAX reference learning-rate
curve.
Launch scripts#
Run the SFT helper from the repository root:
# Pi0.5 + BEHAVIOR-1K
bash examples/sft/run_vla_sft.sh behavior_pi05_vla
# Pi0 + RoboTwin
bash examples/sft/run_vla_sft.sh robotwin_sft_openpi_rlinf
The script forwards the config name to the SFT entry point and writes logs and
checkpoints under the configured runner.logger.log_path. Checkpoints are
saved every runner.save_interval steps under
.../checkpoints/global_step_<N>/.
Converting checkpoints for evaluation#
Use the OpenPI checkpoint converter to convert an SFT checkpoint to the bare
Pi0 layout expected by the evaluation loader:
# Pi0.5 + BEHAVIOR-1K
python -m rlinf.utils.ckpt_convertor.openpi.convert --mode sft_to_openpi_rlinf \
--config-name pi05_behavior \
--dtype bf16 \
--ckpt /path/to/logs/.../checkpoints/global_step_30000 \
--input-norm-stats /path/to/norm_stats.json \
--output-model /path/to/pi05_sft_pytorch_new \
--output-norm-stats /path/to/pi05_sft_pytorch_new/physical-intelligence/behavior/norm_stats.json
# Pi0 + RoboTwin
python -m rlinf.utils.ckpt_convertor.openpi.convert --mode sft_to_openpi_rlinf \
--config-name pi0_aloha_robotwin \
--dtype fp32 \
--ckpt /path/to/logs/.../checkpoints/global_step_30000 \
--input-norm-stats /path/to/pi0_base_pytorch_new/physical-intelligence/robotwin/adjust_bottle/norm_stats.json \
--output-model /path/to/pi0_robotwin_sft_hf \
--output-norm-stats /path/to/pi0_robotwin_sft_hf/physical-intelligence/robotwin/adjust_bottle/norm_stats.json \
--reference-model /path/to/pi0_base_pytorch_new
sft_to_openpi_rlinf strips wrapper/FSDP key prefixes, selects the Pi0 or
Pi0.5 model shape using --config-name, copies normalization statistics, and
writes floating-point tensors using --dtype {fp32,bf16}. For RoboTwin, the
converted directory can be placed in rollout.model.model_path in
evaluations/robotwin/robotwin_adjust_bottle_openpi_rlinf_eval.yaml;
openpi_data.norm_stats_path in the evaluation config must select the same
task statistics.
The converted checkpoint can be evaluated on
BEHAVIOR-1K or
RoboTwin. For other conversion modes
and full argument details, see
rlinf/utils/ckpt_convertor/openpi/README.md.