Part of World Models Handbook
Explains how recurrent state-space models combine deterministic memory with stochastic latents for filtering and imagination, and train them with the ELBO.
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Recurrent State-Space Models
Watch a soccer ball roll behind a sofa. You can often predict where it will reappear, provided nothing hidden changes its motion. That familiar inference suggests a concrete engineering problem: maintain an internal state that keeps evolving when observations stop. A robot cannot pause its predictions whenever a camera is occluded. It must project forward from the information it has, while retaining uncertainty about what happens out of sight. In this chapter we build a small model that filters noisy observations and then predicts without seeing the future. We will also check where that model falls short.
In Part II: Inference, Decisions, and Control you met the classical answer, the Bayesian filter. A Kalman filter represents a Gaussian belief by its mean and covariance. With known linear transition and observation models, a Gaussian initial belief, and independent Gaussian process and measurement noise, its updates give the exact state posterior and minimum-mean-squared-error state estimate. Outside those conditions, ordinary Kalman updates generally cease to be exact Bayesian inference, although optimal linear estimation and other tractable special cases remain possible. In Part III: Representing Agents and Worlds you saw learned latent representations. The recurrent state-space model (RSSM), used by PlaNet and the Dreamer family, combines learned recurrent memory with stochastic latent dynamics.
An RSSM keeps two components of internal state. A deterministic recurrent state , carried by a GRU or LSTM, provides memory. Deterministic describes its update given its inputs; it does not mean that the model is certain about the world. A stochastic latent is sampled from a conditional distribution. During filtering, a posterior network uses the current observation to infer that distribution. A prior network predicts it without the current observation, using past information through . A KL divergence encourages the prior to match the observation-conditioned posterior and regularizes how much that posterior can differ. During acting, the agent can still filter its current observation; only its imagined future lacks future observations and therefore uses the prior alone.
The filtering/imagination distinction is the central operating boundary in the chapter, not a train/test split. Both regimes can be evaluated on held-out data, and filtering also occurs during deployment. The inference model asks, "what latent states explain what I have just observed?" The dynamics model asks, "what latent states should I predict before looking?" Training encourages consistency between them, but it cannot make unavailable future information available. A good predictive prior can therefore differ from the posterior even when neither is defective.
Hidden State: What Deterministic and Stochastic Each Buy You
This section builds the two ingredients separately before combining them. PlaNet's experiments motivate the combination, but neither ingredient is a universal requirement for a capable world model. We need to distinguish how a state is computed from what distribution its outputs can represent.
The problem a hidden state solves
Recall from Bayesian Filtering and Belief States that an observation-action history contains the information available for inferring hidden state. Storing that raw history costs more as an episode grows. Some observations are redundant, while repeated noisy measurements can still help reduce uncertainty. A belief is the distribution of hidden environment state given observations through and earlier actions. It is a sufficient summary for the usual known-model POMDP prediction and decision problem, but a general belief distribution need not have an exact finite-dimensional representation. Classical filters exploit tractable special families. A learned world model instead fits a compact representation intended to retain the information its prediction tasks need.
There are two computational choices to separate:
- A deterministic hidden vector is a fixed function of its inputs and parameters. It can store point estimates, probabilities, variances, or other learned features.
- A stochastic latent is a random variable. Its distribution represents modeled alternatives; one sample is only one realization, not the distribution itself.
A Kalman filter is a useful counterexample to the idea that deterministic memory means certainty: its mean and covariance are updated deterministically, yet jointly describe an uncertain state. Likewise, a neural vector can encode the weights, means, and scales of a mixture distribution. The RSSM chooses a different implementation: recurrent memory plus sampled latent states. That choice can help retain information and generate alternative trajectories, but it does not by itself guarantee good inference or calibrated uncertainty.
Deterministic hidden state
A simple learned filter is a recurrent neural network that ingests observations and actions and maintains a fixed-size vector:
where:
- : the learned hidden state at time , a fixed-size vector that summarizes the observation and action history
- : the learned recurrence, for instance a GRU or LSTM cell, with parameters
- : the action taken at the previous step
- : the observation arriving at the current step
A related recurrent-memory pattern appears in Ha and Schmidhuber's World Models (2018). Its LSTM consumes VAE codes and actions and outputs a mixture distribution, not a point prediction. In our simpler recurrence, is a learned predictive summary, not a guaranteed sufficient statistic. A fixed-width representation puts pressure on the network to retain task-relevant information; it may also forget information that later turns out to matter. The prediction objective determines what is rewarded, and held-out tests tell us whether that training signal was enough.
Deterministic state has real strengths:
- Repeatable updates. With fixed inputs, initialization, parameters, and deterministic numerical execution, the same recurrence returns the same state. This removes latent-sampling variance from that update, not representation or floating-point error.
- Direct differentiation. Ordinary backpropagation follows the recurrence without a fresh sampling operation on each edge. Gradients can still vanish or explode.
- Simple inference. The point-output baseline below needs one recurrent pass and no variational posterior.
The limitation appears when we attach a single point output and train it with mean squared error. If the next observation has two plausible modes, that output cannot report both. Conditional squared risk decomposes into the irreducible conditional variance plus the squared distance from the conditional mean, so the MSE-optimal point forecast is the mean. For a balanced symmetric bimodal distribution, that mean lies between the modes. A forecast of a ball on the left or right of a pillar can therefore average into a low-probability location in the middle. The cause is the point-output objective, not deterministic recurrent memory: a distributional head can report both alternatives.
The failure is easiest to see with numbers rather than words, so we make it concrete before moving on.
import numpy as np
## Bimodal belief over the ball's next position: left of the pillar or right.
try:
_trapz = np.trapezoid # NumPy >= 2.0
except AttributeError:
_trapz = np.trapz # NumPy 1.x
mixture_x = np.linspace(-3.0, 3.0, 400)
mode_centers = np.array([-1.5, 1.5])
mode_std = 0.45
dens = 0.5 * (
np.exp(-0.5 * ((mixture_x - mode_centers[0]) / mode_std) ** 2)
+ np.exp(-0.5 * ((mixture_x - mode_centers[1]) / mode_std) ** 2)
)
dens /= _trapz(dens, mixture_x)
## The MSE-optimal deterministic forecast is the conditional mean, which for
## this symmetric mixture sits exactly between the two modes.
cond_mean = _trapz(mixture_x * dens, mixture_x)
mean_gap_to_modes = float(np.min(np.abs(mode_centers - cond_mean)))conditional mean = -0.000 (midway between modes) distance from conditional mean to the nearest mode: 1.50 density at the conditional mean: 0.0034 peak density: 0.4434

