The AY-Robots training matrix on /train: five trainable policies as rows, four supported arms as columns, every cell linking to that exact training guide
multi-gpu trainingvla fine-tuninggroot n1.7lerobotdistributed training

Multi-GPU Fine-Tuning for VLA Models: When It Helps

AY-Robots ResearchAugust 23, 202622 min read

Adding GPUs to a VLA fine-tune changes batch size, learning rate and step count in ways the flags hide. What GR00T and LeRobot really do, and the distributed failures that cost a day.

Four GPUs, quarter of the wall clock. For a fine-tune on 50 teleop episodes of an SO-100, that is almost never what you get, and the reason is not physics. The flag called batch size means opposite things in the two trainers most people reach for. In LeRobot it is per worker, so adding GPUs multiplies your effective batch. In NVIDIA's Isaac-GR00T N1.7 it is global, so adding GPUs divides the work and leaves the effective batch where it was. Same word, same number, inverted result.

This page describes what the code does today, with the file it came from and the release it shipped in, because both projects moved their multi-GPU story in 2026 and most tutorials online describe the older one. Then the three failures that turn a distributed run into a lost day: a batch that no longer means what you think, a learning rate nobody scaled, and a collective that hangs until NCCL gives up. It ends with the honest part, which is that for a 50-episode policy on a low-cost arm, the second GPU is usually the wrong thing to buy.

The short version

  • LeRobot multiplies. Its docs give the formula: effective_batch_size = batch_size x dp_world_size x gradient_accumulation_steps. Four workers at --batch_size=8 train at 32.
  • Isaac-GR00T N1.7 divides. --global-batch-size 64 over --num-gpus 8 puts 8 samples on each card. Adding hardware changes the time per step, not the effective batch.
  • Neither scales the learning rate for you. LeRobot says so outright: it does not auto-scale the learning rate or the step count when the effective batch grows.
  • Bigger batches are a pretraining tool. GR00T N1's own paper trains at batch 16,384 and post-trains at 128 or 1024, explicitly to avoid overfitting in data-limited settings.
  • GR00T's fine-tune entry point changed file, flag name and flag meaning between the n1.5 release (15 December 2025) and the n1.7 release (18 April 2026). A guide written against the old one trains something else.
  • AY-Robots runs each job on one rented card sized by the model's VRAM requirement. The knobs below are for hardware you rent yourself; the platform's form has no GPU-count field, and this article says where that matters.

What your trainer means by batch size

Before any distributed flag, settle what the number in your config controls. There are three conventions in circulation among VLA trainers, and mixing them up is the single most common way a multi-GPU run produces a worse checkpoint than the single-GPU run it replaced.

Trainer and versionThe batch flagWhat its scope isSamples per optimizer update
LeRobot, main branch (August 2026)--batch_size, default 8per data-parallel workerbatch_size x dp_world_size x gradient_accumulation_steps
LeRobot 0.5.1 (released 7 April 2026)--batch_size, default 8per processbatch_size x num_processes
Isaac-GR00T, n1.7-release (18 April 2026)--global-batch-size, default 64across all GPUs togetherglobal_batch_size x gradient_accumulation_steps
Isaac-GR00T, n1.5-release (15 December 2025)--batch-size, default 32per devicebatch_size x num_gpus
openpi, JAX pathfsdp_devices in the training configa sharding degree, not a batchunchanged; only the memory is split

The GR00T N1.7 line is the one that surprises people. In gr00t/experiment/experiment.py the per-device batch is literally global_batch_size // num_gpus, and a startup check in warn_configs asserts that the division is exact. Going from one H100 to eight does not train on more data per step. It trains the same 64 samples with 8 on each card, a throughput change and nothing else. To get the larger batch too, raise --global-batch-size yourself, which is what NVIDIA's hardware guide does when it pairs 4 to 8 cards with a global batch of 64 to 640.

text
LeRobot (main, Aug 2026)
  effective_batch = batch_size x dp_world_size x gradient_accumulation_steps
  # --batch_size=8, 4 workers, --accelerator.gradient_accumulation.steps=2  ->  64

Isaac-GR00T N1.7
  per_device_train_batch_size = global_batch_size // num_gpus     # experiment.py
  accumulated_batch_size      = global_batch_size x gradient_accumulation_steps
  # --global-batch-size 64 --num-gpus 8  ->  8 per card, 64 per update

Hugging Face Trainer (what GR00T sits on)
  effective_batch = per_device_train_batch_size x num_devices x gradient_accumulation_steps

openpi (JAX)
  --fsdp-devices N  shards parameters over N devices; the batch you configured stays the batch
The three formulas, from the upstream docs and source. Write yours down before you launch.
GR00T N1.7 will refuse to start if the two numbers disagree

In N1.7 you launch torchrun yourself and pass --num-gpus, and the code cross-checks them: if WORLD_SIZE is set and does not equal --num-gpus, it raises. The comment in experiment.py explains why the guard exists: num_gpus drives the per-device batch and the DeepSpeed gating, while the Hugging Face Trainer and the dataset sharding use the real WORLD_SIZE, so an 8-rank launch declared as num_gpus=1 would train at 8x the intended batch without a word of warning. There is a second assertion next to it: global_batch_size must be divisible by num_gpus. A global batch of 64 works on 8 cards and fails on 6.

When a second GPU actually helps

There are three separate reasons to add a card, and only one of them is speed. Being clear about which one you are chasing decides whether you want plain data parallelism, sharding, or neither.

Adding GPUs to a VLA fine-tune
What it genuinely buys
  • Memory, when the model does not fit. openpi puts a full Pi0.5 fine-tune above 70 GB and its LoRA path above 22.5 GB. Sharding with --fsdp-devices in openpi, or --parallelism.dp_shard in current LeRobot, spreads parameters, gradients and optimizer state across cards that could not hold the model alone.
  • Headroom for the expensive switches. GR00T's default fine-tune touches only the projector and the diffusion action head, under about 35 GB per GPU. NVIDIA's guide says --tune-llm or --tune-visual needs 80 GB or more per GPU.
  • Wall clock, once each card has enough work. NVIDIA's recommended setup is 4 to 8 H100 or L40 cards at a global batch of 64 to 640, the regime where collectives are small relative to compute.
  • A large real batch without gradient accumulation, which matters when accumulation is not actually wired into your trainer.
What it does not buy
  • Data efficiency. Goyal et al. matched ImageNet accuracy at batch 8192 with a linear learning-rate rule and a warmup. Nobody has shown that transfers to 50 demonstrations of one task.
  • A shorter queue. Multi-card nodes are scarcer and pricier per hour on spot markets, and a run that waits an hour for a node gave back the hour it saved.
  • Reproducibility. GR00T's fine-tune CLI has no seed field at all, and its trainer warns when it reseeds the sharded dataset on resume: this will make the experiment non-reproducible. LeRobot's default seed is 1000 and is a real flag.
  • Anything for a small policy. ACT at roughly 80 M parameters trained in about 5 hours on one 11 GB RTX 2080 Ti in the original paper. Distributing it is setup cost with no payoff.

Mapped onto the five policies you can train for an SO-100 class arm, the picture is lopsided. Only the ~3 B models sit in a regime where distribution changes the answer, and even there the first question is memory, not speed. The policy comparison page has the params, GPU tier and inference latency for each. The column that matters here is the middle one.

