Humanoid training - #51
Open
vmoens wants to merge 58 commits into
Open
Conversation
vmoens
commented
Mar 20, 2026
Owner
- Add Humanoid zoo env and direct backprop training script
- Handle video encoding failure gracefully in direct backprop training
- Add --compile flag for torch.compile on physics step
- Compile vmapped step with fullgraph=True, add fwd/bwd timing
- Add --compile_mode flag to test compile(vmap) vs vmap(compile)
Add HumanoidEnv to the zoo (Humanoid-v4 semantics: forward velocity + healthy reward + ctrl cost). Add train_direct_humanoid.py that optimizes a policy by backpropagating through the differentiable mujoco-torch simulation using differentiable_mode(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap wandb.Video in try/except so missing moviepy doesn't crash training. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- torch.compile the vmap(step) not the inner step - Use fullgraph=True to verify full graph capture - Log forward/backward times separately to wandb Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the boolean --compile flag with --compile_mode choosing between compile(vmap(fn)), vmap(compile(fn)), or no compile. Keeps --compile as a deprecated alias. Forward/backward timing already present from prior commit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SmoothHumanoidEnv subclass with sigmoid-based reward and no hard termination for gradient-friendly differentiable training - Uses env.step() inside differentiable_mode() instead of manual vmap/step loop — TorchRL env handles batching internally - BatchNorm on observations for implicit exploration noise - Default batch size 1024, horizon 15 (short horizon to limit gradient explosion through contacts) - Gradient accuracy test suite using torch.autograd.gradcheck: single step, multi-step, smooth reward, and full env step verified against finite differences in float64 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3 consecutive physics steps accumulate small floating-point differences between analytical and finite-difference gradients. Widen eps/atol/rtol to account for this while still catching real gradient bugs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New --env flag selects the environment (halfcheetah, humanoid, ant, etc.). DifferentiableEnv wrapper automatically adds fixed_iterations=True and smooth reward overrides for envs with hard thresholds. HalfCheetah reward is already smooth so no override needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HalfCheetah and Ant produce NaN with fixed_iterations=True. This flag lets us try differentiable mode with the default variable-iteration solver to see if useful gradients still flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When gradients explode (norms >10^10), standard clipping effectively zeroes the gradient direction. Normalization preserves the direction while controlling the step size. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standard RL baselines for mujoco-torch zoo environments. Both use TorchRL losses, collectors, transforms, and replay buffers. Large batch sizes with vmap-batched envs for fast training. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
loss_module.actor_network_params.values() returns TensorDicts. Need values(True, True) to get individual parameter tensors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SACLoss computes all losses in a single forward pass with shared computation graphs. Sequential backward calls on individual losses fail because earlier steps modify tensors needed by later losses. Adding retain_graph=True on qvalue and actor backward passes fixes this. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous approach of sequential backward+step caused inplace modification errors because critic_optim.step() modified Q-network weights before actor loss could backpropagate through them. Now all three losses are summed and a single backward() is called before any optimizer steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SACLoss in TorchRL v0.11 doesn't expose .alpha directly. Compute it from log_alpha instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The standard humanoid env (53-dim obs: qpos+qvel) lacks critical proprioceptive data for learning locomotion. HumanoidRichEnv adds cinert (body inertia), cvel (COM velocity), and qfrc_actuator (actuator forces), matching Gymnasium Humanoid-v5's observation space. New files: - mujoco_torch/zoo/humanoid_rich.py: registered as "humanoid_rich" - examples/train_direct_humanoid_rich.py: direct backprop variant - run_humanoid_rich.sh: deploys PPO + SAC + direct on 3 GPUs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8 tests comparing dynamics, rewards, action effects, and zoo env behavior between mujoco-torch and reference MuJoCo C backend. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mujoco-torch includes non-penetrating contacts in the constraint set that MuJoCo C filters out, causing divergent humanoid dynamics with ctrl and blocking locomotion learning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bracket swap conditions were incorrect, causing the humanoid solver to diverge from MuJoCo C with default iterations=1. Three fixes: 1. Replace ad-hoc swap conditions with in_bracket (matching C's updateBracket / MJX), which only accepts candidates that narrow toward zero derivative. 2. Add one-sided phase emulation: when both bracket endpoints have same-sign derivatives (not yet bracketed), also accept candidates closer to zero, matching C's one-sided Newton phase. 3. Add early convergence check after initial Newton step, and add cross-bracket candidate swaps (6 candidates like MJX). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SHAC combines short-horizon differentiable rollouts with a learned value function (terminal bootstrap) and stochastic policy (entropy-regularised) for exploration, addressing the lack of exploration in direct backprop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The actor backward consumes the physics computation graph. The critic loss doesn't need physics gradients, so detach the rollout first. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forward velocity reward is now gated by the soft healthy factor, so the agent only gets locomotion reward while upright. Prevents reward hacking via falling forward (high velocity, short horizon) and flying exploits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Combined backward caused gradient interference between actor and critic, leading to Q-value divergence (8e+15). SAC requires separate backward passes. Also add grad clipping on actor parameters. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TorchRL SACLoss shares the computation graph between losses. Must backward all losses before stepping any optimizer, using retain_graph to keep the shared tensors alive. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…/SHAC - PPO/SAC: add --compile flag to torch.compile the physics step - PPO: scale up to 8K envs, 8M frames/batch, 2048 mini-batch - SAC: scale batch_size to 8K, total_frames to 50M - Direct/SHAC: enable --adaptive_integration for correct contact gradients Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The collector was only doing 1 step per iteration (256 frames), making compile useless and throughput bottlenecked by gradient updates. Now collects 8M frames per batch with 8K envs, matching the PPO setup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --train_device flag to both scripts. Collector uses storing_device to transfer data directly to the training GPU. This fixes PPO OOM (8K envs + 8M batch on single 140GB GPU) by splitting memory across two. GPU layout: PPO on 0+4, SAC on 1+5, Direct on 2, SHAC on 3. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SAC now detects extreme rewards (|reward| > 1e6) and saves the 10-step trajectory slice leading to the blow-up as a .pt file for investigation. Bug report documents the Cholesky failure and degenerate state issue that occurs with 2048+ parallel humanoid envs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single physics step causes cfrc_ext (contact forces) to spike to 1450 from normal ~50-120. Obs indices 256-269 correspond to cfrc_ext in the rich observation. No NaN/Inf, just extreme finite values from solver. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The benchmark shows 200K+ steps/sec at B=2048 but SAC was getting 4K. Root cause: compile wrapped the inner step_fn, then vmap() was called on it every iteration. This prevented compile from fusing the batched kernels. Now we pre-vmap the function and compile the vmapped version, matching the benchmark's approach. Also caches the vmapped function instead of recreating it each call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Frame skip loop (5 iterations) is now inside the compiled function, so torch.compile can fuse all 5 physics steps into one kernel launch - Use update_(ctrl=ctrl) instead of replace(ctrl=ctrl) to avoid shallow-copying the 80-field MjData dict every step - Cache vmap function instead of recreating per call Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds fast-path flags (_trust_step_output, _skip_maybe_reset, update_traj_ids=False) and compares standard vs fast collector throughput. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MujocoTorchEnv handles resets in _reset() via the collector's auto-reset, not inside _step(). _skip_maybe_reset requires self-resetting envs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Profiles 100 collector steps after warmup, exports Chrome trace + summary tables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
with_stack=True + group_by_stack_n=3 used 110GB RAM for summary tables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instruments: ENV._step, _physics_step, _build_obs, _compute_reward, _compute_terminated, _reset, EnvBase.step, step_and_maybe_reset, maybe_reset, _step_proc_data, TransformedEnv._step, each Transform, Actor.forward, and Collector methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace data-dependent tensor shape checks (d.efc_J.numel() == 0) with static Python int checks from constraint_sizes(m) so torch._dynamo traces once without placing guards on tensor shapes. Also ensure make_constraint always uses the static max nefc for the output tensor, matching the MJX approach of fixed-size constraint arrays. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Now that the recompilation fix (5c785f3) is in, we can profile with torch.compile enabled to see actual collector overhead when physics is fast (~16ms vs 739ms eager). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Python loop of n individual clone() + torch.stack with a single expand(n).clone() (one broadcast copy kernel). Use in-place add_ for noise. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When auto_reset=True, done envs are reset inside _step before building observations. This eliminates the separate maybe_reset → _reset path and allows the reset to be part of the same execution flow as physics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up the fused auto-reset and fast collector optimizations (trust_step_output, skip_maybe_reset, update_traj_ids=False). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compiling for 1 env triggers a separate JIT compilation that wastes minutes. Eval is infrequent and doesn't need these optimizations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
With _skip_maybe_reset=True, InitTracker never set is_init on episode boundaries, causing SACLoss value estimation to produce NaN. Instead, keep maybe_reset running but make _reset a no-op when auto_reset is enabled (since _step already handled the reset). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The obs returned by _step must match the state that produced the reward. Previously, we reset done envs before building obs, so the replay buffer stored (terminal_reward, reset_obs) — causing Q-value divergence → NaN. Now: build obs from terminal state first, then reset. maybe_reset calls _reset (no-op) which returns obs from the already-reset state for the next step's input. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The overridden _physics_step was a raw step() call without vmap, causing IndexError when num_envs > 1 (batch dim mistaken for joint dim). Match the base class pattern: vmap for multi-env, raw for single-env, with frame_skip loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Raise _INLINE_CHOLESKY_MAX_SIZE from 16 to 32 so humanoid (nv=28) uses the inline path instead of torch.linalg.cholesky - Add clamp_min(s, 1e-12) to inline diagonal pivots to prevent NaN from numerically non-SPD mass matrices (degenerate physics states) - Add eps*I regularisation to the fallback path (nv > 32) as safety net - All changes are torch.compile friendly (no control flow/error handling) Fixes Cholesky crash in SAC humanoid training when envs reach extreme joint configurations that make the mass matrix non-positive-definite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.