The plot illustrates mode averaging: the conditional mean lies in the low-density valley between the modes, not at the global minimum of the density. Enlarging a model while retaining an MSE point-output objective does not change the optimum into two alternatives. Changing the output distribution or the prediction objective addresses a different problem than adding hidden-state capacity.
A point output also leaves uncertainty unreported. Consider two grasps with the same predicted mean outcome but different failure risks. A planner receiving only those mean outcomes cannot distinguish them by uncertainty. Risk-sensitive decisions or information gathering may need a distribution, quantiles, or other uncertainty-sensitive information. These can come from deterministic memory with a probabilistic head, from ensembles, or from stochastic latent rollouts. A point forecast can still be useful for decisions; it simply does not answer every decision question.
Stochastic hidden state
One way to generate alternatives is to make part of the latent state stochastic. Instead of reporting only a point, the model predicts a distribution and samples from it. One trajectory may go left and another right if its learned distribution represents both outcomes. Sampling represents what the model believes, not a certificate that the belief is correct. Calibration must be checked against held-out outcomes.
Stochastic latent sequence models precede RSSMs: variational RNNs (Chung et al., 2015), stochastic RNNs (Fraccaro et al., 2016), deep variational Bayes filters (Karl et al., 2017), and structured inference networks (Krishnan et al., 2017) develop related latent-state and inference ideas. In these models, distinguish the approximate latent distribution from a sample drawn from it. For an RSSM, the joint distribution of matters: varies across previously sampled latent histories, even though each recurrent update is deterministic conditional on its inputs.
Stochastic recurrence can make retaining information and optimizing long sequences difficult. PlaNet reported that its combined recurrent and stochastic state performed substantially better than the particular single-path variants it tested. That result does not establish a universal law that each additional sample increases gradient variance. For example, in with a terminal linear loss , the pathwise derivative is exactly for every noise draw. Actual gradient variance depends on the transition, objective, and estimator. The RSSM's deterministic path offers a route for recurrent information without requiring a fresh sample on every edge.
The hidden states and are model-internal quantities. Nothing forces them to correspond one-to-one to the environment's true state variables, and in general they will not. What matters is the notion of sufficiency you met in Sufficient State and State Abstraction: the learned state must contain enough information to predict the observations, rewards, and dynamics the agent cares about. In the worked example below, the true state is (position, velocity), but the learned latent may encode position in some nonlinear combination with velocity, and that is fine as long as predictions are good.
The Recurrent State-Space Model
This section assembles the architecture, name by name, and explains the wiring that makes the posterior and prior cooperate. Read it slowly: every design choice here has a reason, and the reasons compound.
The five components
An RSSM for a partially observable environment with observations and actions consists of:
-
Deterministic recurrence (the memory path). A GRU (or LSTM) updates from the previous deterministic state, the previous sampled latent, and the previous action:
-
Prior (transition model). Before seeing anything at time , the model guesses the stochastic state:
-
Posterior (inference model). During training and filtering, an encoder sees the current observation and re-places where the evidence says it should be:
-
Observation model (decoder). Reconstructs or predicts the observation from the joint state:
-
Optional heads. In the Dreamer line, a reward head and a discount or continuation head for episode boundaries (the original Dreamer already modeled early termination). We will use them in Part VII: Planning and Agency; here we focus on the observation model.
where:
- : the GRU or LSTM update, a neural network with parameters applied identically at every step
- , : the prior's mean vector and per-dimension variances, produced from by a small network head
- , : the posterior's mean vector and per-dimension variances, produced from the pair by the encoder with parameters
- : a covariance matrix carrying the given variance vector on its diagonal and zeros elsewhere, so the Gaussian treats the coordinates as independent conditional on its inputs
- : the reward at time
Notice the timing convention, because it trips everyone up at least once. The GRU input at time is , not . So is the model's prediction of the situation at time before the observation arrives. The observation never touches directly; it enters the model only through the posterior, which shapes , which enters the recurrence at the next step. All per-step information about the current observation must therefore flow through the stochastic latent. This is deliberate, and it is what makes the KL term meaningful, as you will see below. If the observation could leak into by some side channel, the prior itself could use the observation it is supposed to predict, and the KL would no longer measure a real prediction gap. By forcing all observation information through the sampled bottleneck , the architecture guarantees that the only way the model can use the present is by encoding it in exactly the place the prior is trying to predict.
The prior is the transition model: what the model expects before it looks. It exists at all times, including during imagination. The posterior is the inference model: an approximate latent distribution after incorporating the evidence. It exists only when an observation is available, such as during training and filtering. The two have the same functional form but different inputs and different jobs.
Separate the modeled structure from assumptions about its adequacy:
- The generative model is Markov in its joint internal state. Whether the learned representation retains enough history for the environment's prediction tasks must be tested, not assumed from its fixed width.
- This example uses stationary dynamics and shared parameters across time. Other settings may require time or regime information in the state or inputs.
- History and actions must contain enough information for the task. Relevant unobservable quantities require a belief over alternatives, not a promise of exact recovery; task-irrelevant hidden variables need not be recovered.
- Recorded actions are available during training and evaluation. The model is not asked to infer which actions occurred.
The figure below shows the wiring unrolled over three steps. Read the middle column: the prior flows down from into ; the posterior flows up from into ; the decoder emits a predicted observation from ; and the GRU carries into . The third column is drawn faded deliberately: it is imagination, where the posterior arrows do not exist and the prior must carry the sequence on its own.

Why keep both paths
The division of labor between and is the design insight that makes the architecture work, so it deserves a careful statement.
The deterministic path provides a direct recurrent route for information and gradients. With differentiable gates, backpropagation through a GRU is an ordinary chain-rule calculation conditional on its inputs. This can help preserve information that would be difficult to carry through repeated stochastic transitions. It does not guarantee small gradient variance or stable optimization: the inputs still include sampled latents, and the recurrent Jacobians can attenuate or amplify gradients. PlaNet's ablation is evidence about its tested architecture and tasks, rather than a proof that stochastic-only models cannot learn.
The stochastic path supplies sampled latent variation. In the graph defined here, enters the joint state through the posterior distribution for , then influences later memory through the recurrence. The KL compares distributions, not two sampled values: even identical distributions can generate different samples, and an observation can change posterior variance without changing its mean. Uncertainty can also reside in distribution parameters carried by memory, in the observation model, and in uncertainty about fitted parameters. No single latent draw contains all of those uncertainties.
The Kalman analogy concerns the order of computation: predict a state distribution, then update it using an observation. A Kalman filter represents uncertainty with both a mean and a covariance, and its innovation is an observation residual, not a KL divergence. Under the linear-Gaussian assumptions its update is exact; an RSSM uses learned nonlinear dynamics and approximate inference. Comparing filtering with imagination can help locate a failure, but does not isolate one guilty component: the encoder, transition, decoder, and available information all influence the observed errors.
DreamerV2 (Hafner et al., 2021) uses 32 categorical variables with 32 classes each, sampled with straight-through gradients; DreamerV3 retains categorical stochastic latents. Categoricals provide discrete alternatives in latent coordinates. A diagonal Gaussian is unimodal conditional on its context in those coordinates, but a nonlinear decoder can still turn Gaussian latents into multimodal or correlated observations. The high-level prior/posterior/recurrence graph remains recognizable across the variants, while the distribution parameterization, KL calculation, and gradient estimator change. Straight-through differentiation is a biased surrogate; its practical usefulness does not imply that score-function estimators inevitably have exploding variance.
Learning the Model: Posterior Inference and the ELBO
You now know what the parts are. This section explains how they are trained, and why the training objective has the peculiar two-part structure it does.
The filtering ELBO
Consider a single training sequence of observations and actions . The generative story is:
- Start from an initial belief ( can be a learned vector or fixed zeros, with the initial latent prior conditioned on it).
- For each : draw from the prior , emit from , then update .
The action-conditioned likelihood integrates over latent trajectories. For a general nonlinear neural model, that sum or integral is intractable; linear-Gaussian models are an important tractable exception. We use the filtering variational factorization , where each depends on earlier sampled latents. Jensen's inequality gives an evidence lower bound on the log likelihood. Written with all expectations visible, it is
The outer expectation averages over the earlier posterior draws that determine ; at there are no earlier latents. The inner expectation averages the current reconstruction likelihood. We estimate these expectations with a sampled trajectory, drawing , where is parameter-independent noise and . This reparameterization exposes a differentiable map from the mean and scale to the sample, so ordinary backpropagation supplies a pathwise Monte Carlo gradient under the usual differentiability and integrability conditions. The expected objective is a lower bound; an individual Monte Carlo estimate is not guaranteed to lie below the log likelihood.
Filtering uses only past and current observations. If a smoothing variational family contains this filtering family, its optimal ELBO cannot be lower, because it can use future observations too. That is a statement about the families' optima, not a promise about two fitted networks. One posterior trajectory is also not a particle filter with importance weighting and resampling. Finally, the decoder log density at a given sample may be analytic while its expectation through a nonlinear decoder is not. The conditional diagonal-Gaussian KL does have the closed form below; its outer expectation over earlier latent history is still estimated by sampling.
where:
- : shorthand for the posterior Gaussian , with mean and standard deviation along latent dimension
- : shorthand for the prior Gaussian , with mean and standard deviation
- : an index over the latent dimensions, so the sum runs over the coordinates of
The three summands divide the work: the log-scale ratio and normalized variance compare their widths, the normalized squared mean gap penalizes mean disagreement, and subtracts the posterior's expected standardized quadratic contribution. The terms cancel jointly: when the two Gaussians coincide the log ratio is zero and the variance term plus the constant sum to zero, so the divergence vanishes exactly when the two Gaussians coincide. This closed form is what the code below implements directly. Notice also the asymmetry of the divergence: because it is the KL with first, the prior's standard deviation appears in the denominator, so a prior that is too confident (small ) pays a steep price when the posterior disagrees with it. For distinct prior-head parameters under this posterior-sampled objective, the denominator punishes prior overconfidence steeply, and the KL is the only term whose gradient reaches the prior distribution's parameters (the mean and scale head of ; the reconstruction gradient still reaches the deterministic transition through the sampled ). Its gradient pulls the posterior toward the prior too. That is the collapse route the stabilizers below are designed to suppress.
The asymmetry is worth seeing as a curve rather than a formula.
import numpy as np
## KL( N(mu_q, 1) || N(mu_p, sigma_p) ) for a unit-width posterior, as the
## posterior mean mu_q moves away from the prior mean mu_p = 0, for several
## prior standard deviations. Fixed seed is not needed: the grid is
## deterministic.
gap = np.linspace(0.0, 3.0, 300)
prior_stds = np.array([0.25, 0.5, 1.0, 2.0])
kl_curves = {}
for sp in prior_stds:
kl_curves[sp] = np.log(sp) + (1.0 + gap**2) / (2.0 * sp**2) - 0.5prior sigma=0.25: KL at gap 1 = 14.11 nats, KL at gap 3 = 78.11 nats prior sigma=0.50: KL at gap 1 = 2.81 nats, KL at gap 3 = 18.81 nats prior sigma=1.00: KL at gap 1 = 0.50 nats, KL at gap 3 = 4.50 nats prior sigma=2.00: KL at gap 1 = 0.44 nats, KL at gap 3 = 1.44 nats