PolicyParamsSingle-card tierWhat a second GPU realistically changes
ACT~80 MRTX 4090 or any 24 GB cardNothing worth the setup. The original paper trained it in about 5 hours on one 11 GB card.
SmolVLA~450 MRTX 4090 or any 24 GB cardOnly if you want a big batch. The paper used 4 GPUs for a global batch of 256 in pretraining and says outright the model can easily be trained on a single GPU.
GR00T N1.5~3 BA100 80 GB or H100 80 GBThroughput, once the per-card batch is large enough to keep the card busy. Old flag semantics: --batch-size is per device.
GR00T N1.7~3 BA100 80 GB or H100 80 GBThroughput, plus the headroom to enable --tune-llm or --tune-visual. --global-batch-size stays fixed as you add cards.
Pi0.5~3 BA100 80 GB or H100 80 GBMemory first. openpi's full fine-tune floor is above 70 GB; fsdp_devices trades speed for fit.
The AY-Robots cost table showing which GPU each policy needs, typical run time and price, and how many episodes are needed before a policy is useful
The /try cost table. The GPU tier column is the one that decides whether multi-GPU is even a question: the 24 GB tier models finish on one card, the 80 GB tier models are where sharding starts to matter.

The learning rate and the step count, which nothing scales for you

Both trainers leave this to you, and LeRobot puts it in bold: it does not auto-scale the learning rate or the number of steps when the effective batch grows, and suggests that with 2 GPUs you either double --optimizer.lr or halve --steps. That is the linear scaling rule from Goyal et al.: multiply the learning rate by the same factor as the minibatch, and add a warmup so the first few hundred training steps do not blow up.

  • If the effective batch grew by k, the linear rule says multiply the learning rate by k and keep a warmup. GR00T's warmup_ratio default is 0.05 with a cosine scheduler.
  • If you would rather not touch the learning rate, divide the step count by k instead. Same samples seen, same schedule shape, fewer updates.
  • Do not do both. Doubling the learning rate and halving the steps is a different run than either one, and it is the version people accidentally ship.
  • Check the noise scale argument before scaling far. McCandlish et al. show that past a critical batch size, predicted by the gradient noise scale, extra batch buys wall clock and stops buying data efficiency. Its measured domains are MNIST, SVHN, CIFAR-10, ImageNet, Billion Word, Atari, Dota and autoencoders, none of them robot imitation, so on 50 episodes treat the threshold as unmeasured rather than known.

The strongest evidence that bigger is not automatically better comes from NVIDIA's own numbers. Table 6 of the GR00T N1 paper lists pretraining and post-training side by side: the batch drops by more than an order of magnitude while the learning rate does not move at all. The stated reason is one line, smaller batches in post-training to avoid overfitting when fine-tuning in data-limited settings.

HyperparameterGR00T N1 pretrainingGR00T N1 post-training
Batch size16,384128 or 1024
Gradient steps200,00020,000 to 60,000
Learning rate1e-41e-4
Optimizer / schedulerAdamW, cosineAdamW, cosine
Warmup ratio0.050.05
Weight decay1e-51e-5
Backbone vision encoderunfrozenunfrozen
Backbone text tokenizerfrozenfrozen
Pretraining scale is not a target you should aim at

The same paper reports up to 1024 H100 GPUs for a single model, and roughly 50,000 H100 GPU hours for GR00T-N1-2B pretraining. That is what produced the base checkpoint you are about to fine-tune. Your job on 50 episodes is the right-hand column above, not the left.

The manual path, with the commands that work today

Both sequences were read out of the upstream repositories in August 2026. Pin your version before copying anything: the flag names below are younger than most blog posts about them.

