
Seeds, nondeterminism and run-to-run variance in VLA fine-tuning: what lerobot and Isaac-GR00T really seed, what a seed cannot fix, and how many rollouts a comparison needs.
What you need to know
- •A seed pins the random numbers a trainer draws. It does not pin the order in which CUDA adds floats together, so two runs from the same seed can still drift apart.
- •lerobot seeds from one number: TrainPipelineConfig.seed defaults to 1000, cudnn_deterministic defaults to False. Isaac-GR00T's fine-tune CLI exposes no seed flag at all, and the seed it uses is hard-wired to 42.
- •NVIDIA's own Isaac-GR00T README tells you to expect 5 to 6 percent variance between runs from nondeterministic image augmentations.
- •Training variance is the small problem. Evaluation variance is the big one: 14 successes in 20 rollouts give an exact 95 percent confidence interval of 45.7 to 88.1 percent.
- •Separating a 70 percent policy from an 80 percent one at the usual thresholds needs roughly 290 rollouts each. Almost no published robot result does that.
- •The practical answer is not bit-exact training. It is pinning what you can pin, running one fixed eval protocol, and reporting a range instead of a number.
Three different things people call reproducibility
You train a policy on Monday, get 14 successes out of 20 attempts, and write down 70 percent. On Friday you train it again with the same dataset, the same config and the same command, and get 11 out of 20. Nothing changed. Before hunting for a bug, it is worth being precise about which kind of sameness you expected, because three different promises get filed under one word.
| Level | What it promises | Achievable for VLA fine-tuning? | What it costs |
|---|---|---|---|
| Bit-identical | Same checkpoint bytes, same loss curve to the last decimal | Only on one GPU, with deterministic kernels forced, same PyTorch build, same card | 10 to 20 percent slower on cuDNN alone, plus hard failures on ops with no deterministic kernel |
| Statistically equivalent | Same distribution of outcomes; individual runs differ | Yes, the normal case once seeds and data are pinned | Nothing, beyond not reading single numbers as facts |
| Decision-equivalent | Both runs lead to the same deploy or discard decision | Yes, if the evaluation has enough trials to support it | Rollouts, far more than most people run |
Only the third one matters for shipping a robot, and it is the only one you can realistically get for a vision-language-action model. Chasing the first is a common way to lose a week.
Every upstream fact below was read from source on 24 August 2026: Isaac-GR00T at main, lerobot at main (version string 0.6.2, latest PyPI release 0.6.1), and the PyTorch 2.13 reproducibility notes, whose footer reads "Last Updated On: May 14, 2026". These repositories move. Re-read the config dataclass before trusting a default you saw in a blog post, including this one.
What each trainer actually does with a seed
The five policies you can train here come from two codebases that disagree about what a seed is for. ACT, SmolVLA and Pi0.5 go through lerobot. GR00T N1.7 and GR00T N1.5 go through NVIDIA's Isaac-GR00T. That split is the single most important thing to understand here.
| Trainer | Seed default | Settable from the CLI? | Determinism flag | What resume does to the RNG |
|---|---|---|---|---|
| lerobot (ACT, SmolVLA, Pi0.5) | 1000 | Yes, --seed=N | --cudnn_deterministic, default False | Restores optimizer, scheduler, step counter and data order |
| Isaac-GR00T (N1.7, N1.5) | 42, from DataConfig.seed | No. FinetuneConfig has no seed field | None exposed | Reseeds the dataset to seed + global_step and prints a warning that the run is no longer reproducible |
lerobot: one seed, wired through everything
In lerobot, seed is a top-level field on TrainPipelineConfig, used for model initialization, dataset shuffling and the evaluation environments. Its default is 1000, not 0 and not 42. The training script calls set_seed once, seeding Python's random, NumPy, torch on CPU, torch on all CUDA devices, and accelerate.
# src/lerobot/configs/train.py (lerobot main, read 2026-08-24)
# `seed` is used for training (eg: model initialization, dataset shuffling)
# AND for the evaluation environments.
seed: int | None = 1000
# Set to True to use deterministic cuDNN algorithms for reproducibility.
# This disables cudnn.benchmark and may reduce training speed by ~10-20 percent.
cudnn_deterministic: bool = False
# src/lerobot/scripts/lerobot_train.py
if cfg.seed is not None:
set_seed(cfg.seed, accelerator=accelerator)
device = accelerator.device
if cfg.cudnn_deterministic:
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
else:
torch.backends.cudnn.benchmark = True
torch.backends.cuda.matmul.allow_tf32 = TrueBatch order is the part lerobot gets genuinely right. EpisodeAwareSampler derives a per-epoch seed with np.random.SeedSequence([seed, epoch]) and feeds it to a fresh torch.Generator. The source comment states the consequence directly: the order is a pure function of (seed, epoch), so every rank reproduces the same permutation without synchronizing an RNG, and resume is sample-exact. Restart from a checkpoint and you get the batches you would have got without the interruption.
# lerobot 0.6.x, ACT on an SO-100 dataset, seed made explicit
lerobot-train \
--dataset.repo_id=${HF_USER}/so100_pick_place \
--policy.type=act \
--policy.device=cuda \
--output_dir=outputs/train/act_seed1000 \
--job_name=act_seed1000 \
--seed=1000 \
--batch_size=8 \
--steps=100000
# same thing, second seed, separate output dir
lerobot-train \
--dataset.repo_id=${HF_USER}/so100_pick_place \
--policy.type=act \
--policy.device=cuda \
--output_dir=outputs/train/act_seed1001 \
--job_name=act_seed1001 \
--seed=1001 \
--batch_size=8 \
--steps=100000Isaac-GR00T: the seed exists, but not for you
GR00T's fine-tuning entry point is gr00t/experiment/launch_finetune.py, a tyro CLI over the FinetuneConfig dataclass. That dataclass has fields for the base model path, dataset path, embodiment tag, which module to tune, augmentation parameters, batch size, learning rate and max steps. It has no seed field.
The seed is not absent from the codebase. One layer down, gr00t/experiment/experiment.py calls set_seed(config.data.seed) and passes seed=config.data.seed into the Hugging Face TrainingArguments, and DataConfig.seed defaults to 42. So GR00T runs are seeded, consistently, at 42, and the launcher gives you no way to change it. Varying it means dropping to launch_train.py and writing a full config, which is a different workflow from the documented one.
The Isaac-GR00T README's Training Tips section says it in plain text: "Users may observe 5-6% variance between runs due to non-deterministic image augmentations. Keep this in mind when comparing to reported benchmarks." That is upstream, in writing, from the vendor. If you fine-tune GR00T twice and the second checkpoint scores a few points worse, you have not broken anything and you have not learned anything. Do not bisect your dataset over a difference this size. The resume path is worse: Gr00tTrainer.get_train_dataloader reseeds the dataset to seed + global_step on restart and prints "Please note that this will make the experiment non-reproducible." A resumed run and an uninterrupted one see different data orders by design.
# Isaac-GR00T, single GPU, from the repo README (main, read 2026-08-24)
CUDA_VISIBLE_DEVICES=0 uv run 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 \
--dataloader-num-workers 4
# there is no --seed. Confirm the current flag list yourself:
uv run python gr00t/experiment/launch_finetune.py --help
The things a seed cannot fix
A seed only controls pseudo-random number generators. It has no authority over the order in which a GPU accumulates partial sums, and floating point addition is not associative: (a + b) + c and a + (b + c) can differ in the last bits. Any CUDA kernel using atomics to accumulate gradients therefore produces a slightly different result each run, seed or no seed. Over 20000 training steps those last bits become different weights, and different weights become a different end effector trajectory.
| Source | Does the seed fix it? | What actually fixes it | Cost |
|---|---|---|---|
| Weight init, dropout masks, augmentation draws | Yes | Nothing more needed | None |
| Batch composition and shuffling order | Yes in lerobot, via EpisodeAwareSampler | Nothing more needed | None |
| cuDNN autotuning picking a different convolution per run | No | cudnn.benchmark = False plus cudnn.deterministic = True | lerobot's own comment: roughly 10 to 20 percent slower |
Atomic gradient accumulation (scatter_add_, index_add, embedding and index_select backward) | No | torch.use_deterministic_algorithms(True) | Slower, and several ops raise RuntimeError rather than fall back |
NumPy or Python random calls inside DataLoader workers | Partially | A worker_init_fn plus an explicit generator | A few lines, but neither trainer passes them |
| Different GPU model, driver or PyTorch build between runs | No | Nothing. PyTorch does not promise this | Not available at any price |
The atomics problem is not theoretical for VLA training. Every one of these models has a language backbone with an embedding table, and embedding backward on CUDA accumulates into the same rows from many tokens at once. PyTorch's list of normally-nondeterministic operations that use_deterministic_algorithms(True) makes deterministic includes scatter_add_ on CUDA, index_add on CUDA, index_select backward and gather backward. Those are exactly the ops a transformer's backward pass leans on.
From the PyTorch 2.13 reproducibility notes: "Completely reproducible results are not guaranteed across PyTorch releases, individual commits, or different platforms. Furthermore, results may not be reproducible between CPU and GPU executions, even when using identical seeds." Everything below that sentence narrows nondeterminism for one specific platform, device and release. It is not a promise of portability, and no framework built on PyTorch can give you one.
# The hardening block, if you genuinely need bit-identical runs.
# Put this before you build the model, not after.
import random, numpy as np, torch
SEED = 1000
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
torch.cuda.manual_seed_all(SEED)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
torch.backends.cuda.matmul.allow_tf32 = False # lerobot turns this ON unconditionally
torch.use_deterministic_algorithms(True) # raises on ops with no deterministic kernel
# DataLoader workers: PyTorch reseeds torch per worker, but not numpy or random
def seed_worker(worker_id):
worker_seed = torch.initial_seed() % 2**32
np.random.seed(worker_seed)
random.seed(worker_seed)
g = torch.Generator()
g.manual_seed(SEED)
loader = torch.utils.data.DataLoader(
ds, batch_size=8, num_workers=4, worker_init_fn=seed_worker, generator=g
)- A failing run can be replayed exactly, turning "the loss exploded at step 4300" from a ghost story into a debuggable event.
- Regression tests on the training pipeline become possible: change one line, assert the loss curve is unchanged.
- A score difference becomes attributable to the change you made rather than to luck.
- It exposes hidden randomness. If runs still differ after this, something is drawing entropy you did not know about.
- lerobot puts
cudnn_deterministicat roughly 10 to 20 percent slower, beforeuse_deterministic_algorithmsis considered. torch.use_deterministic_algorithms(True)raises RuntimeError on common ops including bilinearinterpolatebackward,cumsumon CUDA floats,grid_samplebackward andNLLLosson CUDA.- It buys nothing across hardware. A run pinned on an A100 will not reproduce on an H100.
- It does nothing about evaluation variance, which is the larger term.
- Neither trainer supports it as a flag, so you are maintaining a fork.
How much run-to-run variance to expect
One way to answer this is to reason about the codebase, which we just did. The other is to look at what people who scanned thousands of seeds found, and at what vendors put in their own READMEs.
| Source | Setting | Reported spread | What it tells you |
|---|---|---|---|
| Isaac-GR00T README, Training Tips | GR00T fine-tuning, same config, repeated | "5-6% variance between runs due to non-deterministic image augmentations" | The vendor's own floor. Differences below this are not signal. |
| Picard 2021, arXiv 2109.08203 | ResNet9 on CIFAR-10, 10,000 seeds, short schedule | 89.01% to 90.83%, a 1.82 point spread; the bulk sits between 89.5% and 90.5% | Extremes exist but are rare. The typical run is boring, the outlier gets published. |
| Picard 2021, same paper | Pretrained ResNet50 fine-tuned on ImageNet, 50 seeds | Standard deviation around 0.1%, min to max gap around 0.5% | Pretraining on a large dataset shrinks seed variance sharply. Fine-tuning is the good case. |
| Bouthillier et al. 2021, arXiv 2103.03098 | Five deep learning tasks, variance decomposed by source | Data sampling and hyperparameter choice matter as much as initialization | A fixed seed with a varying data split still leaves variance. |
| ACT paper, arXiv 2304.13705 | Simulated tasks / real-world tasks | 3 seeds with 50 evaluations each / 1 seed with 25 evaluations | The field runs 1 to 3 seeds, and 3 is the generous end. |
Two conclusions follow. First, fine-tuning a pretrained foundation model on 50 episodes puts you closer to Picard's ImageNet case than his CIFAR-10 case, so the seed is not the dominant term. Second, the convention of 1 to 3 seeds means most published robot numbers lack the statistical support to distinguish themselves from each other, and neither will yours.