Read the vertical gap between the curves: at a mean gap of one, the narrowest prior already pays about 13.7 nats more than the widest one (14.11 versus 0.44) for the identical posterior. The KL direction therefore punishes prior overconfidence, which is exactly the behavior a dynamics-learning signal should have.
What each term teaches
The two terms have distinct jobs. Their traces can suggest where to investigate a broken training run, but do not uniquely identify a failing component.
The reconstruction term trains the posterior, decoder, and recurrent transition to make observations predictable. It encourages the posterior to encode current information that helps the decoder, but does not force use of every latent dimension. The current reconstruction has no dependency on : it is later losses that reward retaining useful information there. In this observation-only example, reconstruction is the direct incentive to encode observations; an agent with reward or other auxiliary heads would have additional incentives.
The KL term trains the prior and regularizes the posterior. Either distribution can move to reduce it. Its initial value and subsequent trend depend on both networks, so neither a large starting KL nor a monotonic decline is guaranteed. Residual KL is not sensor-noise variance. In a scalar exact Gaussian update with prior variance and measurement variance , expected information gain is , while innovation variance is . Increasing sensor noise can therefore decrease expected information gain while increasing innovation variance. Nats and squared observation units describe different quantities.
Posterior collapse, free nats, and KL balancing
Posterior collapse means the posterior stops using observation information beyond what the prior already supplies. Exact equality gives zero conditional KL, but zero KL alone does not prove useless prediction: the history may already explain the relevant observation. Collapse is harmful when the latent drops information needed for the task. Check observation dependence and prediction quality, rather than treating a low scalar KL as a complete diagnosis.
Collapse happens when the KL cost of encoding information exceeds the reconstruction benefit of using it. The ELBO literature developed several remedies, and the RSSM line uses two that you should know by name.
Free nats replace the per-step KL by . PlaNet and the original Dreamer use 3 nats; the cited DreamerV3 paper uses 1 nat. Below the threshold, this term is constant and supplies no gradient through either KL argument. It removes marginal regularization pressure in that region; it does not measure irreducible surprise or guarantee that the posterior uses the available capacity. A collapsed posterior equal to its prior also has a clipped value at the floor. We use a 1-nat threshold below and log both raw and clipped KL to keep these cases distinguishable. Reconstruction and shared upstream parameters can still change the distributions while the clipped KL's own gradient is zero.
KL balancing (DreamerV2, kept in DreamerV3): split the KL gradient by stopping gradients through one argument at a time:
where:
- and : shorthand for the per-step posterior and prior
- : stop-gradient, meaning the bracketed distribution is held fixed, so that term's gradient can only reach the other argument
- and : the loss weights. DreamerV2 introduced the balance with the factor , weighting the dynamics term four times the representation term; the DreamerV3 paper v2 applies loss scales of and to the two terms
Both terms are minimized alongside the negative reconstruction objective. The first differentiates through the prior argument with the posterior argument stopped; the second reverses those roles. Shared upstream parameters, including the recurrent context, may influence both distributions after an update. Larger dynamics coefficients emphasize direct prior-fitting gradients, but coefficient ratios are not gradient-norm ratios and do not guarantee an optimizer's outcome. The aim is to reduce the incentive to discard useful observation information merely to make matching easier. DreamerV3 reports results across its evaluated domains with a shared configuration and several coordinated design choices; that empirical result should not be attributed to KL balancing alone.
Loss units matter for both images and low-dimensional observations. A sum over many pixels differs from a mean over pixels, and the observation likelihood's scale determines reconstruction strength. For a fixed scalar Gaussian decoder variance , negative log likelihood equals squared error divided by , plus a parameter-independent constant. Thus squared error plus is, up to positive scaling and an additive constant, a Gaussian negative-ELBO objective with . Here corresponds to variance before clipping. Free-nat clipping modifies that objective; a learned variance would also introduce scale-dependent terms. Scalar loss magnitudes alone do not establish gradient dominance, especially when a large clipped term is locally constant.
The posterior-prior gap
During filtering, the posterior uses the currently available observation; during imagination, future states come from the prior. At fixed recurrent context , averaging over a specified observation distribution gives two terms: the mutual information between and under that joint distribution, plus the KL between the observation-averaged posterior and the prior. Consequently, expected KL upper-bounds that observation information but is not equal to it in general. It mixes useful or irrelevant encoded information with prior mismatch; it is not a direct measure of forecast error.
Keep observation-conditioned filtering and open-loop prediction separate when evaluating a model. The experiment below measures both; it does not train a policy or measure closed-loop decision usefulness. A good posterior reconstruction can coexist with poor imagination because observations supply information that an open-loop forecast lacks. Approximation and training failures can widen that difference too. A high held-out KL is a reason to investigate, not proof of a failed prior: check predictions, interval quality, and which observations change the inferred state.
Multi-Step Latent Rollout
Once trained, the model is used by imagining: rolling the latent state forward under actions, without observations. This section explains how rollouts work, when their error can grow, and what keeps them useful.
How imagination works
A rollout has two phases.
Burn-in. Run posterior updates over an observed prefix to initialize an approximate filtered belief. A known initial environment state can instead inform a suitable initializer; an unconditional constant is not a substitute for arbitrary known states. How much history is needed depends on observability and the model. To represent uncertainty over the installed belief, run several independently sampled posterior histories through the same observations and actions. Repeating one sampled endpoint would condition away the differences between those histories.
Imagination. From the installed belief, iterate the dynamics with the prior only, conditioned on a candidate action sequence:
where:
- : the number of steps beyond the last observed time ; the pair of updates is iterated for over the imagination horizon
- : the candidate action supplied by the planner at step
- : the prior, now the only source of the stochastic state because no observation arrives to correct it
The two updates play the same roles as during training: the GRU refreshes the memory from the previous belief and action, and the prior samples a fresh uncertain state. The agent then decodes whatever it needs from — typically only the reward inside the planning loop, with observations decoded for inspection or evaluation rather than per candidate plan.
Two properties make latent imagination useful in PlaNet and Dreamer. Their action evaluation or actor training can operate through latent reward and value predictions without decoding images at every step. This avoids the image decoder's computation in those implementations. PlaNet evaluates candidate action sequences with a planner; Dreamer instead learns an actor through imagined trajectories and value bootstrapping. A single sampled rollout is one trajectory. Repeated independent posterior and prior noise draws approximate the model's conditional distribution of futures, which still needs validation. We will build the decision-making machinery in Part VII: Planning and Agency.
Why rollout error compounds
In System Identification, you saw why evaluating a one-step model recursively is a different test from predicting with fresh observations. An imperfect prediction can become an input to the next transition and affect later errors. Stable dynamics may attenuate that error; unstable dynamics may amplify it. Neither error growth nor monotonic widening is guaranteed merely by rolling a model forward.
Two contributions are useful to distinguish when interpreting forecast error:
- True conditional variability. Even a perfect conditional-mean predictor makes errors against individual stochastic futures. Process noise and uncertainty left after filtering affect both point forecasts and sampled forecasts. For a stable linear system, predictive covariance can approach a finite stationary level rather than grow forever.
- Model and inference error. Misspecified dynamics, imperfect optimization, a poor initial belief, or evaluation outside the training distribution can add error. Uncertainty about fitted parameters is one part of this problem, often called epistemic uncertainty; it is not a synonym for every approximation error. Recursion may amplify or attenuate these defects.
Accurately represented environmental variability should not be removed just to make intervals narrower. An inaccurate model may instead need better data coverage, inference, or dynamics fitting; shorter rollouts reduce reliance on extrapolation without necessarily fixing it. A point-output model has no interval, but that absence is not a declaration of confidence. Conversely, drawing a broad stochastic ensemble does not establish that the model knows its errors.
Use matched reference predictions when the environment is known. Below we implement the exact linear-Gaussian filter and its open-loop conditional mean and variance under the same observed prefix and actions. This gives a reference for irreducible squared error rather than comparing a random forecast sample with a conditional mean. We also measure interval coverage and width. Empirical 95% coverage at one horizon is only a marginal diagnostic: an overly wide interval can buy coverage, biases can be hidden across subgroups, and finite validation sets give noisy proportions. Neither coverage nor an RMSE curve alone decomposes model error from process noise.
What keeps rollouts usable
Useful tools for managing rollout degradation include:
- Short horizons. PlaNet's reported setup plans 12 steps; the original Dreamer imagines 15 steps and bootstraps values beyond that window. PlaNet replans as observations arrive, while Dreamer acts through a learned policy conditioned on an updated latent state.
- Free nats and KL balancing. These adjust information regularization and gradient allocation. Free nats explicitly stop KL pressure below a threshold; neither technique guarantees a smaller prediction gap.
- Latent overshooting. PlaNet compares multistep prior predictions with posteriors inferred from real observations, adding direct multistep consistency pressure beyond one-step training.
- Stochastic latents. Sampling can retain modeled alternative futures instead of feeding back an intermodal mean. A sample lies in the learned prior's support, not necessarily in a well-trained region or in the true environment's support.
- Limited planner pressure. Optimizing strongly against an imperfect model can exploit its errors. Shorter horizons and uncertainty-sensitive objectives are possible defenses, not guarantees; Part XII: Reliable World Models develops this issue.
Evaluate the operating regimes separately. Posterior-pass error and prior-only rollout error depend on several components, but they ask different questions about access to observations. Reporting only posterior reconstruction can conceal open-loop weaknesses because each fresh observation offers a chance to correct the model. A point-error curve alone cannot assess distribution quality.
Worked Example: Inferring a Velocity You Never See
We now build an RSSM for a small controlled linear-Gaussian environment. The experiment exposes true hidden states and the data-generating equations, so we can check filtering, open-loop forecasts, and uncertainty against a known-model reference. It does not illustrate every difficulty of world modeling: in particular, this environment's conditional futures are Gaussian, not multimodal. Training is CPU-sized; runtime depends on hardware and numerical libraries.
The environment is a discrete spring-damper system with position and velocity as its two state variables. Position is observed with sensor noise; velocity is never an input or target during world-model training. Recorded force actions are also supplied. Position, velocity, actions, and process noise jointly determine future motion. With the known model parameters and Gaussian initial distribution, a Kalman filter gives the exact posterior. The position observation map is , and the two-step observability matrix has rows and , so its determinant is : position history contains information about velocity, even though noise prevents exact recovery.
The experiment asks four questions, each mapping to a section of the chapter:
- Can the model filter? Score posterior decoder means against clean positions, alongside raw-observation echo, a memoryless shrinkage estimate, and a known-model Kalman filter. Beating echo shows lower estimation error, not by itself use of history.
- Can the model imagine? Observe 10 positions, then predict 40 steps under the recorded future actions. Compare the RSSM ensemble mean with the deterministic baseline and the Kalman conditional mean using the same point-RMSE calculation.
- How informative are its intervals? Score central sample-quantile intervals against clean future positions, report width and sampling uncertainty, and compare with exact Kalman intervals. These are diagnostics, not a proof of full calibration.
- What information is recoverable? Fit a linear probe on training representations and test it on held-out sequences. A high velocity supports linear decodability; it does not prove an identifiable physical coordinate, causal use, or Markov sufficiency.
We retain a deterministic GRU with a point decoder as an illustrative baseline. The RSSM has 20,625 parameters and 1,500 updates; the baseline has 10,689 parameters and 800 updates, and their objectives and observation timing differ. This is not a controlled ablation isolating stochastic state. The shared evaluation cutoff, actions, and point-error metric make the resulting forecasts comparable, but cannot attribute their differences to one architectural choice.
Code Implementation
The simulator below uses an explicit-Euler spring-damper transition with step size , plus discrete process noise. It is the discrete model being studied, not the exact sampled transition of a continuous-time stochastic oscillator. Observation measures the position in state ; action affects . We use consistent arbitrary position and time units, so velocity has position-per-time units. The learned models receive positions and actions, not the true state, simulator coefficients, or individual noise realizations.
import numpy as np
import torch
import torch.nn as nn
rng = np.random.default_rng(7)
dt = 0.1
A = np.array([[1.0, dt], [-4.0 * dt, 1.0 - 0.5 * dt]]) # spring-damper dynamics
B = np.array([[0.0], [dt]]) # actions push on velocity
Q = 0.01 * np.eye(2) # process noise
obs_std = 0.25 # sensor noise on position
def simulate_batch(n_seqs, T, rng):
"""Spring-damper with force actions; only noisy positions are observed."""
states = np.zeros((n_seqs, T + 1, 2))
obs = np.zeros((n_seqs, T, 1))
actions = rng.normal(scale=0.5, size=(n_seqs, T, 1))
states[:, 0] = rng.normal(scale=[1.0, 0.5], size=(n_seqs, 2))
for t in range(T):
noise = rng.multivariate_normal(np.zeros(2), Q, size=n_seqs)
states[:, t + 1] = (
states[:, t] @ A.T + np.outer(actions[:, t], B.ravel()) + noise
)
obs[:, t] = states[:, t, 0:1] + rng.normal(
scale=obs_std, size=(n_seqs, 1)
)
return states, obs, actionsNow we generate a training set and a held-out validation set, and pick one demonstration sequence for plotting later.
states, obs, actions = simulate_batch(480, 50, rng)
train_states, val_states = states[:400], states[400:]
train_obs, val_obs = obs[:400], obs[400:]
train_act, val_act = actions[:400], actions[400:]
demo_idx = 3
t_grid = np.arange(50)
env_demo_pos = states[demo_idx, :50, 0]
env_demo_vel = states[demo_idx, :50, 1]
env_demo_obs = obs[demo_idx, :, 0]sequences: train=400, val=80, length T=50 true state: (position, velocity); observation: noisy position only position range: [-3.23, +3.23] velocity range: [-6.25, +6.08] sensor noise std: 0.25


