Probabilistic Embeddings for Actor-Critic RL
Infer which task you face from a few recent steps, then run one task aware policy from that belief with no fresh gradients.
Why Does This Exist?
Fresh training per task is ruinous. Move a cheetah goal velocity from to meters per second and a fixed policy fails, so standard deep RL burns millions of new steps to relearn locomotion it already knew. Real robots and heavy simulators cannot afford that per variant.
Meta-Reinforcement Learning trains across a task family so a new member costs episodes, not millions of steps. Early answers paid their own tax: MAML in RL needs fresh on policy rollouts per meta update, and recurrent black box adapters tangle the adaptation rule inside memory that reuses poorly off policy.
PEARL (Rakelly and coauthors, ICML 2019) takes the inference view. Train one off policy Soft Actor-Critic agent plus a context encoder that reads recent transitions and infers a compact task variable. Meta-training reuses replay data like SAC does; meta-testing adapts by sharpening a small belief, not by differentiating through the policy.
Think of It Like This
The substitute chef who tastes first
You cover a kitchen with two regulars: one orders extra salt, one orders none. Nobody tells you who sits at table six. You do not reread the cookbook. You taste three spoonfuls: fairly salty, very salty, mildly salty. You average the evidence onto one card reading salty strength about of , clip it to your station, and cook the whole service from it.
Tomorrow the spoonfuls taste flat. Same averaging, opposite card, same hands. You commit to one card per service instead of second guessing after every bite.
Where it stops: no palate is a permutation invariant net and no chef pays a KL penalty to stay near a prior. The commit per service carries over; that is posterior sampling for one episode.
How It Actually Works
A task is one MDP from a family . Plain words: the family is the menu of kitchens, tonight is one kitchen. Context is a small bag of transitions with state , action , reward , next state . Latent variable is a short vector naming the task. Encoder with weights maps context to a distribution over . Policy with weights maps state plus task sample to actions. Critic scores state, action, and task guess together.
Context encoder and conditioned SAC
Each transition passes through a shared net giving one Gaussian factor. The encoder multiplies those factors with a standard normal prior and renormalizes. Actor and critic are plain SAC nets that concatenate to their inputs. Training draws two batches per task from separate buffers: a context batch for and an RL batch for Bellman updates. The critic minimizes soft Bellman error plus , where caps carried task detail and measures distance from the prior. The actor maximizes the SAC objective with entropy temperature . The encoder learns from the critic loss only; actor gradients proved too noisy in the paper ablations. Testing samples one per episode and holds it fixed, which is Thompson sampling: broad prior tries something coherent, growing context sharpens the posterior into task locking behavior.
Worked example
Line world, goals at (task R) and (task L). At with three rightward probes , , , toy factor means give summing to . Prior precision plus three unit factors gives total precision , variance , deviation , mean . So . Sample ; toy policy mean at gives toward . The mirror rewards give mean and action . The diagram draws this exact chain. Belief cost against the prior is about nats before weighting.
Code
The snippet replays both task directions with the standard library only.
def encode_context(rewards: list[float], scale: float = 1.5) -> tuple[float, float]: """Product of unit variance factors plus standard normal prior.""" total = sum(scale * r for r in rewards) precision = 1.0 + len(rewards) return total / precision, 1.0 / precision
def policy_mean(z: float, s: float = 0.0) -> float: return 0.8 * z + 0.1 * s
mu, var = encode_context([0.8, 1.0, 0.6])print(round(mu, 2), round(var, 2), round(policy_mean(mu), 2)) # -> 0.9 0.25 0.72
mul, varl = encode_context([-0.8, -1.0, -0.6])print(round(mul, 2), round(varl, 2), round(policy_mean(mul), 2)) # -> -0.9 0.25 -0.72Both prints match the hand derived posteriors and with actions and .
Watch Out For
Testing on a context the encoder never saw
Training curves look healthy, then new tasks adapt slowly or pick the wrong side for episodes. Usual cause is context shift: training sampled large uniform batches of hundreds of transitions while testing feeds the last five online steps. The encoder meets a new size and distribution, its mean lands between tasks, variance stays wide.
Match the pipeline: same context batch sizes at train and test, recent plus uniform mixes as in the paper sampler, context batch separate from the RL batch. Log posterior mean and variance on held out tasks; test variance far above training variance means the pipeline drifted.
z-collapse: the policy stops listening to z
Posterior means sit near zero for every task, variances hug the prior, actions ignore left versus right. Either bottleneck prices any nonzero out of existence, or encoder gradients flow through the actor loss and drown the task signal in policy noise.
Train the encoder through the critic loss only, start small and raise until task clusters just hold, and plot means colored by true task. Distinct clusters carry signal; one blob means the bottleneck is shut and lowering comes before resizing nets.
The Quick Version
- PEARL pairs one SAC agent with a context encoder so a single policy serves a task family via latent .
- The encoder turns a bag of transitions into Gaussian belief , sampled once per episode for coherent search.
- Separate context and RL batches from replay keep sample cost far below on policy meta-RL.
- Rewards give and mean action toward the right goal.
- Context shift and -collapse are the first two checks when adaptation stalls or ignores the task.