LeRobot: torchrun or accelerate launch, then DDP or FSDP

  1. 1
    Install the training extra

    accelerate ships inside LeRobot's training extra. It is used as a plain launcher only; every distributed setting lives in LeRobot's own config system.

    bash
    pip install 'lerobot[training]'
  2. 2
    Launch plain DDP across two GPUs

    With no --parallelism.* flags, a multi-process launch is plain DDP: the full model is replicated on every card. --batch_size stays per worker, so this run sees 16 samples per step, not 8.

    bash
    torchrun --nproc-per-node=2 $(which lerobot-train) \
      --dataset.repo_id=${HF_USER}/my_dataset \
      --policy.type=act \
      --policy.repo_id=${HF_USER}/my_trained_policy \
      --batch_size=8 \
      --output_dir=outputs/train/act_multi_gpu \
      --job_name=act_multi_gpu \
      --wandb.enable=true
  3. 3
    Compensate for the batch you just doubled

    Pick one of the two adjustments, not both. Doubling the learning rate keeps the update size comparable; halving the steps keeps the sample budget comparable.

    bash
    # option A: linear learning-rate scaling
    torchrun --nproc-per-node=2 $(which lerobot-train) --optimizer.lr=2e-4 ...
    
    # option B: same learning rate, half the steps
    torchrun --nproc-per-node=2 $(which lerobot-train) --batch_size=8 --steps=50000 ...
  4. 4
    Shard instead of replicate when the model will not fit

    FSDP2 shards parameters, gradients and optimizer state. --parallelism.dp_shard=-1 shards over however many processes the launcher started. Mixed precision defaults to no, so ask for bf16 explicitly or you will train the shards in fp32.

    bash
    torchrun --nproc-per-node=4 $(which lerobot-train) \
      --dataset.repo_id=${HF_USER}/my_dataset \
      --policy.type=<your_policy> \
      --parallelism.dp_shard=4 \
      --accelerator.mixed_precision=bf16 \
      --output_dir=outputs/train/my_policy_fsdp
  5. 5
    Give FSDP a wrap unit if the policy does not declare one

    Policies declare their wrap units on the class: ACT declares ["ACTEncoderLayer", "ACTDecoderLayer"]. If a policy declares nothing and you pass nothing, the run fails at startup rather than wrapping only the root module and giving up every byte of the saving.

    bash
    --accelerator.fsdp.wrap_modules='["MyTransformerBlock"]'   # explicit class names
    --accelerator.fsdp.min_num_params=1000000                  # or wrap anything above 1M params

Isaac-GR00T N1.7: you drive torchrun, and you tell it the count twice

  1. 1
    Clone with submodules and set up the environment

    GR00T pins dependencies through uv and needs git-lfs for the demo parquet files. The tested dGPU stack is Python 3.12, CUDA 12.6+, PyTorch 2.7+.

    bash
    sudo apt install git-lfs && git lfs install
    git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T
    cd Isaac-GR00T
  2. 2
    Run the single-GPU baseline first

    Get a working run on one card first. It is the reference the multi-GPU run has to match, and the run whose loss curve tells you whether the dataset is the problem.

    bash
    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
  3. 3
    Scale out with torchrun, keeping the two counts equal

    --nproc_per_node and --num-gpus must match or the run raises before loading a frame. And --global-batch-size 32 here means 4 samples per card on 8 GPUs, not 32.

    bash
    uv run torchrun --nproc_per_node=8 --master_port=29500 \
        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 8 \
        --output-dir /tmp/test_finetune_8gpu \
        --max-steps 2000 \
        --global-batch-size 32 \
        --dataloader-num-workers 4
  4. 4
    Use accumulation to buy batch you do not have cards for

    NVIDIA's hardware guide gives the arithmetic directly: 4 GPUs, 8 accumulation steps and a per-GPU batch of 8 gives an effective global batch of 256. In GR00T that is --global-batch-size 32 --gradient-accumulation-steps 8, and the trainer logs the resulting accumulated batch at startup.

    bash
    uv run torchrun --nproc_per_node=4 gr00t/experiment/launch_finetune.py \
        --num-gpus 4 \
        --global-batch-size 32 \
        --gradient-accumulation-steps 8 \
        --max-steps 10000 \
        --save-steps 1000
The numbered step list on the AY-Robots GR00T N1.7 on SO-100 training guide, showing each stage of a run
The GR00T N1.7 on SO-100 guide walks the same run as a step list. The manual commands above are what a single-card version of that guide expands to.

Two routes to the same checkpoint

