Skip to content

Support callable entropy schedules in PPO - #660

Open
PhysicistJohn wants to merge 3 commits into
google:mainfrom
PhysicistJohn:feature/ppo-entropy-annealing
Open

PhysicistJohn wants to merge 3 commits into
google:mainfrom
PhysicistJohn:feature/ppo-entropy-annealing

Conversation

@PhysicistJohn

@PhysicistJohn PhysicistJohn commented Feb 26, 2026

Copy link
Copy Markdown

Summary

PPO currently accepts a fixed entropy coefficient. Allow entropy_cost to also accept an Optax-compatible callable, supporting linear, cosine, warmup, piecewise, and custom schedules without new trainer flags. The scalar default and existing positional arguments are unchanged.

For example:

entropy_cost = optax.cosine_decay_schedule(1e-2, decay_steps=1_000_000)

Pass this as entropy_cost to ppo.train. The schedule receives the total environment-step count, including action_repeat, once per rollout batch. All gradient updates on that batch share the coefficient. Counts start at zero for each train call, including parameter restoration. training/entropy_cost reports the mean applied coefficient.

Testing

  • 60 tests pass across PPO training, losses, checkpointing, and UInt64 types on CPU with Python 3.12 and JAX 0.11.1. This includes 11 new tests for schedule values, UInt64 rollover, scalar equivalence, float64 precision, and the environment-step clock.
  • Four tiny PPO runs match upstream bb77b328 exactly for default and explicit scalar coefficients with JAX x64 disabled and enabled, including parameters and common non-timing metrics.

Adds two new parameters to `ppo.train()`:

    entropy_cost_end: Optional[float] = None
    entropy_schedule: str = 'linear'   # or 'cosine'

When `entropy_cost_end` is set, the entropy coefficient decays from
`entropy_cost` (start) to `entropy_cost_end` over the full training
budget, following the selected schedule.  Default is unchanged —
`entropy_cost_end=None` keeps the existing constant-cost behaviour.

The schedule is computed inside the existing JIT graph using
`training_state.env_steps`, so there is no per-epoch recompilation.

The `if/else` on `entropy_schedule` is a Python-level (static) branch,
so JAX traces only the selected path.  `entropy_cost` is now threaded
as a keyword argument through `training_step → sgd_step →
minibatch_step → loss_and_pgrad_fn`, which is already a pure
`*args, **kwargs` pass-through, so no signature conflicts arise.

The current coefficient is logged to TensorBoard as `training/entropy_cost`.

Motivation: a fixed high entropy cost promotes exploration early in
training but prevents the policy from committing to precise actions
later.  Annealing entropy over the run allows warm exploration followed
by policy consolidation without requiring separate training phases.
@google-cla

google-cla Bot commented Feb 26, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@PhysicistJohn
PhysicistJohn marked this pull request as ready for review February 26, 2026 23:12
@aaprasad

Copy link
Copy Markdown

@PhysicistJohn and brax team, whats the status of this PR, would be a really useful feature!

@PhysicistJohn

Copy link
Copy Markdown
Author

Hey, thanks for the ping. CLA is signed and passing now, so that part's
done. It's stuck behind Google's internal import/copybara review (the
"waiting for internal safe review approval" check above), that's on
their side, not something either of us can push from outside. No ETA
I can give you honestly. Will post here if I hear anything.

@PhysicistJohn PhysicistJohn changed the title ppo: add entropy cost annealing (linear + cosine schedules) Support callable entropy schedules in PPO Sep 9, 2026
@PhysicistJohn

Copy link
Copy Markdown
Author

Updated this to accept an Optax-compatible callable through the existing entropy_cost argument, replacing the separate end-value and schedule-name flags. This supports cosine, linear, warmup, piecewise, and custom schedules while preserving existing scalar callers. I also merged current main. All 60 tests across PPO training, losses, checkpointing, and UInt64 types pass locally on CPU, including 11 new schedule regressions. Four tiny scalar-training comparisons match upstream exactly with JAX x64 disabled and enabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants