Controllers and Policies#
TorchWM provides several controller and policy classes that convert latent representations into environment actions. The right choice depends on whether you plan online (CEM at every step), use a trained actor-critic, or evolve a controller with black-box optimization.
Overview#
Class |
Approach |
Used by |
|---|---|---|
|
Cross-entropy method (CEM) planning in latent space |
PlaNet, RSSM training |
|
Collects environment rollouts with optional policy |
PlaNet, RSSM training |
|
Learned linear mapping |
World Models (Ha & Schmidhuber) |
|
CNN + LSTM + action head (trained with REINFORCE) |
IRIS |
|
CNN + LSTM + value head (λ-return baseline) |
IRIS |
|
Convenience wrapper around |
IRIS |
|
Shared 4-layer CNN backbone |
IRISActor, IRISCritic |
RSSMPolicy — CEM planning#
RSSMPolicy runs a cross-entropy method planner inside an RSSM latent-dynamics
model. At each environment step it samples candidate action sequences, rolls
them out through the RSSM prior, scores them by predicted reward, and refits a
Gaussian to the best candidates.
from torchwm import RSSMPolicy
policy = RSSMPolicy(
model=rssm, # RecurrentStateSpaceModel instance
planning_horizon=20, # H — imagined steps per candidate
num_candidates=1000, # N — candidates per iteration
num_iterations=10, # I — CEM refitting iterations
top_candidates=100, # K — elite candidates kept
device=torch.device("cuda"),
)
# At each environment step:
action = policy.poll(observation)
The CEM loop:
Initialise Gaussian
N(μ, σ)over action sequences of lengthH.Sample
Ncandidate sequences.Roll out each candidate through
deterministic_state_fwd+state_prior, scoring by cumulativepred_reward.Keep the top
Kelite sequences and refit(μ, σ).Repeat for
Iiterations, then execute the first action of the best mean.
Call policy.reset() to zero the hidden and latent state at the start of a new
episode.
RolloutGenerator — collecting rollouts#
RolloutGenerator wraps an environment and an optional policy for collecting
episode data. It produces Episode objects compatible with PlaNet’s
episode-based Memory.
from torchwm import RSSMPolicy, RolloutGenerator, Episode
generator = RolloutGenerator(
env, # Gymnasium-compatible environment
device=torch.device("cuda"),
policy=policy, # RSSMPolicy (or None for random actions)
episode_gen=lambda: Episode(postprocess_fn),
max_episode_steps=1000,
enable_streaming_video=False, # set True to stream rollouts to disk
)
Collecting data#
# Single random episode (warmup):
episode = generator.rollout_once(random_policy=True)
# Multiple episodes:
episodes = generator.rollout_n(n=5, random_policy=True)
# Single episode with learned policy:
episode = generator.rollout_once(explore=True)
Evaluation#
episode, frames, metrics = generator.rollout_eval()
# metrics contains:
# "eval/episode_reward" — total undiscounted return
# "eval/reconstruction_loss" — MSE between reconstructed and true frames
# "eval/reward_pred_loss" — MSE between predicted and true reward
The evaluation rollout collects reconstructed frames through the RSSM decoder and logs prediction quality metrics.
Streaming video#
generator = RolloutGenerator(
env, device,
policy=policy,
enable_streaming_video=True,
streaming_video_path="rollouts/",
streaming_video_fps=20,
)
IRIS Actor-Critic#
IRIS trains a separate actor and critic inside imagined rollouts (no online planning). Both share the same CNN + LSTM architecture but have different output heads.
CNNFeatureExtractor#
The shared 4-layer CNN backbone:
from torchwm import CNNFeatureExtractor
cnn = CNNFeatureExtractor(
frame_shape=(3, 64, 64),
output_size=512,
)
frames = torch.randn(4, 3, 64, 64)
features = cnn(frames) # (4, 512)
Architecture: Conv2D(3→32) → Conv2D(32→64) → Conv2D(64→128) → Conv2D(128→256) → Linear(4096 → output_size), each conv with kernel 3, stride 2, padding 1 and ReLU.
IRISActor#
from torchwm import IRISActor
actor = IRISActor(
action_size=6,
hidden_size=512,
num_layers=4,
frame_shape=(3, 64, 64),
)
# Forward with time dimension:
frames = torch.randn(4, 20, 3, 64, 64) # (B, T, C, H, W)
logits, hidden = actor(frames) # logits: (B, T, 6)
# Single-step action selection:
action = actor.get_action(frame, temperature=1.0, deterministic=False)
The actor processes frames through CNNFeatureExtractor, then a 4-layer LSTM,
then a linear action head. For burn-in (initialising the LSTM hidden state from
context frames), pass burn_in_frames to forward().
IRISCritic#
from torchwm import IRISCritic
critic = IRISCritic(
hidden_size=512,
num_layers=4,
frame_shape=(3, 64, 64),
)
frames = torch.randn(4, 20, 3, 64, 64)
values, hidden = critic(frames) # values: (B, T)
The critic matches the actor’s CNN + LSTM architecture but outputs a scalar
value instead of action logits. It maintains a separate CNNFeatureExtractor
instance from the actor.
IRISPolicy#
from torchwm import IRISPolicy
policy = IRISPolicy(action_size=6)
frames = torch.randn(4, 3, 64, 64)
logits = policy(frames) # (4, 6)
action = policy.act(frames[0]) # scalar action index
hidden = policy.init_hidden(4, "cuda")
IRISPolicy is a convenience wrapper around IRISActor. It does not
automatically create a critic — instantiate IRISCritic separately if needed.
Putting it together#
import torch
import torchwm
actor = torchwm.IRISActor(action_size=6)
critic = torchwm.IRISCritic()
# Imagined rollout loop:
frames = torch.randn(1, 20, 3, 64, 64)
action_logits, _ = actor(frames) # (1, 20, 6)
values, _ = critic(frames) # (1, 20)
# REINFORCE with λ-return baseline:
advantages = ... # computed from values and predicted rewards
actor_loss = -(action_logits * advantages).mean()
critic_loss = F.mse_loss(values, target_values)
Controller — CMA-ES linear policy#
Controller is a simple linear layer that maps the concatenated latent and
deterministic states to an action vector:
from world_models.models.controller import Controller
ctrl = Controller(latent_size=32, hidden_size=256, action_size=3)
action = ctrl(torch.cat([z, h], dim=-1)) # (B, action_size)
Weights are trained with CMA-ES (black-box evolution) rather than gradient
descent. See train_controller.py in the World Models (Ha & Schmidhuber)
pipeline for a complete example.
Export#
RSSM policies and IRIS actor-critic networks can be exported to ONNX / TorchScript for deployment:
# Export RSSMPolicy's underlying RSSM model:
rssm.export("rssm.onnx", format="onnx", example_inputs=...)
# Export IRIS actor:
actor.export("actor.onnx", format="onnx", example_inputs=...)
See Also#
PlaNet — uses RSSMPolicy + RolloutGenerator for online CEM planning
IRIS: Transformers for Sample-Efficient World Models — trains IRIS actor-critic inside imagined rollouts
Dreamer: Model-Based RL with Latent Dynamics — Dreamer-style actor-critic training (separate from these classes)
World Models Study Guide — conceptual overview of World Models pipeline (Controller + CMA-ES)
Exporting Models for Deployment — deploying policies to ONNX / TorchScript