The model receives the noisy position observations shown here and their recorded actions. The true state curves are for our inspection. Try estimating the direction of motion from a short noisy history: a single position generally does not determine velocity, while earlier positions and applied forces provide additional information. Useful predictive memory need not contain a coordinate explicitly named velocity; equivalent learned features can carry motion information.
The model
The RSSM below mirrors the equations of the architecture section exactly: a GRU cell for the deterministic path, two small heads for the prior and posterior Gaussians, and a decoder. Note that the posterior head is the only component that receives the observation as direct input; the decoder touches the observation only indirectly, through z_t sampled from the posterior.
class RSSM(nn.Module):
"""PlaNet-style recurrent state-space model.
h_t = GRU(h_{t-1}, [z_{t-1}, a_{t-1}]) deterministic memory
z_t ~ N(mu, diag(sigma**2)) from prior(h_t) or posterior(h_t, o_t)
o_t is decoded from [h_t, z_t]
"""
def __init__(self, obs_dim=1, act_dim=1, h_dim=48, z_dim=8, hidden=64):
super().__init__()
self.h_dim, self.z_dim = h_dim, z_dim
self.cell = nn.GRUCell(z_dim + act_dim, h_dim)
self.prior_net = nn.Sequential(
nn.Linear(h_dim, hidden), nn.ELU(), nn.Linear(hidden, 2 * z_dim)
)
self.posterior_net = nn.Sequential(
nn.Linear(h_dim + obs_dim, hidden),
nn.ELU(),
nn.Linear(hidden, 2 * z_dim),
)
self.decoder = nn.Sequential(
nn.Linear(h_dim + z_dim, hidden),
nn.ELU(),
nn.Linear(hidden, obs_dim),
)
@staticmethod
def gauss_head(params):
mu, log_std = params.chunk(2, dim=-1)
return mu, log_std.clamp(-5.0, 2.0)The gauss_head splits each head into a mean and log standard deviation. Exponentiation makes the scale positive; clamping the log scale to bounds it to approximately . The mean is not bounded, and this clamp does not prevent posterior collapse or every numerical failure. The GRU uses before the current observation is encoded, matching the timing convention above.
The training objective
The KL helper implements the analytic conditional diagonal-Gaussian expression. The loop updates , computes both heads, samples the posterior, decodes, and accumulates squared reconstruction error plus weighted clipped KL. This is a single-trajectory estimate of a modified variational objective, not the literal unmodified ELBO displayed earlier.
The free-nat floor is 1.0, applied after summing latent dimensions but separately for each sequence and timestep. The KL coefficient is 0.1. With our scalar squared-error reduction, its fixed-Gaussian interpretation uses decoder variance before free-nat clipping. This is a chosen observation-model scale, not an estimate of the simulator's sensor variance . We log raw KL as well as the clipped loss term. Neither their scalar ratio nor convergence near the threshold establishes that the prior has learned all predictable information.
def gaussian_kl(mu_q, ls_q, mu_p, ls_p):
"""Diagonal Gaussian KL; ls values are log standard deviations, not variances."""
return (
ls_p
- ls_q
+ (torch.exp(2 * ls_q) + (mu_q - mu_p) ** 2) / (2 * torch.exp(2 * ls_p))
- 0.5
).sum(dim=-1)
def train_rssm(
model,
obs,
act,
steps=1500,
batch=24,
lr=1e-3,
free_nats=1.0,
kl_weight=0.1,
seed=0,
):
torch.manual_seed(seed)
opt = torch.optim.Adam(model.parameters(), lr=lr)
n_seq, T, _ = obs.shape
obs_t = torch.tensor(obs, dtype=torch.float32)
act_t = torch.tensor(act, dtype=torch.float32)
history = {"step": [], "recon": [], "kl": [], "raw_kl": []}
for step in range(steps):
idx = torch.randint(0, n_seq, (batch,))
o_b, a_b = obs_t[idx], act_t[idx]
h = torch.zeros(batch, model.h_dim)
z = torch.zeros(batch, model.z_dim)
a_prev = torch.zeros(batch, act_t.shape[-1])
recon_losses, kl_losses, raw_kls = [], [], []
for t in range(T):
h = model.cell(torch.cat([z, a_prev], dim=-1), h)
mu_p, ls_p = model.gauss_head(model.prior_net(h))
mu_q, ls_q = model.gauss_head(
model.posterior_net(torch.cat([h, o_b[:, t]], dim=-1))
)
z = mu_q + torch.randn_like(mu_q) * torch.exp(ls_q)
pred = model.decoder(torch.cat([h, z], dim=-1))
recon_losses.append(((pred - o_b[:, t]) ** 2).sum(dim=-1))
kl_t = gaussian_kl(mu_q, ls_q, mu_p, ls_p)
raw_kls.append(kl_t)
kl_losses.append(torch.clamp(kl_t, min=free_nats))
a_prev = a_b[:, t]
recon = torch.stack(recon_losses).mean()
kl = torch.stack(kl_losses).mean()
loss = (
torch.stack(recon_losses).sum(0)
+ kl_weight * torch.stack(kl_losses).sum(0)
).mean()
opt.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 10.0)
opt.step()
if step % 10 == 0 or step == steps - 1:
history["step"].append(step)
history["recon"].append(recon.item())
history["kl"].append(kl.item())
history["raw_kl"].append(torch.stack(raw_kls).mean().item())
return historyThe clamp is applied before the timestep sum. Strictly below the threshold, this KL contribution has zero derivative; other loss paths still train the model. At the threshold, the implementation chooses a subgradient. The loss sums 50 timesteps and then averages the minibatch. Because every sequence has the same length here, averaging over time instead would change the objective by a constant scale; variable-length weighting would need an explicit choice.
torch.manual_seed(0) # seed weight initialization before construction
rssm = RSSM()
history = train_rssm(rssm, train_obs, train_act, steps=1500, seed=0)final reconstruction MSE per step: 0.0175 sensor noise variance: 0.0625 final KL per step: 1.101 nats (free-nat floor = 1.0) final raw KL per step: 1.017 nats