Evaluation noise is bigger than training noise
This is the part that surprises people arriving from a benchmark background. You can spend a week making training deterministic and still not tell two policies apart, because the measurement holds almost all the variance. A rollout either succeeds or it does not. That is a Bernoulli trial, and the standard error of a proportion over n trials is the square root of p(1-p)/n.
| Rollouts per policy | Standard error at 50% true success | at 70% | at 90% |
|---|---|---|---|
| 10 | 15.8 points | 14.5 points | 9.5 points |
| 20 | 11.2 points | 10.2 points | 6.7 points |
| 50 | 7.1 points | 6.5 points | 4.2 points |
| 100 | 5.0 points | 4.6 points | 3.0 points |
| 200 | 3.5 points | 3.2 points | 2.1 points |
Read the second row again. Twenty rollouts is already more than most people run on a real arm, and it still leaves a standard error near 10 points. Score 14 out of 20 and the exact 95 percent confidence interval on the true success rate is 45.7 to 88.1 percent. Score 35 out of 50, the same 70 percent, and it narrows only to 55.4 to 82.1 percent. Your seed-to-seed difference of 5 or 6 points sits comfortably inside the noise of your own measurement.
To separate a 70 percent policy from an 80 percent policy at a two-sided 5 percent significance level with 80 percent power, you need roughly 290 rollouts of each. To separate 70 percent from 75 percent, about 1,248 each. And in a 20-rollout head to head between a genuinely 60 percent policy and a genuinely 50 percent one, the worse policy wins outright about 21 percent of the time. If you pick checkpoints by running 20 trials each and keeping the winner, you are wrong roughly one time in five, and you will never find out.
This is a redirection of effort, not a counsel of despair. If measurement dominates, spend the budget there: standardise the scene, script the reset, use the same object positions in the same order for every checkpoint, and record the rollouts so a disputed success can be re-scored. Projects that take this seriously are explicit about the cost. RoboArena needed more than 600 pairwise real-robot episodes across seven institutions just to rank seven generalist policies against each other.
One cheap trick sidesteps rollout noise almost entirely: open-loop evaluation. Feed the policy observations from held-out episodes and measure the error between predicted and recorded actions. Isaac-GR00T ships gr00t/eval/open_loop_eval.py for exactly this, with per-joint plots and MSE. It costs no hardware time and takes the scene out of the measurement, but it is not itself deterministic: the flow-matching action head starts every prediction from a fresh torch.randn and neither the eval script nor Gr00tPolicy sets a seed, so run it more than once and compare averages. It also will not tell you whether the policy can do the task. Use it to compare seeds, not to decide whether to ship.
A protocol that survives contact with reality
None of this needs a research budget. It needs six things written down and left unchanged between runs.
- 1Pin the dataset by revision, not by name
A dataset that gained three episodes between Monday and Friday explains your whole score difference on its own. Record the exact commit and the format version, because a LeRobot dataset in v3.0 crashes the GR00T loader and has to be converted down to v2.1 first.
bash# what you should be able to paste into the run log curl -s https://huggingface.co/api/datasets/${HF_USER}/so100_pick_place | jq -r '.sha' python -c "import json;print(json.load(open('meta/info.json'))['codebase_version'])" # -> v2.1 (GR00T) or v3.0 (ACT, SmolVLA, Pi0.5) - 2Pin the code, including the trainer
Record the lerobot version or the Isaac-GR00T commit hash. Neither trainer stamps its own version into the checkpoint in a way you can rely on, so a run from three months ago is not comparable to one from today unless you wrote it down.
bashpip show lerobot | grep -i version git -C Isaac-GR00T rev-parse --short HEAD python -c "import torch;print(torch.__version__, torch.version.cuda)" nvidia-smi --query-gpu=name,driver_version --format=csv,noheader - 3Run three seeds, not one
For ACT and SmolVLA this is genuinely cheap: three runs on the 24 GB tier come to roughly 3 to 9 USD total. For GR00T you cannot vary the seed through the documented CLI, so run the same config three times and let the nondeterministic augmentations do the varying. Measuring that 5 to 6 percent on your own data is worth one afternoon.
bashfor S in 1000 1001 1002; do lerobot-train \ --dataset.repo_id=${HF_USER}/so100_pick_place \ --policy.type=smolvla \ --policy.device=cuda \ --seed=$S \ --output_dir=outputs/train/smolvla_s$S \ --job_name=smolvla_s$S done - 4Compare the seeds open-loop before touching the arm
Run every checkpoint against the same held-out episodes and look at the action error. The Isaac-GR00T script below reads GR00T checkpoints; for lerobot policies you need the equivalent on that side. If three runs disagree wildly here, the problem is your dataset or your learning rate, not your luck, and no amount of rollout testing will fix it.
bash# open_loop_eval.py loads a Gr00tPolicy, so it reads GR00T checkpoints. # GR00T has no settable seed, so these are plain repeats of one config. for R in 1 2 3; do uv run python gr00t/eval/open_loop_eval.py \ --dataset-path /data/so100_holdout \ --embodiment-tag NEW_EMBODIMENT \ --model-path outputs/gr00t_run$R/checkpoint-20000 \ --traj-ids 0 1 2 \ --execution-horizon 16 done - 5Fix the evaluation scene before you fix the training
Write down starting positions, lighting, camera mounts and reset procedure, then use exactly those for every checkpoint. An unstandardised scene adds more variance than every seed effect in this article combined, and the same discipline pays off upstream when you are collecting the training data in the first place. A policy that scores well in one setup and badly in another is a known failure mode: policy only works in one setup.
texteval_protocol.md 20 rollouts per checkpoint object start positions: A1 A2 A3 A4 A5, cycled, 4 passes lighting: overhead only, blinds closed reset: operator places object, hands clear, then start scored: success = object fully inside the bowl, hand released recorded: wrist cam + front cam, kept for 30 days - 6Report a range, and say how many trials it came from
"78 percent" is not a result. "14/20, 12/20 and 16/20 across three seeds, so 60 to 80 percent" is a result, and it tells the next person whether your improvement is real. It is also the format that makes a difference between two checkpoints arguable rather than assertable.
textsmolvla, so100_pick_place @ sha 4f21ac9, lerobot 0.6.1, RTX 4090 seed 1000 -> 14/20 (exact 95% CI 45.7 - 88.1%) seed 1001 -> 12/20 (exact 95% CI 36.1 - 80.9%) seed 1002 -> 16/20 (exact 95% CI 56.3 - 94.3%) pooled 42/60 = 70.0% (exact 95% CI 56.8 - 81.2%)
Doing it yourself against doing it here
You control the whole stack, which is the only way to get bit-identical runs. You also own every part of it, including the GPU allocation, which is where reproducibility quietly dies for most people.
- Install lerobot (
pip install lerobot, currently 0.6.1) or clone Isaac-GR00T and install with uv. Record the exact version. - Pin your own GPU. This is what rented capacity cannot give you: the same physical card, driver and CUDA build, run after run.
- For lerobot, pass
--seedand--cudnn_deterministic=trueand accept the roughly 10 to 20 percent slowdown its own comment documents. - For full determinism, patch
torch.use_deterministic_algorithms(True)and aworker_init_fninto the training script, then fix whatever ops start raising RuntimeError. - For GR00T, accept that
launch_finetune.pyhas no seed flag, or drop tolaunch_train.pyand write a config where you can setdata.seedyourself. - Build an eval harness, or use Isaac-GR00T's open-loop script, and run one protocol for every checkpoint.
Hardware stability. PyTorch explicitly does not promise reproducibility across platforms, so if two runs must match to the last bit, they have to happen on the same card. That is an argument for owning a GPU, not for any particular software choice.
The training form sends the defaults below and rents a GPU on a spot market sized by required VRAM. Three of the five policies expose a seed field. Two do not, for the upstream reason described above.
| Policy | Batch size | Learning rate | Max steps | Grad accum | Seed in the form? |
|---|---|---|---|---|---|
| GR00T N1.7 | 32 | 1e-4 | 20000 | 1 (applies) | No |
| GR00T N1.5 | 1 | 1e-5 | 2000 | 16 (applies) | No |
| Pi0.5 | 1 | 5e-5 | 30000 | 16 (does not apply) | Yes |
| SmolVLA | 2 | 1e-4 | 20000 | 8 (does not apply) | Yes |
| ACT | 8 | 1e-5 | 100000 | 1 | Yes |
- The seed field for Pi0.5, SmolVLA and ACT maps to lerobot's
--seed, so sampler and initialization behave exactly as described above. - GR00T N1.7 and N1.5 expose
saveStepsbut no seed, becauseFinetuneConfigupstream has none to expose. - Two gradient-accumulation defaults do not take effect: lerobot 0.5.1 has no such flag for Pi0.5 or SmolVLA. The form shows the intent, the trainer ignores it. Knowing which knobs are inert is part of reproducibility.
- Checkpoints go to object storage, so a run from three weeks ago is still there to re-evaluate against a new one. Comparing two checkpoints beats trying to recreate one.
- Inference pods carry an idle watchdog and destroy themselves after an idle period, so a long evaluation session does not become a silent bill.
It cannot make GR00T reproducible, because upstream does not expose the seed. It cannot promise the same physical GPU twice: the backend rents whatever satisfies the VRAM requirement, so a GR00T run may land on an A100 80 GB one day and an H100 80 GB the next, and PyTorch does not guarantee identical results across those. And it does not run your rollouts. The 20 or 50 trials on your own arm are still yours to do, and per the numbers above, that is where the variance you care about actually lives.
Which policy to pick if you care about this
Reproducibility is a real input to model selection, and one that people rarely weigh. If you plan to run many comparisons, being able to set a seed and re-run cheaply is worth more than a couple of points of headline performance.
| Policy | Params | GPU tier | Cost per run | Seed controllable? | Good for repeated experiments? |
|---|---|---|---|---|---|
| ACT | ~80 M | RTX 4090 or any 24 GB card | about 1 to 3 USD | Yes | Best. Cheap, fast, from scratch, so no base-model drift. |
| SmolVLA | ~450 M | RTX 4090 or any 24 GB card | about 1 to 3 USD | Yes | Very good. Three seeds stays under 10 USD. |
| Pi0.5 | ~3 B | A100 80 GB or H100 80 GB | about 4 to 12 USD | Yes | Workable. Three seeds is 12 to 36 USD. |
| GR00T N1.5 | ~3 B | A100 80 GB or H100 80 GB | about 4 to 12 USD | No | Poor. Repeats vary by the documented 5 to 6 percent. |
| GR00T N1.7 | ~3 B | A100 80 GB or H100 80 GB | about 4 to 12 USD | No | Poor, same reason. |
ACT is the honest recommendation for methodology work. It is around 80 M parameters, trains from scratch rather than fine-tuning a foundation model, has an inference latency near 20 ms per action step, and needs 50 episodes minimum. Run it ten times, learn what your own noise floor looks like, then move to a larger model. The ACT against SmolVLA comparison, the five-policy overview and the background on flow-matching policies behind Pi0.5 cover the rest of the trade, and the model arena lists 85 VLA models with 332 benchmark results, each value linked to its source, which is itself an education in how loosely these figures compare.