You rent or own a multi-GPU node, install the trainer, convert the dataset to the format that trainer wants, and drive torchrun. This is the only path if you want FSDP, more than one node, or a global batch above what one card holds.

  1. Provision a node with 2 to 8 cards of 40 GB or more and confirm the interconnect. NVLink and PCIe behave very differently under all-reduce.
  2. Install the trainer at a pinned version. LeRobot 0.6.1 was released 3 August 2026; the GR00T n1.7-release tag is dated 18 April 2026.
  3. Convert your LeRobot dataset to the version the trainer accepts. GR00T needs v2.0 or v2.1; a v3.0 dataset crashes its loader and has to be converted down.
  4. Run single-GPU first and record the loss curve. Without it you cannot tell a distributed bug from a data bug.
  5. Scale out, then fix the batch semantics: check whether the flag is per worker or global, and adjust learning rate or steps once.
  6. Watch the first hundred steps. If throughput per card fell, you are dataloader bound.
Budget the setup honestly

The commands are short. The time goes into CUDA and NCCL version matching, the dataset conversion, and the first run that hangs for ten minutes before telling you nothing. Reserve an afternoon for the first distributed run on any new node; later ones are cheap.

The distributed failures that cost a day

Single-GPU training fails loudly. Distributed training fails quietly, or ten minutes later in a stack trace that names a collective and nothing about your code. These are the ones that show up on VLA fine-tunes, with what each actually is.

SymptomWhat it really isWhat to do
ValueError at startup about num_gpus not matching WORLD_SIZEGR00T N1.7 cross-checks the launcher world size against --num-gpus before loading anythingSet --num-gpus to the same value as --nproc_per_node. The guard exists because a mismatch silently rescales the effective batch.
AssertionError: global_batch_size must be divisible by num_gpusGR00T computes the per-device batch with integer divisionChoose a global batch that divides evenly by the card count. 64 works on 8 cards, fails on 6.
Run starts, then hangs and dies after exactly ten minutesNCCL's default collective timeout is 10 minutes; other backends default to 30One rank took a different code path and never entered the collective. Look for rank-conditional logic around logging, evaluation and checkpoint saving.
Startup fails complaining about FSDP wrap unitsLeRobot refuses to wrap only the root module rather than silently forfeiting the memory savingPass --accelerator.fsdp.wrap_modules with the block class name, or --accelerator.fsdp.min_num_params.
Throughput per card drops as you add cardsThe dataloader, not the GPU, is the bottleneck. GR00T defaults to 2 workers, LeRobot to 4Raise --dataloader-num-workers or --num_workers and the per-card batch before adding more hardware.
Two GPUs requested, four processes appearThe n1.5 script re-launched itself under torchrun unless IS_TORCHRUN=1 was set, so wrapping it in your own torchrun nested the launchersOn n1.5 call the script directly with --num-gpus N. On n1.7 you always drive torchrun yourself.
Checkpoint from a sharded run will not load with from_pretrainedLeRobot can write DCP shards instead of a single safetensors fileKeep --checkpoint_format=safetensors, or run lerobot-convert-dcp offline to merge the shards.
Out of memory on card 0 onlyRank 0 carries the extra load of logging, gathered checkpoint writes and often the evaluation batchSee the out-of-memory failure page; reduce the per-device batch or move the gather off the critical path.
The trap: the word step means two different things

LeRobot's current documentation states it plainly: --steps counts loop steps, that is micro-batches per worker, not optimizer updates. GR00T sits on the Hugging Face Trainer, whose global_step increments immediately after optimizer.step(), so --max-steps there counts optimizer updates. Put gradient accumulation on top: at accumulation 16, --max-steps 2000 in GR00T is 2000 updates over 32,000 micro-batches, while the same number in LeRobot main is 2000 micro-batches and 125 updates. Halve the step count on top of that because you added a GPU, and 1000 loop steps at accumulation 16 is 62 optimizer updates where you thought you were asking for 2000, then spend a day wondering why the loss plateaued high. Read the startup banner: LeRobot prints the batch factorization, and GR00T logs the accumulated batch size when accumulation is above 1.

bash
# Ask NCCL what it is doing. INFO is usually enough; TRACE is very loud.
export NCCL_DEBUG=INFO