Read raw and clipped KL separately. A clipped value exactly at 1 would only imply that every contributing raw value was at or below 1; it would not prove optimal prediction or identify a noise floor. Posterior reconstruction can fall below the sensor variance because its encoder sees the same noisy observation used as its target. That is allowed by the objective, not an accidental input leak. By contrast, a history-only prediction of a fresh observation has expected squared error at least the independent sensor variance. Clean-position evaluation below asks whether the posterior output denoises, and the Kalman reference shows how far it is from the known-model optimum.
In the seeded reproduction, reconstruction fell to 0.0175 per step. The logged clipped KL began near 1, rose, and finished at 1.101 nats; raw KL finished at 1.017 nats. The final values are last-minibatch diagnostics, not held-out averages. The initial increase is another reason not to interpret every training trace as a prior steadily catching up to a fixed posterior.
A deterministic control model
The baseline GRU receives and predicts with a squared-error point decoder. Its hidden memory could encode motion information or distribution parameters, but this particular output head returns only a point. It differs from the mixture-density RNN in World Models. We leave its small training budget explicit rather than describing the comparison as an isolated test of stochasticity.
class DetSSM(nn.Module):
"""Deterministic RNN world model: h_t = f(h_{t-1}, o_{t-1}, a_{t-1})."""
def __init__(self, obs_dim=1, act_dim=1, h_dim=48, hidden=64):
super().__init__()
self.h_dim = h_dim
self.cell = nn.GRUCell(obs_dim + act_dim, h_dim)
self.decoder = nn.Sequential(
nn.Linear(h_dim, hidden), nn.ELU(), nn.Linear(hidden, obs_dim)
)
def train_det(model, obs, act, steps=800, batch=24, lr=1e-3, seed=1):
torch.manual_seed(seed)
opt = torch.optim.Adam(model.parameters(), lr=lr)
n_seq, T, _ = obs.shape
obs_t = torch.tensor(obs, dtype=torch.float32)
act_t = torch.tensor(act, dtype=torch.float32)
final_loss = None
for step in range(steps):
idx = torch.randint(0, n_seq, (batch,))
o_b, a_b = obs_t[idx], act_t[idx]
h = torch.zeros(batch, model.h_dim)
mse = torch.zeros(batch)
for t in range(1, T):
h = model.cell(torch.cat([o_b[:, t - 1], a_b[:, t - 1]], dim=-1), h)
pred = model.decoder(h)
mse = mse + ((pred - o_b[:, t]) ** 2).sum(dim=-1)
loss = mse.mean() / (T - 1)
opt.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 10.0)
opt.step()
final_loss = loss.item()
return final_losstorch.manual_seed(
1
) # initialize independently of earlier training's RNG consumption
det_model = DetSSM()
det_final_loss = train_det(det_model, train_obs, train_act, steps=800, seed=1)deterministic model final teacher-forced MSE: 0.0997 (teacher-forced: the real previous observation is fed at every step)
Teacher forcing feeds the real previous observation during baseline training; recursive evaluation feeds the prediction instead. This change can expose compounding errors, but does not guarantee that every horizon worsens. The RSSM also changes operating regime when it replaces future posterior updates with prior draws. It supplies an ensemble while this baseline supplies no interval. That difference in output format does not establish that the ensemble is well calibrated.
Filtering versus imagination
Now the two evaluation regimes. The filter runs the posterior at every step: the model always gets to look. The imagination burns in the belief on the first BURN_IN real observations, then rolls out with the prior only, under the true future actions, sampling N_SAMPLES independent futures. The deterministic baseline rolls out by feeding its own predictions back in place of observations, the classic deterministic world-model rollout.
@torch.no_grad()
def rssm_filter(model, obs_seq, act_seq, seed=0, n_samples=64):
"""Mean decoder output over posterior paths; first path supplies probe features."""
if len(obs_seq) == 0 or len(obs_seq) != len(act_seq) or n_samples < 1:
raise ValueError(
"Need aligned nonempty sequences and positive n_samples."
)
g = torch.Generator().manual_seed(seed)
T = obs_seq.shape[0]
h = torch.zeros(n_samples, model.h_dim)
z = torch.zeros(n_samples, model.z_dim)
a_prev = torch.zeros(n_samples, act_seq.shape[-1])
preds, feats, kls = [], [], []
for t in range(T):
h = model.cell(torch.cat([z, a_prev], dim=-1), h)
mu_p, ls_p = model.gauss_head(model.prior_net(h))
mu_q, ls_q = model.gauss_head(
model.posterior_net(
torch.cat([h, obs_seq[t : t + 1].expand(n_samples, -1)], dim=-1)
)
)
z = mu_q + torch.randn(mu_q.shape, generator=g) * torch.exp(ls_q)
preds.append(model.decoder(torch.cat([h, z], dim=-1)).mean(0).numpy())
feats.append(torch.cat([h, z], dim=-1).numpy()[0])
kls.append(gaussian_kl(mu_q, ls_q, mu_p, ls_p).mean().item())
a_prev = act_seq[t : t + 1].expand(n_samples, -1)
return np.array(preds), np.array(feats), np.array(kls)
@torch.no_grad()
def rssm_imagine(model, obs_seq, act_seq, burn_in=10, n_samples=256, seed=0):
"""Independent posterior paths, followed by prior-only decoder-mean paths."""
if (
not 1 <= burn_in < len(obs_seq)
or len(obs_seq) != len(act_seq)
or n_samples < 1
):
raise ValueError(
"Need aligned sequences, 1 <= burn_in < T, and positive n_samples."
)
g = torch.Generator().manual_seed(seed)
T = obs_seq.shape[0]
h = torch.zeros(n_samples, model.h_dim)
z = torch.zeros(n_samples, model.z_dim)
a_prev = torch.zeros(n_samples, act_seq.shape[-1])
for t in range(burn_in):
h = model.cell(torch.cat([z, a_prev], dim=-1), h)
mu_q, ls_q = model.gauss_head(
model.posterior_net(
torch.cat([h, obs_seq[t : t + 1].expand(n_samples, -1)], dim=-1)
)
)
z = mu_q + torch.randn(mu_q.shape, generator=g) * torch.exp(ls_q)
a_prev = act_seq[t : t + 1].expand(n_samples, -1)
imag = []
for t in range(burn_in, T):
h = model.cell(torch.cat([z, a_prev], dim=-1), h)
mu_p, ls_p = model.gauss_head(model.prior_net(h))
z = mu_p + torch.randn(mu_p.shape, generator=g) * torch.exp(ls_p)
imag.append(model.decoder(torch.cat([h, z], dim=-1)).numpy())
a_prev = act_seq[t : t + 1].repeat(n_samples, 1)
return np.stack(imag, axis=0) # (T - burn_in, n_samples, obs_dim)
@torch.no_grad()
def det_imagine(model, obs_seq, act_seq, burn_in=10):
"""Burn in on real observations, then feed the model its own predictions."""
if not 1 <= burn_in < len(obs_seq) or len(obs_seq) != len(act_seq):
raise ValueError("Need aligned sequences and 1 <= burn_in < T.")
T = obs_seq.shape[0]
h = torch.zeros(1, model.h_dim)
for t in range(1, burn_in):
h = model.cell(
torch.cat([obs_seq[t - 1 : t], act_seq[t - 1 : t]], dim=-1), h
)
o_prev = obs_seq[burn_in - 1 : burn_in] # last real observation
imag = []
for t in range(burn_in - 1, T - 1):
h = model.cell(torch.cat([o_prev, act_seq[t : t + 1]], dim=-1), h)
o_prev = model.decoder(h)
imag.append(o_prev.numpy())
return np.concatenate(imag, axis=0) # (T - burn_in, obs_dim)The two RSSM passes reuse the transition and decoder, while changing which distribution supplies the latent when observations are withheld. Future filtering and imagination therefore have different information, even for a perfectly specified model. Here each ensemble member follows its own sampled posterior history during burn-in before continuing with independent prior noise. This retains uncertainty across installed latent states instead of replicating one sampled endpoint. We decode conditional observation means without adding observation-likelihood noise: the bands below describe this decoder-mean ensemble scored against clean position, not the model's full noisy-observation predictive distribution. Its suitability as a clean-state interval is an empirical question, since clean positions were not training targets.
The reference filter uses the exact simulator parameters. At a measurement, it updates a Gaussian mean and covariance with and measurement variance . Between measurements, the mean follows , and covariance follows . The first term propagates remaining uncertainty; the second adds process noise. Starting from the posterior after observation 9 and omitting further measurement updates gives the exact open-loop reference for positions 10 through 49. Its covariance includes uncertainty after burn-in, not only newly added noise.
The memoryless reference deliberately ignores history and actions. It uses the known marginal position variance under the simulator's random action distribution and returns . Its expected error is , illustrating why beating raw-observation echo alone does not prove temporal inference. Both analytical references have privileged model knowledge and are labeled accordingly.
def kalman_reference(obs_seq, act_seq, stop_observing=None):
"""Exact known-model state moments, before or after the observation cutoff."""
T = len(obs_seq)
cutoff = T if stop_observing is None else stop_observing
if len(act_seq) != T or not 0 <= cutoff <= T:
raise ValueError("Need aligned sequences and a cutoff in [0, T].")
m, P = np.zeros(2), np.diag([1.0, 0.25])
C = np.array([1.0, 0.0])
means, covariances = [], []
for t in range(T):
if t < cutoff:
K = P @ C / (C @ P @ C + obs_std**2)
m = m + K * (float(obs_seq[t, 0]) - C @ m)
J = np.eye(2) - np.outer(K, C)
P = J @ P @ J.T + obs_std**2 * np.outer(K, K)
means.append(m.copy())
covariances.append(P.copy())
m = A @ m + B[:, 0] * float(act_seq[t, 0])
P = A @ P @ A.T + Q
return np.array(means), np.array(covariances)
def point_and_sample_rmse(samples, targets):
"""Inputs (sequence, horizon, sample) and (sequence, horizon)."""
if (
samples.ndim != 3
or samples.shape[:2] != targets.shape
or samples.shape[2] < 1
):
raise ValueError(
"Expected aligned sequence/horizon axes and a nonempty sample axis."
)
mean = samples.mean(axis=2)
point = np.sqrt(((mean - targets) ** 2).mean(axis=0))
sample = np.sqrt(((samples - targets[..., None]) ** 2).mean(axis=(0, 2)))
return point, sample
def interval_metrics(lower, upper, targets, alpha=0.05):
"""Per-horizon coverage, width and central interval score (smaller is better)."""
if lower.shape != upper.shape or lower.shape != targets.shape:
raise ValueError(
"Interval bounds and targets must have matching shapes."
)
if not 0 < alpha < 1 or np.any(lower > upper):
raise ValueError("Require ordered bounds and alpha in (0, 1).")
width = upper - lower
score = width + (2 / alpha) * (
np.maximum(lower - targets, 0) + np.maximum(targets - upper, 0)
)
return (
((lower <= targets) & (targets <= upper)).mean(0),
width.mean(0),
score.mean(0),
)
def wilson_interval(proportion, n, z=1.96):
"""Approximate binomial 95% confidence interval for each marginal coverage."""
if n <= 0 or np.any((proportion < 0) | (proportion > 1)):
raise ValueError("Need positive sample size and proportions in [0, 1].")
center = (proportion + z * z / (2 * n)) / (1 + z * z / n)
half = (
z
* np.sqrt(proportion * (1 - proportion) / n + z * z / (4 * n * n))
/ (1 + z * z / n)
)
return center - half, center + halfWe evaluate all 80 held-out sequences. Each RSSM imagination uses 256 particles, independently sampled through burn-in as well as the future. Posterior point estimates average 64 paths; probe features below use the first path, and their labels are never used to train the RSSM. A central sample interval uses the 2.5th and 97.5th percentiles, without assuming Gaussian decoded outputs. Its finite-particle endpoints still have Monte Carlo error.
At each horizon, coverage has 80 independent truth sequences, not 80 times 256 independent outcomes. Wilson intervals describe uncertainty in that marginal proportion; they are not simultaneous confidence bands across horizons, whose outcomes are correlated. We also report interval width and the central interval score: width plus times any distance by which truth misses the interval, where . This penalizes both unnecessary width and missed outcomes. All computations happen before plotting.
BURN_IN, N_SAMPLES, N_EVAL = 10, 256, len(val_obs)
T = val_obs.shape[1]
H = T - BURN_IN
filter_preds, feat_list, kl_list = [], [], []
imag_list, det_list, true_list = [], [], []
oracle_filters, oracle_means, oracle_covs = [], [], []
for i in range(N_EVAL):
o_seq = torch.tensor(val_obs[i], dtype=torch.float32)
a_seq = torch.tensor(val_act[i], dtype=torch.float32)
f_pred, feats, kls = rssm_filter(rssm, o_seq, a_seq, seed=1000 + i)
imag_list.append(
rssm_imagine(rssm, o_seq, a_seq, BURN_IN, N_SAMPLES, seed=2000 + i)
)
det_list.append(det_imagine(det_model, o_seq, a_seq, BURN_IN))
filter_preds.append(f_pred)
feat_list.append(feats)
kl_list.append(kls)
true_list.append(val_states[i, :T, 0])
oracle_filters.append(kalman_reference(val_obs[i], val_act[i])[0][:, 0])
om, op = kalman_reference(val_obs[i], val_act[i], stop_observing=BURN_IN)
oracle_means.append(om[BURN_IN:, 0])
oracle_covs.append(op[BURN_IN:, 0, 0])
true_pos = np.stack(true_list) # (N_EVAL, T)
filt_arr = np.stack(filter_preds) # (N_EVAL, T, 1)
imag = np.stack(imag_list) # (N_EVAL, H, S, 1)
det_arr = np.stack(det_list) # (N_EVAL, H, 1)
val_feats = np.stack(feat_list) # (N_EVAL, T, h_dim + z_dim)
val_kl = np.stack(kl_list) # (N_EVAL, T)
true_future = true_pos[:, BURN_IN:] # (N_EVAL, H)
imag_mean = imag[..., 0].mean(axis=2) # (N_EVAL, H)
imag_std = imag[..., 0].std(axis=2)
imag_rmse, imag_sample_rmse = point_and_sample_rmse(imag[..., 0], true_future)
det_rmse = np.sqrt(((det_arr[..., 0] - true_future) ** 2).mean(axis=0))
filter_err = np.sqrt(((filt_arr[..., 0] - true_pos) ** 2).mean(axis=0))
filter_rmse = np.sqrt(((filt_arr[..., 0] - true_pos) ** 2).mean())
echo_rmse = np.sqrt(((val_obs[:N_EVAL, :, 0] - true_pos) ** 2).mean())
imag_lower, imag_upper = np.quantile(imag[..., 0], [0.025, 0.975], axis=2)
coverage, band_width, interval_score = interval_metrics(
imag_lower, imag_upper, true_future
)
coverage_low, coverage_high = wilson_interval(coverage, N_EVAL)
oracle_mean, oracle_var = np.stack(oracle_means), np.stack(oracle_covs)
oracle_rmse = np.sqrt(((oracle_mean - true_future) ** 2).mean(0))
oracle_expected_rmse = np.sqrt(oracle_var.mean(0))
oracle_filter_rmse = np.sqrt(
((np.stack(oracle_filters) - true_pos) ** 2).mean()
)
oracle_lower = oracle_mean - 1.959963984540054 * np.sqrt(oracle_var)
oracle_upper = oracle_mean + 1.959963984540054 * np.sqrt(oracle_var)
oracle_coverage, oracle_width, oracle_score = interval_metrics(
oracle_lower, oracle_upper, true_future
)
marginal_P = np.diag([1.0, 0.25])
shrink_gains = []
for t in range(T):
shrink_gains.append(marginal_P[0, 0] / (marginal_P[0, 0] + obs_std**2))
marginal_P = A @ marginal_P @ A.T + Q + 0.5**2 * (B @ B.T)
shrink_pred = val_obs[:N_EVAL, :, 0] * np.array(shrink_gains)
shrink_rmse = np.sqrt(((shrink_pred - true_pos) ** 2).mean())
horizons = np.arange(1, H + 1)echo-the-observation RMSE: 0.249
RSSM filtering RMSE (vs true pos): 0.210
known-model memoryless RMSE: 0.231
known-model Kalman filtering RMSE: 0.146
RSSM mean RMSE: h=1 0.198 h=10 0.304 h=40 0.547
deterministic RMSE: h=1 0.190 h=10 0.300 h=40 0.498
Kalman mean RMSE: h=1 0.189 h=10 0.280 h=40 0.488
RSSM sample-path RMSE at h=40: 0.927 (different estimand)
h=1: RSSM interval coverage 100.0% (Wilson interval 95.4%–100.0%), width 1.622, interval score 1.622
Kalman interval coverage 95.0%, width 0.742, interval score 0.930
h=10: RSSM interval coverage 100.0% (Wilson interval 95.4%–100.0%), width 2.108, interval score 2.108
Kalman interval coverage 97.5%, width 1.161, interval score 1.211
h=40: RSSM interval coverage 98.8% (Wilson interval 93.3%–99.8%), width 2.893, interval score 2.901
Kalman interval coverage 93.8%, width 1.912, interval score 2.048
mean prior-posterior KL on val: 1.02 nats/stepRead the filter errors against all three references, not echo alone. A memoryless estimate can beat echo through shrinkage toward a marginal mean without consulting any earlier observations. The Kalman filter uses the supplied simulator parameters, which the neural models do not receive, and is an estimation oracle rather than a fair data-only competitor. The two neural point forecasts are now scored identically: first average RSSM samples, then compute squared error across held-out sequences. Individual-sample RMSE is reported separately because it also includes ensemble variance. Coverage and interval width assess a different output property; neither turns point error into a decomposition of model bias and physical randomness. Raw held-out KL is another diagnostic, not a count of useful facts the model has learned.
In the seeded CPU reproduction (PyTorch 2.13.0, NumPy 2.4.2, two CPU threads), posterior-mean position RMSE was 0.210, versus 0.249 for echo, 0.231 for known-model memoryless shrinkage, and 0.146 for Kalman filtering. This RSSM does denoise on these sequences, but falls short of the known-model reference. At horizon 40, ensemble-mean RMSE was 0.547, versus 0.498 for the point baseline and 0.488 for the Kalman mean. These are one-seed results, not a statistically established ranking across training runs. The Monte Carlo ensemble mean also has finite-particle estimation error.
The sample-path RMSE at horizon 40 was 0.927. This is not a contradiction: for the same target, mean squared sample error equals squared ensemble-mean error plus ensemble variance. At a fixed ensemble mean, greater ensemble variance raises the sample-path metric, even when that variance correctly represents possible futures. That is why the point-error plot uses the ensemble mean and the interval diagnostics are reported separately.
The RSSM intervals covered 100% of held-out positions at horizons 1 and 10, and 98.8% at horizon 40. Their mean widths were 1.622, 2.108, and 2.893 position units, compared with Kalman widths of 0.742, 1.161, and 1.912. The central interval scores were also worse than the oracle's at those horizons. This evidence favors a diagnosis of unnecessarily broad decoder-mean intervals here, not a claim of successful calibration. At horizon 40 the coverage estimate's Wilson interval was approximately 93.3%–99.8%; 80 outcomes still leave substantial tail uncertainty.
One sequence, two regimes
The next figure shows a single held-out sequence, which makes the regime split vivid. Before the vertical line the model filters with real observations; after it, imagination runs on the prior alone.
demo = 0
t_all = np.arange(T)
t_imag = np.arange(BURN_IN, T)
demo_true = true_pos[demo]
demo_obs = val_obs[demo, :, 0]
demo_filter = filt_arr[demo, :, 0]
demo_imag_mean = imag_mean[demo]
demo_imag_lower, demo_imag_upper = imag_lower[demo], imag_upper[demo]
demo_det = det_arr[demo, :, 0]
The posterior trace continues using observations after the cutoff only as an offline comparison; the imagined paths do not. The interval can fluctuate in width with both the inferred dynamics and finite Monte Carlo sampling. Inspect where truth leaves it and compare that with the aggregate coverage and width below. The point baseline has no uncertainty head, so its line alone supplies no interval. Conversely, a visibly broad RSSM band does not certify reliable uncertainty.
Error versus horizon
Aggregating over all evaluated sequences shows the horizon-dependent errors quantitatively.

