@@ -166,8 +166,8 @@ vendored modeling code under `openvla_oft/` comes from
166166[ SimpleVLA-RL] ( https://github.com/PRIME-RL/SimpleVLA-RL ) (MIT).
167167
168168Important compatibility note: the official continuous-head OpenVLA-OFT
169- checkpoints are not interchangeable with this token-head variant. Use the
170- SimpleVLA-RL SFT checkpoints, for example ` Haozhan72/* ` :
169+ checkpoints are not interchangeable with this token-head variant for GRPO
170+ training. Use the SimpleVLA-RL SFT checkpoints, for example ` Haozhan72/* ` :
171171
172172``` python
173173from openvla import OpenVLAOFTWrapper
@@ -196,38 +196,58 @@ pytest sota-implementations/vla_grpo/test_openvla.py
196196## What gets logged
197197
198198With a logger configured (` logger.backend=wandb ` , the default), each iteration
199- logs reward curves (` train/reward_mean ` , ` train/reward_max ` ), success rate, and
200- throughput split into collection and optimization:
199+ logs the training success rate (` train/success_rate ` ), trajectory-return
200+ aggregates (` collector/trajectory_return_sum ` ,
201+ ` collector/trajectory_return_max ` ), and throughput split into collection and
202+ optimization:
201203
202204- ` throughput/inference_env_steps_per_s `
203205- ` throughput/inference_decisions_per_s `
204206- ` throughput/train_decisions_per_s `
205207- ` throughput/optim_steps_per_s `
206208
207- Eval rollouts can also be rendered to video (` logger.record_video=true ` , on by
208- default). A dedicated single-environment recorder is built with
209- ` from_pixels=True ` : ` ToyVLAEnv ` renders the tracking scene, while ` LiberoEnv `
210- exposes its camera. ` torchrl.record.VideoRecorder ` writes
211- ` logger.video_episodes ` greedy episodes to ` eval/video ` on every eval. wandb
212- video encoding needs ` moviepy ` from the ` dev ` dependency group. Disable videos
213- with ` logger.record_video=false ` .
209+ The collector path is fixed: a TorchRL ` MultiCollector ` launches rollout
210+ workers, each worker owns a sync ` ParallelEnv(envs_per_collector) ` , and all
211+ workers plus the evaluator share one process policy server.
212+
213+ ``` bash
214+ python sota-implementations/vla_grpo/vla-grpo.py --config-name vla_grpo_libero
215+ ```
216+
217+ Rollout clients request random sampling; eval and video clients request
218+ deterministic decoding from the same server, so rollout and eval are synced by
219+ one explicit TensorDict weight update after each optimizer step. The replay
220+ buffer is passed to the collector and receives complete trajectories as they
221+ finish; training waits until the consuming replay buffer has enough sampleable
222+ decisions.
214223
215- Checkpointing is shared by the toy and LIBERO configs. ` checkpoint_latest.pt `
216- is written to the hydra run directory every ` checkpoint.save_iter ` iterations;
224+ With the thread evaluator backend, eval rollouts are rendered to video whenever
225+ a logger is configured. A dedicated single-environment evaluator is built with
226+ ` from_pixels=True ` : ` ToyVLAEnv ` renders the tracking scene, while ` LiberoEnv `
227+ exposes its camera. ` torchrl.record.VideoRecorder ` writes the evaluator rollout
228+ to ` eval/video ` on every eval. wandb video encoding needs ` moviepy ` from the
229+ ` dev ` dependency group. Process-backend evaluator video dumping still needs a
230+ TorchRL-side remote ` VideoRecorder.dump ` path.
231+
232+ Checkpointing is shared by the toy and LIBERO configs. ` checkpoint_latest `
233+ is written as a TensorDict directory in the hydra run directory every
234+ ` checkpoint.save_iter ` iterations;
217235resume with:
218236
219237``` bash
220238python sota-implementations/vla_grpo/vla-grpo.py \
221- checkpoint.resume=/path/to/checkpoint_latest.pt
239+ checkpoint.resume=/path/to/checkpoint_latest
222240```
223241
224242## LIBERO configuration details
225243
226244The full LIBERO config follows the SimpleVLA-RL hyper-parameter shape:
227245
228246- groups of ` n=8 ` rollouts per initial state;
229- - 64 initial states per iteration, for 512 trajectories before dynamic
230- filtering;
247+ - 40 initial states per iteration (` collector.groups_per_iter ` ), for 320
248+ trajectories before dynamic filtering -- one aligned group wave across the
249+ 320 rollout envs; the paper uses 64 initial states (512 trajectories) per
250+ iteration, which is a known deviation of the shipped config;
231251- 512 base environment steps, or 64 chunk decisions, per episode;
232252- rollout temperature 1.6 and greedy evaluation;
233253- dynamic sampling bounds ` (0.1, 0.9) ` to drop groups that are all failure or
@@ -241,47 +261,53 @@ A sequence-level ratio remains available as a config switch for ablations, but
241261for a 56-token action chunk it saturates the clip range much more easily than
242262per-token ratios.
243263
244- LIBERO simulation runs in parallel worker processes (` env.num_envs ` , one MuJoCo
245- instance each), and policy inference batches across workers. Group accounting is
246- the main thing to keep in mind: GRPO needs repeated attempts from the same
247- initial state under the same policy. Each worker owns a disjoint ` group_id `
248- block so advantages never mix across unrelated groups.
249-
250- Because groups are repeated serially within each worker, ` env.num_envs ` should
251- not exceed ` collector.groups_per_iter ` ; otherwise many same-policy collection
252- polls are needed before each worker can finish all ` group_size ` rollouts for a
253- group and the replay buffer receives advantaged decisions. For best throughput,
254- set ` env.num_envs ` to a divisor of ` collector.groups_per_iter ` , often the same
255- value.
256-
257- When ` env.parallel_group_repeats=true ` , ` env.num_envs / collector.group_size `
258- logical workers each run one repeated-initial-state group in parallel. In this
259- mode, prefer setting ` collector.groups_per_iter ` to that logical worker count
260- so one target group wave is aligned. If ` collector.candidate_group_size ` is
261- larger than ` collector.group_size ` , each worker in a logical group repeats the
262- same initial state serially enough times to produce up to the requested
263- candidate count. For example, 8 parallel workers x 2 serial repeats gives at
264- most 16 candidates. Groups can be written earlier if the candidates already
265- contain a useful selected subset.
266-
267- The replay-buffer writer polls the collector at one outer step per worker, so
268- complete trajectories are handed to the replay buffer shortly after they finish
269- instead of waiting for a full max-length rollout from every worker.
264+ LIBERO simulation runs through ` collector.num_collectors ` MultiCollector
265+ workers. Each worker hosts a synchronous
266+ ` ParallelEnv(collector.envs_per_collector) ` . Policy inference runs on the
267+ shared process server and each worker owns a disjoint ` group_id ` block so
268+ advantages never mix across unrelated groups.
269+
270+ Without ` env.parallel_group_repeats ` , groups are repeated serially within each
271+ worker, so the total rollout worker count should not exceed
272+ ` collector.groups_per_iter ` . For that serial mode, set
273+ ` collector.num_collectors * collector.envs_per_collector ` to a divisor of
274+ ` collector.groups_per_iter ` , often the same value.
275+
276+ When ` env.parallel_group_repeats=true ` , the shared replay buffer centralizes
277+ ` MCAdvantage ` write state, so same-initial-state groups may straddle
278+ subcollectors. The logical worker count is the total rollout worker count
279+ divided by ` collector.group_size ` . In this mode, prefer setting
280+ ` collector.groups_per_iter ` to that logical worker count so one target group
281+ wave is aligned. If ` collector.candidate_group_size ` is larger than
282+ ` collector.group_size ` , each worker in a logical group repeats the same initial
283+ state serially enough times to produce up to the requested candidate count. For
284+ example, 8 parallel workers x 2 serial repeats gives at most 16 candidates.
285+ Groups can be written earlier if the candidates already contain a useful
286+ selected subset.
287+
288+ The training script starts the collector once, waits until the consuming replay
289+ buffer has enough sampleable decisions, pauses collection, runs the PPO update,
290+ clears incomplete same-policy advantage queues and partial trajectories, pushes
291+ the TensorDict policy weights to the shared policy server, and then lets the
292+ collector resume.
270293` MCAdvantage ` runs as the replay-buffer transform and keeps incomplete groups
271- queued across same-policy polls until all siblings arrive.
272- ` max_collect_batches_per_iter ` sets the safety cap in target group waves, and
273- ` collector.min_replay_decisions ` can require a minimum number of useful replay
274- decisions before the PPO update.
294+ queued only within a single policy window. ` collector.min_replay_decisions ` can
295+ require a minimum number of useful replay decisions before the PPO update.
296+ Set ` TORCHRL_MC_ADVANTAGE_LOCAL_QUEUES=1 ` to keep grouping state in each replay
297+ writer instead of a multiprocessing manager. At every policy boundary the
298+ trainer reads those worker-local counters while collection is paused, clears
299+ their queues, and resets in-flight collector trajectories before the policy
300+ version advances.
275301
276302Candidate selection is delegated to ` MCAdvantageSelector ` (` first ` , ` uniform ` ,
277303or ` balanced ` ), so the replay-buffer transform owns the sample-selection policy
278- while the collector only supplies same-policy completed trajectories. At the
279- policy-update boundary the replay buffer, incomplete advantage queues, and
280- in-flight collector trajectories are cleared before the next policy is rolled
281- out. LIBERO workers stamp parallel-repeat group ids from the cycled
282- initial-state id so fast and slow sibling workers can still complete a
283- same-initial-state GRPO group under the same policy even when their episode
284- lengths differ.
304+ while the collector only supplies same-policy completed trajectories. The
305+ consuming replay buffer removes sampled decisions after
306+ ` buffer.consume_after_n_samples ` samples, and the policy-boundary pause keeps
307+ rollout and optimization phases explicit. LIBERO workers stamp
308+ parallel-repeat group ids from the cycled initial-state id so fast and slow
309+ sibling workers can still complete a same-initial-state GRPO group under the
310+ same policy even when their episode lengths differ.
285311
286312Run the LIBERO recipe with:
287313
@@ -295,31 +321,82 @@ Requirements beyond the toy scale: LIBERO (see the `torchrl.envs.LiberoEnv`
295321docs for install notes), ` transformers ` , ` timm ` , ` Pillow ` , and ` peft ` when
296322` policy.lora_rank ` is set.
297323
324+ For reference-parity rollouts, set ` policy.image_backend=tensorflow ` . This uses
325+ the SimpleVLA JPEG, Lanczos resize, and center-crop order. Normalized
326+ vocabulary-tail action tokens are detokenized through the NumPy float64 CPU
327+ path before the gripper transform is applied once in the environment. Use
328+ ` env.train_init_state_mode=fixed env.train_init_state_id=<id> ` for a fixed
329+ LIBERO initial state. ` collector.policy_micro_batch_size ` only slices actual
330+ model calls inside the inference-server policy; it does not change PPO
331+ minibatching.
332+
298333## Hardware notes
299334
300- - The default configuration trains a LoRA adapter (` policy.lora_rank: 32 ` ) on a
301- single GPU while the simulation workers occupy CPU cores. Rollout wall-clock
302- dominates, so scale ` env.num_envs ` with the available cores first, while
303- keeping it within the GRPO grouping constraint above.
304- - Set ` collector.policy_device ` to a different CUDA device to keep rollout
305- inference on a separate policy replica. The training loop copies only the
306- trainable state dict after optimizer updates, so this split is intended for
307- LoRA/adapters rather than full-parameter fine-tuning.
335+ - The default H100 configuration trains a LoRA adapter
336+ (` policy.lora_rank: 32 ` ) on ` policy.device: cuda:0 ` and serves rollout plus
337+ evaluator inference from ` collector.policy_device: cuda:1 ` . Four
338+ collectors each run ` ParallelEnv(80) ` for 320 rollout envs total. On
339+ single-GPU runs, override ` policy.device=null ` ,
340+ ` collector.policy_device=null ` , ` collector.num_collectors=1 ` , and
341+ ` collector.envs_per_collector ` to the number of local envs.
342+ - Rollout wall-clock dominates, so scale ` collector.num_collectors ` and
343+ ` collector.envs_per_collector ` with the available CPU cores while keeping
344+ the GRPO grouping constraint above. The H100 default uses
345+ ` collector.num_collectors=4 ` , ` collector.envs_per_collector=80 ` ,
346+ ` collector.groups_per_iter=40 ` , and parallel group repeats enabled. The
347+ training loop pushes TensorDict policy weights to the shared policy server
348+ after optimizer updates.
308349- Headless LIBERO rendering uses MuJoCo/robosuite EGL by default
309350 (` env.render_backend: egl ` ). ` env.render_gpu_ids ` controls the EGL-visible
310- render device ids assigned to workers, round-robin. The default ` [0] ` works
311- on a single-GPU allocation; on a multi-GPU node, override it, for example
312- ` env.render_gpu_ids=[0,1,2,3] ` , to spread render workers across GPUs. These
313- ids are the devices visible to EGL inside the process/container and may not
314- match global CUDA ordinals.
315- - Set ` logger.eval_process=true ` to move greedy eval into a dedicated process.
316- Use ` logger.eval_device ` for its policy device and ` env.eval_render_gpu_ids `
317- for its EGL render workers; when the latter is left null, eval reuses
318- ` env.render_gpu_ids ` .
351+ render device ids assigned to rollout workers, round-robin. The H100 default
352+ spreads rollout rendering over ` [2,3,4,5] ` and reserves
353+ ` env.eval_render_gpu_ids=[7] ` for eval/video rendering. These ids are the
354+ devices visible to EGL inside the process/container and may not match global
355+ CUDA ordinals.
356+ - Use ` logger.eval_backend ` for the TorchRL evaluator backend. The evaluator
357+ shares the same policy server as rollout and uses ` env.eval_render_gpu_ids `
358+ for EGL rendering; when the latter is left null, eval reuses
359+ ` env.render_gpu_ids ` . The LIBERO default uses ` process ` to isolate simulator
360+ work; use ` thread ` only when local VideoRecorder dumping is required. Note
361+ the caveat: with a logger, the thread backend swaps the eval env for a
362+ single-env video recorder bound to the first task, so on a multi-task suite
363+ ` eval/success_rate ` covers one task instead of the whole suite (and
364+ ` env.eval_num_envs ` is ignored). This requires the explicit opt-in
365+ ` logger.record_video_single_task=true ` .
319366- Minimal CUDA containers often lack the NVIDIA EGL/GLVND userspace stack.
320367 Before debugging TorchRL, verify that ` libEGL_nvidia ` , ` libnvidia-eglcore ` ,
321368 ` libGLX_nvidia ` , and ` /usr/share/glvnd/egl_vendor.d/10_nvidia.json ` are
322369 visible in the runtime.
370+ - On an H200 container with the 595 driver, the userspace libraries can be
371+ extracted without installing Debian packages into the image:
372+
373+ ``` bash
374+ mkdir -p /opt/nvidia-595-deb/download /opt/nvidia-595-deb/extract
375+ cd /opt/nvidia-595-deb/download
376+ apt-get download \
377+ libnvidia-gl-595 libegl1 libglvnd0 libopengl0 libgl1 libgles2 libglx0
378+ for deb in ./* .deb; do
379+ dpkg-deb -x " $deb " /opt/nvidia-595-deb/extract
380+ done
381+
382+ export LIBDIR=/opt/nvidia-595-deb/extract/usr/lib/x86_64-linux-gnu
383+ export LD_LIBRARY_PATH=" $LIBDIR :${LD_LIBRARY_PATH:- } "
384+ export LD_PRELOAD=" $LIBDIR /libOpenGL.so.0${LD_PRELOAD: +: $LD_PRELOAD } "
385+ export __EGL_VENDOR_LIBRARY_FILENAMES=/opt/nvidia-595-deb/extract/usr/share/glvnd/egl_vendor.d/10_nvidia.json
386+ export MUJOCO_GL=egl PYOPENGL_PLATFORM=egl ROBOT_PLATFORM=LIBERO
387+
388+ python - << 'PY '
389+ from OpenGL import EGL, GL
390+ import mujoco
391+ from libero.libero import benchmark
392+
393+ assert EGL is not None and GL.glGetError is not None
394+ assert mujoco is not None and benchmark is not None
395+ PY
396+ ```
397+
398+ The validated parity runtime pins `mujoco==3.2.3`, `robosuite==1.4.1`,
399+ `transformers==4.40.1`, and `peft==0.11.1`.
323400- Full-parameter fine-tuning of the 7B model requires sharded training (FSDP)
324401 and a multi-GPU inference/training split with explicit weight
325402 synchronization. That topology should be sized on the target hardware:
0 commit comments