World Model RSSM
Encoder CNN 64x64
→
GRU plus stochastic latent model
→
Decoder transposed CNN
Imagination Rollout
State s0
→
Action a0
→
Imagined future states
→
Lambda-return target
Actor-Critic Learning
Actor policy
Critic value model
### Detailed architecture (RSSM)
```{mermaid}
graph TD
A["Image x_t"] --> B["ConvEncoder"]
B --> C["Obs embed e_t"]
D["Prev state h_{t-1}, s_{t-1}"] --> E["GRU (deter)"]
F["Prev action a_{t-1}"] --> E
E --> G["h_t (deterministic)"]
G --> H["Prior model p(s_t | h_t)"]
H --> I["s_t (stochastic prior)"]
C --> J["Posterior model q(s_t | h_t, e_t)"]
G --> J
J --> K["s_t (stochastic posterior)"]
K --> L["ConvDecoder → x̂_t"]
K --> M["Reward head → r̂_t"]
K --> N["Discount head → γ̂_t (V2 only)"]
```
### Recurrent State-Space Model (RSSM)
The core of Dreamer is the RSSM, defined in `simulusion.models.dreamer_rssm.RSSM`.
It maintains a hybrid state with two components:
**1. Deterministic state** `h_t` — a GRU hidden state that captures temporal
dependencies and deterministic transitions:
```{math}
h_t = \text{GRU}(h_{t-1}, [s_{t-1}, a_{t-1}])
```
**2. Stochastic state** `s_t` — a latent variable representing uncertainty.
- **V1:** Diagonal Gaussian with `stoch_size` means and variances.
- **V2:** Concatenation of `num_categories` one-hot categoricals, each
with `classes` categories. Default: 32 classes × 32 categories = 1024 total.
The model operates in two modes:
**Observe mode** (training — uses real observations):
```{math}
\text{Posterior: } s_t \sim q(s_t | h_t, \text{enc}(x_t))
```
**Imagine mode** (policy training — no observations):
```{math}
\text{Prior: } s_t \sim p(s_t | h_t)
```
Key insight: the prior learns to predict the posterior without seeing the
observation. During imagination, the prior serves as the dynamics model.
### CNN Encoder (`simulusion.vision.dreamer_encoder.ConvEncoder`)
Four-layer CNN with increasing channels (32 → 64 → 128 → 256) and ReLU
activations. Strided convolutions (stride 2) halve spatial resolution at
each layer. Output is flattened to `obs_embed_size` (default 1024).
```
Input: (3, 64, 64)
└─ Conv2D(3, 32, 4×4, stride 2) → (32, 31, 31)
└─ Conv2D(32, 64, 4×4, stride 2) → (64, 14, 14)
└─ Conv2D(64, 128, 4×4, stride 2) → (128, 6, 6)
└─ Conv2D(128, 256, 4×4, stride 2) → (256, 2, 2)
└─ Flatten → Linear(1024, embed_size)
Output: embed_size-d vector
```
### CNN Decoder (`simulusion.vision.dreamer_decoder.ConvDecoder`)
Mirrored transposed-CNN structure:
```
Input: stoch + deter state (e.g. 1030-d)
└─ Linear(1030, 1024) → reshape to (256, 2, 2)
└─ ConvT2D(256, 128, 5×5, stride 2) → (128, 6, 6)
└─ ConvT2D(128, 64, 5×5, stride 2) → (64, 14, 14)
└─ ConvT2D(64, 32, 6×6, stride 2) → (32, 31, 31)
└─ ConvT2D(32, 3, 6×6, stride 2) → (3, 64, 64)
Output: reconstructed image
```
### Reward and Discount Heads (`DenseDecoder`)
Two-layer MLPs with ELU activations predicting scalar reward, and in V2,
episode discount (termination probability). The discount head is trained with
binary cross-entropy:
```{math}
\mathcal{L}_{\text{disc}} = \text{BCE}(\gamma_t, \hat{\gamma}_t)
```
### Action Decoder (`ActionDecoder`)
Outputs the policy distribution over actions. For continuous actions, predicts
a tanh-squashed Gaussian. For discrete actions, predicts a categorical
distribution. Uses REINFORCE gradient through the world model.
## Shared Training
### Training Loop
DreamerAgent follows a cyclic training loop:
1. **Collect**: Interact with environment using current policy (+ exploration noise). Store experience in `ReplayBuffer`.
2. **Train world model** (every step): Sample batch of `batch_size` sequences of length `train_seq_len` from buffer. Update encoder, RSSM, decoder, reward head, and discount head.
3. **Train actor-critic** (every step after `seed_steps`): Imagine `imagine_horizon` steps using prior dynamics. Compute λ-returns. Update actor and critic.
4. **Log**: Metrics, video reconstructions, and checkpointing.
```{math}
\begin{aligned}
&\text{for each environment step:} \\
&\quad \text{collect } (x_t, a_t, r_t, \gamma_t) \\
&\quad \text{if } step > seed\_steps: \\
&\qquad \text{sample batch from buffer} \\
&\qquad \text{update world model (encoder, RSSM, decoder, reward)} \\
&\qquad \text{imagine } H \text{ steps} \\
&\qquad \text{update actor, critic} \\
&\quad \text{log every } log\_every \text{ steps}
\end{aligned}
```
## Usage in Simulusion
### Using config directly
```python
from simulusion import DreamerAgent, DreamerConfig
cfg = DreamerConfig()
cfg.env_backend = "dmc"
cfg.env = "walker-walk"
cfg.total_steps = 5_000_000
agent = DreamerAgent(cfg)
agent.train()
```
### Environment backends
| Parameter | Default | Description |
|-----------|---------|-------------|
| `stoch_size` | 30 | Stochastic latent dimensions |
| `deter_size` | 200 | Deterministic hidden size |
| `embed_size` | 1024 | Encoder embedding size |
| `imagine_horizon` | 15 | Imagination rollout length |
| `discount` | 0.99 | Discount factor γ |
| `td_lambda` | 0.95 | λ-return parameter |
| `kl_loss_coeff` | 1.0 | KL divergence weight |
### Learning Objectives
**World Model Loss**:
```{math}
\begin{aligned}
\mathcal{L}_\mathrm{world}
&= \mathcal{L}_\mathrm{reconstruction}
+ \mathcal{L}_\mathrm{reward}
+ \beta \cdot \mathcal{L}_\mathrm{KL}
\end{aligned}
```
**Actor Loss** (REINFORCE):
```{math}
\mathcal{L}_\mathrm{actor}
= -\mathbb{E}\left[\log \pi(\mathbf{a} \mid \mathbf{s}) \cdot (G - V(\mathbf{s}))\right]
```
**Critic Loss** (MSE):
```{math}
\mathcal{L}_\mathrm{critic} = \mathbb{E}[(G - V(\mathbf{s}))^2]
```
## DreamerV2 Enhancements
DreamerV2 introduces several improvements:
1. **Discrete latents**: Categorical latent variables instead of Gaussian
2. **KL balancing**: Separate weighting for prior/posterior KL
3. **Discount model**: Learns to predict episode termination
4. **Layer normalization**: More stable training
## Configuration and Checkpoints
Dreamer configs are serializable, so experiments can be reproduced from the
YAML saved with each run or checkpoint:
```python
from simulusion import DreamerConfig, DreamerAgent
cfg = DreamerConfig()
cfg.env = "walker-walk"
cfg.to_yaml("configs/dreamer_walker.yaml")
agent = DreamerAgent.from_config("configs/dreamer_walker.yaml", seed=7)
agent.train()
# Checkpoints save `config.yaml` beside the weights automatically.
agent.dreamer.save("runs/walker/ckpts/model.pt")
restored = DreamerAgent.from_pretrained("runs/walker/ckpts")
print(restored.summary()["total_parameters"])
```
For lower-level workflows, the core `Dreamer` class also supports
`Dreamer.from_config(...)`, `Dreamer.from_pretrained(...)`,
`Dreamer.summary()`, and `Dreamer.parameter_count()`.
## Environment Support
Dreamer supports multiple backends:
```python
cfg = DreamerConfig()
cfg.env_backend = "dmc" # DeepMind Control Suite
cfg.env = "walker-walk"
cfg.env_backend = "gym" # Gym/Gymnasium
cfg.env = "Pendulum-v1"
cfg.env_backend = "dmlab" # DeepMind Lab
cfg.env = "rooms_collect_good_objects_train"
cfg.dmlab_action_repeat = 4
cfg.env_backend = "mujoco" # MuJoCo
cfg.env = "Humanoid-v4"
cfg.env_backend = "brax" # JAX/Brax
cfg.env = "ant"
cfg.env_backend = "procgen" # Procgen
cfg.env = "coinrun"
cfg.env_backend = "unity_mlagents" # Unity ML-Agents
cfg.unity_file_name = "env.exe"
```
### CLI
```bash
simulusion train dreamer --env dmc/walker-walk --device cuda
```
See {doc}`configs_reference` for the full DreamerConfig field reference with defaults.
### Key Hyperparameters
#### World Model
| Parameter | V1 Default | V2 Default | Effect |
|-----------|------------|------------|--------|
| `stoch_size` | 30 | 32 × 32 classes | Total stochastic capacity |
| `deter_size` | 200 | 200 | GRU hidden size |
| `model_learning_rate` | 6e-4 | 3e-4 | World model learning rate |
| `train_seq_len` | 50 | 50 | Sequence length per batch |
| `batch_size` | 50 | 16 | Sequences per batch |
| `free_nats` | 3.0 | 3.0 | KL free-nats floor, per Dreamer v1 Appendix A. Raw KL below this contributes zero gradient, so the floor caps how hard the KL compresses the posterior. Single shared field: both variants read `DreamerConfig.free_nats` |
| `reward_scale` | 1.0 | 1.0 | Multiplier on replayed rewards. Raise/lower for envs outside DMC's `[0, 1]` per-step range |
#### Actor-Critic
| Parameter | V1 Default | V2 Default | Effect |
|-----------|------------|------------|--------|
| `actor_learning_rate` | 8e-5 | 8e-5 | Policy learning rate |
| `value_learning_rate` | 8e-5 | 8e-5 | Critic learning rate |
| `imagine_horizon` | 15 | 15 | Imagination rollout length |
| `discount` | 0.99 | 0.997 | Discount factor |
| `td_lambda` | 0.95 | 0.95 | λ-return parameter |
| `kl_loss_coeff` | 1.0 | 1.0 | KL loss coefficient |
| `kl_alpha` | — | 0.8 | KL balancing weight (V2 only) |
#### Environment Interaction
| Parameter | Default | Effect |
|-----------|---------|--------|
| `action_repeat` | 2 | Repeat each action N times |
| `action_noise` | 0.3 | Exploration noise std |
| `seed_steps` | 5000 | Random steps before training |
| `total_steps` | 5e6 | Total environment steps |
| `collect_steps` | 1000 | Steps between model updates |
## Common Pitfalls
### Posterior collapse
If the stochastic state is ignored by the dynamics, the model reduces to a
deterministic RNN. Symptoms: good reconstruction but imagination diverges.
**Fixes:**
- Decrease `kl_loss_coeff` or adjust `kl_alpha` (V2)
- Increase `free_nats`
- Reduce `stoch_size`
Both of the first two reduce the KL pressure that drives the posterior toward
the prior. Free nats exist precisely to prevent collapse: raw KL below the
floor is unpenalized, so raising `free_nats` lets the posterior carry more
information. Lowering it (or raising `kl_loss_coeff`) tightens the squeeze and
makes collapse worse.
### Imagination divergence
The prior predicts states that drift from realistic latents over long horizons.
**Fixes:**
- Keep `imagine_horizon` short (10–15)
- Verify multi-step prediction, not just one-step
### NaN loss during training
**Fixes:**
- Reduce `model_learning_rate` to 1e-4
- Tighten gradient clipping (default 100 → 10)
- Enable layer norm
### Actor never improves
**Fixes:**
- Increase `imagine_horizon` for delayed rewards
- Increase exploration noise via `action_noise`
- Verify critic loss is decreasing
## See Also
- {doc}`modular_rssm_guide` — swap Dreamer's encoder, backbone, or decoder for research
- {doc}`iris` — discrete world model alternative to Dreamer
- {doc}`diamond` — diffusion-based world model alternative
## References
- Hafner, D., Lillicrap, T., Fischer, I., Vuong, Q., Held, D., Haarnoja, T., & Abbeel, P. (2019). Dreamer: Learning Latent Dynamics for Planning from Pixels.
- Hafner, D., Lillicrap, T., Ba, J., & Norouzi, M. (2020). Mastering Atari with Discrete World Models.
- Hafner, D., Lillicrap, T., Norouzi, M., & Ba, J. (2021). Mastering Atari with Discrete World Models (DreamerV2). *ICLR 2021.*