Both neural point forecasts and the exact Kalman mean incur error against stochastic futures. Even the oracle cannot predict the realized process noise. Its covariance recursion supplies the irreducible conditional mean-squared-error reference; finite held-out RMSE also fluctuates with the sampled outcomes. An excess over the oracle in expectation can reflect model or inference defects, while a crossing on a small test set is not proof that a learned model beats the conditional-mean optimum. The posterior curve uses observations throughout and is shown only for scale. It need not be flat, and none of these curves is required to rise monotonically.
What the belief actually contains
Finally, we test linear recoverability of physical-state information. The RSSM is frozen. A ridge readout sees training features and true-state labels, and is evaluated on held-out sequences. Those labels train the probe, not the world model. Success supports a relationship between the learned representation and velocity under this data distribution; it does not establish how the RSSM uses that information or whether its full belief is sufficient.
probe_feats, probe_targets = [], []
for i in range(0, len(train_obs), 4):
o_seq = torch.tensor(train_obs[i], dtype=torch.float32)
a_seq = torch.tensor(train_act[i], dtype=torch.float32)
_, feats, _ = rssm_filter(rssm, o_seq, a_seq, seed=i, n_samples=64)
probe_feats.append(feats)
probe_targets.append(train_states[i, :T, :])
Xp = np.concatenate(probe_feats)
yp = np.concatenate(probe_targets)
Xv = val_feats.reshape(-1, val_feats.shape[-1])
yv = np.stack([val_states[i, :T, :] for i in range(N_EVAL)]).reshape(-1, 2)
def ridge_r2(X_tr, y_tr, X_te, y_te, lam=1e-2):
x_mean, y_mean = X_tr.mean(0), y_tr.mean(0)
Xc, yc = X_tr - x_mean, y_tr - y_mean
gram = Xc.T @ Xc + lam * np.eye(X_tr.shape[1])
w = np.linalg.solve(gram, Xc.T @ yc)
pred = (X_te - x_mean) @ w + y_mean
ss_res = ((y_te - pred) ** 2).sum(axis=0)
ss_tot = ((y_te - y_te.mean(axis=0)) ** 2).sum(axis=0)
if np.any(ss_tot == 0):
raise ValueError("R-squared requires nonconstant held-out targets.")
return 1.0 - ss_res / ss_tot
r2 = ridge_r2(Xp, yp, Xv, yv)
## Fit PCA on training features; project held-out features without using labels.
Xall = val_feats.reshape(-1, val_feats.shape[-1])
Xc = Xall - Xp.mean(axis=0)
_, _, Vt = np.linalg.svd(Xp - Xp.mean(axis=0), full_matrices=False)
belief_pc = Xc @ Vt[:2].T
train_pc = (Xp - Xp.mean(axis=0)) @ Vt[:2].T
pca_r2 = ridge_r2(train_pc, yp, belief_pc, yv)
vel_color = np.stack([val_states[i, :T, 1] for i in range(N_EVAL)]).reshape(-1)linear probe R^2, belief -> position: 0.94 linear probe R^2, belief -> velocity: 0.93 two-PC probe R^2, projection -> velocity: 0.05 (velocity labels train only the separate probe, not the RSSM)