# Two GPUs on a host where peer-to-peer is broken or virtualised badly:
# force the fallback path and see whether the hang disappears.
export NCCL_P2P_DISABLE=1

# Multi-node on a box with several NICs: pin the interface NCCL is allowed to use.
export NCCL_SOCKET_IFNAME=eth0

# No InfiniBand, or an IB stack that is present but not usable:
export NCCL_IB_DISABLE=1

# Then reproduce on the smallest possible run.
torchrun --nproc-per-node=2 $(which lerobot-train) --steps=50 --batch_size=2 ...
First moves when a distributed run hangs rather than crashing.

Two rules make these cheap. First, never debug a distributed run at full scale: reproduce with 50 steps and a batch of 2, because a hang at step 50 costs a minute and a hang at step 5000 costs an hour. Second, if the single-GPU run of the same config was never green, stop; you are debugging your dataset through a distributed launcher, the worst available microscope. The failure-mode index has the single-GPU versions of most of these, including a job that never leaves the queue and a loss that falls while the policy does nothing.

What moved upstream, and when

This is the part that dates fastest, so here is the state as of August 2026 with the release each change landed in. If a tutorial you are following uses a name from the left-hand column, it predates the current code.

What changedBeforeNowDated by
GR00T fine-tune entry pointscripts/gr00t_finetune.pygr00t/experiment/launch_finetune.pyn1.5-release 15 Dec 2025 to n1.7-release 18 Apr 2026
GR00T batch flag--batch-size, default 32, per device--global-batch-size, default 64, across all GPUssame window
GR00T launcher behaviourthe script re-executed itself under torchrun when num_gpus > 1you run torchrun; --num-gpus must equal WORLD_SIZEsame window
GR00T multi-GPU backendplain DDP: the n1.5 script passed an empty DeepSpeed configDeepSpeed ZeRO-2 by default when num_gpus > 1 and use_ddp is falsegr00t/configs/training/training_config.py on main
LeRobot shardingDDP through accelerate onlyFSDP2 and HSDP via --parallelism.dp_shard and --parallelism.dp_replicateafter 0.5.1, released 7 Apr 2026
LeRobot accelerate YAML configaccelerate config was the documented optionrefused at startup unless LEROBOT_ALLOW_ACCELERATE_ENV=1after 0.5.1
LeRobot FSDP checkpointsa gathered full optimizer stateDCP shards; 0.6.x and earlier FSDP checkpoints cannot be resumeddocumented on main; 0.6.1 released 3 Aug 2026

The LeRobot YAML change breaks a habit. Older guides tell you to run accelerate config and answer the interactive questions. Current LeRobot refuses to start when accelerate environment variables are set, for a good reason: those variables configure the engine behind LeRobot's back, so the train_config.json saved next to your checkpoint would no longer describe the run that produced it. Put the settings in --parallelism.* and --accelerator.* flags instead.

Where more GPUs stop helping, including here

AY-Robots does not do multi-GPU training. The form picks model, dataset and hyperparameters, and the backend rents one card sized by the VRAM that model needs. If your plan is a 640-sample global batch across eight H100s, this is not the tool; rent a node and use the commands above. What the platform removes is the part that eats the day: environment setup, dataset format conversion, and checkpoint plumbing.

  • For ACT and SmolVLA on 30 to 50 episodes, one 24 GB card finishes in 2 to 5 hours for 1 to 3 USD. There is no wall clock problem to solve.
  • For GR00T N1.7 and Pi0.5, one 80 GB card finishes in 3 to 6 hours for 4 to 12 USD. Four cards buy back hours you were not going to spend watching it anyway.
  • Multi-GPU pays when the model will not fit on the largest card you can get, when you run the same fine-tune dozens of times a week, or when you are pretraining rather than post-training. None of those describe a first SO-100 policy.
  • What most often decides whether the policy works is upstream of all this: how many clean episodes you recorded and whether the cameras stayed consistent. That is covered in the data collection write-up.

