Skip to content

Repository files navigation

Recurrent PPO agent for a three-patch foraging task

A PyTorch implementation of a recurrent actor-critic agent, trained with PPO, on a simulated version of a naturalistic foraging task: three patches ("boxes") that become rewarded at stochastic intervals, a noisy cue on each patch, and a cost for every push. The agent has to decide where to push, when to push, and when to do nothing, from a partial and noisy view of the world. It is the model-side companion to our primate foraging experiments.

Push intervals vs reward intervals, reliable cue Push intervals vs reward intervals, unreliable cue Behavioural error vs decoding error

Left, middle: the trained agent spaces its pushes to each patch according to that patch's reward interval (dashed: identity); with an unreliable cue (middle) the scaling flattens and the agent waits too long at the fast patch, the same undermatching seen in animals. Right: a ridge readout of true reward availability from the LSTM state; trial-by-trial decoding error correlates with the agent's behavioural error (grey: shuffle control), so the recurrent state carries a belief about each patch.

The task (env.py)

Element Definition
Patches 3, with mean reward intervals drawn as a random permutation of (3, 6, 9) steps per episode
Reward timing After each push on patch i, its next reward becomes available after a Gamma(shape 10, mean mu_i) interval
Actions Push patch 1, 2, 3, or do nothing (action 4)
Reward +1 if the patch was armed, a cost of 0.01 for every push; doing nothing is free
Observation Per-patch elapsed time over interval, clipped to [0, 1], corrupted by multiplicative Gaussian noise with std obs_std drawn per episode from [0, 1); obs_std itself is given as input
Agent input observation (4) + last action + last reward + normalised time (7 dims)
Episode 1000 steps, 500 episodes run in parallel as a batch

The agent (Model.py, Agent.py)

  • Two LSTMs (256 units each): one for the actor, one for the critic; linear heads on top.
  • PPO with clipped objective (eps 0.2), GAE (gamma 0.9, lambda 1), entropy bonus 0.01, 80 epochs of full-sequence updates per batch, Adam at 3e-4.
  • Hidden states are carried across the update so truncated-sequence training sees the right context.
  • Orthogonal / Xavier initialisation of recurrent weights, small-gain orthogonal policy head.

Files

Path Purpose
env.py ForagingEnv: the vectorised task (reward timing, noisy cues, push cost); fully documented
config.py ConfigCore: all hyperparameters; saves and loads itself next to the checkpoints
Model.py ActorCritic recurrent network and weight initialisation
Agent.py Agent (action selection, GAE, PPO update, save/load) and rollout Buffer
train.py Command-line trainer (python train.py --seed 0); writes checkpoints to runs/seed<k>/
train.ipynb The same training loop, interactive
figures.ipynb Loads a trained agent, simulates it at several noise levels, and produces the figures above plus decoding analyses
tests/test_env.py Unit tests for the environment (pytest)
requirements.txt torch, numpy, scipy, scikit-learn, matplotlib, jupyter, pytest

Running it

pip install -r requirements.txt
pytest                                   # environment tests
python train.py --seed 0 --episodes 201  # ~200 episodes converge; a few minutes on a GPU
python train.py --seed 0 --episodes 5 --batch-size 16 --steps 100 --device cpu   # 30-second smoke test
jupyter lab figures.ipynb                # point agent.load(...) at a checkpoint name from runs/seed<k>/

train.py picks cuda:0 when available, otherwise CPU. Trained checkpoints are not stored in the repository.

Background

Built in 2023 as the modelling half of a project on foraging under sensory uncertainty (see foraging-allocation-dynamics and the 2026 bioRxiv preprint linked there). The task abstracts a freely moving macaque arena with three push-button patches and cues of controlled reliability.

Licence

MIT (see LICENSE).

About

Recurrent PPO (LSTM actor-critic) agent that learns a three-patch foraging task with stochastic reward timing and noisy cues. PyTorch.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages