This repository contains the official implementation of PhaseLoRA: Control-Regime-Conditioned Low-Rank Adaptation for Continuous-Action Vision-Language-Action Policies. It is built on top of Physical Intelligence's OpenPI and extends the upstream π₀/π₀.₅ model and policy interfaces with phase-aware low-rank adaptation. The codebase provides reproducible PyTorch training, policy serving, and benchmark evaluation workflows for LIBERO and ALOHA.
- PyTorch fine-tuning for π₀ and π₀.₅, including LoRA, full-parameter training, DDP, optional ZeRO-1 optimizer sharding, checkpoint resume, and safetensors checkpoints.
- Phase-aware LoRA variants: multi-bank routing, single-bank P/E-routed adapters, bimanual P/E/coordination routing, and input-conditioned spectral LoRA with active-rank pruning.
- Optional flow-matching consistency losses, direct action regression, action-branch reinitialization, condition dropout, and frequency-aware image augmentation.
- A stateful websocket inference protocol with reset support, request timeouts, reproducible sampling seeds, optional externally supplied diffusion noise, and checkpoint provenance in server metadata.
- LIBERO evaluation with task-range resume, per-task and aggregate metrics, JSON snapshots, optional videos, and a fixed-initial-state/fixed-noise diagnostic.
- ALOHA simulation evaluation with configurable episode horizon and machine-readable metrics.
PhaseLoRA is implemented on the PyTorch model path. The original OpenPI JAX training and inference entry points remain available for upstream-compatible configurations.
- Ubuntu 22.04 (the tested platform)
- Python 3.11
- An NVIDIA GPU with CUDA 12 support
uv
Approximate single-GPU memory requirements depend on the chosen configuration:
| Workload | Typical memory |
|---|---|
| Inference | 8+ GB |
| LoRA fine-tuning | 24+ GB |
| Full fine-tuning | 70+ GB, or multiple GPUs with optimizer sharding |
Clone with the upstream submodules and install the workspace:
git clone --recurse-submodules https://github.com/Grinffin/PhaseLoRA.git
cd openpi
GIT_LFS_SKIP_SMUDGE=1 uv sync
GIT_LFS_SKIP_SMUDGE=1 uv pip install -e .If the repository was cloned without submodules:
git submodule update --init --recursiveThe PyTorch implementation currently relies on the pinned Transformers compatibility patch:
cp -r src/openpi/models_pytorch/transformers_replace/* \
.venv/lib/python3.11/site-packages/transformers/The project pins transformers==4.53.2; check it with uv pip show transformers. See
docs/docker.md for the container workflow.
The upstream combined LIBERO dataset (physical-intelligence/libero) works with the standard configs. To create a
LeRobot dataset from the public RLDS releases instead:
uv run --group rlds examples/libero/convert_libero_data_to_lerobot.py \
--data-dir /path/to/libero_rlds \
--repo-name your_hf_username/liberoTo publish one LeRobot repository per suite, add --separate-subsets. The resulting repository names are
<repo-name>_libero_spatial, <repo-name>_libero_object, <repo-name>_libero_goal, and
<repo-name>_libero_10.
Several experiment configs use your_hf_username/... placeholders. Override them from the command line, or replace
them in src/openpi/training/config.py:
--data.repo-id your_hf_username/libero_libero_spatialSupervised PhaseLoRA configurations consume a precomputed JSON label mapping through
--data.coarse-fine-label-path. Pass a compatible label file when running a supervised routed configuration;
unsupervised and baseline configurations do not require this argument.
The training pipeline expects labels keyed by LeRobot sample index. With strict loading enabled, a missing sample raises an error rather than silently using a fallback label.
Download and convert the upstream JAX checkpoint once:
uv run examples/convert_jax_model_to_pytorch.py \
--checkpoint_dir ~/.cache/openpi/openpi-assets/checkpoints/pi05_base \
--config_name pi05_libero \
--output_path ~/.cache/openpi/openpi-assets/checkpoints/pi05_base_pytorchMake the converted checkpoint discoverable without hard-coding a machine-specific path:
export OPENPI_PI05_BASE_CHECKPOINT="$HOME/.cache/openpi/openpi-assets/checkpoints/pi05_base_pytorch"
export OPENPI_PI0_BASE_CHECKPOINT="$HOME/.cache/openpi/openpi-assets/checkpoints/pi0_base_pytorch"You may instead pass --pytorch-weight-path /absolute/path/to/checkpoint to every training command. A converted
checkpoint directory must contain model.safetensors.
For Hugging Face-style PI0 checkpoints whose model keys have a model. prefix and whose normalization buffers are
stored in safetensors, use:
uv run scripts/convert_pi0_hf_checkpoint.py \
--checkpoint-dir /path/to/hf_checkpoint \
--output-checkpoint-dir /path/to/openpi_checkpoint \
--asset-id physical-intelligence/liberoRepresentative configurations are listed below; the complete registry is in
src/openpi/training/config.py.
| Configuration family | Example |
|---|---|
| Upstream π₀.₅ baseline | pi05_libero |
| Standard LoRA | pi05_libero_low_mem_finetune |
| Full fine-tuning | pi05_libero_spatial_full_finetune |
| VLM LoRA + full action expert | pi05_libero_spatial_vlm_lora_action_full_finetune |
| Spectral LoRA | pi05_libero_spatial_lora_sp_baseline_finetune |
| Single-bank P/E router | pi05_libero_spatial_single_lora_tau_gated_router_finetune |
| Multi-bank PhaseLoRA | pi05_libero_spatial_phaselora_no_pe_supervision_finetune |
| Bimanual PhaseLoRA | pi05_aloha_sim_transfer_cube_bimanual_phaselora_rank48_96_finetune |
| Direct action regression | pi05_libero_low_mem_finetune_regression |
First compute normalization statistics for the exact dataset/config combination:
uv run scripts/compute_norm_stats.py \
--config-name pi05_libero_spatial_single_lora_tau_gated_router_finetune \
--repo-id your_hf_username/libero_libero_spatialThen launch a single-GPU run:
uv run scripts/train_pytorch.py \
pi05_libero_spatial_single_lora_tau_gated_router_finetune \
--exp-name spatial_router_seed42 \
--data.repo-id your_hf_username/libero_libero_spatial \
--data.coarse-fine-label-path /path/to/spatial_labels.json \
--pytorch-weight-path "$OPENPI_PI05_BASE_CHECKPOINT" \
--overwriteFor distributed training, keep batch_size as the global batch size and use torchrun:
uv run torchrun --standalone --nproc-per-node=4 scripts/train_pytorch.py \
pi05_libero_spatial_full_finetune \
--exp-name spatial_full_ft \
--data.repo-id your_hf_username/libero_libero_spatial \
--pytorch-weight-path "$OPENPI_PI05_BASE_CHECKPOINT" \
--shard-optimizer \
--overwriteCheckpoints are written to checkpoints/<config>/<exp-name>/<step>/. Use --resume to continue from the latest
valid step. --resume and --overwrite are mutually exclusive. Weights & Biases logging is enabled by default; use
--no-wandb-enabled for an offline run.
Start the policy server in the main environment:
uv run scripts/serve_policy.py \
policy:checkpoint \
--policy.config pi05_libero_spatial_single_lora_tau_gated_router_finetune \
--policy.dir checkpoints/<config>/<exp-name>/<step> \
--port 8000 \
--inference-seed 0Run the simulator in the LIBERO environment described in examples/libero/README.md:
python examples/libero/main.py \
--args.task-suite-name libero_spatial \
--args.num-trials-per-task 50 \
--args.results-out-path results/libero_spatial.json \
--args.video-out-path data/libero/videosUseful evaluation options:
--args.start-task-idand--args.end-task-idevaluate an inclusive task range.--args.no-save-videosdisables video output.--args.results-out-pathwrites a progress snapshot after every completed task.--args.request-timeout-scontrols the websocket inference timeout.
To evaluate the four public suites sequentially:
NUM_TRIALS=50 uv run bash run_libero.shThe fixed-state noise diagnostic sends explicit diffusion noise through the websocket protocol and records each rollout's video, noise array, and summary:
python examples/libero/eval_fixed_seed_noise_sweep.py \
--args.task-suite-name libero_10 \
--args.task-id 0 \
--args.init-state-id 0 \
--args.num-rollouts 8After starting a compatible policy server:
uv run examples/aloha_sim/main.py \
--num-episodes 10 \
--max-episode-steps 400 \
--metrics-path results/aloha_transfer_cube.jsonThe evaluator records per-episode seeds, rewards, steps, success flags, and an aggregate success rate.
uv run scripts/benchmark_checkpoint_router_stats.py \
--checkpoint CONFIG_NAME /path/to/checkpoint experiment_label \
--warmup 5 \
--runs 20uv run ruff check src scripts examples packages
uv run pytest -q -m "not manual"The full upstream suite instantiates multi-billion-parameter models and needs substantial accelerator memory. The portable CI check focuses on the new LoRA, transform, and client code:
uv run pytest -q \
src/openpi/models_pytorch/gemma_pytorch_test.py \
src/openpi/transforms_test.pysrc/openpi/models_pytorch/ PyTorch model, LoRA, router, and spectral adapter implementations
src/openpi/training/ Training/data configuration and data loading
scripts/train_pytorch.py Single-GPU and distributed PyTorch trainer
scripts/serve_policy.py Policy server entry point
examples/libero/ LIBERO conversion and evaluation
examples/aloha_sim/ ALOHA simulation evaluation
packages/openpi-client/ Websocket client protocol
Keep normalization statistics with the checkpoint used for evaluation so that the policy and training data transforms stay consistent.
PhaseLoRA is built on the OpenPI codebase and model releases from Physical Intelligence, plus LIBERO, LeRobot, Gemma, and the other upstream projects listed in the dependency files and submodules.
OpenPI code is distributed under the terms in LICENSE. Gemma components are covered by
LICENSE_GEMMA.txt. Third-party submodules retain their own licenses.