One more limit, usually discovered after the training question is settled: distribution changes nothing about deployment. A trained vision-language-action model still runs one action step at a time, and on this platform that is 20 ms for ACT up to 485 ms for Pi0.5. Inference has to sit next to the servos for fast tasks; public-internet round trips turn a working policy into a hesitant one. Remote inference is viable for slow pick-and-place, not for fast reactive motion. For deployment numbers before you pick a model, the GR00T N1.7 against Pi0.5 comparison and the model arena with its 85 models and 332 benchmark results are the places to start. the VLA overview covers what these models do in the first place.

Train a policy on your own arm without building a cluster

Pick the model and the arm, and the guide gives you the dataset format, the defaults the trainer really sends, and what the run costs. One rented card per run, checkpoints written to storage, no NCCL debugging.

Open the training guides

Questions people actually ask

Does adding a second GPU halve my fine-tune time?

Only if each card stays busy, and only where the batch flag is per worker. In LeRobot, two workers at --batch_size=8 process 16 samples per step: the wall clock per epoch roughly halves, but the effective batch doubled and you now have a learning-rate decision. In Isaac-GR00T N1.7, --global-batch-size is divided across the cards, so eight GPUs at --global-batch-size 32 put four samples on each; the step gets faster, the batch does not change. And if the dataloader cannot feed the cards, nothing gets faster at all. GR00T defaults to two dataloader workers and LeRobot to four, which is often the real ceiling.

Should I double the learning rate when I double the GPUs?

If the effective batch doubled, the linear scaling rule from Goyal et al. says yes, with a warmup so the early steps do not diverge. LeRobot's documentation suggests exactly that, or halving --steps instead. Pick one, not both. Note the counter-evidence for small datasets: NVIDIA's GR00T N1 paper keeps the learning rate at 1e-4 while dropping the batch from 16,384 in pretraining to 128 or 1024 in post-training, to avoid overfitting in data-limited settings. On 50 episodes you are in that regime.

Can I run multi-GPU training on AY-Robots?

No. Every run here is sized to a single rented GPU by the VRAM the model needs: the 80 GB tier for GR00T N1.5, GR00T N1.7 and Pi0.5, a 24 GB card for SmolVLA and ACT. The training form exposes batch size, learning rate, max steps, gradient accumulation and a few per-model extras, and no GPU count. For a 30 to 50 episode dataset that is the right shape and the cheaper one: roughly 1 to 3 USD per run on the 24 GB tier, 4 to 12 USD on the 80 GB tier. If you need sharding across cards, rent a node and use the upstream commands above.

FSDP or DDP for a 3B VLA?

DDP if the model fits, FSDP if it does not. DDP replicates the whole model on every card and communicates only gradients: simpler and faster per step. FSDP shards parameters, gradients and optimizer state, which is what lets a model train on cards that could not hold it, at the cost of gathering each unit during forward and backward. openpi describes fsdp_devices as reducing memory in exchange for slower training. In current LeRobot the switch is --parallelism.dp_shard, and you must give FSDP a wrap unit unless the policy class declares one.

My GR00T run refuses to start with a divisibility error. What is wrong?

N1.7 computes the per-device batch as global_batch_size divided by num_gpus with integer division, and asserts the division is exact before loading anything. A global batch of 64 works on 1, 2, 4 or 8 cards and fails on 3, 5 or 6. A second check sits next to it: if the launcher set WORLD_SIZE and it does not equal --num-gpus, the run raises, because that mismatch would silently rescale the effective batch.

My distributed run hangs and then dies after ten minutes. Where do I look?

Ten minutes is NCCL's default collective timeout in PyTorch; other backends default to thirty. The pattern almost always means one rank entered a collective the others did not, so start with anything that runs on one rank only: logging, evaluation, checkpoint saving, Hub uploads. Set NCCL_DEBUG=INFO and reproduce with a 50-step run at batch 2, not at full scale. On a two-GPU box where peer-to-peer is broken or badly virtualised, NCCL_P2P_DISABLE=1 is the quickest way to confirm it.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started