The full-feature probe and the two-dimensional PCA answer different questions. The probe tests whether a supervised linear readout can recover state information on held-out sequences. PCA retains directions of high feature variance without using velocity labels. Mixed colors in its first two components therefore do not contradict strong recoverability from all 56 features; informative directions may lie elsewhere. A weak linear probe would not rule out nonlinear information either. The held-out split reduces direct trajectory memorization, but additional seeds, input-only or untrained-feature controls, and tests under new action distributions would be needed for stronger conclusions.
The useful result is more limited than autonomous discovery of a named physical variable: a predictive model can learn features from noisy positions and recorded actions from which a separate readout recovers velocity-related information. It was not given true velocities, the spring constant, or damping during world-model training. Good probe scores do not by themselves establish a calibrated posterior, good long-horizon forecasts, or causal use of the recovered feature. The experiment keeps those questions separate.
The reproduced held-out full-feature probe scored about 0.94 for position and 0.93 for velocity. Restricting the probe to the first two training-fitted principal components reduced velocity to about 0.05. The mixed colors in this projection are therefore a useful warning: the directions with most representation variance are not necessarily those carrying the state information of interest. Neither number establishes Markov sufficiency.
Limitations and Impact
The limitations discussed here concern inference and rollout quality, representation, and computation.
The first theme is inference and rollout quality. During acting, observations can update the posterior; beyond the latest observation, imagination uses the prior with historical context. Some difference between these regimes is unavoidable because they have different information. Approximation, poorly fitted dynamics, and distribution shift can add further error. Held-out raw KL helps diagnose the representation, while point forecasts and distribution-sensitive tests assess its consequences. Free nats, balancing, and overshooting address different parts of training; none certifies trustworthy imagination.
The second theme is representation. A diagonal Gaussian restricts the conditional latent density at fixed context to an independent, unimodal family in its chosen coordinates. It does not forbid correlated or multimodal decoded physical variables: nonlinear transformations and mixtures over histories can produce both. Factorized categorical latents change those conditional modeling choices without guaranteeing an adequate belief. A finite-precision recurrent state can also lose relevant history, although some processes admit finite sufficient statistics even for unbounded histories. Low current-observation reconstruction error alone will not reveal every loss of predictive information.
The third theme is computation. An ordinary nonlinear GRU processes a length- sequence through sequential recurrent updates. Transformer training can process positions in parallel within each layer when the whole sequence is available, though this does not mean constant total work or universally faster training. Practical cost depends on sequence length, model size, hardware, and latency needs. The next chapter, Transformers and Neural State-Space Architectures, compares attention with structured and selective state-space sequence layers. S4 and Mamba also summarize history but are not RSSM Bayesian filters, and their update structure differs materially from a GRU. The autoregressive token models that follow inherit the evaluation questions: what history is represented, how do predictions change without future observations, and where do they fail?
PlaNet demonstrated latent-space planning from images. Dreamer learned an actor and value model through latent imagination; DreamerV2 reported Atari results, and DreamerV3 extended the evaluated domains to tasks including Minecraft. DayDreamer applied world-model learning to physical robots, including a quadruped learning to stand and walk in one hour. These systems reuse Bayesian terms such as prior, posterior, and belief rather than originating them. Their lasting practical question is whether an internal predictive state remains useful when it must continue without the observations that trained or corrected it.
Summary
- An RSSM combines recurrent memory with a stochastic latent . Deterministic computation is not certainty, and one sampled joint state is not a full belief distribution.
- The prior predicts before the current observation; the posterior incorporates it during filtering, including at deployment. Their conditional KL trains the distinct prior head and regularizes the posterior, but is not forecast error or sensor-noise variance.
- The deterministic recurrent edge bypasses fresh sampling operations; the latent distribution can carry observation-conditioned alternatives. PlaNet's ablations support this combination in its tested setup, not a universal necessity theorem.
- The exact filtering ELBO averages over latent histories. A Monte Carlo estimate need not itself be a lower bound. The example uses a weighted, free-nat-clipped modification; free nats and KL balancing adjust gradients without guaranteeing prevention of collapse.
- Imagination starts from independently sampled posterior histories and proceeds under actions with prior draws only. Compare ensemble-mean and point-forecast RMSE on equal terms, and evaluate intervals separately with coverage, width, and a known-model reference when available.
- The worked example tests filtering, open-loop prediction, and linear state recoverability separately. A velocity probe does not establish Markov sufficiency, and a broad interval does not establish calibration.
- Approximate inference, restricted conditional latent families, finite memory, and sequential computation motivate comparison with the architectures in the next chapter.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about recurrent state-space models, filtering, and imagination.
Recurrent State-Space Models Quiz
Reference
Citation details
Cite or share this article.
Continue with the full handbook
This chapter is part of World Models Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.
Explore World Models HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

Comments
No comments yet. Be the first to share your thoughts!