OpenVLA-7B β LIBERO-Spatial LoRA (20k steps, with gradient clipping)
Fine-tuned from openvla/openvla-7b on the libero_spatial_no_noops task suite using LoRA (rank=32) for 20,000 gradient steps across two sequential runs on an NVIDIA GH200 (ARM64/aarch64).
Key contribution: includes a gradient clipping fix missing from the upstream OpenVLA training script β without it, training collapses at ~6k steps due to an unclamped gradient spike.
Evaluation Results
Evaluated on LIBERO-Spatial (10 tasks, 5 trials per task = 50 episodes total).
| Setup | Steps | Score |
|---|---|---|
| Official paper (x86, flash-attn) | 10k | 84.7% |
Official openvla-7b-finetuned-libero-spatial (ARM64 eval) |
10k | 37/50 = 74% |
| Our run 1 β no gradient clipping | 10k | 20/50 = 39% |
| Our run 2 β with gradient clipping | 10k | 28/50 = 56% |
| This checkpoint β grad clipping, 20k steps (ARM64 eval) | 20k | 36/50 = 72% |
Our 20k checkpoint matches the official published 10k checkpoint (74% vs 72%, within statistical noise at 5 trials/task) on the same ARM64 hardware, confirming the gradient clipping fix is sufficient to recover training stability.
The ~12pp gap vs. the paper (84.7%) is a hardware effect: the official paper used x86 with native flash attention; on ARM64 the pre-built flash-attn binary is x86-only. Flash-attn 2.5.8 was built from source for run 3 but not run 2, so training used eager attention for most of the run. The official checkpoint evaluated at 74% on the same ARM64 setup, confirming this gap is hardware-driven, not a model quality issue.
Per-task breakdown (this checkpoint, 5 trials each)
| Task | Successes | Rate |
|---|---|---|
| Task 1 | 3/5 | 60% |
| Task 2 | 4/5 | 80% |
| Task 3 | 4/5 | 80% |
| Task 4 | 5/5 | 100% |
| Task 5 | 3/5 | 60% |
| Task 6 | 2/5 | 40% |
| Task 7 | 5/5 | 100% |
| Task 8 | 5/5 | 100% |
| Task 9 | 3/5 | 60% |
| Task 10 | 2/5 | 40% |
| Total | 36/50 | 72% |
Training Curves (20k steps)
- Green = Run 2 (steps 0β10k, starting from base
openvla/openvla-7b) - Blue = Run 3 (steps 10kβ20k, starting from the run 2 merged checkpoint)
Loss descends steadily from ~2.4 β ~0.68. Action token accuracy rises from ~0.05 β ~0.82. No loss spikes β contrast with run 1 (no gradient clipping) which spiked at step ~6k and never recovered.
Training Details
| Setting | Value |
|---|---|
| Base model | openvla/openvla-7b |
| Dataset | libero_spatial_no_noops |
| Total steps | 20,000 (2 Γ 10k sequential runs) |
| Batch size | 16 |
| Learning rate | 5e-4 |
| LoRA rank | 32 |
| LoRA dropout | 0.0 |
| Quantization | false |
| Image augmentation | true |
| Shuffle buffer | 10,000 |
| Hardware | NVIDIA GH200 (ARM64/aarch64, 480GB HBM3) |
Resuming from a checkpoint
finetune.py has no native resume support. We resumed by setting VLA_PATH to the merged run 2 checkpoint. The model starts from those weights (all learned knowledge preserved); only the step counter and optimizer state reset. This is equivalent to a warm-start.
Key Fix: Gradient Clipping
The upstream vla-scripts/finetune.py in the OpenVLA repo is missing gradient norm clipping before optimizer.step(). A single batch with an outlier action can produce an unclamped gradient large enough to permanently corrupt the model weights.
What we saw: In run 1 (no clipping), loss jumped from 2.2 β 5.2 at step ~6k and action accuracy collapsed from 0.35 β 0.0 and never recovered. Final score: 39%.
The fix:
# Add before optimizer.step() in the training loop:
torch.nn.utils.clip_grad_norm_(trainable_params, max_norm=1.0)
optimizer.step()
With clipping, both runs (2 and 3) converged cleanly. See openvla/openvla#333 for the full analysis with loss curves.
Usage
This is a merged checkpoint β LoRA adapter weights are already merged into the base model. No PEFT library needed.
import torch
from transformers import AutoModelForVision2Seq, AutoProcessor
processor = AutoProcessor.from_pretrained(
"shant0602/openvla-7b-libero-spatial-lora-20k",
trust_remote_code=True,
)
model = AutoModelForVision2Seq.from_pretrained(
"shant0602/openvla-7b-libero-spatial-lora-20k",
torch_dtype=torch.bfloat16,
trust_remote_code=True,
).cuda()
# Inference
inputs = processor(image, instruction, return_tensors="pt").to("cuda")
action = model.predict_action(**inputs, unnorm_key="libero_spatial", do_sample=False)
The unnorm_key="libero_spatial" is required β it selects the action normalization statistics from dataset_statistics.json that were computed from the LIBERO-Spatial training data.
- Downloads last month
- 4
Model tree for shant0602/openvla-7b-libero-spatial-lora-20k
Base model
openvla/openvla-7b