MBPO
BetaWorks, but under active development — APIs and results may shift. Learns, but still short of the reference on HalfCheetah — see the open gap below.
Model-Based Policy Optimization is SAC plus a learned dynamics model that manufactures extra training data.
from noeira.deep_agents.mbpo import MBPOAgentThe idea
Section titled “The idea”Train a probabilistic dynamics ensemble on real transitions. Then branch short imagined rollouts from real states, and train SAC on a mixture of real and synthetic data.
The gain is straightforward: each real transition seeds many synthetic ones, so the policy sees far more data than the environment provided.
Short rollouts are the entire trick. Model error compounds with every imagined step — a 5-step branch stays close enough to reality to help, while a 50-step branch trains the policy on fiction. MBPO’s contribution over earlier model-based methods is largely the insight that rollout length should be short and that branching from real states beats rolling from an initial state.
Both halves of that sentence do work, and the second is the one people skip.
Rolling from an initial state gives you a trajectory whose every state after
the first is imagined, so error accumulates over the full episode and the state
distribution drifts away from anything the model was trained on. Branching
re-anchors to ground truth at every branch point: the imagined states are never
more than k steps from a state the environment actually produced. Error is
bounded by k, not by episode length — which is why k can be as low as 1
and the method still pays, and it is k = 1 that the shipped HalfCheetah
configuration uses.
This is Dyna-Q with a neural dynamics model and a continuous-control learner.
The dynamics ensemble
Section titled “The dynamics ensemble”Each member predicts a distribution over the next state and reward, trained
with GaussianNLLLoss:
from noeira.nn.loss.gaussian_nll_loss import GaussianNLLLossTwo kinds of uncertainty come out of this, and both matter:
- Aleatoric — each member’s predicted variance, capturing genuine environment stochasticity
- Epistemic — disagreement between members, capturing where the model has not seen data
Disagreement is the useful signal: it says where imagined rollouts should not be trusted.
The HalfCheetah configuration follows the reference: 7 members, of which the
5 best on a held-out split are kept as elites and used for rollouts; 200-unit
Swish MLPs; and a per-member output layout of 2 · (1 + OBS_DIM) —
[ r_mean, r_logvar, Δobs_mean[OBS_DIM], Δobs_logvar[OBS_DIM] ]Two details in there are easy to miss and both are load-bearing.
The model predicts a state delta, not the next state. Regressing s′
directly means most of the network’s capacity goes into reproducing s. The
residual is small, roughly zero-mean, and far better conditioned.
The elite is redrawn per transition, not per rollout. Assigning one elite to a whole batch of rollouts means consecutive synthetic transitions all carry the same member’s particular bias, which shows up downstream as oscillating Q-values. Sampling a member independently for every transition averages that out.
Building it
Section titled “Building it”from noeira.deep_agents.mbpo import MBPOAgentMBPO composes an SAC trainer with the ensemble and a synthetic replay buffer, so the SAC hyperparameters carry over unchanged, plus the model-specific ones — ensemble size, rollout length, real-to-synthetic ratio, model retrain cadence.
pixi run -e nvidia mojo run -I . examples/half_cheetah/mbpo_half_cheetah_nn_gpu.mojopixi run mojo run -I . examples/half_cheetah/mbpo_half_cheetah_training.mojopixi run mojo run -I . examples/pendulum/pendulum_mbpo_training.mojoThe CPU and GPU HalfCheetah examples deliberately use different hyperparameters, and copying one into the other makes things worse. The GPU example can afford the reference data regime — 100k fresh rollouts per model retrain, and a 5% real fraction in the SAC minibatch — which keeps the synthetic buffer in-distribution. At the CPU rollout budget that same recipe amplifies stale-synthetic drift instead, so the CPU example runs a conservative configuration with a much higher real fraction.
Watch out for
Section titled “Watch out for”Rollout length is the sensitive knob. Too long and the policy trains on model error; too short and there is no benefit over plain SAC. Start short.
The real-to-synthetic ratio matters. Training entirely on synthetic data lets the policy drift into regions the model invented.
Model retraining cadence. The dynamics model must keep up with the state distribution the improving policy visits, or its rollouts become stale.
Watch synthetic reward against real reward. The diagnostic that actually detects model exploitation is comparing mean reward on synthetic transitions against mean reward on real ones. A healthy model is slightly pessimistic; a model predicting 3× the real per-step reward is being exploited, and no amount of critic tuning will fix that. See Logging and metrics.
MBPO or REDQ?
Section titled “MBPO or REDQ?”Both buy sample efficiency over SAC, by different means:
| MBPO | REDQ | |
|---|---|---|
| Mechanism | synthetic data from a learned model | more updates on real data |
| Extra compute | model training + rollout generation | ~20× gradient updates |
| Main risk | model error corrupts the policy | value divergence |
| Extra hyperparameters | rollout length, ratio, ensemble, cadence | ensemble size, subset size, UTD |
REDQ is simpler to get working — it has no model to go wrong. MBPO can go further when the dynamics are genuinely learnable.