
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.
| Property | Value | Source |
|---|---|---|
| Total parameters | about 450 M | paper |
| Action expert | about 100 M, flow matching | paper |
| VLM backbone | HuggingFaceTB/SmolVLM2-500M-Video-Instruct | vlm_model_name |
| VLM layers used | first 16 of the language model | num_vlm_layers = 16 |
| Visual tokens per frame | 64, pixel shuffle, no tiling | paper |
| Pretraining | 481 community datasets, 22.9 K episodes, 10.6 M frames; 200000 steps at global batch 256 on 4 GPUs | paper |
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.
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.
| Group | Policies | Peak VRAM (batch 8, AdamW) | Starter GPUs |
|---|---|---|---|
| Light BC | act, vqbet, tdmpc | about 2 to 6 GB | RTX 3060, L4 |
| Diffusion | diffusion, multi_task_dit | about 8 to 14 GB | RTX 4070+, L4 |
| Small VLA | smolvla | about 10 to 16 GB | RTX 4080+, L4, A10G |
| Large VLA | pi0, pi0_fast, pi05, xvla, wall_x | about 24 to 40 GB | A100 40 GB+ |
| Multimodal | groot, eo1 | about 24 to 40 GB | A100 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.

- 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.
- 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.
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.
# 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_placeMore 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
# 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-infoThe 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=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
- 1Authenticate against the Hub
The base checkpoint comes from the Hub, and your dataset probably does too.
bashhf auth login - 2Read the options once
Every field of the pipeline and policy config is a flag. Skim it before digging in the source.
bashlerobot-train --help - 3Start 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.
bashlerobot-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 - 4Read 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 - 5Collect 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 - 6Resume 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.
bashlerobot-train \ --config_path=<path to the saved train_config.json> \ --resume=true
The flags that actually change the outcome
| Flag | What it does | On 24 GB |
|---|---|---|
| --batch_size | Samples per step, roughly linear in VRAM | 4 to 8 |
| --steps | Total optimizer steps | 20000 first pass |
| --policy.scheduler_decay_steps | Cosine decay length, preset 30000 | Only bites above 30000 |
| --policy.use_amp | Mixed precision; SmolVLA has no dtype field | true when memory is tight |
| --num_workers | Dataloader processes, default 4 | Raise until data_s stops climbing |
| --dataset.eval_split | Fraction of episodes held out per task | 0.1, with --eval_steps |
| --policy.freeze_vision_encoder | Keeps the vision tower frozen | true on 24 GB |
| --policy.train_expert_only | Only the ~100 M expert gets gradients | true first |
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.
| Setting | Default in lerobot 0.6.1 | Defined in |
|---|---|---|
| chunk_size / n_action_steps | 50 / 50 | SmolVLAConfig |
| num_steps (flow matching denoise) | 10 | SmolVLAConfig |
| optimizer_lr | 1e-4 | SmolVLAConfig |
| scheduler_warmup_steps | 1000 | SmolVLAConfig |
| scheduler_decay_steps | 30000 | SmolVLAConfig |
| scheduler_decay_lr | 2.5e-6 | SmolVLAConfig |
| freeze_vision_encoder | true | SmolVLAConfig |
| train_expert_only | true | SmolVLAConfig |
| batch_size / steps | 8 / 100000 | TrainPipelineConfig |
| seed / save_freq / num_workers | 1000 / 20000 / 4 | TrainPipelineConfig |
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 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.
| Setup | Policy | Batch | Wall clock |
|---|---|---|---|
| Single L4 / A10G (24 GB) | smolvla | 4 | about 3 to 6 h |
| Single A100 40 GB | smolvla | 16 | about 1 to 2 h |
| 4 x H100 80 GB with accelerate | smolvla | 32 | about 1 to 2 h |
| Single RTX 4090 / RTX 3090 (24 GB) | act | 8 | about 30 to 60 min |
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
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_placeThe 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) | Synchronous | Asynchronous |
|---|---|---|
| Completion time, pick and place, 10 trials | 13.75 s | 9.70 s |
| Pick and place cycles in a fixed time window | 9 | 19 |
| Success rate averaged over the three tasks | 78.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.
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.
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.
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=trueThe same run behind a form: you pick model and dataset, the backend rents a GPU by required VRAM, runs the trainer and writes checkpoints to object storage. The dataset can come from a Hugging Face repo id, the public directory, or your machine. Start at SmolVLA on SO-100, or the matrix on the training page.
| Field | Default the platform sends for SmolVLA | Note |
|---|---|---|
| batch size | 2 | Conservative for the 24 GB tier |
| learning rate | 1e-4 | The lerobot preset |
| max steps | 20000 | The reference run in the LeRobot docs |
| gradient accumulation | 8 | Shown in the form, not applied |
| extra knobs | seed, logFreq | Seed makes the run repeatable |
- 2 to 5 hours on the 24 GB tier, about 1 to 3 USD per run.
- The same operations from a terminal at /cli and from AI agents at /mcp.
- Inference pods carry an idle watchdog, so a forgotten pod destroys itself instead of billing quietly.
- No arm yet? /live streams a physical SO-100 you can drive without signing up.
A job stuck in the queue is a spot market symptom, not a bug: training job stuck queued. Step by step: train your first policy and the training docs.
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.

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 checklist before you scale anything
- Did the
lrreach its 2.5e-6 floor? Below 30000 steps lerobot rescales the decay and says so at startup; above it, set--policy.scheduler_decay_stepsyourself. - More than one checkpoint, and episodes held out with
--dataset.eval_splitso the eval loss means something. - Does the policy move at all? A falling loss with a motionless arm has specific causes: loss falls, policy does nothing.
- Does it survive a change of scene? If not: policy only works in one setup.
- Did you write down the seed? lerobot defaults to 1000, so two untouched runs stay comparable.
- 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 guidesCan 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.
Sources
- SmolVLA: A Vision-Language-Action Model for Affordable and Efficient Robotics
- SmolVLA: Efficient Vision-Language-Action Model (Hugging Face blog)
- lerobot/smolvla_base model card
- LeRobot docs: SmolVLA
- LeRobot docs: Compute HW Guide for LeRobot Training
- LeRobot docs: Installation
- LeRobot docs: LeRobotDataset v3.0 and the v2.1 converter
- LeRobot docs: Asynchronous Inference
- lerobot v0.6.1: configuration_smolvla.py
- lerobot v0.6.1: TrainPipelineConfig
- lerobot v0.6.1: CosineDecayWithWarmupSchedulerConfig
- lerobot v0.6.1: lerobot_rollout.py (strategies and RTC inference)
- lerobot v0.6.1: pyproject.toml (extras and console entry points)
- lerobot on PyPI
- lerobot/svla_so100_pickplace dataset (50 episodes, 19631 frames, v3.0)
Sources
- SmolVLA: A Vision-Language-Action Model for Affordable and Efficient Robotics
- SmolVLA: Efficient Vision-Language-Action Model (Hugging Face blog)
- lerobot/smolvla_base model card
- LeRobot docs: SmolVLA
- LeRobot docs: Compute HW Guide for LeRobot Training
- LeRobot docs: Installation
- LeRobot docs: LeRobotDataset v3.0 and the v2.1 converter
- LeRobot docs: Asynchronous Inference
- lerobot v0.6.1: configuration_smolvla.py
- lerobot v0.6.1: TrainPipelineConfig
- lerobot v0.6.1: CosineDecayWithWarmupSchedulerConfig
- lerobot v0.6.1: lerobot_rollout.py (strategies and RTC inference)
- lerobot v0.6.1: pyproject.toml (extras and console entry points)
- lerobot on PyPI
- lerobot/svla_so100_pickplace dataset (50 episodes, 19631 frames, v3.0)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started