The SmolVLA model page on AY-Robots showing parameter count, GPU tier, inference latency per action step and the minimum episode count
SmolVLALeRobotVLA TrainingFine-TuningSO-100

How to Train SmolVLA on a 24 GB GPU (lerobot 0.6.1)

AY-Robots ResearchAugust 23, 202617 min read

SmolVLA is a 450 M parameter VLA that fine-tunes on a single 24 GB card. Real lerobot 0.6.1 commands, the actual defaults, the traps that cost a day, and what a run costs.

SmolVLA in one screen

  • 450 M parameters, about 100 M of them a flow matching action expert. lerobot trains only that expert and keeps the VLM frozen, which is why it fits on one card.
  • LeRobot's compute guide puts the smolvla group at roughly 10 to 16 GB of peak VRAM at batch 8 with AdamW. Hence 24 GB.
  • The entry point is lerobot-train. The June 2025 SmolVLA blog post still prints python lerobot/scripts/train.py, a path that no longer exists. Everything below is lerobot 0.6.1.
  • The cosine schedule is preset to decay over 30000 steps. lerobot 0.6.1 rescales that down for a shorter run and logs it, but never up: the stock 100000 step run ends on the 2.5e-6 floor for 70000 steps.
  • Thirty episodes is the AY-Robots minimum, on a 24 GB tier costing 1 to 3 USD a run against 4 to 12 for the 80 GB models.

Most people who want a vision language action model on a real arm stop at the hardware line. GR00T N1.7 and Pi0.5 are around three billion parameters each and want an A100 80 GB or an H100. If what you own is a gaming PC with an RTX 4090, that is the end of the road. SmolVLA is the exception: 450 M parameters, inside LeRobot, built to fine-tune on one consumer card and serve from a CPU.

The manual route first: install lerobot, pull the lerobot/smolvla_base checkpoint, run the real command, read the run while it happens. Then the platform route, and where it does not help.

What SmolVLA is, in numbers you can check

SmolVLA is a flow matching policy bolted onto a small vision language model. The backbone is SmolVLM2-500M-Video-Instruct; the paper keeps only the first 16 layers of its language model, limits each camera frame to 64 visual tokens with a pixel shuffle instead of image tiling, and interleaves cross attention with a self attention layer every second block. Inference cost was a design constraint, not an afterthought.

PropertyValueSource
Total parametersabout 450 Mpaper
Action expertabout 100 M, flow matchingpaper
VLM backboneHuggingFaceTB/SmolVLM2-500M-Video-Instructvlm_model_name
VLM layers usedfirst 16 of the language modelnum_vlm_layers = 16
Visual tokens per frame64, pixel shuffle, no tilingpaper
Pretraining481 community datasets, 22.9 K episodes, 10.6 M frames; 200000 steps at global batch 256 on 4 GPUspaper

The benchmarks are why people bother with a 450 M model. On LIBERO it averages 87.3 percent against 76.5 for OpenVLA at 7 B and 86.0 for a robotics-pretrained Pi0 at 3.3 B; on Meta-World, 57.3 against 47.9. On real SO-100 hardware, multi-task training gives 75 percent on pick and place, 90 on stacking, 70 on sorting, averaging 78.3, where ACT trained per task averaged 48.3. The same rows sit beside every published VLA in the SmolVLA arena entry and the ACT against SmolVLA comparison.

The honest version of the size claim

SmolVLA is not better than a 3 B model at everything. The paper's own SO-101 table is the tell: 90 percent success in distribution, 50 percent out of it, on a platform it was never pretrained on. What it does claim, and supports, is that against Pi0 it trains around 40 percent faster on 6 times less memory.

Why a 24 GB card is the right first run

LeRobot ships a compute sizing guide, the most useful page in the repo for this. It groups policies by backbone size and gives one VRAM envelope per group, measured at batch size 8 with AdamW, the lerobot default. Optimizer state alone adds 30 to 100 percent over a bare forward and backward pass, so these are not weights-only figures.

GroupPoliciesPeak VRAM (batch 8, AdamW)Starter GPUs
Light BCact, vqbet, tdmpcabout 2 to 6 GBRTX 3060, L4
Diffusiondiffusion, multi_task_ditabout 8 to 14 GBRTX 4070+, L4
Small VLAsmolvlaabout 10 to 16 GBRTX 4080+, L4, A10G
Large VLApi0, pi0_fast, pi05, xvla, wall_xabout 24 to 40 GBA100 40 GB+
Multimodalgroot, eo1about 24 to 40 GBA100 40 GB+

Ten to sixteen gigabytes at batch 8 is the argument: a 24 GB card fits that plus the dataloader. AY-Robots puts SmolVLA on the RTX 4090 or any 24 GB card, minimum 30 episodes, LeRobot v3.0 data, 245 ms per action step. Rented, that is 2 to 5 hours at 0.30 to 0.60 USD an hour, about 1 to 3 USD a fine-tuning run, against 4 to 12 USD on the 80 GB tier GR00T and Pi0.5 need (pricing). A failed SmolVLA run is a coffee; a failed GR00T run, lunch.

AY-Robots cost table: card per policy, run time, price per run, episodes needed
SmolVLA and ACT sit on the 24 GB row, the three 3 B models on the 80 GB row.
Starting with SmolVLA instead of a 3 B model
What you get
  • Fits hardware you may already own: roughly 10 to 16 GB at batch 8.
  • A wasted run costs hours and single-digit dollars, so you can afford to be wrong about the dataset.
  • Pretrained on community datasets shared under the lerobot tag, with real SO-100 and SO-101 results.
  • It lives in lerobot itself: no vendor repo, and smolvla_base is not gated.
What you give up
  • 450 M is still 450 M: out of distribution success drops from 90 to 50 percent in the paper's SO-101 table.
  • It wants LeRobot v3.0 data; a v2.1 recording has to be converted (dataset rejected v3).
  • 245 ms per action step is a competent pick and place controller, not a reactive one.
  • The docs example runs batch 64 on an A100; on 24 GB you trade batch for wall clock.

Step 0: the dataset decides the run, not the flags

Nothing below matters if the recording is bad. The LeRobot SmolVLA page is blunt: the reference dataset was 50 episodes across 5 cube positions, 10 per position, and the same task at 25 episodes performed badly. Repetition per variation generalises, raw episode count does not. Never recorded one? Start at record your first dataset with the desktop client, which writes LeRobot format out of a teleoperation session, or borrow one from the dataset directory.

  • At least 30 episodes on AY-Robots, about 50 for the LeRobot reference recipe.
  • Every variation you expect at rollout, repeated several times.
  • One task string, spelled identically at record and rollout time. The model is conditioned on that text.
  • Fixed cameras. A camera moved between recording and rollout is the most common reason a clean loss curve gives a motionless arm.
  • A held-out variation you never trained on, so you have something honest to test against.
The dataset trap that costs a day

SmolVLA, Pi0.5 and ACT want LeRobot v3.0. GR00T N1.7 and N1.5 want v2.0 or v2.1 and their loader crashes on v3.0. Record once, plan to compare models later, and you will convert one way or the other: dataset rejected v3.

bash
# v2.1 -> v3.0: aggregates per-episode files into shards and writes the episode offsets
python -m lerobot.scripts.convert_dataset_v21_to_v30 --repo-id=${HF_USER}/so100_pick_place
The converter ships inside lerobot, so there is nothing extra to install. There is no converter in the other direction in the package.

More on this in how to collect high quality VLA training data. The short version: 30 to 50 clean episodes of one task with deliberate variation beat 200 sloppy episodes of three, by a margin no hyperparameter closes.

Install lerobot 0.6.1

bash
# LeRobot needs Python 3.12 or newer as of 0.6.x
conda create -y -n lerobot python=3.12
conda activate lerobot

# TorchCodec decodes the dataset videos and wants ffmpeg
conda install ffmpeg -c conda-forge

# Base package is deliberately thin; extras pull the rest
pip install 'lerobot[smolvla,training,core_scripts]'

# Sanity check: prints the lerobot version, the GPU torch sees, and the console scripts you got
lerobot-info
The PyPI path. To patch the trainer, clone the repo and use pip install -e ".[smolvla,training]" instead.

The base lerobot install is thin and gates heavy dependencies behind extras: smolvla adds transformers, num2words and accelerate, training the dataset stack and wandb, core_scripts the hardware and visualisation deps. On Linux the install path also decides your CUDA wheel: the PyPI default is a cu130 wheel with a driver floor of 580.65, so on an older driver install torch from the cu128 index first, then lerobot.

policy.path and policy.type are not the same flag

--policy.path=lerobot/smolvla_base loads the pretrained 450 M checkpoint and fine-tunes it. --policy.type=smolvla builds a fresh SmolVLA, and the config default load_vlm_weights = False means it does not even pull the SmolVLM2 backbone weights unless you ask. Get it wrong and the run trains happily, costs the same, and learns nothing transferable.

The training run, command by command

  1. 1
    Authenticate against the Hub

    The base checkpoint comes from the Hub, and your dataset probably does too.

    bash
    hf auth login
  2. 2
    Read the options once

    Every field of the pipeline and policy config is a flag. Skim it before digging in the source.

    bash
    lerobot-train --help
  3. 3
    Start the fine-tune

    The docs example runs batch 64 on a single A100; the compute guide's own A100 40 GB anchor is batch 16, and 8 is the 24 GB equivalent. No scheduler flag here on purpose, see below.

    bash
    lerobot-train \
      --policy.path=lerobot/smolvla_base \
      --dataset.repo_id=${HF_USER}/so100_pick_place \
      --batch_size=8 \
      --steps=20000 \
      --save_freq=2000 \
      --log_freq=200 \
      --seed=1000 \
      --output_dir=outputs/train/smolvla_pick_place \
      --job_name=smolvla_pick_place \
      --policy.device=cuda \
      --wandb.enable=true
  4. 4
    Read the log line, not just the loss

    Every --log_freq steps lerobot prints loss, grdn, lr, updt_s, data_s, smp/s and, on CUDA, mem_gb. mem_gb says whether the batch fits, lr whether the schedule is decaying, and data_s approaching updt_s means the dataloader is the bottleneck, not the GPU.

    bash
    # if data_s creeps toward updt_s
    lerobot-train ... --num_workers=8
  5. 5
    Collect checkpoints you can compare

    save_freq defaults to 20000, so a 20000 step run leaves one checkpoint and nothing to compare it against. Set 2000. Pushing to the Hub needs --policy.repo_id.

    bash
    --save_freq=2000 \
    --policy.repo_id=${HF_USER}/smolvla_pick_place \
    --save_checkpoint_to_hub=true
  6. 6
    Resume if the machine dies

    Point --config_path at the train_config.json next to the checkpoint. lerobot refuses to start into an existing output_dir unless you are resuming, so you cannot overwrite a run by accident.

    bash
    lerobot-train \
      --config_path=<path to the saved train_config.json> \
      --resume=true

The flags that actually change the outcome

FlagWhat it doesOn 24 GB
--batch_sizeSamples per step, roughly linear in VRAM4 to 8
--stepsTotal optimizer steps20000 first pass
--policy.scheduler_decay_stepsCosine decay length, preset 30000Only bites above 30000
--policy.use_ampMixed precision; SmolVLA has no dtype fieldtrue when memory is tight
--num_workersDataloader processes, default 4Raise until data_s stops climbing
--dataset.eval_splitFraction of episodes held out per task0.1, with --eval_steps
--policy.freeze_vision_encoderKeeps the vision tower frozentrue on 24 GB
--policy.train_expert_onlyOnly the ~100 M expert gets gradientstrue first
The schedule: what 0.6.1 handles, and what it does not

SmolVLA presets a cosine schedule: scheduler_warmup_steps = 1000, scheduler_decay_steps = 30000, scheduler_decay_lr = 2.5e-6. Older advice says a 20000 step run therefore stalls mid-decay. In 0.6.1 it does not: CosineDecayWithWarmupSchedulerConfig.build() is handed --steps, and below num_decay_steps it rescales both, warmup 1000 to 666 and decay 30000 to 20000, printing Auto-scaling LR scheduler as it does. It never rescales upward: the decay is clamped with min(current_step, decay_steps), so the stock --steps=100000 sits on the floor from step 30000 to the end, 70 percent of the run. Only that long side still needs --policy.scheduler_decay_steps. The lr column is where you check.

The defaults you inherit if you touch nothing

A policy config in lerobot carries its own optimizer and scheduler preset, and unless you set use_policy_training_preset=false those presets win. Half the questions people ask about SmolVLA training are answered by a default they did not know existed.

SettingDefault in lerobot 0.6.1Defined in
chunk_size / n_action_steps50 / 50SmolVLAConfig
num_steps (flow matching denoise)10SmolVLAConfig
optimizer_lr1e-4SmolVLAConfig
scheduler_warmup_steps1000SmolVLAConfig
scheduler_decay_steps30000SmolVLAConfig
scheduler_decay_lr2.5e-6SmolVLAConfig
freeze_vision_encodertrueSmolVLAConfig
train_expert_onlytrueSmolVLAConfig
batch_size / steps8 / 100000TrainPipelineConfig
seed / save_freq / num_workers1000 / 20000 / 4TrainPipelineConfig

