Code and data for What Does a ProcGen Generalization Gap Measure? Action Rules, Residual Entropy, and the Missing Random Floor (Abhisek Keshari, arXiv:2609.32532).
A generalization gap in RL (return on training levels minus return on held-out levels) is usually reported without a reference point. This repository measures one: the return of a uniform-random policy on the same levels under the same evaluation harness. Results cover eight ProcGen environments with PPO at 8M steps (three games extended to 25M):
- The test-time action rule decides which policy is measured. In miner, the sampled policy scores 5.1× the floor on held-out levels while its argmax scores below the floor in every run. Greedy evaluation places two environments significantly below the floor.
- Raw policy entropy over-calls non-convergence. It flags six of eight environments, but 32–66% of that entropy lies on actions with identical effects.
- Against the floor, five of eight sampled policies are clearly above it on held-out levels, and heist's cannot be distinguished from it.
Code, per-episode evaluation returns and analysis scripts are all included. The scripts here
recompute every number, table and figure in the paper from the files in results/.
| Path | What it is |
|---|---|
train_ppo_procgen.py, encoders/ |
PPO agent (CleanRL ProcGen configuration) and the IMPALA / multiscale-FPN encoders |
scripts/eval_gap_curves.py |
evaluation harness: seeded level draws, balanced per-slot episode counting, burn-in |
scripts/tmlr_eval.py |
evaluates final checkpoints under sampled, greedy and (exploratory) merged-argmax rules on both level sets and three seeded draws, with entropy on the visited states |
scripts/tmlr_floor.py |
uniform-random floor on the same draws |
scripts/measurements/ |
shared harness, per-state action-equivalence probe (merged_entropy.py) and effective-action classes (effective_actions.py) |
scripts/tmlr_aggregate.py, tmlr_stats.py, tmlr_entropy.py, tmlr_tables.py, tmlr_cobbe.py, tmlr_merged_stats.py, tmlr_carry.py, tmlr_fig1.py |
aggregation, statistics, tables, normalized scores, exploratory analysis, appendix tables, Figure 1 |
results/tmlr/raw/ |
every evaluation cell (one row per checkpoint × level set × rule × draw) with all 128 episode returns (returns_json) |
results/tmlr/T2_floor.csv |
uniform-random floor, per draw, with episode returns |
results/tmlr/T4_plan.md |
the statistical analysis plan, written before any aggregate was computed |
results/tmlr/T4_addendum_merged_argmax.md |
the exploratory merged-argmax addendum, written before that analysis was run |
results/tmlr/*.csv, tables/ |
aggregated results, statistics and the generated tables |
results/measurements/ |
effective-action partitions and the earlier single-draw evaluation cells used for the exact-reproduction check |
results/cka* |
representation-diagnostic source values (appendix) |
The statistics are a pre-specified confirmatory re-analysis, not a pre-registration: after an initial single-draw analysis of the same checkpoints, the analysis plan (results/tmlr/T4_plan.md) fixed the tests, families, margins and stop conditions, and the three-draw re-analysis (which includes the original draw) was run under it. results/tmlr/T4_plan.md and results/tmlr/T4_addendum_merged_argmax.md are included as written before the corresponding analyses were run; only file paths were updated to this repository's layout. In these and the acceptance reports, "v1" denotes an earlier evaluation of the same checkpoints (one seeded level draw per run, in results/measurements/), which the current evaluation reproduces exactly (results/tmlr/T1_acceptance.md, T2_acceptance.md).
pip install -r requirements.txtThe scripts use python3 (set PY to override). Tested with Python 3.10, torch 2.5, gym 0.26.2, procgen 0.10.7, gymnasium 1.x.
bash scripts/reproduce_tables.shThis recomputes the run-level results, the pre-specified statistics, the entropy screen, all
tables (results/tmlr/tables/), the normalized scores, the exploratory merged-argmax analysis
and Figure 1 from the included per-episode returns.
Model checkpoints are not included because of their size. With checkpoints laid out as
<dir>/{8M,25M}/ppo_<env>_<encoder>_s<seed>/final.pt:
export PROCGEN_CKPT_DIR=<dir>
python scripts/tmlr_floor.py # uniform-random floor
python scripts/tmlr_eval.py --budget 8M --env heist --tag 8M # one environment; see --help
python scripts/tmlr_eval.py --budget 8M --rules greedy_merged --prefix M1 --tag m # exploratory ruleRun tmlr_eval.py once per environment and budget (it appends to results/tmlr/raw/ and skips finished cells, so runs can be split or resumed). Evaluation is deterministic given the seeds recorded in every output row (level-draw seed,
torch seed, reservoir seed). Training: python train_ppo_procgen.py --help (optional experiment tracking with --track needs the Weights & Biases client).
@article{keshari2026procgengap,
title = {What Does a {ProcGen} Generalization Gap Measure? Action Rules, Residual Entropy, and the Missing Random Floor},
author = {Keshari, Abhisek},
journal = {arXiv preprint arXiv:2609.32532},
year = {2026}
}MIT (see LICENSE).