Run the same config three times and find your own noise floor
The training form picks the model, the dataset and the hyperparameters, rents a GPU by required VRAM and writes checkpoints to object storage. On the 24 GB tier a run is about 1 to 3 USD, so a three-seed study costs less than a cable.
Start a training runWhy do two training runs with the same seed still produce different checkpoints?▾
The seed controls pseudo-random number generators, not the order in which the GPU accumulates floating point values. Gradient accumulation for embeddings and other indexed operations uses CUDA atomics, and float addition is not associative, so the same sum in a different order gives different bits. cuDNN also autotunes convolutions per run unless you disable benchmarking. PyTorch's reproducibility notes state that completely reproducible results are not guaranteed across releases, commits or platforms, even with identical seeds.
Can I set a seed for GR00T N1.7 fine-tuning?▾
Not through the documented path. The entry point gr00t/experiment/launch_finetune.py is a tyro CLI over the FinetuneConfig dataclass, and that dataclass has no seed field as of main on 24 August 2026. One layer down, experiment.py does call set_seed(config.data.seed) and passes seed=config.data.seed to the Hugging Face TrainingArguments, where DataConfig.seed defaults to 42. Runs are seeded at 42, you just cannot change it from the fine-tune launcher. The README additionally warns of 5 to 6 percent variance between runs from nondeterministic image augmentations.
What is lerobot's default seed?▾
1000. TrainPipelineConfig.seed is typed int or None and defaults to 1000, and the source comment says it is used for model initialization, dataset shuffling and the evaluation environments. Batch order is handled by EpisodeAwareSampler, which derives a per-epoch seed from np.random.SeedSequence([seed, epoch]), making data order a pure function of seed and epoch and making resume sample-exact. There is also a cudnn_deterministic flag, default False, whose comment warns it may reduce training speed by roughly 10 to 20 percent.
How many rollouts do I need to say one policy is better than another?▾
More than you want to run. For a two-sided test at the 5 percent level with 80 percent power, separating a 70 percent policy from an 80 percent one takes roughly 290 rollouts each, and separating 70 from 75 percent takes about 1,248 each. At 20 rollouts each, a genuinely 50 percent policy beats a genuinely 60 percent one about 21 percent of the time. Published robot papers commonly use 10 to 50 trials, so most reported gaps are not statistically separated, including yours.
Is it worth forcing full determinism with torch.use_deterministic_algorithms?▾
For debugging a training pipeline, yes: a run you can replay exactly turns intermittent failures into reproducible ones. For comparing policy quality, usually no. It costs speed, it raises RuntimeError on several common ops including bilinear interpolate backward, cumsum on CUDA floats and grid_sample backward, it does not survive a change of GPU, and it does nothing about evaluation variance, which is the larger term by a wide margin.
If runs vary anyway, why set a seed at all?▾
Because it removes one source of variance for free and makes the remainder interpretable. If you pin the seed and two runs still differ, you have learned the difference comes from kernel-level nondeterminism or from your environment, which is a much shorter list to debug than anything at all. It also means a resumed run continues from the same data order rather than a new one, which lerobot does and Isaac-GR00T explicitly does not.
What to do on Monday
Pick the cheapest model resembling your task, which for most people on an SO-100 means ACT or SmolVLA. Train it three times with three seeds and write the three numbers down. That is your noise floor, measured on your data and your arm, and it is what lets you interpret every experiment afterwards. Then standardise the evaluation scene and stop changing it. When you move to GR00T or Pi0.5, carry that floor with you as a lower bound: a bigger model on a rented card adds sources of variation rather than removing them. If a change moves your score by less than the floor, it did not move your score. The first-policy walkthrough, the training documentation and the failure-mode index cover the mechanics, and the VLA overview covers what these models are. This article covers deciding whether the mechanics worked.
Sources
- Isaac-GR00T README, Training Tips: 5-6% variance between runs
- Isaac-GR00T: FinetuneConfig, the tyro CLI dataclass with no seed field
- Isaac-GR00T: DataConfig.seed defaults to 42
- Isaac-GR00T: set_seed(config.data.seed) and the TrainingArguments wiring
- Isaac-GR00T: Gr00tTrainer reseeds on resume and warns the run is non-reproducible
- lerobot: TrainPipelineConfig, seed=1000 and cudnn_deterministic=False
- lerobot: lerobot_train.py, set_seed and the cuDNN / TF32 block
- lerobot: set_seed, seeded_context and RNG state serialization
- lerobot: EpisodeAwareSampler, data order as a pure function of (seed, epoch)
- PyTorch reproducibility notes (2.13)
- torch.use_deterministic_algorithms: the list of affected operations
- Picard 2021: Torch.manual_seed(3407) is all you need
- Bouthillier et al. 2021: Accounting for Variance in Machine Learning Benchmarks
- ACT / ALOHA: Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware
- RoboArena: Distributed Real-World Evaluation of Generalist Robot Policies
Sources
- Isaac-GR00T README, Training Tips: 5-6% variance between runs
- Isaac-GR00T: FinetuneConfig, the tyro CLI dataclass with no seed field
- Isaac-GR00T: DataConfig.seed defaults to 42
- Isaac-GR00T: set_seed(config.data.seed) and the TrainingArguments wiring
- Isaac-GR00T: Gr00tTrainer reseeds on resume and warns the run is non-reproducible
- lerobot: TrainPipelineConfig, seed=1000 and cudnn_deterministic=False
- lerobot: lerobot_train.py, set_seed and the cuDNN / TF32 block
- lerobot: set_seed, seeded_context and RNG state serialization
- lerobot: EpisodeAwareSampler, data order as a pure function of (seed, epoch)
- PyTorch reproducibility notes (2.13)
- torch.use_deterministic_algorithms: the list of affected operations
- Picard 2021: Torch.manual_seed(3407) is all you need
- Bouthillier et al. 2021: Accounting for Variance in Machine Learning Benchmarks
- ACT / ALOHA: Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware
- RoboArena: Distributed Real-World Evaluation of Generalist Robot Policies
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started