Downloads · 30 days
0
katsukiono/smolvla-libero-spatial-4arm
smolvla-libero-spatial-4arm is a robotics model from katsukiono. Use it for the robotics task on the model card, and read the license before you ship it in a product. It is set up for lerobot. The card lists the license as apache-2.0.
This repository contains four SmolVLA checkpoints from a single-GPU proof-of-concept study that compared four post-training recipes on the liberospatial suite of LIBERO-Plus, all starting from the same supervised fine…
Downloads · 30 days
0
Access
Public
Updated Jul 29, 2026
Repo size
3.6 GB
Likes
0
Public
Click a slice to open those files.
.safetensors3.6 GB · 100%
From the Hugging Face model README
libero_spatial — four post-training armsThis repository contains four SmolVLA checkpoints from a single-GPU
proof-of-concept study that compared four post-training recipes on the
libero_spatial suite of LIBERO-Plus, all starting from the same supervised
fine-tune:
| subfolder | arm | what it is |
|---|---|---|
sft/ | SFT | supervised fine-tune of lerobot/smolvla_base; the common initialization for the other three, and the baseline every contrast is measured against |
rs_sft/ | RS-SFT | rejection-sampling SFT on successful rollouts collected from sft |
dpo/ | flow-DPO | preference optimization on pairs from the same rollout collection |
grpo/ | Flow-SDE GRPO | online RL (run8 final, 20 iterations) |
These weights are published so that a third party can re-run the reported evaluations. They are not published because any arm is good, and this card does not recommend any arm over any other. Success rates are low in absolute terms (13–43% depending on the evaluation), the study is n=1 per arm, and the two findings below are the honest summary of what the comparison established.
The study's only confirmatory result was preregistered — design, primary
statistic and decision rule were all committed before a single episode ran. It
contrasted grpo/ against its own initialization sft/ on tasks that were never
trained on:
| arm | successes | success rate (task-bootstrap 95% CI) |
|---|---|---|
sft/ (the initialization) | 19/138 | 13.8% [7.3, 21.7] |
grpo/ | 20/138 | 14.5% [8.0, 21.7] |
Do not choose grpo/ over sft/ expecting better performance. There is no
established advantage. A null is also not proof of equivalence or inferiority —
the interval rules out an effect as large as an earlier exploratory probe had
suggested, and nothing more.
rs_sft/ and dpo/ carry a known bias — the trained-task table flatters themBoth rs_sft/ and dpo/ were trained on rollouts collected at the very
initial states that the trained-task evaluation (eval A) then used.
LIBERO-Plus does not let you choose the initial state with the reset seed:
LiberoEnv.reset() applies the seed and then overwrites the state with
set_init_state(init_states[init_state_id % N]), where init_state_id starts
from the sub-environment's episode_index and advances by n_envs on every
reset. Eval A was run with a seed chosen to be disjoint from all training seeds,
in the belief that this produced held-out initial conditions. It did not: eval A
ran on training initial states.
This matters asymmetrically. RS-SFT behaviour-clones successful trajectories
from exactly those states and flow-DPO's preference pairs come from the same
collection, so eval A favours these two arms by construction. Adding
episodes would not remove the bias. Two of the eight training tasks (1817, 1955)
have len(init_states) == 1, so collection, RL training and every eval-A episode
on them began from the identical state.
RS-SFT topping the eval A table is exactly what the bias predicts, and cannot be distinguished from a real effect. Anyone who reads only the eval A numbers will conclude RS-SFT is the best arm. That conclusion is not supported.
Restricted to the genuinely paired subset — 56 of the 105 episodes per arm, see below — RS-SFT leads SFT by +12.5 pp but the contrast is inconclusive: exact McNemar p = 0.092, task-clustered CI [−3.6, +30.4] pp.
The manuscript is a preprint (10.5281/zenodo.21596932; v1 is 10.5281/zenodo.21596933). It has not been published or accepted anywhere and no venue has been selected, so do not cite it as a published paper. Every claim on this card is also backed by a file in the public repository, so you can check any of it without reading the preprint.
Each subfolder is a complete LeRobot save_pretrained directory (config.json,
model.safetensors, and the pre/post-processor files). SHA-256 is over the
file bytes; model.safetensors holds the weights. sft/ carries one extra file,
train_config.json — the full LeRobot training configuration of the SFT run
(dataset, policy, optimizer, scheduler, steps, seed), kept for reproducibility.
No optimizer state (optim.pt) is published, so these checkpoints are for
inference and evaluation, not for resuming training.
| subfolder | files | total size | model.safetensors SHA-256 | folder content hash |
|---|---|---|---|---|
sft/ | 7 | 864.9 MiB | dc5c3cbcce8a55716ecb50d3a13aaaa80fc5e8b084b815514769855ff9d6e8bb | f8eb8ad0d1537d5335e5a312fb1825deb024581ea522ea182adac3f96b842e3a |
rs_sft/ | 6 | 864.7 MiB | 5f15bf3bc489c938d166fbe4bfd4a58edc4465182a7e85ac3b17534de8909491 | 02b75dcc33000c1aeca20eca68251e3f0a235b913ec4194a39601a3bffc37a54 |
dpo/ | 6 | 864.7 MiB | 90bb4b7f6d75a7214797e037b0248c259ea31581206e8b1c7d62dbd15db4d4c3 | 3de2a572457d54cab84406f00f7e31d3a00af8ec34aafa4c14d591c208895ba8 |
grpo/ | 6 | 864.7 MiB | c37f5100ce63a594d0ca4cfa82c55615dd662dbc074e3ce3949a61eb17a7a973 | 946d6fdeeb70022372a07773fa7c1a9ba5686d140f1cb2b723d0c114e092f017 |
The folder content hash is sha256 over the sorted "<filename> <sha256>\n" lines of the files listed above, so it identifies the exact set of bytes published in that subfolder. Reproduce it with:
for f in $(ls | sort); do printf '%s %s\n' "$f" "$(sha256sum "$f" | cut -d' ' -f1)"; done | sha256sum
Source checkpoint directories on the author's Drive (for the record):
sft/ ← MyDrive/SmolVLA_RL/outputs/sft_base_heldout/checkpoints/020000/pretrained_modelrs_sft/ ← MyDrive/SmolVLA_RL/rs_sft_run/checkpoints/last/pretrained_modeldpo/ ← MyDrive/SmolVLA_RL/dpo_run/checkpoints/last/pretrained_modelgrpo/ ← MyDrive/SmolVLA_RL/grpo_run8/ckpt_latest/pretrained_modelCommon ancestor: lerobot/smolvla_base
(~450M params: SmolVLM2 vision-language backbone + flow-matching action expert).
lerobot/smolvla_base
└─ sft/ SFT on lerobot/libero_plus, ALL camera_pose categories held out
├─ rs_sft/ rejection-sampling SFT ┐ both trained on the SAME 256-episode
├─ dpo/ flow-DPO ┘ rollout collection from sft/
└─ grpo/ Flow-SDE GRPO (run8 final, 20 iterations)
sft/ | 20 000 steps, batch 32, lr 1e-4 cosine. Data: lerobot/libero_plus minus all camera_pose perturbation categories (11 476 train / 2 871 held-out episodes). Source checkpoint dir: outputs/sft_base_heldout/checkpoints/020000. |
| rollout collection | 8 training tasks × 8 episodes × 4 rounds = 256 episodes, SDE sampling (10 steps, noise 0.35), max 300 env steps, seed 7. Shared by rs_sft/ and dpo/. |
rs_sft/ | 400 optimizer steps, lr 1e-5, micro-batch 8, keeps tasks with ≥4 successes. |
dpo/ | 300 steps, lr 5e-6, β=100, 4 pairs/step, winning-side SFT anchor coefficient 0.1. |
grpo/ | Flow-SDE GRPO, 20 iterations × 64 episodes = 1280 episodes, group size 8, clip 0.2, kl_coef = 0 (no anchor to SFT), lr 1e-5, 10 SDE steps, noise 0.35, VLM frozen (action expert only, ~100M params trained), episode_equal_weight on. One full-batch optimizer step per iteration → 20 parameter updates total. Config: configs/grpo_run8.yaml. |
Training tasks (LeRobot 0-based ids, libero_spatial): 79, 108, 1477, 1530,
1817, 1955, 2126, 2172 — selected by the reproducible rule in
src/select_grpo_tasks.py, not by hand.
GRPO's reward is a monotonically gated staged privileged reward read from simulator state (0.15 approach + 0.20 reached + 0.20 grasp_attempted + 0.20 lifted + 0.25 success) with a target-identity lock. It does not transfer to settings without privileged state.
grpo/ vs sft/ only)48 unseen libero_spatial tasks were drawn; 2 errored for both arms and were
dropped under the prespecified rule, leaving 46 effective tasks × 3 paired
initial states = 138 episodes per arm. Both arms saw identical initial-state
sets per task (asserted per episode). The vector environment is rebuilt per wave
so the initial state is a function of (offset, wave, batch) only, never of an
arm's own success pattern. Result: the table in §1 — NOT ESTABLISHED.
rs_sft/ and dpo/ were not run in this evaluation. There is therefore no
unconfounded head-to-head number for them anywhere in this study.
data/eval/PREREGISTRATION_unseen_task_eval.md (includes Amendment 1)data/eval/eval_unseen_prereg_result.jsondata/eval/raw/sft_result.json, data/eval/raw/grpo_result.jsonpython analysis/analyze_unseen_prereg.py \
data/eval/raw/sft_result.json data/eval/raw/grpo_result.json
Four arms on the same 7 trained tasks (task 1477 excluded: deterministic LIBERO-Plus renderer shape bug for all arms), 15 episodes/task = 105 episodes per arm. Read §2 above before reading this table.
| arm | success (task-boot. 95% CI) | Δ vs SFT (task-clustered 95% CI) | paired wins/losses | McNemar p |
|---|---|---|---|---|
sft/ | 28.6% [15.2, 41.9] | — (reference) | — | — |
rs_sft/ | 42.9% [24.8, 61.9] | +14.3 pp [+2.9, +28.6] | 19 / 4 | 0.003 |
dpo/ | 35.2% [22.9, 48.6] | +6.7 pp [−3.8, +16.2] | 16 / 9 | 0.230 |
grpo/ | 31.4% [17.1, 44.8] | +2.9 pp [−1.9, +7.6] | 11 / 8 | 0.648 |
Two separate defects, both consequences of the same mechanism:
rs_sft/ and
dpo/ (§2).LiberoEnv.step's internal reset plus gymnasium's NEXT_STEP autoreset — at
least, because the slot keeps being stepped afterwards and can terminate
again), so from the second wave onward each arm's initial states depended on
its own outcome pattern. Only 56 of 105 episodes per arm (wave 0) are
genuinely paired. That restriction is unaffected by the correction below: it
holds because wave 0 precedes any outcome-dependent history, which is
exactly why we restrict rather than try to reconstruct.On the genuinely paired wave-0 subset:
| arm | wave-0 successes | rate |
|---|---|---|
sft/ | 17/56 | 30.4% |
rs_sft/ | 24/56 | 42.9% |
Δ = +12.5 pp, exact McNemar p = 0.092, task-clustered CI
[−3.6, +30.4] pp → not significant. The point estimate is unchanged, but
the significance reported over the full 105 episodes rests substantially on
wave 1, where the pairing assumption fails (wave 1 alone: p = 0.0215 — reported
only to show where the full-sample significance came from). This re-analysis was
done for rs_sft/ vs sft/; it was not extended to the other arms.
An offset cannot repair eval A: because a terminated episode causes extra
resets, collection touches a scattered, outcome-dependent set of indices —
and we cannot say which. src/init_state_coverage.py estimated that it reaches
as high as 79 and, wrapping via % N at the real N = 50, blocks 32 of 50 states
leaving no free window of the required width 15.
Correction (2026-07-29). Those figures are an estimate, not an established fact, and this card previously presented them as fact. The reconstruction assumes a terminated episode costs exactly one internal reset plus one autoreset. It can be wrong in both directions: a done slot is not frozen — the collection loop runs until every slot is done, so it keeps being stepped, runs a fresh episode, and can terminate again (undercount) — while a termination on the step that ends the loop never gets its autoreset (overcount).
terminated = done or is_successalso means termination is not synonymous with success. Because later rounds start wherever the counter happens to be, one wrong round relocates every subsequent index, so the reconstructed set is neither a subset nor a superset of the true one.The conclusion is unchanged and now rests on the weaker claim that does hold: the visited set is outcome-dependent and not reliably reconstructible, so no offset can be shown to be held out. Evaluating tasks that were never trained on — what we actually ran — is immune either way. Found by adversarial review of our reply on huggingface/lerobot#4152.
data/eval/eval_A_summary.jsondata/eval/eval_A_wave0_reanalysis.jsonscripts/repro_libero_init_state.py reproduces the init-state behaviour8 unseen tasks × 3 episodes = 24 episodes per arm per pool. grpo/ has the
highest point estimate in both pools (29.2% in-dist, 20.8% held-out-camera, vs
≤12.5% for the others), but every interval includes zero, the winner is
selected post hoc as the max over 4 arms × 2 pools with no multiplicity control,
and 6 of GRPO's 7 in-dist successes come from just two tasks at 3/3 — excluding
those two leaves 1/18, at or below SFT on the same six tasks. The two pools are
different task sets, so their gap conflates viewpoint sensitivity with task
difficulty. This is the hypothesis the preregistered test was written to
decide, and it did not replicate.
data/eval/eval_secondary_summary.json
Weights live in subfolders, so from_pretrained("<repo id>") will not work at the
repository root. Download the arm you want, then load the local directory:
from huggingface_hub import snapshot_download
from lerobot.policies.smolvla.modeling_smolvla import SmolVLAPolicy
arm = "grpo" # or "sft", "rs_sft", "dpo"
path = snapshot_download(
"katsukiono/smolvla-libero-spatial-4arm",
allow_patterns=f"{arm}/*",
)
policy = SmolVLAPolicy.from_pretrained(f"{path}/{arm}")
Pin the LeRobot commit in the repository's requirements.txt for exact
reproduction. The preregistered run used lerobot 0.6.1
(1bb9933215dcb7ffeeae6d3746cda3f73f5a59e2), gymnasium 1.3.0. Evaluation wiring
(environment construction, camera-name mapping, per-wave rebuild) is in
src/eval_heldout.py; scripts/eval_unseen_prereg.sh runs the preregistered
evaluation.
If you re-run the four-arm evaluation, note that
scripts/eval_4arm.shdefaults its GRPO checkpoint togrpo_run7. The GRPO arm published here and used in every number above is run8. OverrideGRPO_CKPTaccordingly.
Research use only, in simulation.
grpo/ was trained with no KL anchor (kl_coef = 0); drift from the SFT
policy increases monotonically over the run and performance plateaus late.grpo/'s reward requires privileged simulator state; the staged milestone
detectors have task-dependent false negatives (~20% of successes carry no lift
flag) and "grasped" means "grasp_attempted" (gripper-close command within 6 cm).camera_pose categories were deliberately excluded from SFT training, so
held-out-viewpoint performance is expected to be degraded — but no evaluation
in this study measures that gap cleanly (see the two-pool caveat above).Released under Apache-2.0. What was actually checked, and the one gap:
huggingface/lerobot — the training
and inference code — is Apache-2.0 (verified via the GitHub license API).HuggingFaceTB/SmolVLM2-500M-Video-Instruct,
…-256M-…) is tagged license:apache-2.0.lerobot/smolvla_base carries
no license field in its model-card frontmatter, no license: tag in its
hub metadata, and no LICENSE file in the repo. This was verified, not assumed.smolvla_libero_plus,
smolvla_libero, smolvla_metaworld, smolvla_robotwin, smolvla_robomme,
smolvla_robocerebra, smolvla_robocasa, smolvla_vlabench are all tagged
license:apache-2.0 with base_model: lerobot/smolvla_base. The upstream
rights-holder therefore treats derivative weights of smolvla_base as
Apache-2.0 redistributable, and these checkpoints follow that precedent.lerobot tag".If you are the upstream rights-holder and this reading is wrong, please open a discussion on this repository and the weights will be taken down.
Cite the preprint for the study and its findings, and the archived software record for the code and data:
@misc{ono2026smolvlarlpaper,
author = {Ono, Katsuki},
title = {Low-Budget Post-Training of a Flow-Matching VLA: Flow-SDE GRPO
Diagnostics, an Evaluation Audit, and a Preregistered Null},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.21596932},
url = {https://doi.org/10.5281/zenodo.21596932},
note = {Preprint; not published or accepted anywhere}
}
@software{ono2026smolvlarl,
author = {Ono, Katsuki},
title = {SmolVLA\_RL: Low-Budget Flow-SDE GRPO Fine-Tuning of a
Flow-Matching VLA},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.21585106},
url = {https://doi.org/10.5281/zenodo.21585106}
}
Upstream work these checkpoints build on: SmolVLA (arXiv:2506.01844), LIBERO-Plus (arXiv:2510.13626), π_RL / Flow-SDE (arXiv:2510.25889).