### Install Project Dependencies Source: https://context7.com/milkclouds/vla0-trl/llms.txt Installs the project and its dependencies, including LeRobot and evaluation extras. Ensure you activate the virtual environment after installation. ```bash uv venv --python 3.11 uv pip install -e . GIT_LFS_SKIP_SMUDGE=1 uv pip install git+https://github.com/huggingface/lerobot.git@f39652707caed42a7cd5ab36066da5663b9565eb uv pip install -e ".[eval]" source .venv/bin/activate ``` -------------------------------- ### Install Dependencies with uv Source: https://github.com/milkclouds/vla0-trl/blob/main/README.md Install project dependencies using uv, including the LeRobot library from a specific Git commit. Ensure your virtual environment is activated. ```bash uv venv --python 3.11 uv pip install -e . # LeRobot dependency GIT_LFS_SKIP_SMUDGE=1 uv pip install git+https://github.com/huggingface/lerobot.git@f39652707caed42a7cd5ab36066da5663b9565eb # For evaluation uv pip install -e ".[eval]" # Do not forget activating your venv source .venv/bin/activate ``` -------------------------------- ### Initialize LIBERO Environment and Get Tasks Source: https://context7.com/milkclouds/vla0-trl/llms.txt These functions interface with the LIBERO benchmark library. `get_evaluation_tasks` lists available tasks, while `init_libero_env` initializes a specific MuJoCo environment for a given task. ```python from rv_eval.libero_env import init_libero_env, get_evaluation_tasks tasks = get_evaluation_tasks(task_suite_name="libero_spatial") # {"libero_spatial": ["LIVING_ROOM_SCENE2_put_the_red_mug...", ...]} # 10 tasks all_tasks = get_evaluation_tasks() for suite, task_list in all_tasks.items(): print(f"{suite}: {len(task_list)} tasks") # libero_spatial: 10 tasks # libero_object: 10 tasks # libero_goal: 10 tasks # libero_10: 10 tasks env, init_states, max_steps, instruction = init_libero_env( task_name="LIVING_ROOM_SCENE2_put_the_red_mug_on_the_left_plate", task_suite_name="libero_spatial", seed=7, ) print(instruction) # "put the red mug on the left plate" print(max_steps) # 220 print(len(init_states)) # 50 obs = env.set_init_state(init_states[0]) env.close() ``` -------------------------------- ### Evaluate Robot Actions using CLI Source: https://context7.com/milkclouds/vla0-trl/llms.txt This section provides command-line examples for evaluating robot actions using the `scripts/eval.py` script. It covers evaluating a full task suite with sharding and evaluating all suites without sharding. ```bash # Evaluate a full suite (single GPU, shard 0/10) python scripts/eval.py \ --model_path ./runs/vla0_sft/checkpoint-80000 \ --task_suite libero_spatial \ --action_horizon 8 \ --ensemble_prediction 8 \ --torch_compile \ --skip_evaluated \ --shard_id 0 --num_shards 10 \ --log_dir ./eval_logs/vla0_sft/checkpoint-80000 # Evaluate all suites (single shard, no sharding) python scripts/eval.py \ --model_path ./runs/vla0_sft/checkpoint-80000 ``` -------------------------------- ### Execute First Action in Environment Source: https://context7.com/milkclouds/vla0-trl/llms.txt This snippet demonstrates how to execute the first action from a pre-defined action sequence in an environment. It includes binarizing the gripper command before stepping the environment. ```python print(actions.shape) # torch.Size([8, 7]) print(actions[0]) # tensor([ 0.012, -0.003, 0.015, 0.001, -0.002, 0.018, 1.000]) # Execute first action in environment for t in range(8): act = actions[t].tolist() act[-1] = 1.0 if act[-1] > 0 else -1.0 # binarize gripper obs, reward, done, info = env.step(act) ``` -------------------------------- ### Load Qwen2.5-VL for Fine-Tuning Source: https://context7.com/milkclouds/vla0-trl/llms.txt Loads the Qwen2.5-VL model in bfloat16 for full parameter fine-tuning. Optionally enables Flash Attention 3 for improved performance on compatible hardware. ```python from rv_train.model import load_model_for_training # Standard load (no flash attention) model = load_model_for_training( model_id="Qwen/Qwen2.5-VL-3B-Instruct", use_flash_attention=False, ) # With Flash Attention 3 (recommended for H100+) model = load_model_for_training( model_id="Qwen/Qwen2.5-VL-3B-Instruct", use_flash_attention=True, ) print(model.config.model_type) # qwen2_5_vl print(next(model.parameters()).dtype) # torch.bfloat16 ``` -------------------------------- ### Load Qwen2.5-VL Processor Source: https://context7.com/milkclouds/vla0-trl/llms.txt Instantiates the Qwen2.5-VL processor, configuring image processing parameters like resolution and tiling. The `tile_images=True` setting concatenates camera feeds, effectively doubling the pixel count. ```python from rv_train.model import load_processor # Single-camera, 224×224 processor = load_processor( model_id="Qwen/Qwen2.5-VL-3B-Instruct", img_size=224, num_cams=1, tile_images=False, ) # pixel count = 224 * 224 = 50176 # Dual-camera tiled (default training setting) processor = load_processor( model_id="Qwen/Qwen2.5-VL-3B-Instruct", img_size=224, num_cams=2, tile_images=True, ) # pixel count = 224 * 224 * 2 = 100352 print(processor.image_processor.min_pixels) # 100352 ``` -------------------------------- ### load_processor Source: https://context7.com/milkclouds/vla0-trl/llms.txt Instantiates the Qwen2.5-VL processor, configuring image input size and tiling for dual cameras. This ensures consistent input dimensions for training. ```APIDOC ## load_processor — Load Qwen2.5-VL Image+Text Processor Instantiates the Qwen2.5-VL processor with `min_pixels` and `max_pixels` locked to a fixed resolution, preventing dynamic patching during training. When `tile_images=True`, both cameras are tiled horizontally, doubling the pixel count. ### Parameters - **model_id** (str) - Required - The identifier for the Qwen2.5-VL model. - **img_size** (int) - Required - The target image size (e.g., 224). - **num_cams** (int) - Required - The number of cameras to process (1 or 2). - **tile_images** (bool) - Optional - Whether to tile images from multiple cameras horizontally. Defaults to `False`. ### Returns - **processor** (PreTrainedProcessor) - The configured Qwen2.5-VL processor. ### Request Example ```python from rv_train.model import load_processor # Single-camera, 224×224 processor = load_processor( model_id="Qwen/Qwen2.5-VL-3B-Instruct", img_size=224, num_cams=1, tile_images=False, ) # pixel count = 224 * 224 = 50176 # Dual-camera tiled (default training setting) processor = load_processor( model_id="Qwen/Qwen2.5-VL-3B-Instruct", img_size=224, num_cams=2, tile_images=True, ) # pixel count = 224 * 224 * 2 = 100352 print(processor.image_processor.min_pixels) # 100352 ``` ``` -------------------------------- ### Initialize and Use LiberoDataset Source: https://context7.com/milkclouds/vla0-trl/llms.txt Instantiate the LiberoDataset for training. Ensure correct repository ID and augmentation parameters are set. Access dataset statistics for normalization. ```python from rv_train.dataset import LiberoDataset dataset = LiberoDataset( repo_id="physical-intelligence/libero", history=1, horizon=8, img_size=224, crop_ratio=0.875, tile_images=True, brightness_aug=0.2, contrast_aug=0.2, saturation_aug=0.2, hue_aug=0.05, ) print(f"Dataset size: {len(dataset)}") # e.g., 197088 sample = dataset[0] # sample["messages"] = [ # {"role": "system", "content": [{"type": "text", "text": "Analyze the input image..."}]}, # {"role": "user", "content": [{"type": "image"}, {"type": "text", "text": "put the white mug on..."}]}, # {"role": "assistant", "content": [{"type": "text", "text": "474 479 460 669 391 674 0 ..."}]}, # ] # sample["images"] = [] # tiled wrist+agentview print(sample["messages"][-1]["content"][0]["text"]) # "474 479 460 669 391 674 0 471 479 460 669 ..." # Access dataset statistics for inference normalization print(dataset.stats) # {"out_ori_act": {"min": [-0.28, ...], "max": [0.27, ...]}} ``` -------------------------------- ### Load QwenVLActor for Inference Source: https://context7.com/milkclouds/vla0-trl/llms.txt Initialize the QwenVLActor for robot action prediction. Load the trained model and dataset statistics. Specify model parameters like horizon, action dimension, and number of bins. Use `torch_compile=True` for performance on compatible hardware. ```python from rv_train.model import QwenVLActor from PIL import Image # Load trained model actor = QwenVLActor( model_path="./runs/vla0_sft/checkpoint-80000", stats_path="./runs/vla0_sft/dataset_stats.json", horizon=8, action_dim=7, num_bins=1000, device="cuda", torch_compile=True, # ~2× faster on H100 ) # Run inference image = Image.open("obs.png") # 448×224 tiled dual-camera image instruction = "put the white mug on the left plate" actions = actor.predict(image, instruction, temperature=0.1) # actions: torch.Tensor shape [8, 7] ``` -------------------------------- ### Train Model with Single GPU Source: https://github.com/milkclouds/vla0-trl/blob/main/README.md Initiate the training process for the VLA model on a single GPU using the specified configuration file. ```bash python scripts/train.py --config configs/vla0.yaml ``` -------------------------------- ### Verify Migration Script Source: https://github.com/milkclouds/vla0-trl/blob/main/MIGRATION.md Run this command to execute the verification script and check for equivalence in various components between the original and the current repository. ```bash pytest scripts/verify_migration.py -v ``` -------------------------------- ### Execute Training Script Source: https://context7.com/milkclouds/vla0-trl/llms.txt Launch the training pipeline using `scripts/train.py`. Supports single GPU, multi-GPU with accelerate, and SLURM job submission. Configuration can be managed via YAML files and CLI overrides. ```bash # Single GPU python scripts/train.py --config configs/vla0.yaml # Multi-GPU (8 GPUs, matches paper) accelerate launch --num_processes=8 scripts/train.py --config configs/vla0.yaml # Override individual parameters on the CLI accelerate launch --num_processes=8 scripts/train.py \ --config configs/vla0.yaml \ --output_dir ./runs/vla0_my_run \ --run_name vla0_my_run \ --max_grad_norm 1.0 \ --learning_rate 2e-5 # SLURM (8×H100, ~18h for 80k steps) sbatch scripts/train.sbatch # Contents: accelerate launch --num_processes=8 scripts/train.py --config configs/vla0.yaml \ # --output_dir ./runs/vla0_sft-lr4e-5-gradclip --max-grad-norm 1.0 ``` -------------------------------- ### load_model_for_training Source: https://context7.com/milkclouds/vla0-trl/llms.txt Loads Qwen2.5-VL in bfloat16 for full parameter fine-tuning. Supports optional Flash Attention 3 for enhanced performance on compatible hardware. ```APIDOC ## load_model_for_training — Load Qwen2.5-VL for Full Fine-Tuning Loads `Qwen2.5-VL` in `bfloat16` for full parameter fine-tuning. Optionally enables Flash Attention 3 via the `kernels-community` backend. ### Parameters - **model_id** (str) - Required - The identifier for the Qwen2.5-VL model. - **use_flash_attention** (bool) - Optional - Whether to enable Flash Attention 3. Defaults to `False`. ### Returns - **model** (torch.nn.Module) - The loaded and configured Qwen2.5-VL model. ### Request Example ```python from rv_train.model import load_model_for_training # Standard load (no flash attention) model = load_model_for_training( model_id="Qwen/Qwen2.5-VL-3B-Instruct", use_flash_attention=False, ) # With Flash Attention 3 (recommended for H100+) model = load_model_for_training( model_id="Qwen/Qwen2.5-VL-3B-Instruct", use_flash_attention=True, ) print(model.config.model_type) # qwen2_5_vl print(next(model.parameters()).dtype) # torch.bfloat16 ``` ``` -------------------------------- ### Action Discretization and Conversion Utilities Source: https://context7.com/milkclouds/vla0-trl/llms.txt The `ActionProcessor` class handles converting continuous robot actions to discretized text and vice-versa. It requires dataset statistics for normalization and supports padding/truncation to a specified horizon. ```python from rv_train.utils import ActionProcessor import torch proc = ActionProcessor(num_bins=1000, action_dim=7, horizon=8) # Set stats (loaded from dataset_stats.json) proc.set_stats({ "min": [-0.28, -0.28, -0.28, -3.14, -3.14, -3.14, -1.0], "max": [ 0.28, 0.28, 0.28, 3.14, 3.14, 3.14, 1.0], }) # Continuous → text (for training label generation) actions = torch.zeros(1, 8, 7) # [batch, horizon, action_dim] texts = proc.action_to_text(actions.reshape(1, -1)) print(texts[0][:30]) # "500 500 500 500 500 500 500 50" (midpoint = bin 500) # Text → continuous (for inference decoding) text = "474 479 460 669 391 674 0 471 479 460 669 391 674 0" actions_out = proc.text_to_action([text]) print(actions_out.shape) # torch.Size([1, 8, 7]) print(actions_out[0, 0]) # tensor([-0.006, -0.003, -0.022, 0.881, -0.763, 0.951, -1.000]) # Get the system prompt string print(proc.get_system_prompt()) # "Analyze the input image and predict robot actions for the next 8 timesteps..." ``` -------------------------------- ### Train Model with Multi-GPU using Accelerate Source: https://github.com/milkclouds/vla0-trl/blob/main/README.md Launch distributed training across multiple GPUs using the accelerate library with the specified configuration. ```bash accelerate launch --num_processes=8 scripts/train.py --config configs/vla0.yaml ``` -------------------------------- ### LIBERO Environment Utilities Source: https://context7.com/milkclouds/vla0-trl/llms.txt Initialize LIBERO environments and retrieve evaluation tasks. These functions interface with the LIBERO benchmark library to load scenarios and create MuJoCo environments. ```APIDOC ## `init_libero_env` / `get_evaluation_tasks` — LIBERO Environment Utilities Low-level helpers that interface with the LIBERO benchmark library to enumerate tasks, load BDDL scenario files, and instantiate `OffScreenRenderEnv` MuJoCo environments. ```python from rv_eval.libero_env import init_libero_env, get_evaluation_tasks # List all tasks in a suite tasks = get_evaluation_tasks(task_suite_name="libero_spatial") # {"libero_spatial": ["LIVING_ROOM_SCENE2_put_the_red_mug...", ...]} # 10 tasks # List tasks across all suites all_tasks = get_evaluation_tasks() for suite, task_list in all_tasks.items(): print(f"{suite}: {len(task_list)} tasks") # libero_spatial: 10 tasks # libero_object: 10 tasks # libero_goal: 10 tasks # libero_10: 10 tasks # Initialize a single environment env, init_states, max_steps, instruction = init_libero_env( task_name="LIVING_ROOM_SCENE2_put_the_red_mug_on_the_left_plate", task_suite_name="libero_spatial", seed=7, ) print(instruction) # "put the red mug on the left plate" print(max_steps) # 220 print(len(init_states)) # 50 obs = env.set_init_state(init_states[0]) env.close() ``` ``` -------------------------------- ### Initialize and Configure LiberoEvaluator Source: https://context7.com/milkclouds/vla0-trl/llms.txt This snippet shows how to initialize the `LiberoEvaluator` for orchestrating episodic evaluations on the LIBERO benchmark. It configures parameters like logging directory, video saving, and ensemble prediction settings. ```python from rv_eval.evaluator import LiberoEvaluator from rv_train.model import QwenVLActor model = QwenVLActor( model_path="./runs/vla0_sft/checkpoint-80000", stats_path="./runs/vla0_sft/dataset_stats.json", ) evaluator = LiberoEvaluator( model=model, log_dir="./eval_logs/vla0/checkpoint-80000", save_video=True, seed=7, action_horizon=8, # execute 8 actions before re-querying frame_skip=10, # warm-up frames at episode start img_size=224, crop_ratio=0.875, tile_images=True, shard_id=0, # run first shard of 10 num_shards=10, skip_evaluated=True, # resume interrupted eval from existing videos ensemble_prediction=8, # average 8 overlapping action chunks ensemble_version=1, # flat 0.5 weight for old chunks ensemble_weight=0.5, ) # Evaluate all tasks in one suite results = evaluator.evaluate(task_suite_name="libero_spatial") # Prints: # ============================================================ # --- libero_spatial --- # LIVING_ROOM_SCENE2_put_the_red_mug_on_the_left_plate: 48/50 (96.0%) # ... # Suite Total: 475/500 (95.0%) # Evaluate a single specific task results = evaluator.evaluate( task_suite_name="libero_10", task_name="KITCHEN_SCENE10_close_the_top_drawer_of_the_cabinet", ) ``` -------------------------------- ### Project Structure Overview Source: https://github.com/milkclouds/vla0-trl/blob/main/README.md This snippet shows the directory structure of the vla0-trl project, highlighting key configuration, script, and source code directories. ```text ├── configs/vla0.yaml # Training config ├── scripts/ │ ├── train.py # Training entry │ └── eval.py # Evaluation entry └── src/ ├── rv_train/ # Dataset, collator, model └── rv_eval/ # LIBERO evaluator ``` -------------------------------- ### Preprocess Observations for QwenVLA Source: https://context7.com/milkclouds/vla0-trl/llms.txt This function converts raw LIBERO observation dictionaries into tiled PIL images. It applies center cropping and resizing, preparing images for model prediction. Use `tile_images=True` for a single output image or `False` for a list of camera images. ```python from rv_eval.evaluator import preprocess_obs import numpy as np # obs is the dict returned by env.step() or env.set_init_state() # obs["agentview_image"]: (256, 256, 3) uint8, flipped # obs["robot0_eye_in_hand_image"]: (256, 256, 3) uint8, flipped image = preprocess_obs( obs, img_size=224, crop_ratio=0.875, tile_images=True, # → single 448×224 PIL image ) print(image.size) # (448, 224) # Single-camera mode images_list = preprocess_obs(obs, tile_images=False) # → [PIL.Image 224×224, PIL.Image 224×224] ``` -------------------------------- ### scripts/eval.py Source: https://context7.com/milkclouds/vla0-trl/llms.txt Command-line interface for evaluating models using LiberoEvaluator and QwenVLActor, with automatic detection of dataset statistics. ```APIDOC ## `scripts/eval.py` — Evaluation Entry Point CLI wrapper for `LiberoEvaluator` + `QwenVLActor`. Auto-detects `dataset_stats.json` from the checkpoint parent directory. ```bash # Evaluate a full suite (single GPU, shard 0/10) python scripts/eval.py \ --model_path ./runs/vla0_sft/checkpoint-80000 \ --task_suite libero_spatial \ --action_horizon 8 \ --ensemble_prediction 8 \ --torch_compile \ --skip_evaluated \ --shard_id 0 --num_shards 10 \ --log_dir ./eval_logs/vla0_sft/checkpoint-80000 # Evaluate all suites (single shard, no sharding) python scripts/eval.py \ --model_path ./runs/vla0_sft/checkpoint-80000 ``` ``` -------------------------------- ### Initialize and Use VLACollator for Training Source: https://context7.com/milkclouds/vla0-trl/llms.txt Set up the VLACollator for batching samples from LiberoDataset. Configure the processor and action mask augmentation percentage. The collator prepares data for SFTTrainer by applying chat templates, processing images, and masking labels. ```python from rv_train.collator import VLACollator from rv_train.model import load_processor from rv_train.dataset import LiberoDataset from torch.utils.data import DataLoader processor = load_processor("Qwen/Qwen2.5-VL-3B-Instruct", img_size=224, num_cams=2, tile_images=True) collator = VLACollator( processor=processor, action_mask_aug_pct=0.4, # mask up to 40% of action tokens randomly ) dataset = LiberoDataset(repo_id="physical-intelligence/libero") loader = DataLoader(dataset, batch_size=2, collate_fn=collator) batch = next(iter(loader)) # batch keys: input_ids, attention_mask, pixel_values, image_grid_thw, labels print(batch["input_ids"].shape) # torch.Size([2, 433]) print(batch["labels"].shape) # torch.Size([2, 433]) # Labels: first ~226 tokens masked (-100), last 207 are action tokens unmasked = (batch["labels"][0] != -100).sum() print(f"Trainable tokens per sample: {unmasked.item()}") # ~207 ``` -------------------------------- ### ActionProcessor Source: https://context7.com/milkclouds/vla0-trl/llms.txt Utility class for converting between continuous robot actions and their discretized text representations, including normalization and padding. ```APIDOC ## `ActionProcessor` — Action Discretization Utilities Standalone utility class for converting between continuous robot actions and their discretized text representations. Handles normalization using dataset min/max statistics, padding/truncation to horizon length, and graceful fallback on parse failure. ```python from rv_train.utils import ActionProcessor import torch proc = ActionProcessor(num_bins=1000, action_dim=7, horizon=8) # Set stats (loaded from dataset_stats.json) proc.set_stats({ "min": [-0.28, -0.28, -0.28, -3.14, -3.14, -3.14, -1.0], "max": [ 0.28, 0.28, 0.28, 3.14, 3.14, 3.14, 1.0], }) # Continuous → text (for training label generation) actions = torch.zeros(1, 8, 7) # [batch, horizon, action_dim] texts = proc.action_to_text(actions.reshape(1, -1)) print(texts[0][:30]) # "500 500 500 500 500 500 500 50" (midpoint = bin 500) # Text → continuous (for inference decoding) text = "474 479 460 669 391 674 0 471 479 460 669 391 674 0" actions_out = proc.text_to_action([text]) print(actions_out.shape) # torch.Size([1, 8, 7]) print(actions_out[0, 0]) # tensor([-0.006, -0.003, -0.022, 0.881, -0.763, 0.951, -1.000]) # Get the system prompt string print(proc.get_system_prompt()) # "Analyze the input image and predict robot actions for the next 8 timesteps..." ``` ``` -------------------------------- ### Evaluate Model Checkpoint Source: https://github.com/milkclouds/vla0-trl/blob/main/README.md Run evaluation for a trained model checkpoint on a specific task suite. Supports parallel evaluation using shards and enables torch.compile for faster inference. ```bash python scripts/eval.py \ --model_path ./runs/vla0/checkpoint-xxx \ --task_suite libero_spatial \ --action_horizon 8 \ --ensemble_prediction 8 \ --torch_compile \ --skip_evaluated \ --shard_id 0 --num_shards 10 ``` -------------------------------- ### Summarize Evaluation Results from Logs Source: https://context7.com/milkclouds/vla0-trl/llms.txt This script scans the `eval_logs/` directory to aggregate results from `results.csv` files. It prints a formatted table of success rates per suite, grouped by model and checkpoint. It can be run while evaluations are in progress. ```bash # Summarize all evals in ./eval_logs/ python scripts/summarize_eval.py # Custom log directory python scripts/summarize_eval.py --eval_logs ./my_eval_logs # Example output: # Model: vla0_sft-lr4e-5-gradclip # Checkpoint libero_spatial libero_object libero_goal libero_10 Average # ------------------------------------------------------------------------------------- # checkpoint-80000 95.2% (476/500) 96.0% (480/500) 92.6% (463/500) 84.8% (424/500) 92.2% ``` -------------------------------- ### Original LR Scaling with GPU Count Source: https://github.com/milkclouds/vla0-trl/blob/main/MIGRATION.md The original VLA-0 repository scaled the learning rate by the number of GPUs used during training. This behavior is different in the current implementation. ```python optimizer = AdamW(params, lr=cfg.TRAIN.lr * num_gpus) # 5e-6 × 8 = 4e-5 ``` -------------------------------- ### Submit Evaluation Job with SLURM Source: https://context7.com/milkclouds/vla0-trl/llms.txt This command submits an evaluation job using SLURM. It's designed for distributed execution across multiple GPUs and sub-shards. ```bash sbatch scripts/eval.sbatch ``` -------------------------------- ### Evaluate a Specific Task with Python Script Source: https://context7.com/milkclouds/vla0-trl/llms.txt Use this command to run evaluation for a single task. Specify the model path, task suite, and the exact task name. ```bash python scripts/eval.py \ --model_path ./runs/vla0_sft/checkpoint-80000 \ --task_suite libero_object \ --task_name "LIVING_ROOM_SCENE1_put_the_black_bowl_on_the_stove" ``` -------------------------------- ### Observation Preprocessing Source: https://context7.com/milkclouds/vla0-trl/llms.txt Preprocesses raw LIBERO observation dictionaries into tiled PIL images suitable for model prediction. This function applies center cropping and resizing to the specified training resolution. ```APIDOC ## `preprocess_obs` — Observation Preprocessing for Evaluation Converts raw LIBERO observation dictionaries (flipped NumPy arrays from MuJoCo) into tiled PIL images suitable for `QwenVLActor.predict()`. Applies center crop (not random) and resizes to the training resolution. ```python from rv_eval.evaluator import preprocess_obs import numpy as np # obs is the dict returned by env.step() or env.set_init_state() # obs["agentview_image"]: (256, 256, 3) uint8, flipped # obs["robot0_eye_in_hand_image"]: (256, 256, 3) uint8, flipped image = preprocess_obs( obs, img_size=224, crop_ratio=0.875, tile_images=True, # → single 448×224 PIL image ) print(image.size) # (448, 224) # Single-camera mode images_list = preprocess_obs(obs, tile_images=False) # → [PIL.Image 224×224, PIL.Image 224×224] ``` ``` -------------------------------- ### LiberoEvaluator Source: https://context7.com/milkclouds/vla0-trl/llms.txt Orchestrates episodic evaluation on the LIBERO benchmark, handling environment initialization, episode execution, result recording, and optional video saving. ```APIDOC ## `LiberoEvaluator` — LIBERO Benchmark Evaluator Orchestrates episodic evaluation on the LIBERO benchmark: initializes MuJoCo environments, runs `run_episode()` for each initial state, records success/failure to a parallel-safe CSV (with `FileLock`), and optionally saves MP4 videos. Supports sharding for distributed evaluation across multiple GPUs. ```python from rv_eval.evaluator import LiberoEvaluator from rv_train.model import QwenVLActor model = QwenVLActor( model_path="./runs/vla0_sft/checkpoint-80000", stats_path="./runs/vla0_sft/dataset_stats.json", ) evaluator = LiberoEvaluator( model=model, log_dir="./eval_logs/vla0/checkpoint-80000", save_video=True, seed=7, action_horizon=8, # execute 8 actions before re-querying frame_skip=10, # warm-up frames at episode start img_size=224, crop_ratio=0.875, tile_images=True, shard_id=0, # run first shard of 10 num_shards=10, skip_evaluated=True, # resume interrupted eval from existing videos ensemble_prediction=8, # average 8 overlapping action chunks ensemble_version=1, # flat 0.5 weight for old chunks ensemble_weight=0.5, ) # Evaluate all tasks in one suite results = evaluator.evaluate(task_suite_name="libero_spatial") # Prints: # ============================================================ # --- libero_spatial --- # LIVING_ROOM_SCENE2_put_the_red_mug_on_the_left_plate: 48/50 (96.0%) # ... # Suite Total: 475/500 (95.0%) # Evaluate a single specific task results = evaluator.evaluate( task_suite_name="libero_10", task_name="KITCHEN_SCENE10_close_the_top_drawer_of_the_cabinet", ) ``` ``` -------------------------------- ### Aggregate Evaluation Results Source: https://context7.com/milkclouds/vla0-trl/llms.txt Scans the evaluation logs directory, parses `results.csv` files, and prints a formatted table of per-suite success rates. This script can be run while evaluations are in progress. ```APIDOC ## `scripts/summarize_eval.py` — Aggregate Evaluation Results Scans the `eval_logs/` directory tree, parses all `results.csv` files, and prints a formatted table of per-suite success rates grouped by model and checkpoint. Safe to run while evaluation is still in progress. ```bash # Summarize all evals in ./eval_logs/ python scripts/summarize_eval.py # Custom log directory python scripts/summarize_eval.py --eval_logs ./my_eval_logs # Example output: # Model: vla0_sft-lr4e-5-gradclip # Checkpoint libero_spatial libero_object libero_goal libero_10 Average # ------------------------------------------------------------------------------------- # checkpoint-80000 95.2% (476/500) 96.0% (480/500) 92.6% (463/500) 84.8% (424/500) 92.2% ``` ```python # Programmatic usage from pathlib import Path from scripts.summarize_eval import discover_evals, parse_results_csv, compute_suite_stats evals = discover_evals(Path("./eval_logs")) for model, checkpoints in evals.items(): for ckpt, suites in checkpoints.items(): for suite, csv_path in suites.items(): results = parse_results_csv(csv_path) rate, succ, total = compute_suite_stats(results) print(f"{model}/{ckpt}/{suite}: {rate*100:.1f}% ({succ}/{total})") ``` ``` -------------------------------- ### Decoded Robot Actions Source: https://github.com/milkclouds/vla0-trl/blob/main/MIGRATION.md Displays the sequence of predicted robot actions for the next 8 timesteps, with each action represented by 7 dimensions. The output is a single sequence of 56 space-separated integers. ```text <|im_start|>system Analyze the input image and predict robot actions for the next 8 timesteps. Each action has 7 dimensions. Output a single sequence of 56 integers (0-1000 each), representing the 8 timesteps sequentially. Provide only space separated numbers. Nothing else.<|im_end|> <|im_start|>user Picture 1: <|vision_start|><|image_pad|>...(128 pads)...<|vision_end|>put the white mug on the left plate and put the yellow and white mug on the right plate<|im_end|> <|im_start|>assistant 474 479 460 669 391 674 0 471 479 460 669 391 674 0 458 479 460 669 391 674 0 455 472 460 669 391 674 0 455 444 460 669 391 674 0 455 396 449 669 391 674 0 449 335 422 669 391 674 0 422 282 397 669 391 674 0<|im_end|> ``` -------------------------------- ### Labels (Tokens) Source: https://github.com/milkclouds/vla0-trl/blob/main/MIGRATION.md The sequence of labels, corresponding to the input IDs, with the first 226 tokens masked. This is used for training and evaluation. ```text ``` -------------------------------- ### Input IDs (Tokens) Source: https://github.com/milkclouds/vla0-trl/blob/main/MIGRATION.md The sequence of input IDs representing the tokenized input, including system prompts, user instructions, and image placeholders. This is a numerical representation used by the model. ```python [151644, 8948, 198, 2082, 55856, 279, 1946, 2168, 323, 7023, 12305, 6168, 369, 279, 1790, 220, 23, 259, 76632, 13, 8886, 1917, 702, 220, 22, 15336, 13, 9258, 264, 3175, 8500, 315, 220, 20, 21, 25780, 320, 15, 12, 16, 15, 15, 15, 1817, 701, 14064, 279, 220, 23, 259, 76632, 94559, 13, 39565, 1172, 3550, 18663, 5109, 13, 12064, 770, 13, 151645, 198, 151644, 872, 198, 24669, 220, 16, 25, 220, 151652, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 151655, 11968, 151645, 198, 151644, 77091, 198, 19, 22, 19, 220, 19, 22, 24, 220, 19, 21, 15, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 22, 16, 220, 19, 22, 24, 220, 19, 21, 15, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 20, 23, 220, 19, 22, 24, 220, 19, 21, 15, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 20, 20, 220, 19, 22, 17, 220, 19, 21, 15, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 20, 20, 220, 19, 22, 16, 220, 19, 22, 24, 220, 19, 21, 15, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 20, 20, 220, 18, 24, 21, 220, 19, 19, 24, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 19, 24, 220, 18, 18, 20, 220, 19, 17, 17, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 17, 17, 220, 17, 23, 17, 220, 18, 24, 22, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 220, 19, 17, 17, 220, 17, 23, 17, 220, 18, 24, 22, 220, 21, 21, 24, 220, 18, 24, 16, 220, 21, 22, 19, 220, 15, 151645, 198] ``` -------------------------------- ### Programmatic Evaluation Result Aggregation Source: https://context7.com/milkclouds/vla0-trl/llms.txt Use these functions to programmatically discover evaluation logs, parse CSV results, and compute suite statistics. This allows for custom analysis and integration into other workflows. ```python # Programmatic usage from pathlib import Path from scripts.summarize_eval import discover_evals, parse_results_csv, compute_suite_stats evals = discover_evals(Path("./eval_logs")) for model, checkpoints in evals.items(): for ckpt, suites in checkpoints.items(): for suite, csv_path in suites.items(): results = parse_results_csv(csv_path) rate, succ, total = compute_suite_stats(results) print(f"{model}/{ckpt}/{suite}: {rate*100:.1f}% ({succ}/{total})") ``` -------------------------------- ### VLA-0 Citation (BibTeX) Source: https://github.com/milkclouds/vla0-trl/blob/main/README.md BibTeX entry for citing the original VLA-0 paper. Include this when referencing the foundational work. ```bibtex @misc{vla0-trl, author = {Suhwan Choi}, title = {vla0-trl: Minimal VLA-0 Reimplementation with TRL}, year = {2025}, publisher = {GitHub}, url = {https://github.com/MilkClouds/vla0-trl}, doi = {10.5281/ZENODO.18712424} } @article{goyal2025vla0, title={VLA-0: Building State-of-the-Art VLAs with Zero Modification}, author={Goyal, Ankit and Hadfield, Hugo and Yang, Xuning and Blukis, Valts and Ramos, Fabio}, journal={arXiv preprint arXiv:2510.13054}, year={2025} } ``` -------------------------------- ### Constrain Model Output to Digits and Space Source: https://context7.com/milkclouds/vla0-trl/llms.txt Use `NumberSpaceOnlyProcessor` to ensure a model only generates digits (0-9), space, and EOS tokens. This is useful for preventing parse errors in action prediction tasks. ```python from rv_train.model import NumberSpaceOnlyProcessor from transformers import Qwen2_5_VLProcessor processor = Qwen2_5_VLProcessor.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct") logits_processor = NumberSpaceOnlyProcessor(processor.tokenizer) # Allowed token IDs: digits 0-9, space, EOS print(len(logits_processor.allowed_tokens)) # 12 # Used automatically in QwenVLActor.predict(): output_ids = model.generate( **inputs, max_new_tokens=256, logits_processor=[logits_processor], temperature=0.1, ) # Output is guaranteed to be parseable as space-separated integers ```