The AY-Robots training matrix at /train, with the five trainable policies as rows and the four supported robot arms as columns, every cell linking to a specific training guide
LoRAFine-TuningVLA ModelsGR00T N1.7SmolVLAGPU Memory

LoRA vs Full Fine-Tuning for VLA Models: What Actually Trains

AY-Robots ResearchAugust 23, 202617 min read

LoRA against full fine-tuning for VLA policies: what really trains in each case, how much VRAM each needs, the measured OpenVLA gap, and whether it matters on a single-task SO-100 arm.

What you need to know

  • LoRA freezes the pretrained weights and trains two small matrices per adapted layer. OpenVLA at rank 32: 97.6 M trainable parameters instead of 7,188.1 M, 59.7 GB at batch 16 instead of 163.3 GB, and 68.2 +/- 7.5 percent against 69.7 +/- 7.2 for full fine-tuning. The gap is smaller than either error bar.
  • For a 3 B VLA, "full fine-tuning" is rarely what anyone runs. Isaac-GR00T ships tune_llm=False and tune_visual=False, so only the projector and diffusion head train.
  • Isaac-GR00T removed LoRA outright: gr00t/utils/peft.py exists at n1.5-release and 404s at n1.6, n1.7 and main, so --lora_rank no longer parses.
  • lerobot still supports it through --peft.method_type=LORA, but of the five policies this platform trains only SmolVLA and Pi0.5 ship default adapter targets, and ACT cannot use it at all.
  • On a single-task SO-100 this choice rarely decides whether the policy works. Camera placement, dataset quality and latency do.

The question, stated precisely

When people ask whether to use LoRA or full fine-tuning on a vision-language-action model, they are asking three things at once. How much GPU memory do I need. How much accuracy do I give up. And can I do it at all with the trainer I have. Only the third has a hard answer, and for one of the two big VLA trainers it changed in the last two releases.

All of it was checked on 23 August 2026 against the original LoRA paper, OpenVLA's fine-tuning table, and the Isaac-GR00T and lerobot source trees at the tags named. Where upstream changed, the tag is part of the claim.

python
# W0 is frozen. Only A and B carry gradients.
#
#   h = W0 @ x  +  (lora_alpha / r) * (B @ A @ x)
#
#   W0 : (d_out, d_in)   frozen, bfloat16
#   A  : (r,     d_in)   trainable, random Gaussian init
#   B  : (d_out, r)      trainable, initialised to zero
#
# Trainable params per adapted layer: r * (d_in + d_out)
#
# For d_in = d_out = 2048 and r = 32:
#   LoRA :  32 * (2048 + 2048) =   131,072
#   full :       2048 * 2048   = 4,194,304   -> 32x more
LoRA as huggingface/peft implements it. B starts at zero, so the adapted model begins bit-identical to the base model.
The alpha over r scaling is not cosmetic

LoRA scales the update by alpha / r so you need not retune the learning rate when you change the rank. The trap is the defaults. lerobot's PeftConfig sets r=16 and lora_alpha=None, and its own comment spells out the consequence: a None alpha falls through to PEFT's default of 8, which "may dampen high-rank adapters". That is a scaling of 0.5 at rank 16, 0.125 at rank 64. Set it explicitly, to r or 2r.

Full fine-tuning of a 3 B VLA is not what most people run

The phrase gets used loosely, and the vendor defaults sit in the middle. At n1.7-release the GR00T fine-tune entry point is gr00t/experiment/launch_finetune.py, a thin tyro wrapper around the FinetuneConfig dataclass in gr00t/configs/finetune_config.py. This is what it sets when you pass no tuning flags at all.

FieldDefaultWhat it does
tune_llmFalseLanguage backbone stays frozen
tune_visualFalseVision encoder stays frozen
tune_projectorTrueThe multimodal projector trains
tune_diffusion_modelTrueThe diffusion action decoder trains
learning_rate1e-4AdamW, warmup_ratio 0.05, weight_decay 1e-5
global_batch_size64Summed across GPUs, pre-accumulation. NVIDIA's examples/finetune.sh passes 32
max_steps10000Total optimizer steps
save_steps / save_total_limit1000 / 5Older checkpoints are deleted
state_dropout_prob0.2CLI default. The N1.7 model config sets 0.8
seedno such fieldThe only seed under gr00t/configs is the dataloader seed 42 in data_config.py

The shipped recipe is already a partial fine-tune. NVIDIA's hardware recommendation guide puts a number on it: the default "tunes the projector + diffusion action head (not the full LLM backbone), keeping peak VRAM under ~35 GB per GPU", while tune_llm or tune_visual means "80 GB+ per GPU recommended". The same page sets the floor at 40 GB. That is the memory story for GR00T N1.7. You are choosing between roughly 35 and 80 GB, and LoRA is not the only lever in between.

The default is not a middle ground you can ignore

Because tune_projector and tune_diffusion_model are already True, adding LoRA on top of the GR00T default would only shrink what is already frozen. The real comparison is the default partial tune against unfreezing the backbone, and turning tune_llm or tune_visual on with a 40 GB card gets you an out-of-memory kill, not a slower run. See out of memory during training.

The one clean measurement: OpenVLA's fine-tuning table

OpenVLA is still the best apples-to-apples comparison published for a VLA: five strategies on one 7 B model, the same Franka-Tabletop tasks, the same batch size, LoRA on all linear layers. Table 1 in section 5.3 of OpenVLA: An Open-Source Vision-Language-Action Model reports the following, success averaged over 33 rollouts per approach, VRAM measured at batch 16.

StrategySuccess rateTrainable paramsVRAM at batch 16
Full fine-tuning69.7 +/- 7.2 %7,188.1 M163.3 GB (2 GPUs, FSDP)
Last layer only30.3 +/- 6.1 %465.1 M51.4 GB
Frozen vision47.0 +/- 6.9 %6,760.4 M156.2 GB (2 GPUs, FSDP)
Sandwich fine-tuning62.1 +/- 7.9 %914.2 M64.0 GB
LoRA, rank 3268.2 +/- 7.5 %97.6 M59.7 GB
LoRA, rank 6468.2 +/- 7.8 %195.2 M60.5 GB

Read the error bars before the means. Full fine-tuning scored 1.5 points higher than rank 32, and both intervals are around 7 points wide. The paper concludes that LoRA "achieves the best trade-off between performance and training memory consumption" while training 1.4 percent of the parameters, a new task taking 10 to 15 hours on one A100, an 8x compute reduction.

Two other rows matter more than the headline. Last-layer-only collapsed to 30.3 percent, so a VLA has to change deep representations to adapt. Frozen-vision cost more VRAM than LoRA and scored twenty points lower: freezing the wrong half is worse than freezing most of everything. Rank 64 bought nothing over rank 32, and the paper recommends r = 32.

The AY-Robots policy comparison table: parameter count, required GPU, inference latency and minimum episode count for GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT
Parameter count decides whether the question is worth asking at all.

Where the VRAM actually goes

LoRA's saving does not come from the weights, it comes from the optimizer. Under AdamW every trainable parameter carries two fp32 moment estimates plus an fp32 gradient on top of itself. Freeze it and all three disappear. The frozen weight still sits there in bfloat16, but that is the small part.

  • Frozen parameter: weight only, bfloat16.
  • Trainable parameter: weight, gradient, two AdamW moments.
  • Activations scale with batch size and sequence length, and LoRA does not remove them. That is why rank 32 on OpenVLA still needed 59.7 GB at batch 16.
The checkpoint size is the underrated win

The 10,000x everyone quotes is the checkpoint, not the run: on GPT-3 175B, training VRAM fell only from 1.2 TB to 350 GB, while the saved state at r=4 fell from 350 GB to 35 MB. Keep the two apart, because vendors quote whichever flatters them. Train one policy per task and you keep one base checkpoint plus a folder of adapters.

LoRA on the stacks you would actually use, August 2026

lerobot: supported, and the defaults are per policy

lerobot documents its PEFT integration in docs/source/peft_training.mdx. The config object is PeftConfig in src/lerobot/configs/default.py, exposed on lerobot-train under the peft prefix. On main (0.6.2 in pyproject, latest release v0.6.1 of 3 August 2026) its fields are target_modules, full_training_modules, method_type, init_type, r and lora_alpha.

  1. 1
    Install the PEFT extra

    The feature sits behind an optional dependency; without it the run fails at config time.

    bash
    pip install "lerobot[peft]"
    
    # lerobot pins peft>=0.18.0,<1.0.0 in pyproject.toml
    python -c "import peft; print(peft.__version__)"
  2. 2
    Point at a pretrained base, never at scratch

    LoRA adapts existing weights, and the base policy class enforces it.

    bash
    # correct
    --policy.path=lerobot/smolvla_base
    
    # wrong: raises ValueError before training starts
    --policy.type=smolvla
  3. 3
    Turn the adapter on and pick a rank

    lerobot's documented command, retargeted at an SO-100 dataset. Rank 64 with alpha 64 gives a scaling of 1.0.

    bash
    lerobot-train \
      --policy.path=lerobot/smolvla_base \
      --dataset.repo_id=<your_hf_user>/<your_so100_dataset> \
      --policy.device=cuda \
      --policy.optimizer_lr=1e-3 \
      --policy.scheduler_decay_lr=1e-4 \
      --steps=100000 \
      --batch_size=32 \
      --peft.method_type=LORA \
      --peft.r=64 \
      --peft.lora_alpha=64
  4. 4
    Raise the learning rate by about 10x

    The docs say it "can usually be scaled by a factor of 10 compared to the learning rate used for full fine-tuning (e.g., 1e-4 normal, so 1e-3 using LoRA)". Leaving it at the full value is the commonest reason a LoRA run looks like it is doing nothing.

    bash
    # full fine-tune
    --policy.optimizer_lr=1e-4
    
    # LoRA equivalent
    --policy.optimizer_lr=1e-3 --policy.scheduler_decay_lr=1e-4
  5. 5
    Optionally retarget the adapted modules

    SmolVLA adapts the LM expert's q and v projections plus the state and action projections. This moves the adapter to the MLPs instead.

    bash
    --peft.target_modules='(model\.vlm_with_expert\.lm_expert\..*\.(down|gate|up)_proj|.*\.(state_proj|action_in_proj|action_out_proj|action_time_mlp_in|action_time_mlp_out))'

Read the per-policy defaults in the source. Four policies define adapter targets on main: SmolVLA, Pi0, Pi0.5 and MolmoAct2. SmolVLA adapts the LM expert's q and v projections plus the state and action projections; Pi0 and Pi0.5 use the same set on the gemma_expert self-attention. SmolVLA and Pi0 leave modules_to_save empty. Pi0.5 does not: with proprioceptive memory on it adds model.proprio_history_proj, a layer absent from lerobot/pi05_base that an adapter cannot start from.

That is the general pattern rather than a Pi0.5 quirk. Fine-Tuning Vision-Language-Action Models: Optimizing Speed and Success builds OpenVLA-OFT the same way: a rank-32 adapter of 111 M parameters on the pretrained backbone, plus a fully trained 151 M action head and a 17 M proprioception projector, 279 M trainable in all. New heads train in full, only pretrained weights get an adapter. lerobot exposes that split as --peft.full_training_modules.

Version trap: lora_alpha did not always exist

At the v0.5.1 tag (7 April 2026) lerobot's PeftConfig has target_modules, full_training_modules, method_type, init_type and r, and no lora_alpha field, so --peft.lora_alpha=64 fails to parse there. That tag also has no gradient accumulation option, which is why the AY-Robots trainer sends a grad-accum value for Pi0.5 that lerobot cannot apply. Run pip show lerobot before copying any command, including these.

Isaac-GR00T: LoRA was there, and then it was removed

This is the part that costs an afternoon. GR00T N1.5 shipped a LoRA path: scripts/gr00t_finetune.py at the n1.5-release tag has lora_rank (default 0, off), lora_alpha (16), lora_dropout (0.1) and lora_full_model (False, so only the action head is adapted). The helper in gr00t/utils/peft.py picked targets by walking the model for Linear layers named q_proj, v_proj, k_proj, to_q, to_v or to_k. The README answered it as an FAQ, recommending "the full model finetuning for better performance".

That file is gone. The listings for the later tags contain zero paths matching lora or peft (363 files at n1.6-release, 423 at n1.7-release), and gr00t/utils/peft.py returns 404 at n1.6-release, n1.7-release and main. The config dataclass behind the new entry point has no LoRA field, so an N1.5-era command makes tyro reject the argument.

bash
# Isaac-GR00T at tag n1.5-release: LoRA available
python scripts/gr00t_finetune.py \
  --dataset-path ./demo_data/robot_sim.PickNPlace \
  --num-gpus 1 \
  --lora_rank 64 --lora_alpha 128

# Isaac-GR00T at tag n1.7-release: no such flags exist
CUDA_VISIBLE_DEVICES=0 python gr00t/experiment/launch_finetune.py \
  --base_model_path nvidia/GR00T-N1.7-3B \
  --dataset_path demo_data/cube_to_bowl_5 \
  --embodiment_tag NEW_EMBODIMENT \
  --modality_config_path examples/SO100/so100_config.py \
  --num_gpus 1 --output_dir /tmp/test_finetune \
  --max_steps 2000 --global_batch_size 32 \
  --learning_rate 1e-4 --save_steps 1000 --save_total_limit 5

# The memory lever on N1.7 is which components you tune:
#   default                  -> projector + diffusion head, ~35 GB peak
#   tune_llm or tune_visual  -> 80 GB+ per GPU recommended
What changed between the N1.5 and N1.7 fine-tuning entry points. The N1.7 invocation is the one NVIDIA's own examples/finetune.sh builds.
GR00T runs are not reproducible, with or without LoRA

FinetuneConfig has no seed field, so the CLI gives you no way to pin one. The only seed under gr00t/configs is a dataloader seed of 42 in data/data_config.py, which does not cover augmentation, and the N1.7 README is explicit: "Users may observe 5-6% variance between runs due to non-deterministic image augmentations." See a couple of percent between two GR00T runs and you have measured noise. lerobot defaults seed to 1000.

The lever that replaced it

Removing the adapter did not remove the choice, it moved into the unfreeze flags. lerobot's own GR00T policy, which loads nvidia/GR00T-N1.7-3B on its nvidia/Cosmos-Reason2-2B backbone, adds two finer flags to NVIDIA's four: tune_vlln and tune_top_llm_layers. The first, on by default, trains the vision-language LayerNorm and VL self-attention projector in the action head. The second defaults to 0 and unfreezes only the top n language-backbone layers. OpenVLA measured that shape as sandwich fine-tuning: 62.1 percent against 68.2 for rank 32, on a different model, so take it as a hint.

SettingWhat trains on top of the frozen restWhere it comes from
DefaultProjector and diffusion action headtune_projector, tune_diffusion_model: True in both configs
tune_vllnAdds the VL LayerNorm and self-attention projectorlerobot GrootConfig only, True by default
tune_top_llm_layers=nAdds the top n language-backbone layerslerobot GrootConfig only, 0 by default
tune_llm or tune_visualWhole language backbone or vision towerBoth configs, False by default. 80 GB+ per GPU
LoRA adapterTwo low-rank matrices per targeted Linear layerGone from Isaac-GR00T after n1.5-release

ACT: the question does not apply

ACT trains from scratch. There is no base model to adapt, and lerobot enforces that: the base policy class raises before the first training step if no pretrained path is given, and ACT defines no PEFT targets at all. If you are running ACT on an SO-100, the question is closed.

python
if not self.config.pretrained_path:
    raise ValueError(
        "Training from scratch using PEFT is unlikely to yield good results. "
        "Supply a `policy.pretrained_path` to fine-tune an existing model."
    )
src/lerobot/policies/pretrained.py. This fires for any policy without pretrained weights, ACT included.
The numbered step list on the AY-Robots GR00T N1.7 on SO-100 guide, from dataset selection through GPU provisioning to checkpoint
The run is a fixed sequence. The tuning decisions happen before you press start.
LoRA on a small-fleet VLA project
Advantages
  • Memory headroom. OpenVLA fine-tuned in 59.7 GB at rank 32 instead of 163.3 GB, and the repo says a smaller card works "as long as it has at least ~27 GB".
  • Cheap runs: 10 to 15 hours on one A100 per task, an 8x compute reduction, and one base plus many small adapters instead of full checkpoints.
  • No inference penalty once merged: the update folds back into W.
  • Better retention. LoRA Learns Less and Forgets Less found it holds performance outside the target domain better than full fine-tuning, and mitigates forgetting more than weight decay or dropout.
Trade-offs
  • It does not remove activation memory: you still forward through the frozen backbone.
  • Low-rank updates underfit large shifts: the same study found full fine-tuning learns perturbations of 10 to 100x higher rank, and LoRA "substantially underperforms" on code and math.
  • More hyperparameters to get wrong: rank, alpha, which modules, a 10x learning rate. And support moves: Isaac-GR00T dropped it after N1.5, lerobot added lora_alpha after v0.5.1.
  • Not standalone: lerobot stores the base path as PEFT's base_model_name_or_path, so the base must be present at serving time.

You own the GPU, the install and the version drift. For SmolVLA at about 450 M parameters, a 24 GB card already fits the run without an adapter, so LoRA buys batch size rather than feasibility. NVIDIA puts the GR00T N1.7 floor at 40 GB instead.

bash
# 1. what card is this, and does peft install
nvidia-smi --query-gpu=name,memory.total --format=csv
pip install "lerobot[peft]"
pip show lerobot peft | grep -i -e name -e version

# 2. dataset version: lerobot wants v3.0, GR00T wants v2.0/v2.1
head -20 <dataset_root>/meta/info.json

# 3. run it: rank and alpha explicit, 10x lr, seed pinned
lerobot-train \
  --policy.path=lerobot/smolvla_base \
  --dataset.repo_id=<your_hf_user>/<your_so100_dataset> \
  --policy.device=cuda \
  --policy.optimizer_lr=1e-3 \
  --steps=20000 --batch_size=32 \
  --peft.method_type=LORA \
  --peft.r=64 --peft.lora_alpha=64 \
  --seed=1000

# 4. the adapter references the base by path: keep them together
ls outputs/train/*/checkpoints/last/pretrained_model/
The whole self-hosted path for a SmolVLA LoRA run on an SO-100 dataset.

Budget the version archaeology as part of the job. The SO-100 setup and training guide covers the hardware side, and the VLA overview covers the model family this applies to.

Does the accuracy gap matter on a single-task arm?

Mostly, no. Most SO-100 owners work in one regime: one task, one camera setup, one arm, 50 to 200 recorded episodes. That is narrow, in-distribution adaptation, where LoRA and full fine-tuning converge. The OpenVLA-OFT authors read the earlier result the same way: LoRA "enables effective adaptation to single-arm robots operating at low control frequencies", with bimanual arms at 25 to 50 Hz as the case it did not cover.

The gap opens up in the opposite regime: a lot of data, or a target far from what the base model saw. LoRA Learns Less and Forgets Less tested that on language models with roughly 100 K prompt-response pairs and 20 B continued-pretraining tokens, and found LoRA substantially behind in standard low-rank settings, with full fine-tuning learning perturbations of 10 to 100x higher rank. The OpenVLA repository says the same operationally: fully fine-tune only "if the fine-tuning distribution varies drastically from the pretraining distribution".

Your situationWhat to runWhy
One task, one SO-100, 50 to 200 episodes, known embodimentVendor default, or LoRA if memory boundIn-distribution adaptation. The gap is inside the noise.
Only a 24 GB cardLoRA, or SmolVLA or ACT insteadNVIDIA sets the GR00T floor at 40 GB
A new embodiment the base model has never seenWide partial or full fine-tuneLow-rank updates underfit large shifts
Reproducing a GR00T N1.7 resultDefault recipe, then the unfreeze ladderLoRA is gone after n1.5-release
The AY-Robots cost table: which GPU card each policy needs, typical run duration and price, and episodes needed before a policy is useful
The card tier, not the adapter, moves a run between the 1 to 3 and 4 to 12 USD bands.

What I would actually do

If the goal is a working policy on one arm, spend the effort elsewhere. What kills SO-100 policies is not adapter rank. It is a wrist camera that moved between recording and inference, an action chunking horizon that does not match the control loop, duplicated frames, and inference latency added by a network hop. The failure-mode pages and the guide on collecting high-quality VLA training data pay back better than a rank sweep.

  1. Start with the vendor default recipe. On GR00T that already is a partial fine-tune.
  2. Reach for LoRA when a number forces you to: a card that cannot hold the run, or a disk that cannot hold the checkpoints. Then set lora_alpha explicitly and raise the learning rate by about 10x, the two things that explain most reports of a LoRA run that trains but does not learn.
  3. Do not treat a two-point difference on GR00T as signal: no seed, 5 to 6 percent documented variance. And check the trainer version against the flags you are about to use, every time.
Before any of this, check the servo voltage

Unrelated to training and far more expensive. The SO-100 and SO-101 use Feetech STS3215 bus servos on a 7.4 V rail, and 12 V destroys them. A Koch v1.1 uses Dynamixel servos on 5 V and 12 V rails; a LeKiwi runs a 7.4 V arm on a 12 V base. No fine-tuning strategy recovers a burnt servo.

Train a policy on your own SO-100 without building the stack

Pick a model and a dataset. The backend rents the right GPU, runs the vendor recipe and writes the checkpoints. A GR00T N1.7 run costs about 4 to 12 USD.

Open the training guides
Does AY-Robots let me enable LoRA on a training run?

No. The form sends the vendor recipe with the hyperparameters listed on each guide page: batch size, learning rate, max steps, gradient accumulation, plus per-policy extras such as saveSteps for GR00T or chunkSize for ACT. There is no adapter toggle.

Can I still use LoRA with GR00T N1.7?

Not through the official NVIDIA repository. gr00t/utils/peft.py exists at n1.5-release and 404s at n1.6-release, n1.7-release and main, and the current FinetuneConfig has no lora_rank, lora_alpha or lora_dropout field, so the old flags no longer parse. The lever on N1.7 is which components you tune: the default trains the projector and diffusion head at roughly 35 GB peak per GPU, while tune_llm or tune_visual pushes it to 80 GB or more.

What rank should I use for a SmolVLA or Pi0.5 fine-tune?

lerobot's PeftConfig defaults to r=16 and its documented example uses r=64 with lora_alpha=64. OpenVLA found rank 32 and 64 scored identically at 68.2 percent and recommends r=32. Start at 32 or 64 with alpha equal to rank, and treat rank as the last knob you tune.

Why does my LoRA run train but the loss barely moves?

Two usual causes. Learning rate: scale it by roughly 10x versus full fine-tuning, so 1e-3 where you would use 1e-4. And alpha: lerobot defaults lora_alpha to None, which falls through to PEFT's default of 8. On a rank-64 adapter that is a scaling of 8/64, one eighth of the intended update.

Does LoRA slow down inference on the robot?

No, once merged into the base weights: the LoRA paper's design point was that the update folds into W, unlike bottleneck adapters that add layers at serving time. Latency comes from the policy and the network instead. Here GR00T N1.7 takes 152 ms per action step, GR00T N1.5 165 ms, Pi0.5 485 ms, SmolVLA 245 ms, ACT 20 ms, and public-internet round trips turn a working policy into a hesitant one.

Is LoRA the same thing as QLoRA?

No. QLoRA adds quantisation of the frozen base to 4-bit NormalFloat, plus double quantisation and paged optimizers, which is what lets a 65 B model be fine-tuned on one 48 GB GPU. Quantisation is a separate axis: OpenVLA reported 71.9 +/- 4.7 percent at int4 inference against 71.3 +/- 4.8 at bfloat16, on 7.0 GB instead of 16.8 GB.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started