The two lines that surprise people are freeze_vision_encoder and train_expert_only, both true. Out of the box you train roughly 100 M parameters, not 450 M, which is why it fits on 24 GB. LeRobot's own reference run on a four-GPU H100 cluster flips both to false; on one 24 GB card that turns a working run into an out of memory crash.

The memory knob that is not there

The guide's advice when you are memory-bound is to drop the batch size and use gradient accumulation to recover the effective batch. There is no gradient accumulation in lerobot 0.6.1: TrainPipelineConfig has no such field and the string appears nowhere in the released package. The AY-Robots form shows a gradient accumulation value of 8 for SmolVLA and does not apply it either. Your levers on 24 GB are --batch_size, the two freeze defaults, and --policy.use_amp.

How long the run takes, and how many steps is enough

LeRobot publishes wall-clock anchors for five epochs over a roughly 50 episode dataset, about 45000 frames at 30 fps. Order of magnitude figures, the docs say, but they are the difference between expecting an hour and a day.

SetupPolicyBatchWall clock
Single L4 / A10G (24 GB)smolvla4about 3 to 6 h
Single A100 40 GBsmolvla16about 1 to 2 h
4 x H100 80 GB with acceleratesmolvla32about 1 to 2 h
Single RTX 4090 / RTX 3090 (24 GB)act8about 30 to 60 min
Do the epoch arithmetic before you pick --steps

The rule is 5 to 10 epochs over the dataset, not a fixed step count: steps_per_epoch = ceil(total_frames / (num_gpus x batch_size)). On the reference dataset the docs point at, lerobot/svla_so100_pickplace, the metadata reports 50 episodes and 19631 frames: batch 8 gives about 2454 steps per epoch, so 20000 steps is roughly 8 epochs. Halve the batch and the same budget buys half the epochs, so redo this whenever you touch --batch_size.

Running the fine-tuned policy back on the arm

bash
lerobot-rollout \
  --strategy.type=base \
  --robot.type=so100_follower \
  --robot.port=/dev/ttyACM0 \
  --robot.id=my_follower_arm \
  --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
  --task="Grasp the cube and put it in the box." \
  --policy.path=${HF_USER}/smolvla_pick_place
The task string has to match the one you recorded with. The model is conditioned on that text, so a paraphrase is a different instruction.

The 245 ms per action step is easy to misread: the policy emits chunk_size = 50 actions per forward pass and executes n_action_steps = 50 of them, so how often you pay that cost is set by those knobs, not by how often the servos get a command. That is what action chunking buys, and why a 245 ms model can drive a 30 Hz arm. What is left is inference latency at the end of the chunk.

Measurement (SmolVLA, real SO-100)SynchronousAsynchronous
Completion time, pick and place, 10 trials13.75 s9.70 s
Pick and place cycles in a fixed time window919
Success rate averaged over the three tasks78.3 %73.3 %

That third row is what most write-ups skip. Async inference is about 30 percent faster and roughly doubles throughput in a fixed window, and the paper calls the success rates comparable, which on the average they are. Underneath it, sorting fell from 70 to 50 percent while pick and place gained 5. lerobot 0.6.1 carries the other lever in the same binary: --inference.type=rtc switches the rollout to real time chunking, which the script's own usage block recommends for the slow VLAs, Pi0, Pi0.5 and SmolVLA.

Inference has to sit next to the servos

The control loop is 20 to 485 ms per action step depending on the model, and public-internet round trips on top turn a working policy into a hesitant one. Remote inference is viable for slow pick and place, not fast reactive motion: if the task needs quick corrections, the GPU belongs on the same LAN as the arm.

7.4 V, not 12 V

Off topic for a training run, but it ends more SO-100 projects than any hyperparameter. The Feetech STS3215 servos in the SO-100 and SO-101 run at 7.4 V; 12 V destroys them. The LeKiwi mixes a 7.4 V arm with a 12 V base, which is how the wrong barrel jack finds the wrong socket.

Two routes to the same checkpoint

You own the machine, the environment and the debugging. The only cloud dependency is the Hub download of the base checkpoint. The right route if you want to modify the policy, if the data cannot leave your network, or if the card is idle.

  • You control the CUDA wheel, the driver, the ffmpeg build and the dataloader.
  • You can patch configuration_smolvla.py and retrain the same afternoon.
  • You pay in electricity and time, not per run, and debug TorchCodec yourself.
bash
pip install 'lerobot[smolvla,training,core_scripts]'
hf auth login

lerobot-train \
  --policy.path=lerobot/smolvla_base \
  --dataset.repo_id=${HF_USER}/so100_pick_place \
  --batch_size=8 --steps=20000 \
  --save_freq=2000 --seed=1000 \
  --output_dir=outputs/train/smolvla_pick_place \
  --job_name=smolvla_pick_place \
  --policy.device=cuda --wandb.enable=true
The whole manual route in one block, lerobot 0.6.1.

When SmolVLA is the wrong choice

The test for whether SmolVLA was the right first run is not whether it worked, but whether the failure told you something. Reach 60 or 70 percent and a bigger model is a reasonable next spend: the data carries signal. Reach 10 percent and a 3 B model most likely also reaches 10 percent, which you just learned for three dollars instead of twelve.

The AY-Robots training matrix: five policies as rows, four robot arms as columns
Every cell is its own guide. SmolVLA has one for each of the four arms.

Worth reading before spending more: Pi0.5 against SmolVLA for more capacity on the same idea, and GR00T N1.7 against SmolVLA for the NVIDIA route, both 80 GB tier at 4 to 12 USD a run. The other way, ACT is the cheaper baseline: 80 M parameters, 20 ms per action step, no language conditioning. All five sit on the policies page; the arena has 85 models and 332 benchmark results.

The AY-Robots policies comparison: parameters, GPU tier, latency, minimum episodes
The four numbers that decide a run.

The checklist before you scale anything

  1. Did the lr reach its 2.5e-6 floor? Below 30000 steps lerobot rescales the decay and says so at startup; above it, set --policy.scheduler_decay_steps yourself.
  2. More than one checkpoint, and episodes held out with --dataset.eval_split so the eval loss means something.
  3. Does the policy move at all? A falling loss with a motionless arm has specific causes: loss falls, policy does nothing.
  4. Does it survive a change of scene? If not: policy only works in one setup.
  5. Did you write down the seed? lerobot defaults to 1000, so two untouched runs stay comparable.
  6. Only then: more episodes, more variation, or a bigger model. In that order.

For why these models exist and what they do with the language input, vision language action models is the background; the SO-100 complete guide runs assembly through calibration to the first imitation learning run. For the finished checkpoint, run your first policy; if the arm never shows up, the failure mode index.

Train SmolVLA on your own arm

Pick the model and the arm and the guide gives you the exact defaults, the dataset format and what the run costs. SmolVLA sits on the 24 GB tier at 1 to 3 USD per run.

Open the training guides
Can I really fine-tune SmolVLA on an RTX 4090?

Yes. LeRobot's compute guide puts SmolVLA at roughly 10 to 16 GB of peak VRAM at batch 8 with AdamW and lists 24 GB consumer cards as comfortable for it. The batch 64 in the docs example is paired with a single A100. Memory scales roughly linearly with batch, so use 4 or 8 and watch mem_gb.

How many episodes do I actually need?

AY-Robots sets the minimum at 30. The LeRobot docs recommend about 50 and report that 25 episodes of the same task performed badly. Structure beats count: the reference set was 5 cube positions with 10 episodes each, and that repetition is what generalises.

The docs say batch 64, the platform sends batch 2. Which is right?

Both, for different hardware. The docs example uses batch 64 and quotes about 4 hours for 20000 steps on a single A100; the compute guide's A100 40 GB anchor is batch 16. Batch 2 is what AY-Robots sends on the 24 GB tier. Locally 4 to 8 is the middle, and the epoch arithmetic changes with it.

SmolVLA or ACT for a first run on an SO-100?

ACT if the task is one repetitive motion and you want the fastest loop: 20 ms per action step, 80 M parameters, no language conditioning. SmolVLA if you want language conditioning, several task strings in one checkpoint, and a pretrained base. Both sit on the 24 GB tier, so the choice is the task, not the budget.

Do I have to set --policy.scheduler_decay_steps?

Only when --steps is above 30000. SmolVLA presets the cosine decay at 30000 steps, and lerobot 0.6.1 rescales it down by itself for a shorter run, logging "Auto-scaling LR scheduler" when it does. It never scales up, so the stock --steps=100000 leaves the last 70000 steps on the 2.5e-6 floor.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started