Refresh artefacts; align notebook numbers with the updated paper
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +4 -0
- .gitignore +272 -0
- README.md +577 -40
- ablation_assets/diagnosis_decision_tree.png +0 -0
- ablation_assets/eval_scores_over_training.png +3 -0
- ablation_assets/final_score_comparison.png +2 -2
- ablation_assets/gradient_alignment.png +2 -2
- ablation_assets/gradient_conflict_map.png +2 -2
- ablation_assets/group_comparison.png +0 -0
- ablation_assets/group_summary.csv +5 -5
- ablation_assets/hypothesis_verdict.csv +25 -25
- ablation_assets/main_results.csv +25 -25
- ablation_assets/per_env.csv +25 -25
- ablation_assets/per_env_delta.png +2 -2
- ablation_assets/representation_drift.png +2 -2
- ablation_assets/results.json +0 -0
- ablation_assets/score_delta_over_baseline_rl.png +0 -0
- checkpoints/offline/Minihack-Offline-Diffusion-BC-100M/config.yaml +3 -4
- checkpoints/offline/Minihack-Offline-Diffusion-BC-100M/model.safetensors +1 -1
- checkpoints/offline/Minihack-Offline-Diffusion-BC-100M/selection.json +1 -1
- checkpoints/online/Minihack-Online-Diffusion-DAgger-100M/config.yaml +5 -6
- checkpoints/online/Minihack-Online-Diffusion-DAgger-100M/model.safetensors +1 -1
- checkpoints/online/Minihack-Online-Diffusion-DAgger-100M/selection.json +4 -4
- demo_minihack.ipynb +989 -0
- experiments/rl_finetuning/outputs/minihack_ablations/diagnosis.md +63 -63
- experiments/rl_finetuning/outputs/minihack_ablations/figures/cka_similarity.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_action_diversity.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_advantage_clip.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_attention_only.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_baseline_rl.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_bc_wins.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_entropy_bonus.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_ewc.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_ffn_only.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_frozen_backbone.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_gradient_surgery.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_head_only.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_kl_penalty.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_layer_ablation_top1.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_layer_ablation_top2.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_layer_ablation_top3.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_llrd.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_lora.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_low_t.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_mixed_replay.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_normalized_adv.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_reward_filtering.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_reward_model.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_running_stats.png +2 -2
- experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_t_curriculum.png +2 -2
.gitattributes
CHANGED
|
@@ -120,3 +120,7 @@ experiments/rl_finetuning/outputs/minihack_ablations/figures/t_bin_grad_norms_tr
|
|
| 120 |
experiments/rl_finetuning/outputs/minihack_ablations/figures/t_bin_norms_heatmap.png filter=lfs diff=lfs merge=lfs -text
|
| 121 |
experiments/rl_finetuning/outputs/minihack_ablations/figures/t_distribution_analysis.png filter=lfs diff=lfs merge=lfs -text
|
| 122 |
experiments/rl_finetuning/outputs/minihack_ablations/figures/win_rate_and_effective_batch_size.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
experiments/rl_finetuning/outputs/minihack_ablations/figures/t_bin_norms_heatmap.png filter=lfs diff=lfs merge=lfs -text
|
| 121 |
experiments/rl_finetuning/outputs/minihack_ablations/figures/t_distribution_analysis.png filter=lfs diff=lfs merge=lfs -text
|
| 122 |
experiments/rl_finetuning/outputs/minihack_ablations/figures/win_rate_and_effective_batch_size.png filter=lfs diff=lfs merge=lfs -text
|
| 123 |
+
ablation_assets/eval_scores_over_training.png filter=lfs diff=lfs merge=lfs -text
|
| 124 |
+
ablation_assets/score_delta_over_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 125 |
+
experiments/rl_finetuning/outputs/minihack_ablations/figures/per_layer_grad_heatmap_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 126 |
+
experiments/rl_finetuning/outputs/minihack_ablations/figures/score_delta_over_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ============================================================
|
| 2 |
+
# Project-specific
|
| 3 |
+
# ============================================================
|
| 4 |
+
tmp/
|
| 5 |
+
wandb/
|
| 6 |
+
res/
|
| 7 |
+
runs/
|
| 8 |
+
data/
|
| 9 |
+
checkpoints/
|
| 10 |
+
!checkpoints/*.pth
|
| 11 |
+
play_data/
|
| 12 |
+
texture_cache.pbz2
|
| 13 |
+
texture_cache*.pbz2
|
| 14 |
+
checkpoints*/
|
| 15 |
+
artifacts/
|
| 16 |
+
*.pth
|
| 17 |
+
*.pt
|
| 18 |
+
|
| 19 |
+
# ============================================================
|
| 20 |
+
# Byte-compiled / optimized / DLL files
|
| 21 |
+
# ============================================================
|
| 22 |
+
**/__pycache__/
|
| 23 |
+
*.py[codz]
|
| 24 |
+
*$py.class
|
| 25 |
+
|
| 26 |
+
# C extensions
|
| 27 |
+
*.so
|
| 28 |
+
|
| 29 |
+
# ============================================================
|
| 30 |
+
# Distribution / packaging
|
| 31 |
+
# ============================================================
|
| 32 |
+
.Python
|
| 33 |
+
build/
|
| 34 |
+
develop-eggs/
|
| 35 |
+
dist/
|
| 36 |
+
downloads/
|
| 37 |
+
eggs/
|
| 38 |
+
.eggs/
|
| 39 |
+
lib/
|
| 40 |
+
lib64/
|
| 41 |
+
parts/
|
| 42 |
+
sdist/
|
| 43 |
+
var/
|
| 44 |
+
wheels/
|
| 45 |
+
share/python-wheels/
|
| 46 |
+
*.egg-info/
|
| 47 |
+
.installed.cfg
|
| 48 |
+
*.egg
|
| 49 |
+
MANIFEST
|
| 50 |
+
|
| 51 |
+
# ============================================================
|
| 52 |
+
# PyInstaller
|
| 53 |
+
# ============================================================
|
| 54 |
+
*.manifest
|
| 55 |
+
*.spec
|
| 56 |
+
|
| 57 |
+
# ============================================================
|
| 58 |
+
# Installer logs
|
| 59 |
+
# ============================================================
|
| 60 |
+
pip-log.txt
|
| 61 |
+
pip-delete-this-directory.txt
|
| 62 |
+
|
| 63 |
+
# ============================================================
|
| 64 |
+
# Unit test / coverage reports
|
| 65 |
+
# ============================================================
|
| 66 |
+
htmlcov/
|
| 67 |
+
.tox/
|
| 68 |
+
.nox/
|
| 69 |
+
.coverage
|
| 70 |
+
.coverage.*
|
| 71 |
+
.cache
|
| 72 |
+
nosetests.xml
|
| 73 |
+
coverage.xml
|
| 74 |
+
*.cover
|
| 75 |
+
*.py.cover
|
| 76 |
+
.hypothesis/
|
| 77 |
+
.pytest_cache/
|
| 78 |
+
cover/
|
| 79 |
+
|
| 80 |
+
# ============================================================
|
| 81 |
+
# Translations
|
| 82 |
+
# ============================================================
|
| 83 |
+
*.mo
|
| 84 |
+
*.pot
|
| 85 |
+
|
| 86 |
+
# ============================================================
|
| 87 |
+
# Django
|
| 88 |
+
# ============================================================
|
| 89 |
+
*.log
|
| 90 |
+
local_settings.py
|
| 91 |
+
db.sqlite3
|
| 92 |
+
db.sqlite3-journal
|
| 93 |
+
|
| 94 |
+
# ============================================================
|
| 95 |
+
# Flask
|
| 96 |
+
# ============================================================
|
| 97 |
+
instance/
|
| 98 |
+
.webassets-cache
|
| 99 |
+
|
| 100 |
+
# ============================================================
|
| 101 |
+
# Scrapy
|
| 102 |
+
# ============================================================
|
| 103 |
+
.scrapy
|
| 104 |
+
|
| 105 |
+
# ============================================================
|
| 106 |
+
# Sphinx documentation
|
| 107 |
+
# ============================================================
|
| 108 |
+
docs/_build/
|
| 109 |
+
|
| 110 |
+
# ============================================================
|
| 111 |
+
# PyBuilder
|
| 112 |
+
# ============================================================
|
| 113 |
+
.pybuilder/
|
| 114 |
+
target/
|
| 115 |
+
|
| 116 |
+
# ============================================================
|
| 117 |
+
# Jupyter / IPython
|
| 118 |
+
# ============================================================
|
| 119 |
+
.ipynb_checkpoints
|
| 120 |
+
profile_default/
|
| 121 |
+
ipython_config.py
|
| 122 |
+
|
| 123 |
+
# ============================================================
|
| 124 |
+
# Package managers / lock files
|
| 125 |
+
# (lock files are generally recommended for version control;
|
| 126 |
+
# uncomment to ignore them if your workflow requires it)
|
| 127 |
+
# ============================================================
|
| 128 |
+
|
| 129 |
+
# pyenv
|
| 130 |
+
# .python-version
|
| 131 |
+
|
| 132 |
+
# pipenv
|
| 133 |
+
# Pipfile.lock
|
| 134 |
+
|
| 135 |
+
# uv
|
| 136 |
+
# uv.lock
|
| 137 |
+
|
| 138 |
+
# poetry
|
| 139 |
+
# poetry.lock
|
| 140 |
+
# poetry.toml
|
| 141 |
+
|
| 142 |
+
# pdm
|
| 143 |
+
# pdm.lock
|
| 144 |
+
# pdm.toml
|
| 145 |
+
.pdm-python
|
| 146 |
+
.pdm-build/
|
| 147 |
+
|
| 148 |
+
# pixi
|
| 149 |
+
# pixi.lock
|
| 150 |
+
.pixi/
|
| 151 |
+
|
| 152 |
+
# PEP 582
|
| 153 |
+
__pypackages__/
|
| 154 |
+
|
| 155 |
+
# ============================================================
|
| 156 |
+
# Task queues / message brokers
|
| 157 |
+
# ============================================================
|
| 158 |
+
|
| 159 |
+
# Celery
|
| 160 |
+
celerybeat-schedule
|
| 161 |
+
celerybeat.pid
|
| 162 |
+
|
| 163 |
+
# Redis
|
| 164 |
+
*.rdb
|
| 165 |
+
*.aof
|
| 166 |
+
*.pid
|
| 167 |
+
|
| 168 |
+
# RabbitMQ
|
| 169 |
+
mnesia/
|
| 170 |
+
rabbitmq/
|
| 171 |
+
rabbitmq-data/
|
| 172 |
+
|
| 173 |
+
# ActiveMQ
|
| 174 |
+
activemq-data/
|
| 175 |
+
|
| 176 |
+
# ============================================================
|
| 177 |
+
# Environments
|
| 178 |
+
# ============================================================
|
| 179 |
+
.env
|
| 180 |
+
.envrc
|
| 181 |
+
.venv
|
| 182 |
+
env/
|
| 183 |
+
venv/
|
| 184 |
+
ENV/
|
| 185 |
+
env.bak/
|
| 186 |
+
venv.bak/
|
| 187 |
+
|
| 188 |
+
# ============================================================
|
| 189 |
+
# Type checkers / linters
|
| 190 |
+
# ============================================================
|
| 191 |
+
|
| 192 |
+
# mypy
|
| 193 |
+
.mypy_cache/
|
| 194 |
+
.dmypy.json
|
| 195 |
+
dmypy.json
|
| 196 |
+
|
| 197 |
+
# Pyre
|
| 198 |
+
.pyre/
|
| 199 |
+
|
| 200 |
+
# pytype
|
| 201 |
+
.pytype/
|
| 202 |
+
|
| 203 |
+
# Ruff
|
| 204 |
+
.ruff_cache/
|
| 205 |
+
|
| 206 |
+
# ============================================================
|
| 207 |
+
# Misc Python tools
|
| 208 |
+
# ============================================================
|
| 209 |
+
|
| 210 |
+
# SageMath
|
| 211 |
+
*.sage.py
|
| 212 |
+
|
| 213 |
+
# Cython debug symbols
|
| 214 |
+
cython_debug/
|
| 215 |
+
|
| 216 |
+
# Spyder
|
| 217 |
+
.spyderproject
|
| 218 |
+
.spyproject
|
| 219 |
+
|
| 220 |
+
# Rope
|
| 221 |
+
.ropeproject
|
| 222 |
+
|
| 223 |
+
# Abstra
|
| 224 |
+
.abstra/
|
| 225 |
+
|
| 226 |
+
# mkdocs
|
| 227 |
+
/site
|
| 228 |
+
|
| 229 |
+
# ============================================================
|
| 230 |
+
# Notebooks / interactive tools
|
| 231 |
+
# ============================================================
|
| 232 |
+
|
| 233 |
+
# Marimo
|
| 234 |
+
marimo/_static/
|
| 235 |
+
marimo/_lsp/
|
| 236 |
+
__marimo__/
|
| 237 |
+
|
| 238 |
+
# Streamlit
|
| 239 |
+
.streamlit/secrets.toml
|
| 240 |
+
|
| 241 |
+
# ============================================================
|
| 242 |
+
# IDEs
|
| 243 |
+
# (kept as comments — commit these if your whole team uses
|
| 244 |
+
# the same editor, otherwise add to your global gitignore)
|
| 245 |
+
# ============================================================
|
| 246 |
+
|
| 247 |
+
# PyCharm / JetBrains
|
| 248 |
+
.idea/
|
| 249 |
+
|
| 250 |
+
# VS Code
|
| 251 |
+
# .vscode/
|
| 252 |
+
|
| 253 |
+
# ============================================================
|
| 254 |
+
# PyPI
|
| 255 |
+
# ============================================================
|
| 256 |
+
.pypirc
|
| 257 |
+
|
| 258 |
+
# ============================================================
|
| 259 |
+
# OS-specific
|
| 260 |
+
# ============================================================
|
| 261 |
+
|
| 262 |
+
# macOS
|
| 263 |
+
.DS_Store
|
| 264 |
+
|
| 265 |
+
__pycache__/
|
| 266 |
+
*.pyc
|
| 267 |
+
|
| 268 |
+
CHANGES.md
|
| 269 |
+
CLI_CHANGES.md
|
| 270 |
+
|
| 271 |
+
# Claude Code: per-machine settings stay local; the rest of .claude/ is shared
|
| 272 |
+
.claude/settings.local.json
|
README.md
CHANGED
|
@@ -1,63 +1,600 @@
|
|
| 1 |
---
|
| 2 |
-
license:
|
|
|
|
|
|
|
| 3 |
tags:
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
-
# ReMDM Planner
|
| 12 |
|
| 13 |
-
Anonymous
|
| 14 |
-
|
|
|
|
| 15 |
|
| 16 |
-
This
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
|
| 21 |
-
##
|
| 22 |
|
| 23 |
```
|
| 24 |
-
src/
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
```
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
-
##
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
##
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
| 49 |
|
| 50 |
## Citation
|
| 51 |
|
| 52 |
```bibtex
|
| 53 |
-
@inproceedings{
|
| 54 |
-
title
|
| 55 |
-
author
|
| 56 |
-
|
| 57 |
-
|
|
|
|
| 58 |
}
|
| 59 |
```
|
| 60 |
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
library_name: pytorch
|
| 4 |
+
pipeline_tag: reinforcement-learning
|
| 5 |
tags:
|
| 6 |
+
- reinforcement-learning
|
| 7 |
+
- planning
|
| 8 |
+
- discrete-diffusion
|
| 9 |
+
- remdm
|
| 10 |
+
- minihack
|
| 11 |
+
- nethack
|
| 12 |
+
- pytorch
|
| 13 |
---
|
| 14 |
|
| 15 |
+
# ReMDM Planner — MiniHack artefacts
|
| 16 |
|
| 17 |
+
Anonymous release accompanying *Return-Weighted ELBO Fine-Tuning Degrades Masked
|
| 18 |
+
Diffusion Planners* (under double-blind review). It contains the code, the trained
|
| 19 |
+
checkpoints and the pre-computed results behind every MiniHack number in the paper.
|
| 20 |
|
| 21 |
+
**Anonymity notice.** This repository is an anonymised mirror prepared for
|
| 22 |
+
double-blind review. Author names, institutional affiliations, experiment-tracking
|
| 23 |
+
identifiers and absolute filesystem paths have been removed or replaced with
|
| 24 |
+
placeholders. Cluster hardware is referred to only as `GPU-24GB` and `GPU-H200`.
|
| 25 |
|
| 26 |
+
## What is here
|
| 27 |
|
| 28 |
```
|
| 29 |
+
src/ configs/ environments/ experiments/ scripts/ tests/
|
| 30 |
+
main.py pyproject.toml uv.lock LICENSE demo_minihack.ipynb
|
| 31 |
+
|
| 32 |
+
checkpoint_inference.pth Stripped EMA weights only (~21 MB), what the notebook loads
|
| 33 |
+
checkpoints/
|
| 34 |
+
online/Minihack-Online-Diffusion-DAgger-100M/ DAgger planner (the checkpoint every
|
| 35 |
+
ablation fine-tunes)
|
| 36 |
+
offline/Minihack-Offline-Diffusion-BC-100M/ Offline BC planner
|
| 37 |
+
|
| 38 |
+
ablation_assets/ Flat copy of the assets the notebook reads:
|
| 39 |
+
8 PNGs + 4 CSVs + results.json
|
| 40 |
+
experiments/rl_finetuning/outputs/minihack_ablations/
|
| 41 |
+
figures/ tables/ results.json
|
| 42 |
+
results/ Aggregated CSVs and single-seed inference evaluations
|
| 43 |
```
|
| 44 |
|
| 45 |
+
## Headline numbers (MiniHack, ID win rate)
|
| 46 |
+
|
| 47 |
+
| | Win rate |
|
| 48 |
+
|---|---|
|
| 49 |
+
| Pretrained (DAgger) checkpoint, ablation-harness protocol | **47.5%** |
|
| 50 |
+
| Baseline return-weighted ELBO fine-tuning, 500 iters | 43.8% ± 6.1 |
|
| 51 |
+
| Best of 25 conditions (head-only) | 49.6% ± 3.9 |
|
| 52 |
+
| Worst of 25 conditions (normalised advantages) | 12.1% ± 4.1 |
|
| 53 |
+
|
| 54 |
+
Three conditions finish nominally above the checkpoint, but the best-versus-baseline
|
| 55 |
+
difference has a bootstrap interval of [−2.1, +13.7] points at p = 0.40, so the paper
|
| 56 |
+
does not read them as improvements. MiniHack is reported as a matching ordering, not
|
| 57 |
+
as a second confirmation; the claim rests on Craftax Classic, where the pretrained
|
| 58 |
+
checkpoint scores 11.81 and no condition of the 25 recovers it.
|
| 59 |
+
|
| 60 |
+
Direct evaluation of the same checkpoint at 50 episodes per environment gives 48.5%
|
| 61 |
+
in distribution and 4.7% on the three held-out layouts (paper Table 6).
|
| 62 |
|
| 63 |
+
## Evaluation path
|
| 64 |
|
| 65 |
+
MiniHack has a single evaluation path. `demo_minihack.ipynb` calls
|
| 66 |
+
`Evaluator._run_episodes_batched` from `src/planners/inference.py`, and the ablation
|
| 67 |
+
harness (`experiments/rl_finetuning/ablations/training.py`) imports the same
|
| 68 |
+
`Evaluator` and calls `evaluator.evaluate(...)`. Notebook and paper numbers therefore
|
| 69 |
+
come from one sampler and one replanning rule, differing only in episode count and
|
| 70 |
+
seed offset.
|
| 71 |
|
| 72 |
+
## Quick start
|
| 73 |
|
| 74 |
+
```python
|
| 75 |
+
from huggingface_hub import snapshot_download
|
| 76 |
+
path = snapshot_download(repo_id="AnonMLuser/remdm-planner-minihack", local_dir="remdm-planner-minihack")
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
Then open `demo_minihack.ipynb`, or see the project documentation below.
|
| 80 |
|
| 81 |
## Citation
|
| 82 |
|
| 83 |
```bibtex
|
| 84 |
+
@inproceedings{anonymous2026returnweighted,
|
| 85 |
+
title = {Return-Weighted ELBO Fine-Tuning Degrades Masked Diffusion Planners},
|
| 86 |
+
author = {Anonymous},
|
| 87 |
+
booktitle = {Submitted to NeurIPS 2026},
|
| 88 |
+
year = {2026},
|
| 89 |
+
note = {Under double-blind review}
|
| 90 |
}
|
| 91 |
```
|
| 92 |
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
# ReMDM Planner for MiniHack
|
| 96 |
+
|
| 97 |
+
PyTorch implementation of **ReMDM** (Remasking Discrete Diffusion Model) for action-sequence planning in [MiniHack](https://github.com/facebookresearch/minihack) navigation environments. A dual-stream transformer generates 64-step action plans by iteratively denoising masked token sequences, conditioned on a 9x9 local crop and the full 21x79 dungeon map. Trained with **DAgger** under BFS oracle supervision, from scratch; generalises zero-shot from 4 in-distribution to 3 out-of-distribution environments.
|
| 98 |
+
|
| 99 |
+
The sibling repository [`craftax/`](../craftax) implements the same method in JAX on Craftax. Both repos share the same CLI, config layout and README structure; commands transfer between them by swapping the repo name and benchmark-specific values.
|
| 100 |
+
|
| 101 |
+
## Method
|
| 102 |
+
|
| 103 |
+
The planner starts from a fully-masked action sequence and iteratively unmasks tokens over `K` denoising steps via the ReMDM Algorithm 1 posterior (per-token Bernoulli unmasking), while ReMDM remasking lets committed tokens be re-predicted for plan refinement. Two independent training pipelines are compared head-to-head in the accompanying paper (under submission; citation to follow): **online DAgger** under a BFS oracle (primary) and **offline behavioural cloning** on pre-collected oracle datasets. See [Architecture](#architecture) and [Diffusion](#diffusion) for details.
|
| 104 |
+
|
| 105 |
+
## Setup
|
| 106 |
+
|
| 107 |
+
Prerequisites: Python 3.12+, [uv](https://docs.astral.sh/uv/). `nle` compiles from source on macOS.
|
| 108 |
+
Linux GPU use needs NVIDIA driver >= 580 for CUDA 13, or >= 525 with `--extra cuda12`.
|
| 109 |
+
|
| 110 |
+
```bash
|
| 111 |
+
# macOS (arm64)
|
| 112 |
+
brew install cmake
|
| 113 |
+
|
| 114 |
+
# Linux (x86_64), if building from source
|
| 115 |
+
sudo apt-get install build-essential cmake bison flex libbz2-dev
|
| 116 |
+
|
| 117 |
+
git clone https://github.com/ANONYMOUS/remdm-planners.git
|
| 118 |
+
cd remdm-planners/minihack
|
| 119 |
+
|
| 120 |
+
# Default. macOS gets the PyPI build (MPS); Linux gets PyPI's CUDA 13.0 build.
|
| 121 |
+
# Installs the dev group (pytest) too.
|
| 122 |
+
uv sync
|
| 123 |
+
|
| 124 |
+
# Linux, explicit CUDA 13.2 (driver >= 580)
|
| 125 |
+
uv sync --extra cuda13
|
| 126 |
+
|
| 127 |
+
# Linux, CUDA 12.6 fallback (driver >= 525, or Maxwell/Pascal cards)
|
| 128 |
+
uv sync --extra cuda12
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
Extras: `cuda13` and `cuda12` are mutually exclusive and Linux-only. Neither is needed on a
|
| 132 |
+
modern driver: plain `uv sync` already yields a CUDA 13.0 build on Linux. Use `cuda12` only
|
| 133 |
+
if `nvidia-smi` reports a driver older than 580.
|
| 134 |
+
|
| 135 |
+
> **Install path must not contain spaces.** MiniHack's `mh_patch_nhdat.sh` interpolates paths unquoted and fails silently on whitespace, leaving every environment as the same default level with no goal staircase. `src/envs/minihack_env.py` detects this and substitutes a Python implementation, but a space-free path avoids the issue entirely.
|
| 136 |
+
|
| 137 |
+
## Repo layout
|
| 138 |
+
|
| 139 |
+
```
|
| 140 |
+
minihack/
|
| 141 |
+
├── configs/ Experiment configs (defaults.yaml + presets, see Configuration)
|
| 142 |
+
├── environments/ Custom .des scenario files (empty; user-supplied)
|
| 143 |
+
├── src/ Model, diffusion, envs, planner pipelines
|
| 144 |
+
├── experiments/
|
| 145 |
+
│ └── rl_finetuning/ RL fine-tuning ablation suite (run_ablations.py)
|
| 146 |
+
├── scripts/ HF upload utilities, DAgger and ablation profilers
|
| 147 |
+
├── tests/ Smoke suite — uv run pytest
|
| 148 |
+
├── checkpoints/ Gitignored — offline/, online/ (see Checkpoints)
|
| 149 |
+
├── results/inference/ Eval JSONs from --mode inference (published, see Checkpoints)
|
| 150 |
+
├── demo_minihack.ipynb Demo notebook
|
| 151 |
+
├── main.py CLI entry point
|
| 152 |
+
└── pyproject.toml uv project — deps, cuda extra, dev group
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
## Quickstart
|
| 156 |
+
|
| 157 |
+
Collects a few oracle trajectories, trains under a 5k env-step budget, prints ID evaluation. A few minutes on CPU.
|
| 158 |
+
|
| 159 |
+
```bash
|
| 160 |
+
python main.py --mode smoke
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
## Training
|
| 164 |
+
|
| 165 |
+
Two independent training methods; neither depends on the other. An offline BC checkpoint can warm-start DAgger via `--checkpoint`, but this was not used for the paper results.
|
| 166 |
+
|
| 167 |
+
### Online DAgger (primary)
|
| 168 |
+
|
| 169 |
+
```bash
|
| 170 |
+
python main.py --mode online # full paper recipe (defaults.yaml)
|
| 171 |
+
python main.py --mode online --config configs/final_minihack_gpu_24gb.yaml
|
| 172 |
+
python main.py --mode online --override total_timesteps=1000000 --override dagger_lr=0.0001
|
| 173 |
+
python main.py --mode online --checkpoint checkpoints/iter600.pth # resume
|
| 174 |
+
python main.py --mode online --checkpoint checkpoints/iter600.pth --no-warm-start
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
Per iteration: curriculum-sampled model rollouts, BFS oracle labelling on the same seeds, efficiency filtering into the replay buffer, `grad_steps_per_iteration` gradient steps. Halts when cumulative env steps reach `total_timesteps`. See [DAgger training loop](#dagger-training-loop).
|
| 178 |
+
|
| 179 |
+
### Offline BC
|
| 180 |
+
|
| 181 |
+
First collect a dataset, then train on it:
|
| 182 |
+
|
| 183 |
+
```bash
|
| 184 |
+
python main.py --mode collect # 5000 eps/env -> data/dataset.pt
|
| 185 |
+
python main.py --mode collect --data data/small.pt --override collect_episodes_per_env=2000
|
| 186 |
+
|
| 187 |
+
python main.py --mode offline --data data/dataset.pt
|
| 188 |
+
python main.py --mode offline --data data/dataset.pt --override total_timesteps=500000
|
| 189 |
+
|
| 190 |
+
# Resume (restores optimizer, scheduler, step counter, W&B run)
|
| 191 |
+
python main.py --mode offline --data data/dataset.pt --checkpoint checkpoints/offline_step40000.pth
|
| 192 |
+
```
|
| 193 |
+
|
| 194 |
+
Gradient steps default to `total_timesteps // offline_batch_size`; ID + OOD eval runs on the `id_eval_every_timesteps` / `ood_eval_every_timesteps` cadence. The `offline_*_grad_steps` keys override that in grad-step units — see the hazard note under [Configuration](#configuration). See the hazard note under [Configuration](#configuration).
|
| 195 |
+
|
| 196 |
+
## Evaluation from a checkpoint
|
| 197 |
+
|
| 198 |
+
```bash
|
| 199 |
+
python main.py --mode inference --checkpoint checkpoints/iter600.pth # all ID + OOD
|
| 200 |
+
python main.py --mode inference --checkpoint wandb:entity/project/checkpoint-iter600:latest
|
| 201 |
+
|
| 202 |
+
# Specific environments, save JSON
|
| 203 |
+
python main.py --mode inference --checkpoint checkpoints/iter600.pth \
|
| 204 |
+
--envs MiniHack-Room-Random-5x5-v0 MiniHack-MazeWalk-45x19-v0 \
|
| 205 |
+
--episodes 100 --output results/inference/eval.json
|
| 206 |
+
|
| 207 |
+
python main.py --mode inference --checkpoint checkpoints/iter600.pth \
|
| 208 |
+
--des environments/<your_level>.des # custom .des scenarios (dir ships empty)
|
| 209 |
+
python main.py --mode inference --checkpoint checkpoints/iter600.pth --no-ema
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
`--checkpoint` accepts a local `.pth` path or a `wandb:` artifact reference (`wandb:entity/project/name:version`). Inference uses EMA weights unless `--no-ema` is given.
|
| 213 |
+
|
| 214 |
+
Write eval JSONs into `results/inference/` (created for you): `scripts/hf_upload.py` publishes every JSON it finds there.
|
| 215 |
+
|
| 216 |
+
**Match the config to the checkpoint.** The model is built from the config, not the checkpoint, and a mismatch raises at load. Always evaluate with the checkpoint's own config snapshot:
|
| 217 |
+
|
| 218 |
+
```bash
|
| 219 |
+
DIR=checkpoints/online/Minihack-Online-Diffusion-DAgger-100M
|
| 220 |
+
python main.py --mode inference --config $DIR/config.yaml --checkpoint $DIR/iter563.pth
|
| 221 |
+
```
|
| 222 |
+
|
| 223 |
+
## Baselines and ablations
|
| 224 |
+
|
| 225 |
+
### RL and imitation baselines
|
| 226 |
+
|
| 227 |
+
Six algorithms: SB3 discrete-action RL (`ppo`, `a2c`, `dqn`, `ppo-rnn`), Behavioural Cloning (`bc`) on oracle demos, and a causal Decision Transformer (`dt`). All share `total_timesteps`, so numbers are comparable to DAgger and offline BC. Hyperparameters live under the `baselines_*` config namespace; outputs go to `baselines_output_dir`.
|
| 228 |
+
|
| 229 |
+
```bash
|
| 230 |
+
python main.py --mode baselines --algo ppo
|
| 231 |
+
python main.py --mode baselines --algo a2c
|
| 232 |
+
python main.py --mode baselines --algo dqn --seeds 0 1 2
|
| 233 |
+
python main.py --mode baselines --algo ppo-rnn
|
| 234 |
+
python main.py --mode baselines --algo bc --num-seeds 3
|
| 235 |
+
python main.py --mode baselines --algo dt --seeds 0 1 2
|
| 236 |
+
python main.py --mode baselines --algo ppo --output results/ppo.json
|
| 237 |
+
python main.py --mode baselines --algo ppo --override total_timesteps=5650000 # match ReMDM online budget
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
### Architecture ablations
|
| 241 |
+
|
| 242 |
+
```bash
|
| 243 |
+
# Local-only planner (no global stream, no goal head), trained from scratch
|
| 244 |
+
python main.py --mode online --config configs/ablation_local_only.yaml
|
| 245 |
+
|
| 246 |
+
# Blind-global: zero the global observation of a trained dual-stream model at eval
|
| 247 |
+
python main.py --mode inference --checkpoint checkpoints/iter600.pth --blind-global
|
| 248 |
+
```
|
| 249 |
+
|
| 250 |
+
### RL fine-tuning ablation suite
|
| 251 |
+
|
| 252 |
+
25 registered ablations (same names as in the craftax repo). See `experiments/README.md`.
|
| 253 |
+
|
| 254 |
+
```bash
|
| 255 |
+
python experiments/rl_finetuning/run_ablations.py --list
|
| 256 |
+
python experiments/rl_finetuning/run_ablations.py --checkpoint path/to/ckpt.pth --all
|
| 257 |
+
python experiments/rl_finetuning/run_ablations.py \
|
| 258 |
+
--checkpoint wandb:entity/project/checkpoint-iter600:latest \
|
| 259 |
+
--ablations baseline_rl kl_penalty --fast
|
| 260 |
+
```
|
| 261 |
+
|
| 262 |
+
## Configuration
|
| 263 |
+
|
| 264 |
+
One YAML config holds the experiment; the CLI holds the run.
|
| 265 |
+
|
| 266 |
+
- **`configs/defaults.yaml`**: the **shared final paper recipe**, not a cheap baseline. Both clusters train exactly this; running with no `--config` trains it too.
|
| 267 |
+
- **Config files** (`configs/*.yaml`): any file passed via `--config` is deep-merged onto `defaults.yaml`, so presets contain **only their deltas** — never re-state a default value. Presets are a single layer: they never inherit from one another.
|
| 268 |
+
- **CLI flags**: per-invocation values — `--seed`, `--checkpoint`, `--data`, `--output`, `--episodes`, `--envs`, mode switches.
|
| 269 |
+
- **`--override KEY=VALUE`** (repeatable): ad hoc config overrides. Keys are validated against `defaults.yaml` and values are cast to the key's type; a typo is an error, not a silent no-op.
|
| 270 |
+
|
| 271 |
+
Precedence, lowest to highest: `configs/defaults.yaml` < `--config` file < `--override` and run flags.
|
| 272 |
+
|
| 273 |
+
> **Hazard when writing a preset.** Four keys silently *override* an env-step-derived value when non-null, and `defaults.yaml` now sets all four as part of the recipe: `offline_total_grad_steps`, `offline_eval_every_grad_steps`, `offline_checkpoint_every_grad_steps`, `offline_buffer_capacity`. A preset that wants its own `total_timesteps` to govern the offline budget must pin them back to **explicit `null`** — omitting them inherits the pins. Left unpinned, `smoke.yaml` would train 60,000 offline gradient steps instead of 19. `tests/test_config.py` enforces the pins for every preset that derives its own budget.
|
| 274 |
+
|
| 275 |
+
| Preset | Purpose |
|
| 276 |
+
|---|---|
|
| 277 |
+
| `configs/defaults.yaml` | **Shared final paper recipe** — the full run both clusters train |
|
| 278 |
+
| `configs/smoke.yaml` | Smoke test (`total_timesteps=5000`, small buffer, W&B off) |
|
| 279 |
+
| `configs/ablation_local_only.yaml` | Local-only planner ablation (`use_global_stream: false`) |
|
| 280 |
+
| `configs/gpu_24gb_bigger_model.yaml` | 24 GB GPU, larger model (384D, 6 heads) |
|
| 281 |
+
| `configs/gpu_24gb_learning_behaviour.yaml` | 24 GB GPU learning-behaviour study (eta=0.18, B=6144) |
|
| 282 |
+
| `configs/final_minihack_gpu_h200.yaml` | **Paper run, H200.** Machine values only: worker counts (32) and dataset path |
|
| 283 |
+
| `configs/final_minihack_gpu_24gb.yaml` | **Paper run, RTX 3090 Ti.** Machine values only: dataset path (workers stay at the default 8) |
|
| 284 |
+
|
| 285 |
+
Key hyperparameters are documented inline in `configs/defaults.yaml`; the [appendix](#key-hyperparameters) tabulates them.
|
| 286 |
+
|
| 287 |
+
## Checkpoints
|
| 288 |
+
|
| 289 |
+
Training writes to a unique run directory under `checkpoint_dir` (default `checkpoints/`), named `{tag}_{YYYYMMDD}_{HHMMSS}_{hex4}`. DAgger saves `iter{N}.pth` on the `checkpoint_every_timesteps` cadence; offline BC saves `offline_step{N}.pth` and `offline_final.pth`. Checkpoints also upload as versioned W&B artifacts (type `model`) when `use_wandb` is on. All checkpoints store the W&B run ID, so passing them back via `--checkpoint` resumes the same W&B curve automatically.
|
| 290 |
+
|
| 291 |
+
`checkpoints/` is gitignored.
|
| 292 |
+
|
| 293 |
+
Released weights and the pre-computed ablation outputs are mirrored on the
|
| 294 |
+
Hugging Face Hub at [`AnonMLuser/remdm-planner-minihack`](https://huggingface.co/AnonMLuser/remdm-planner-minihack),
|
| 295 |
+
an anonymous account holding the layout below.
|
| 296 |
+
|
| 297 |
+
```bash
|
| 298 |
+
uv run hf download AnonMLuser/remdm-planner-minihack --include "checkpoints/**" --local-dir .
|
| 299 |
+
```
|
| 300 |
+
|
| 301 |
+
| Directory | Method | Selected at | Sample-equivalents |
|
| 302 |
+
|---|---|---|---|
|
| 303 |
+
| `checkpoints/online/Minihack-Online-Diffusion-DAgger-100M` | DAgger (main result) | `iter563` | 100M |
|
| 304 |
+
| `checkpoints/offline/Minihack-Offline-Diffusion-BC-100M` | Offline BC baseline | `offline_step50000` | 100M |
|
| 305 |
+
|
| 306 |
+
Each published directory ships `<step>.pth` (full training state), `model.safetensors` (EMA weights only, no pickle), `config.yaml` (config snapshot) and `selection.json`. The `-100M` suffix counts **sample-equivalents, not env steps** — the runs behind these train 5,650,000 env steps. See [Checkpoint format](#checkpoint-format) for the `.pth` schema and programmatic loading.
|
| 307 |
+
|
| 308 |
+
Historical note: the released DAgger `selection.json` records `"every": null, "configured_max": null` and `"unit": "dagger_iterations"`. It was published by a version of `selection()` that read two config keys which had been renamed out of the config, so the values came back empty. It is **historical and noncanonical** and stays as published (author decision 2026-08-17); the checkpoint's own `config_<step>.yaml` carries the real cadence and budget, so nothing is lost. A publish from the current code records the candidate set in env steps — `"every": 940000, "configured_max": 5650000` for the shipped recipe — and raises rather than writing a null for any key it cannot read.
|
| 309 |
+
|
| 310 |
+
### Publishing to the Hub
|
| 311 |
+
|
| 312 |
+
`scripts/hf_upload.py` rediscovers and uploads three things, each keeping its repo-relative path: `checkpoints/` (adding a `model.safetensors` EMA export and `selection.json` per directory), every `experiments/rl_finetuning/outputs/<run>/` holding a `results.json` (with `diagnosis.md`, `tables/`, `figures/`), and the eval JSONs in `results/inference/`. It drops W&B and hub config keys, shortens absolute paths and regenerates the model card.
|
| 313 |
+
|
| 314 |
+
```bash
|
| 315 |
+
HF_TOKEN=hf_xxx uv run python scripts/hf_upload.py --repo-id <ANON_HF_REPO_ID> \
|
| 316 |
+
--selection-metric "mean ID+OOD win rate" --dry-run
|
| 317 |
+
```
|
| 318 |
+
|
| 319 |
+
`--dry-run` prints the staged tree and card without uploading; drop it to upload. `--selection-metric` records what the best-of-N checkpoints were chosen on. Also `--inference-results <FILE|DIR> ...` (eval JSONs kept elsewhere), `--private`, `--yes`. Publish one model per directory, with a single `.pth` and config.
|
| 320 |
+
|
| 321 |
+
**Checkpoint discovery expects the released layout**, `checkpoints/<role>/<name>/*.pth` — the layout the Hub repo mirrors. A training run writes to its own `checkpoints/dagger_<timestamp>/` directory, so copy the checkpoints you mean to release into `checkpoints/{offline,online}/<name>/` first, or nothing is staged. `checkpoints/hf/` is skipped: that is where a Hub *download* lands, and publishing from it would push already-published artefacts back up into a nested `checkpoints/hf/checkpoints/...` tree.
|
| 322 |
+
|
| 323 |
+
## Results, citation, licence
|
| 324 |
+
|
| 325 |
+
Results tables and the full method description are in the accompanying paper (under submission); `demo_minihack.ipynb` reproduces the headline comparison. Citation to be added on publication. Licence: MIT, see `LICENSE`.
|
| 326 |
+
|
| 327 |
+
---
|
| 328 |
+
|
| 329 |
+
# Appendix: benchmark-specific detail
|
| 330 |
+
|
| 331 |
+
## Environments
|
| 332 |
+
|
| 333 |
+
| In-distribution (training) | Out-of-distribution (zero-shot eval) |
|
| 334 |
+
|---|---|
|
| 335 |
+
| `MiniHack-Room-Random-5x5-v0` (small random room) | `MiniHack-Room-Dark-15x15-v0` (dark room) |
|
| 336 |
+
| `MiniHack-Room-Random-15x15-v0` (large random room) | `MiniHack-Corridor-R5-v0` (five-room corridor) |
|
| 337 |
+
| `MiniHack-Corridor-R2-v0` (two-room corridor) | `MiniHack-MazeWalk-45x19-v0` (large maze) |
|
| 338 |
+
| `MiniHack-MazeWalk-9x9-v0` (small maze) | |
|
| 339 |
+
|
| 340 |
+
## Architecture
|
| 341 |
+
|
| 342 |
+
**`LocalDiffusionPlannerWithGlobal`** (~5.2M parameters):
|
| 343 |
+
|
| 344 |
+
```
|
| 345 |
+
Local stream: 9x9 glyphs -> Embedding(6000,64) -> CNN(64->32->64) -> Linear -> 1 token
|
| 346 |
+
Global stream: 21x79 glyphs -> Embedding(6000,32) -> CNN(32->32->64) -> Pool(2,4) -> 8 tokens
|
| 347 |
+
Goal head: mean(global) -> MLP -> [B,2] staircase coords (aux loss)
|
| 348 |
+
Gate: sigmoid(learnable scalar, init=-3.0) * global_tokens
|
| 349 |
+
Action stream: Embedding(14, 256) + timestep_emb(100, 256) + position_emb(64, 256)
|
| 350 |
+
Transformer: concat [1 + 8 + 64 = 73 tokens] -> 4-layer encoder (256D, 4 heads, pre-norm)
|
| 351 |
+
Output head: last 64 tokens -> Linear(256, 12) -> action logits
|
| 352 |
+
```
|
| 353 |
+
|
| 354 |
+
Signature: `(local_obs, global_obs, noisy_action_seq, t_discrete)` -> `{"actions": [B,64,12], "goal_pred": [B,2]}`.
|
| 355 |
+
|
| 356 |
+
`LocalDiffusionPlanner` (no global stream, no goal head) is the `ablation_local_only` variant. Supported by `--mode offline` and `--mode online`; the `experiments/` ablation suite assumes the goal head is present.
|
| 357 |
+
|
| 358 |
+
## Diffusion
|
| 359 |
+
|
| 360 |
+
- **Forward process (MDLM):** each action token is independently replaced with `MASK` (12) with probability `1 - alpha(t)`, `alpha(t)` linear or cosine. PAD (13) is never masked.
|
| 361 |
+
- **Loss:** continuous-time MDLM NELBO: per sample `w(t) * sum_masked(CE) / L` with `w(t) = -alpha'(t) / (1 - alpha(t))` clipped to `[0, 1000]`; optional `label_smoothing`.
|
| 362 |
+
- **Greedy sampling:** used for DAgger collection. Same MaskGIT loop, argmax decoding, no temperature/top-K/remasking, `diffusion_steps_collect` steps.
|
| 363 |
+
|
| 364 |
+
**Reverse sampling (ReMDM Algorithm 1)**, over `K` steps (default 10):
|
| 365 |
+
|
| 366 |
+
1. Predict logits; apply temperature and top-p (nucleus) filtering; sample predictions and record each committed token's decode probability `psi`.
|
| 367 |
+
2. **Unmask:** each masked position commits independently with the posterior probability `(alpha_s - (1 - sigma) alpha_t) / (1 - alpha_t)`.
|
| 368 |
+
3. **ReMDM remask:** each committed position re-masks with probability `sigma` from the configured Section-4.1 schedule.
|
| 369 |
+
4. Final step: any remaining masked positions are committed by a greedy cleanup pass.
|
| 370 |
+
|
| 371 |
+
| Strategy | Formula | Description |
|
| 372 |
+
|---|---|---|
|
| 373 |
+
| `rescale` | `p = eta * sigma_max` | Proportional to noise level |
|
| 374 |
+
| `cap` | `p = min(eta, sigma_max)` | Fixed upper bound |
|
| 375 |
+
| `conf` | `p = softmax(-confidence) * eta * sigma_max` | Low-confidence tokens remasked more |
|
| 376 |
+
|
| 377 |
+
## Key hyperparameters
|
| 378 |
+
|
| 379 |
+
**Model**
|
| 380 |
+
|
| 381 |
+
| Parameter | Default | Description |
|
| 382 |
+
|---|---|---|
|
| 383 |
+
| `n_embd` | 256 | Transformer hidden dimension |
|
| 384 |
+
| `n_head` | 4 | Attention heads |
|
| 385 |
+
| `n_layer` | 4 | Transformer blocks |
|
| 386 |
+
| `n_global_tokens` | 8 | Global stream context tokens |
|
| 387 |
+
| `seq_len` | 64 | Action plan length |
|
| 388 |
+
| `dropout` | 0.0 | Forward masking already regularises |
|
| 389 |
+
| `ema_decay` | 0.999 | EMA smoothing for inference weights |
|
| 390 |
+
| `global_gate_init` | -3.0 | Initial global gate logit |
|
| 391 |
+
| `use_global_stream` | true | `false` builds the local-only ablation variant |
|
| 392 |
+
|
| 393 |
+
**Diffusion**
|
| 394 |
+
|
| 395 |
+
| Parameter | Default | Description |
|
| 396 |
+
|---|---|---|
|
| 397 |
+
| `noise_schedule` | `linear` | `linear`, `cosine`, or `cosine_sq` (MDLM App E.1 naming) |
|
| 398 |
+
| `num_diffusion_steps` | 100 | Discrete timestep resolution |
|
| 399 |
+
| `diffusion_steps_eval` | 10 | Denoising iterations at inference |
|
| 400 |
+
| `diffusion_steps_collect` | 5 | Denoising iterations during collection |
|
| 401 |
+
| `remask_strategy` | `conf` | `rescale`, `cap`, or `conf` |
|
| 402 |
+
| `eta` | 0.15 | Remasking strength |
|
| 403 |
+
| `temperature` | 0.5 | Sampling temperature |
|
| 404 |
+
| `top_p` | 0.9 | Nucleus threshold (ReMDM Sec 5) |
|
| 405 |
+
| `replan_every` | 16 | Env steps before replanning; the actions already executed in the current plan window are locked into the new plan (inpainting) |
|
| 406 |
+
| `loss_weight_clip` | 1000.0 | NELBO weight clip bound |
|
| 407 |
+
| `label_smoothing` | 0.0 | Cross-entropy label smoothing |
|
| 408 |
+
| `physics_aware_sampling` | false | Penalise hazardous actions at inference |
|
| 409 |
+
|
| 410 |
+
**Training budget (unified).** Offline BC, DAgger and the SB3 baselines share one env-step budget. This is the only knob that should change to scale a run.
|
| 411 |
+
|
| 412 |
+
| Parameter | Default | Description |
|
| 413 |
+
|---|---|---|
|
| 414 |
+
| `total_timesteps` | 5,650,000 | Shared env-step budget |
|
| 415 |
+
| `id_eval_every_timesteps` | 470,000 | ID eval cadence |
|
| 416 |
+
| `ood_eval_every_timesteps` | 470,000 | OOD eval cadence |
|
| 417 |
+
| `checkpoint_every_timesteps` | 940,000 | Checkpoint cadence |
|
| 418 |
+
|
| 419 |
+
- **Offline BC:** gradient steps = `total_timesteps // offline_batch_size`. The cosine LR `T_max` derives from the same quantity, so any run length decays to the 10% floor at its end.
|
| 420 |
+
- **DAgger:** tracks cumulative `env.step()` calls (model + oracle) and halts at `total_timesteps`. `episodes_per_iteration` and `grad_steps_per_iteration` set the collect/train ratio and **must not** scale with the budget.
|
| 421 |
+
- **Caveat, `ema_decay`:** an absolute-update-count constant (half-life ~ `1 / (1 - decay)` steps). Shifting `total_timesteps` by more than ~2x changes the fraction of training the EMA window covers; set a matching decay manually for very short or long runs.
|
| 422 |
+
|
| 423 |
+
**Training**
|
| 424 |
+
|
| 425 |
+
| Parameter | Default | Description |
|
| 426 |
+
|---|---|---|
|
| 427 |
+
| `offline_lr` | 0.0003 | BC LR (cosine-decayed to 10%) |
|
| 428 |
+
| `dagger_lr` | 0.00003 | DAgger LR (constant) |
|
| 429 |
+
| `offline_batch_size` | 2048 | Offline BC batch size |
|
| 430 |
+
| `dagger_batch_size` | 2048 | DAgger batch size |
|
| 431 |
+
| `offline_grad_clip` | 1.0 | Gradient norm clip (offline) |
|
| 432 |
+
| `dagger_grad_clip` | 1.0 | Gradient norm clip (DAgger) |
|
| 433 |
+
| `weight_decay` | 0.0 | AdamW weight decay (core training; the ablation suite keeps 1e-4) |
|
| 434 |
+
| `grad_steps_per_iteration` | 100 | Gradient steps per DAgger iteration |
|
| 435 |
+
| `episodes_per_iteration` | 30 | Episodes per DAgger iteration |
|
| 436 |
+
| `aux_loss_weight` | 0.5 | Auxiliary goal loss weight |
|
| 437 |
+
| `buffer_capacity` | 10000 | Replay buffer size (windows) |
|
| 438 |
+
| `efficiency_multiplier` | 1.5 | DAgger efficiency filter threshold |
|
| 439 |
+
| `curriculum_preseed` | true | Pre-seed curriculum with 50/50 prior |
|
| 440 |
+
| `curriculum_queue_size` | 100 | Curriculum window size per environment |
|
| 441 |
+
|
| 442 |
+
**Collection, evaluation, performance, logging**
|
| 443 |
+
|
| 444 |
+
| Parameter | Default | Description |
|
| 445 |
+
|---|---|---|
|
| 446 |
+
| `collect_episodes_per_env` | 5000 | Oracle episodes per ID environment |
|
| 447 |
+
| `collect_num_workers` | 8 | Process workers for collection |
|
| 448 |
+
| `collect_output` | `data/dataset.pt` | Collected dataset path (per-run: `--data`) |
|
| 449 |
+
| `eval_episodes_per_env` | 50 | Episodes per env at eval (per-run: `--episodes`) |
|
| 450 |
+
| `checkpoint_eval_episodes` | 50 | Episodes per env at checkpoint eval |
|
| 451 |
+
| `use_amp` | true | Mixed precision via `torch.amp` |
|
| 452 |
+
| `torch_compile` | true | `torch.compile` the model |
|
| 453 |
+
| `num_collection_workers` | 8 | Workers for DAgger collection |
|
| 454 |
+
| `use_wandb` | true | Enable W&B logging |
|
| 455 |
+
| `wandb_project` | `remdm-planner-minihack` | W&B project |
|
| 456 |
+
| `wandb_resume_id` | null | W&B run ID for resumption |
|
| 457 |
+
| `offline_log_every` | 50 | Log frequency (offline steps) |
|
| 458 |
+
| `seed` | null | RNG seed (null = random; per-run: `--seed`) |
|
| 459 |
+
|
| 460 |
+
## DAgger training loop
|
| 461 |
+
|
| 462 |
+
1. **Curriculum sampling:** pick an environment weighted by difficulty (low win-rate sampled more).
|
| 463 |
+
2. **Model rollout:** EMA model, greedy sampling, replanning every 16 steps with the executed prefix locked, `episodes_per_iteration` episodes.
|
| 464 |
+
3. **Oracle rollout:** BFS oracle on the **same seed**.
|
| 465 |
+
4. **Efficiency filter:** add the oracle trajectory if the model failed or took >1.5x the oracle's steps.
|
| 466 |
+
5. **Budget accounting:** `env_steps_total += model_steps + oracle_steps`; halt at `total_timesteps`.
|
| 467 |
+
6. **Training:** sample the buffer, run `grad_steps_per_iteration` steps, update EMA after each.
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
BFS oracle priority: (1) kick adjacent doors, (2) BFS to staircase, (3) BFS to frontier, (4) BFS to farthest tile, (5) random cardinal.
|
| 471 |
+
|
| 472 |
+
## Reward shaping
|
| 473 |
+
|
| 474 |
+
| Component | Value | Condition |
|
| 475 |
+
|---|---|---|
|
| 476 |
+
| Win bonus | +20.0 | Episode won |
|
| 477 |
+
| BFS progress | +0.5 * (prev_dist - curr_dist) | Closer to staircase |
|
| 478 |
+
| Exploration | +0.05 | New tile visited |
|
| 479 |
+
| Step penalty | -0.01 | Every step |
|
| 480 |
+
|
| 481 |
+
## Checkpoint format
|
| 482 |
+
|
| 483 |
+
```python
|
| 484 |
+
# DAgger
|
| 485 |
+
{
|
| 486 |
+
"model_state_dict": ..., "ema_state_dict": ...,
|
| 487 |
+
"optimizer_state_dict": ..., "scheduler_state_dict": ...,
|
| 488 |
+
"curriculum_state": {...},
|
| 489 |
+
"iteration": int,
|
| 490 |
+
"env_steps": int, # cumulative env.step() calls
|
| 491 |
+
"wandb_run_id": str | None,
|
| 492 |
+
"rng_states": {"torch", "numpy", "python"},
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
# Offline BC, step-level (offline_step{N}.pth, when checkpoint_every_timesteps > 0)
|
| 496 |
+
{
|
| 497 |
+
"model_state_dict": ..., "ema_state_dict": ...,
|
| 498 |
+
"optimizer_state_dict": ..., "scheduler_state_dict": ...,
|
| 499 |
+
"step": int,
|
| 500 |
+
"env_steps": int, # step * offline_batch_size
|
| 501 |
+
"wandb_run_id": str | None,
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
# Offline BC, final (offline_final.pth)
|
| 505 |
+
{"model_state_dict": ..., "ema_state_dict": ..., "wandb_run_id": str | None}
|
| 506 |
+
```
|
| 507 |
+
|
| 508 |
+
### Load programmatically
|
| 509 |
+
|
| 510 |
+
```python
|
| 511 |
+
# Inference, from safetensors (already EMA weights)
|
| 512 |
+
from safetensors.torch import load_file
|
| 513 |
+
from src.config import load_config
|
| 514 |
+
from src.models.denoiser import make_model
|
| 515 |
+
|
| 516 |
+
DIR = "checkpoints/online/Minihack-Online-Diffusion-DAgger-100M"
|
| 517 |
+
cfg = load_config(f"{DIR}/config.yaml")
|
| 518 |
+
model = make_model(cfg)
|
| 519 |
+
model.load_state_dict(load_file(f"{DIR}/model.safetensors"))
|
| 520 |
+
model.eval()
|
| 521 |
+
```
|
| 522 |
+
|
| 523 |
+
```python
|
| 524 |
+
# From the full .pth, to resume or to pick training vs EMA weights
|
| 525 |
+
import torch
|
| 526 |
+
from src.config import load_config
|
| 527 |
+
from src.models.denoiser import make_model, ModelEMA
|
| 528 |
+
|
| 529 |
+
DIR = "checkpoints/online/Minihack-Online-Diffusion-DAgger-100M"
|
| 530 |
+
cfg = load_config(f"{DIR}/config.yaml")
|
| 531 |
+
ckpt = torch.load(f"{DIR}/iter563.pth", map_location="cpu", weights_only=False)
|
| 532 |
+
|
| 533 |
+
model = make_model(cfg)
|
| 534 |
+
model.load_state_dict(ckpt["model_state_dict"])
|
| 535 |
+
|
| 536 |
+
ema = ModelEMA(model, decay=cfg.ema_decay)
|
| 537 |
+
ema.load_state_dict(ckpt["ema_state_dict"])
|
| 538 |
+
ema.apply_to(model) # what evaluation uses by default
|
| 539 |
+
model.eval()
|
| 540 |
+
```
|
| 541 |
+
|
| 542 |
+
### W&B artifacts and run resumption
|
| 543 |
+
|
| 544 |
+
W&B model artifacts contain the `.pth` and a `config.yaml` snapshot. Reference format is `wandb:entity/project/artifact-name:version`, version being `latest`, `v0`, `v1`.
|
| 545 |
+
|
| 546 |
+
All training loops store the W&B run ID in their checkpoints. Resuming extracts it and passes it to `wandb.init(resume="must")`, so curves continue with no gaps.
|
| 547 |
+
|
| 548 |
+
```bash
|
| 549 |
+
python main.py --mode online --checkpoint checkpoints/iter600.pth # automatic
|
| 550 |
+
# Manual override (checkpoint predates the feature)
|
| 551 |
+
python main.py --mode online --checkpoint old.pth --override wandb_resume_id=abc123xyz
|
| 552 |
+
```
|
| 553 |
+
|
| 554 |
+
## W&B metric namespaces
|
| 555 |
+
|
| 556 |
+
| Namespace | Contents |
|
| 557 |
+
|---|---|
|
| 558 |
+
| `diffusion/` | `loss`, `loss_diff`, `loss_aux` |
|
| 559 |
+
| `train/` | `buffer_size`, `buffer_online_frac`, `model_won`, `added_to_buffer`, `episodes_collected`, `model_steps`, `oracle_steps`, `efficiency_ratio`, `lr`, `grad_norm`, `global_gate`, `env_steps`, `progress` |
|
| 560 |
+
| `speed/` | `iter_time_sec`, `collect_time_sec`, `train_step_time_sec`, `samples_per_sec`, `env_steps_per_sec`, `gpu_memory_mb` |
|
| 561 |
+
| `model/` | `param_norm`, `param_drift_from_init`, `ema_gate_value` (every 10 iters) |
|
| 562 |
+
| `eval_id/{env}/`, `eval_ood/{env}/` | Per-env win rate, avg steps, avg reward |
|
| 563 |
+
| `eval_id/`, `eval_ood/` | `mean_win_rate` |
|
| 564 |
+
| `curriculum/{env}/` | `win_rate` per training environment |
|
| 565 |
+
| `ckpt_eval_id/`, `ckpt_eval_ood/` | Per-env metrics at checkpoint time |
|
| 566 |
+
| `ckpt_eval/` | `id_winrate`, `ood_winrate` |
|
| 567 |
+
| `offline/` | `final_loss`, `total_steps`, `total_timesteps` (summary only) |
|
| 568 |
+
|
| 569 |
+
DAgger and offline BC both emit to `eval_id/` and `eval_ood/`, through the same `Evaluator` and EMA-weight path.
|
| 570 |
+
|
| 571 |
+
## Performance tuning
|
| 572 |
+
|
| 573 |
+
| Key | Default | Effect |
|
| 574 |
+
|---|---|---|
|
| 575 |
+
| `use_amp` | **true** | `torch.amp.autocast("cuda")` + `GradScaler` in both trainers. Roughly 2x on gradient steps, with loss and win rates statistically equivalent to FP32. No-op on CPU |
|
| 576 |
+
| `torch_compile` | **true** | `torch.compile(model, mode="default")`. No measured gain beyond AMP |
|
| 577 |
+
| `num_collection_workers` | 8 | Affects the threaded CPU fallback. Collection auto-selects GPU-batched (CUDA, `episodes_per_iteration > 1`) > threaded CPU > sequential |
|
| 578 |
+
|
| 579 |
+
Profile with `python scripts/profile_dagger.py [--override key=value ...]`.
|
| 580 |
+
|
| 581 |
+
## Testing
|
| 582 |
+
|
| 583 |
+
```bash
|
| 584 |
+
uv run pytest # 15 modules; `slow` deselected by default
|
| 585 |
+
uv run pytest -m slow # slow entry points only (BC + PPO baselines)
|
| 586 |
+
```
|
| 587 |
+
|
| 588 |
+
`conftest.py` forces CPU and disables W&B. `test_spec_*.py` and `test_method_spec*.py` pin each canonical statement of the method/training specification against the implementation; `test_config.py` and `test_recipe_values.py` guard the preset, delta-only and poolability rules and the shipped recipe values; `test_ablation_perf.py` and `test_gpu_step_perf.py` hold measured perf expectations. `test_smoke_src.py` and `test_smoke_experiments.py` cover both pipelines: modules import, the model builds from `configs/defaults.yaml`, a forward pass returns the expected shape and dtype with no NaNs, one training step gives a finite loss, save/reload reproduces identical output, each entry point runs, and all 25 registry ablations step. They assert things *run*, not that results are good. CPU-only, seeded, synthetic data; nothing written outside `tmp_path`. For a quality signal, use `--mode smoke`.
|
| 589 |
+
|
| 590 |
+
## Implementation notes
|
| 591 |
|
| 592 |
+
- **MDLM loss** returns `0.0` (not NaN) when no masked positions exist. NELBO-weighted per MDLM eq (10).
|
| 593 |
+
- **PAD tokens** are never masked and are excluded from the loss.
|
| 594 |
+
- **Sampling paths:** evaluation uses stochastic ReMDM (temperature, top-p, remasking, `diffusion_steps_eval`); DAgger collection uses greedy argmax (`diffusion_steps_collect`).
|
| 595 |
+
- **`remdm_sample`** guarantees a fully committed output via a final greedy cleanup of any remaining masked positions.
|
| 596 |
+
- **EMA** updates after every gradient step, not per iteration. `DataCollector` syncs EMA weights before each rollout.
|
| 597 |
+
- **Curriculum** starts from a 50/50 prior per environment and buckets the rolling win-rate: `[0, 0.15)` -> 0.2, `[0.15, 0.85)` -> 1.0, `[0.85, 1.0]` -> 0.1.
|
| 598 |
+
- **Replay buffer** pins offline data at the front; only online samples are FIFO-evicted. Returns `None` when empty.
|
| 599 |
+
- **Global gate** starts at `sigmoid(-3.0) ~ 0.047`, nearly closed, so the global stream cannot destabilise early training.
|
| 600 |
+
- **DAgger warm-start:** iteration 0 seeds the buffer with 3 oracle trajectories per ID environment (12 total).
|
ablation_assets/diagnosis_decision_tree.png
DELETED
|
Binary file (34.4 kB)
|
|
|
ablation_assets/eval_scores_over_training.png
ADDED
|
Git LFS Details
|
ablation_assets/final_score_comparison.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
ablation_assets/gradient_alignment.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
ablation_assets/gradient_conflict_map.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
ablation_assets/group_comparison.png
CHANGED
|
|
ablation_assets/group_summary.csv
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
Group,N,Mean,Best,Worst,StdDev
|
| 2 |
-
Baseline,1,0.
|
| 3 |
-
A,6,0.
|
| 4 |
-
B,7,0.
|
| 5 |
-
C,7,0.
|
| 6 |
-
D,4,0.
|
|
|
|
| 1 |
Group,N,Mean,Best,Worst,StdDev
|
| 2 |
+
Baseline,1,0.4375,0.4375,0.4375,0.0
|
| 3 |
+
A,6,0.4278,0.4417,0.4125,0.0095
|
| 4 |
+
B,7,0.3857,0.4875,0.1208,0.1164
|
| 5 |
+
C,7,0.4399,0.4958,0.3917,0.0408
|
| 6 |
+
D,4,0.3802,0.4417,0.3208,0.0428
|
ablation_assets/hypothesis_verdict.csv
CHANGED
|
@@ -1,26 +1,26 @@
|
|
| 1 |
Method,Group,Score,Delta_Baseline,Verdict,Hypothesis
|
| 2 |
-
action_diversity,D,0.
|
| 3 |
-
advantage_clip,B,0.
|
| 4 |
-
attention_only,C,0.
|
| 5 |
-
baseline_rl,Baseline,0.
|
| 6 |
-
bc_wins,B,0.
|
| 7 |
-
entropy_bonus,B,0.
|
| 8 |
-
ewc,A,0.
|
| 9 |
-
ffn_only,C,0.
|
| 10 |
-
frozen_backbone,C,0.
|
| 11 |
-
gradient_surgery,B,0.
|
| 12 |
-
head_only,C,0.
|
| 13 |
-
kl_penalty,A,0.
|
| 14 |
-
layer_ablation_top1,C,0.
|
| 15 |
-
layer_ablation_top2,C,0.
|
| 16 |
-
layer_ablation_top3,C,0.
|
| 17 |
-
llrd,A,0.
|
| 18 |
-
lora,A,0.
|
| 19 |
-
low_t,B,0.
|
| 20 |
-
mixed_replay,A,0.
|
| 21 |
-
normalized_adv,B,0.
|
| 22 |
-
reward_filtering,D,0.
|
| 23 |
-
reward_model,D,0.
|
| 24 |
-
running_stats,D,0.
|
| 25 |
-
t_curriculum,B,0.
|
| 26 |
-
trust_region_kl,A,0.
|
|
|
|
| 1 |
Method,Group,Score,Delta_Baseline,Verdict,Hypothesis
|
| 2 |
+
action_diversity,D,0.375,-0.0625,COLLAPSE,If diversity filtering helps: degenerate plans corrupt training
|
| 3 |
+
advantage_clip,B,0.3375,-0.1,COLLAPSE,If clipping helps: large advantage magnitudes destabilise training
|
| 4 |
+
attention_only,C,0.3958,-0.0417,NEUTRAL,"If attention-only works: model needs routing updates, not feature updates"
|
| 5 |
+
baseline_rl,Baseline,0.4375,0.0,NEUTRAL,Diagnoses whether the RL signal alone causes collapse
|
| 6 |
+
bc_wins,B,0.4583,0.0208,NEUTRAL,If BC on wins helps: the return weighting is the specific cause
|
| 7 |
+
entropy_bonus,B,0.4458,0.0083,NEUTRAL,If entropy bonus helps: collapse is mode-collapse; not a gradient problem
|
| 8 |
+
ewc,A,0.4333,-0.0042,NEUTRAL,If EWC helps: forgetting pretrained representations is the proximate cause
|
| 9 |
+
ffn_only,C,0.4375,0.0,NEUTRAL,If FFN-only works: stored knowledge (FFN as memory) needs updating; not attention
|
| 10 |
+
frozen_backbone,C,0.4667,0.0292,IMPROVEMENT,If frozen backbone helps: deep gradient flow into backbone causes collapse
|
| 11 |
+
gradient_surgery,B,0.4875,0.05,IMPROVEMENT,If PCGrad helps: gradients are conflicting and resolvable by projection
|
| 12 |
+
head_only,C,0.4958,0.0583,IMPROVEMENT,If head-only works: backbone representations are fine; only decision boundary needs updating
|
| 13 |
+
kl_penalty,A,0.4417,0.0042,NEUTRAL,If this helps: catastrophic forgetting is the primary cause; soft regularisation suffices
|
| 14 |
+
layer_ablation_top1,C,0.4875,0.05,IMPROVEMENT,Minimal unfrozen depth needed; collapse depth correlates with gradient flow depth
|
| 15 |
+
layer_ablation_top2,C,0.4042,-0.0333,NEUTRAL,Minimal unfrozen depth needed; collapse depth correlates with gradient flow depth
|
| 16 |
+
layer_ablation_top3,C,0.3917,-0.0458,NEUTRAL,Minimal unfrozen depth needed; collapse depth correlates with gradient flow depth
|
| 17 |
+
llrd,A,0.4208,-0.0167,NEUTRAL,If LLRD helps: deep gradient flow into early layers corrupts representations
|
| 18 |
+
lora,A,0.4333,-0.0042,NEUTRAL,If LoRA works: too many unconstrained degrees of freedom cause collapse
|
| 19 |
+
low_t,B,0.4208,-0.0167,NEUTRAL,If low-t helps: high-t (coarse-structure) gradients are biased
|
| 20 |
+
mixed_replay,A,0.4125,-0.025,NEUTRAL,If mixed replay helps: online data distribution alone is too corrupted
|
| 21 |
+
normalized_adv,B,0.1208,-0.3167,COLLAPSE,If std normalisation helps: simple mean normalisation is too loose
|
| 22 |
+
reward_filtering,D,0.4417,0.0042,NEUTRAL,If filtering helps: noisy/low-return data poisons gradients
|
| 23 |
+
reward_model,D,0.3208,-0.1167,COLLAPSE,If reward model helps: raw returns are too sparse; learned model smooths signal
|
| 24 |
+
running_stats,D,0.3833,-0.0542,COLLAPSE,If running stats help: batch normalisation is too noisy for small batches
|
| 25 |
+
t_curriculum,B,0.4292,-0.0083,NEUTRAL,If curriculum helps: ordering of learning signals matters
|
| 26 |
+
trust_region_kl,A,0.425,-0.0125,NEUTRAL,If hard constraint helps: soft KL is insufficient -- a hard boundary is needed
|
ablation_assets/main_results.csv
CHANGED
|
@@ -1,26 +1,26 @@
|
|
| 1 |
Method,Group,Score,Score_Std,Delta_Pretrained,Delta_Baseline,Verdict
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
layer_ablation_top1,C,0.
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
low_t,B,0.
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
running_stats,D,0.
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
normalized_adv,B,0.
|
|
|
|
| 1 |
Method,Group,Score,Score_Std,Delta_Pretrained,Delta_Baseline,Verdict
|
| 2 |
+
head_only,C,0.4958,0.0386,0.0208,0.0583,IMPROVEMENT
|
| 3 |
+
gradient_surgery,B,0.4875,0.0354,0.0125,0.05,IMPROVEMENT
|
| 4 |
+
layer_ablation_top1,C,0.4875,0.0204,0.0125,0.05,IMPROVEMENT
|
| 5 |
+
frozen_backbone,C,0.4667,0.0059,-0.0083,0.0292,IMPROVEMENT
|
| 6 |
+
bc_wins,B,0.4583,0.0358,-0.0167,0.0208,NEUTRAL
|
| 7 |
+
entropy_bonus,B,0.4458,0.0328,-0.0292,0.0083,NEUTRAL
|
| 8 |
+
kl_penalty,A,0.4417,0.0212,-0.0333,0.0042,NEUTRAL
|
| 9 |
+
reward_filtering,D,0.4417,0.0503,-0.0333,0.0042,NEUTRAL
|
| 10 |
+
baseline_rl,Baseline,0.4375,0.0612,-0.0375,0.0,NEUTRAL
|
| 11 |
+
ffn_only,C,0.4375,0.0102,-0.0375,0.0,NEUTRAL
|
| 12 |
+
ewc,A,0.4333,0.0118,-0.0417,-0.0042,NEUTRAL
|
| 13 |
+
lora,A,0.4333,0.0156,-0.0417,-0.0042,NEUTRAL
|
| 14 |
+
t_curriculum,B,0.4292,0.0257,-0.0458,-0.0083,NEUTRAL
|
| 15 |
+
trust_region_kl,A,0.425,0.0102,-0.05,-0.0125,NEUTRAL
|
| 16 |
+
llrd,A,0.4208,0.0118,-0.0542,-0.0167,NEUTRAL
|
| 17 |
+
low_t,B,0.4208,0.0524,-0.0542,-0.0167,NEUTRAL
|
| 18 |
+
mixed_replay,A,0.4125,0.0468,-0.0625,-0.025,NEUTRAL
|
| 19 |
+
layer_ablation_top2,C,0.4042,0.0156,-0.0708,-0.0333,NEUTRAL
|
| 20 |
+
attention_only,C,0.3958,0.0236,-0.0792,-0.0417,NEUTRAL
|
| 21 |
+
layer_ablation_top3,C,0.3917,0.0257,-0.0833,-0.0458,NEUTRAL
|
| 22 |
+
running_stats,D,0.3833,0.0312,-0.0917,-0.0542,COLLAPSE
|
| 23 |
+
action_diversity,D,0.375,0.0408,-0.1,-0.0625,COLLAPSE
|
| 24 |
+
advantage_clip,B,0.3375,0.051,-0.1375,-0.1,COLLAPSE
|
| 25 |
+
reward_model,D,0.3208,0.0118,-0.1542,-0.1167,COLLAPSE
|
| 26 |
+
normalized_adv,B,0.1208,0.0412,-0.3542,-0.3167,COLLAPSE
|
ablation_assets/per_env.csv
CHANGED
|
@@ -1,26 +1,26 @@
|
|
| 1 |
Method,MiniHack-Corridor-R2-v0,MiniHack-MazeWalk-9x9-v0,MiniHack-Room-Random-15x15-v0,MiniHack-Room-Random-5x5-v0
|
| 2 |
-
action_diversity,0.
|
| 3 |
-
advantage_clip,0.
|
| 4 |
-
attention_only,0.
|
| 5 |
-
baseline_rl,0.
|
| 6 |
-
bc_wins,0.
|
| 7 |
-
entropy_bonus,0.
|
| 8 |
-
ewc,0.
|
| 9 |
-
ffn_only,0.
|
| 10 |
-
frozen_backbone,0.
|
| 11 |
-
gradient_surgery,0.
|
| 12 |
-
head_only,0.
|
| 13 |
-
kl_penalty,0.
|
| 14 |
-
layer_ablation_top1,0.
|
| 15 |
-
layer_ablation_top2,0.
|
| 16 |
-
layer_ablation_top3,0.
|
| 17 |
-
llrd,0.
|
| 18 |
-
lora,0.
|
| 19 |
-
low_t,0.
|
| 20 |
-
mixed_replay,0.
|
| 21 |
-
normalized_adv,0.
|
| 22 |
-
reward_filtering,0.
|
| 23 |
-
reward_model,0.
|
| 24 |
-
running_stats,0.
|
| 25 |
-
t_curriculum,0.
|
| 26 |
-
trust_region_kl,0.
|
|
|
|
| 1 |
Method,MiniHack-Corridor-R2-v0,MiniHack-MazeWalk-9x9-v0,MiniHack-Room-Random-15x15-v0,MiniHack-Room-Random-5x5-v0
|
| 2 |
+
action_diversity,0.4333,0.1,0.2667,0.7667
|
| 3 |
+
advantage_clip,0.4333,0.15,0.1,0.6833
|
| 4 |
+
attention_only,0.3667,0.1667,0.25,0.85
|
| 5 |
+
baseline_rl,0.45,0.1333,0.3333,0.8333
|
| 6 |
+
bc_wins,0.4333,0.1833,0.2833,0.8167
|
| 7 |
+
entropy_bonus,0.45,0.1,0.3333,0.8333
|
| 8 |
+
ewc,0.45,0.15,0.3333,0.8333
|
| 9 |
+
ffn_only,0.4833,0.1667,0.4,0.7833
|
| 10 |
+
frozen_backbone,0.4167,0.15,0.4833,0.9167
|
| 11 |
+
gradient_surgery,0.45,0.15,0.4333,0.85
|
| 12 |
+
head_only,0.45,0.15,0.4667,0.9
|
| 13 |
+
kl_penalty,0.4167,0.15,0.25,0.8167
|
| 14 |
+
layer_ablation_top1,0.4833,0.1833,0.45,0.8667
|
| 15 |
+
layer_ablation_top2,0.4,0.15,0.3,0.7833
|
| 16 |
+
layer_ablation_top3,0.4333,0.1667,0.25,0.7667
|
| 17 |
+
llrd,0.4333,0.1333,0.2667,0.8667
|
| 18 |
+
lora,0.4833,0.15,0.4167,0.85
|
| 19 |
+
low_t,0.4667,0.1167,0.3333,0.85
|
| 20 |
+
mixed_replay,0.4167,0.1167,0.3167,0.8
|
| 21 |
+
normalized_adv,0.0833,0.0333,0.1,0.2833
|
| 22 |
+
reward_filtering,0.4,0.1667,0.4333,0.8167
|
| 23 |
+
reward_model,0.3667,0.1,0.1167,0.6833
|
| 24 |
+
running_stats,0.4333,0.15,0.2833,0.7333
|
| 25 |
+
t_curriculum,0.4167,0.1167,0.2667,0.7667
|
| 26 |
+
trust_region_kl,0.4333,0.15,0.3167,0.9
|
ablation_assets/per_env_delta.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
ablation_assets/representation_drift.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
ablation_assets/results.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
ablation_assets/score_delta_over_baseline_rl.png
CHANGED
|
|
Git LFS Details
|
checkpoints/offline/Minihack-Offline-Diffusion-BC-100M/config.yaml
CHANGED
|
@@ -19,9 +19,9 @@ baselines_eval_episodes_per_env: null
|
|
| 19 |
baselines_eval_freq_env_steps: 10000
|
| 20 |
baselines_n_envs_per_id: 2
|
| 21 |
baselines_output_dir: outputs/baselines
|
| 22 |
-
baselines_wandb_project:
|
| 23 |
buffer_capacity: 10000
|
| 24 |
-
checkpoint_dir:
|
| 25 |
checkpoint_eval_episodes: 50
|
| 26 |
checkpoint_every_timesteps: 940000
|
| 27 |
collect_episodes_per_env: 5000
|
|
@@ -88,5 +88,4 @@ torch_compile: true
|
|
| 88 |
total_timesteps: 5650000
|
| 89 |
use_amp: true
|
| 90 |
use_global_stream: true
|
| 91 |
-
|
| 92 |
-
weight_decay: 0.0001
|
|
|
|
| 19 |
baselines_eval_freq_env_steps: 10000
|
| 20 |
baselines_n_envs_per_id: 2
|
| 21 |
baselines_output_dir: outputs/baselines
|
| 22 |
+
baselines_wandb_project: remdm-planner-minihack-baselines
|
| 23 |
buffer_capacity: 10000
|
| 24 |
+
checkpoint_dir: checkpoints/offline_20260822_022901_0edc
|
| 25 |
checkpoint_eval_episodes: 50
|
| 26 |
checkpoint_every_timesteps: 940000
|
| 27 |
collect_episodes_per_env: 5000
|
|
|
|
| 88 |
total_timesteps: 5650000
|
| 89 |
use_amp: true
|
| 90 |
use_global_stream: true
|
| 91 |
+
weight_decay: 0.0
|
|
|
checkpoints/offline/Minihack-Offline-Diffusion-BC-100M/model.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 20974876
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a288ee5349613239757bfcc6e1847af89a9b5a28ff0a2843862476bdf3c9d3ed
|
| 3 |
size 20974876
|
checkpoints/offline/Minihack-Offline-Diffusion-BC-100M/selection.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
"selected": {
|
| 4 |
"gradient_step": 50000
|
| 5 |
},
|
| 6 |
-
"selection_metric":
|
| 7 |
"candidates": {
|
| 8 |
"unit": "gradient_steps",
|
| 9 |
"every": 10000,
|
|
|
|
| 3 |
"selected": {
|
| 4 |
"gradient_step": 50000
|
| 5 |
},
|
| 6 |
+
"selection_metric": "mean ID win rate over 50 episodes/env, EMA weights",
|
| 7 |
"candidates": {
|
| 8 |
"unit": "gradient_steps",
|
| 9 |
"every": 10000,
|
checkpoints/online/Minihack-Online-Diffusion-DAgger-100M/config.yaml
CHANGED
|
@@ -19,14 +19,14 @@ baselines_eval_episodes_per_env: null
|
|
| 19 |
baselines_eval_freq_env_steps: 10000
|
| 20 |
baselines_n_envs_per_id: 2
|
| 21 |
baselines_output_dir: outputs/baselines
|
| 22 |
-
baselines_wandb_project:
|
| 23 |
buffer_capacity: 10000
|
| 24 |
-
checkpoint_dir:
|
| 25 |
checkpoint_eval_episodes: 50
|
| 26 |
checkpoint_every_timesteps: 940000
|
| 27 |
collect_episodes_per_env: 5000
|
| 28 |
-
collect_num_workers:
|
| 29 |
-
collect_output: data/
|
| 30 |
crop_size: 9
|
| 31 |
curriculum_preseed: true
|
| 32 |
curriculum_queue_size: 100
|
|
@@ -60,7 +60,7 @@ n_global_tokens: 8
|
|
| 60 |
n_head: 4
|
| 61 |
n_layer: 4
|
| 62 |
noise_schedule: linear
|
| 63 |
-
num_collection_workers:
|
| 64 |
num_diffusion_steps: 100
|
| 65 |
offline_batch_size: 2048
|
| 66 |
offline_buffer_capacity: 1500000
|
|
@@ -88,5 +88,4 @@ torch_compile: true
|
|
| 88 |
total_timesteps: 5650000
|
| 89 |
use_amp: true
|
| 90 |
use_global_stream: true
|
| 91 |
-
use_wandb: true
|
| 92 |
weight_decay: 0.0001
|
|
|
|
| 19 |
baselines_eval_freq_env_steps: 10000
|
| 20 |
baselines_n_envs_per_id: 2
|
| 21 |
baselines_output_dir: outputs/baselines
|
| 22 |
+
baselines_wandb_project: remdm-planner-minihack-baselines
|
| 23 |
buffer_capacity: 10000
|
| 24 |
+
checkpoint_dir: checkpoints/dagger_20260819_195240_ad84
|
| 25 |
checkpoint_eval_episodes: 50
|
| 26 |
checkpoint_every_timesteps: 940000
|
| 27 |
collect_episodes_per_env: 5000
|
| 28 |
+
collect_num_workers: 8
|
| 29 |
+
collect_output: data/oracle_bc_gpu_24gb.pt
|
| 30 |
crop_size: 9
|
| 31 |
curriculum_preseed: true
|
| 32 |
curriculum_queue_size: 100
|
|
|
|
| 60 |
n_head: 4
|
| 61 |
n_layer: 4
|
| 62 |
noise_schedule: linear
|
| 63 |
+
num_collection_workers: 8
|
| 64 |
num_diffusion_steps: 100
|
| 65 |
offline_batch_size: 2048
|
| 66 |
offline_buffer_capacity: 1500000
|
|
|
|
| 88 |
total_timesteps: 5650000
|
| 89 |
use_amp: true
|
| 90 |
use_global_stream: true
|
|
|
|
| 91 |
weight_decay: 0.0001
|
checkpoints/online/Minihack-Online-Diffusion-DAgger-100M/model.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 20974876
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:045148d40b8bbccf0cbad981209adbae1da1caf3e11f6b57ae00490501fe7b4d
|
| 3 |
size 20974876
|
checkpoints/online/Minihack-Online-Diffusion-DAgger-100M/selection.json
CHANGED
|
@@ -3,11 +3,11 @@
|
|
| 3 |
"selected": {
|
| 4 |
"dagger_iteration": 563
|
| 5 |
},
|
| 6 |
-
"selection_metric":
|
| 7 |
"candidates": {
|
| 8 |
-
"unit": "
|
| 9 |
-
"every":
|
| 10 |
-
"configured_max":
|
| 11 |
},
|
| 12 |
"eval_protocol": {
|
| 13 |
"episodes_per_env": 50,
|
|
|
|
| 3 |
"selected": {
|
| 4 |
"dagger_iteration": 563
|
| 5 |
},
|
| 6 |
+
"selection_metric": "mean ID win rate over 50 episodes/env, EMA weights",
|
| 7 |
"candidates": {
|
| 8 |
+
"unit": "env_steps",
|
| 9 |
+
"every": 940000,
|
| 10 |
+
"configured_max": 5650000
|
| 11 |
},
|
| 12 |
"eval_protocol": {
|
| 13 |
"episodes_per_env": 50,
|
demo_minihack.ipynb
ADDED
|
@@ -0,0 +1,989 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# ReMDM MiniHack — Demo Notebook\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"This notebook is a fully self-contained demonstration of **Remasking Discrete Diffusion Models (ReMDM) for action-sequence planning in MiniHack navigation environments**. It accompanies the paper *Return-Weighted ELBO Fine-Tuning Degrades Masked Diffusion Planners*. We study what the standard tractable objective for aligning a masked discrete diffusion planner with a reward actually does: across 25 ablation conditions in MiniHack and Craftax Classic, three seeds each, it **makes the planner worse** — no condition on Craftax Classic recovers the checkpoint it started from — and an exact decomposition of its gradient shows that the return weighting is **not** what does the damage.\n",
|
| 12 |
+
"\n",
|
| 13 |
+
"Running this notebook top-to-bottom in a fresh Google Colab runtime downloads the source code, the pre-trained DAgger checkpoint, and the pre-computed RL fine-tuning ablation assets from a public HuggingFace repository, then evaluates the model live on procedurally generated MiniHack layouts and visualises both the agent's behaviour and the underlying diffusion denoising process.\n",
|
| 14 |
+
"\n",
|
| 15 |
+
"**How to use this notebook**\n",
|
| 16 |
+
"\n",
|
| 17 |
+
"1. Open this notebook in Colab (`File > Upload notebook` > select `demo_minihack.ipynb`).\n",
|
| 18 |
+
"2. *(Optional)* Tweak the configuration constants in **Cell 1** below — change `ID_ENVS` / `OOD_ENVS` to test other registered environments, change `SEED` to test fresh procedurally-generated layouts, raise `EPISODES_PER_ENV` for tighter error bars, or set `CUSTOM_DES_FILE` to a hand-authored MiniHack `.des` level uploaded to Colab.\n",
|
| 19 |
+
"3. *(Optional)* `Runtime > Change runtime type > T4 GPU` for the fastest run.\n",
|
| 20 |
+
"4. `Runtime > Run all`.\n",
|
| 21 |
+
"\n",
|
| 22 |
+
"**Runtime budget** (default `EPISODES_PER_ENV=20`): ≈10 min on a Colab T4 GPU, ≈25 min on CPU. The first cell installs NLE which compiles C code — this takes ~3 min by itself.\n",
|
| 23 |
+
"\n",
|
| 24 |
+
"**Which evaluation path this notebook uses.** Cell 5 calls `Evaluator._run_episodes_batched` from `src/planners/inference.py` — the *same* evaluator the ablation harness calls (`experiments/rl_finetuning/ablations/training.py` imports `Evaluator` and calls `evaluator.evaluate(...)` at every eval point). The notebook and the paper's MiniHack numbers therefore come from one code path, with one sampler and one replanning rule. The only difference is episode count and seed offset.\n",
|
| 25 |
+
"\n",
|
| 26 |
+
"**Reproducibility statement.** Training is done **offline** — this notebook only runs inference on the supplied checkpoint, so that the notebook stays runnable on a free Colab runtime and can be tested on unseen inputs. Because MiniHack environments are procedurally generated, every run with a different `SEED` exposes the model to layouts it has never seen, including in the four in-distribution maps it was trained on."
|
| 27 |
+
],
|
| 28 |
+
"id": "d866ea1602067016"
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"cell_type": "markdown",
|
| 32 |
+
"metadata": {},
|
| 33 |
+
"source": [
|
| 34 |
+
"## Cell 1 — Configuration\n",
|
| 35 |
+
"\n",
|
| 36 |
+
"Everything you may want to change lives in this single cell."
|
| 37 |
+
],
|
| 38 |
+
"id": "bed5b72429321b9d"
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"cell_type": "code",
|
| 42 |
+
"execution_count": null,
|
| 43 |
+
"metadata": {},
|
| 44 |
+
"outputs": [],
|
| 45 |
+
"source": [
|
| 46 |
+
"# ============================================================================\n",
|
| 47 |
+
"# Configuration — change these to test on unseen inputs\n",
|
| 48 |
+
"# ============================================================================\n",
|
| 49 |
+
"\n",
|
| 50 |
+
"# Public HuggingFace repo containing source code, the stripped pre-trained\n",
|
| 51 |
+
"# checkpoint, and the pre-computed ablation assets. No auth required.\n",
|
| 52 |
+
"# This is an anonymous mirror published for double-blind review, so it carries\n",
|
| 53 |
+
"# no account name or URL that would identify the authors. Point it at your own\n",
|
| 54 |
+
"# Hub repo holding the same layout, or train from source (see README.md).\n",
|
| 55 |
+
"HF_REPO_ID: str = \"AnonMLuser/remdm-planner-minihack\"\n",
|
| 56 |
+
"\n",
|
| 57 |
+
"# Reproducibility seed. The live evaluator generates env seeds as\n",
|
| 58 |
+
"# (SEED + episode_index) per environment, so changing this number tests the\n",
|
| 59 |
+
"# model on a fresh batch of procedurally generated layouts.\n",
|
| 60 |
+
"SEED: int = 42\n",
|
| 61 |
+
"\n",
|
| 62 |
+
"# Episodes per environment for the live evaluation pass. Higher = tighter\n",
|
| 63 |
+
"# error bars but slower. The reported numbers in the paper used 50.\n",
|
| 64 |
+
"EPISODES_PER_ENV: int = 20\n",
|
| 65 |
+
"\n",
|
| 66 |
+
"# In-distribution training environments (4 maps).\n",
|
| 67 |
+
"ID_ENVS: list[str] = [\n",
|
| 68 |
+
" \"MiniHack-Room-Random-5x5-v0\",\n",
|
| 69 |
+
" \"MiniHack-Room-Random-15x15-v0\",\n",
|
| 70 |
+
" \"MiniHack-Corridor-R2-v0\",\n",
|
| 71 |
+
" \"MiniHack-MazeWalk-9x9-v0\",\n",
|
| 72 |
+
"]\n",
|
| 73 |
+
"\n",
|
| 74 |
+
"# Out-of-distribution zero-shot evaluation environments (3 maps).\n",
|
| 75 |
+
"OOD_ENVS: list[str] = [\n",
|
| 76 |
+
" \"MiniHack-Room-Dark-15x15-v0\",\n",
|
| 77 |
+
" \"MiniHack-Corridor-R5-v0\",\n",
|
| 78 |
+
" \"MiniHack-MazeWalk-45x19-v0\",\n",
|
| 79 |
+
"]\n",
|
| 80 |
+
"\n",
|
| 81 |
+
"# Optional path to a custom .des MiniHack scenario file uploaded to Colab.\n",
|
| 82 |
+
"# Leave as None to skip; set to e.g. \"/content/my_level.des\" to evaluate the\n",
|
| 83 |
+
"# model on a hand-authored MiniHack level alongside the registry envs.\n",
|
| 84 |
+
"CUSTOM_DES_FILE: str | None = None\n",
|
| 85 |
+
"\n",
|
| 86 |
+
"# Inference device. None = auto-detect (CUDA if available, else CPU).\n",
|
| 87 |
+
"INFERENCE_DEVICE: str | None = None\n",
|
| 88 |
+
"\n",
|
| 89 |
+
"# Local directory the HF snapshot is downloaded into.\n",
|
| 90 |
+
"SNAPSHOT_DIR: str = \"remdm-planner-minihack\""
|
| 91 |
+
],
|
| 92 |
+
"id": "3b72b898aab60cdf"
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"cell_type": "markdown",
|
| 96 |
+
"metadata": {},
|
| 97 |
+
"source": [
|
| 98 |
+
"## Cell 2 — Setup & installation\n",
|
| 99 |
+
"\n",
|
| 100 |
+
"The next three cells install NetHack Learning Environment (NLE), MiniHack, PyTorch, and the supporting libraries; verify that NLE compiled correctly; and download the project source + checkpoint from HuggingFace. **NLE is the highest-risk failure point on Colab** because it has to compile C code under the hood. If the verification cell fails with an import error, restart the Colab runtime (`Runtime > Restart runtime`) and re-run from the install cell."
|
| 101 |
+
],
|
| 102 |
+
"id": "34dd96e5ef1cb0ef"
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"cell_type": "code",
|
| 106 |
+
"execution_count": null,
|
| 107 |
+
"metadata": {},
|
| 108 |
+
"outputs": [],
|
| 109 |
+
"source": [
|
| 110 |
+
"# ── 1. System dependencies + Python packages ─────────────────────────────\n",
|
| 111 |
+
"import os\n",
|
| 112 |
+
"import subprocess\n",
|
| 113 |
+
"import sys\n",
|
| 114 |
+
"\n",
|
| 115 |
+
"print(\"[1/3] Installing system dependencies for NLE (NetHack)...\")\n",
|
| 116 |
+
"try:\n",
|
| 117 |
+
" _apt = subprocess.run(\n",
|
| 118 |
+
" [\n",
|
| 119 |
+
" \"apt-get\", \"install\", \"-y\", \"-q\",\n",
|
| 120 |
+
" \"cmake\", \"build-essential\", \"bison\", \"flex\", \"libbz2-dev\",\n",
|
| 121 |
+
" ],\n",
|
| 122 |
+
" check=False, capture_output=True, text=True,\n",
|
| 123 |
+
" )\n",
|
| 124 |
+
" if _apt.returncode != 0:\n",
|
| 125 |
+
" # Non-Colab environments (e.g. local Jupyter) will fail apt-get; that is\n",
|
| 126 |
+
" # fine if the deps are already installed. Print and continue.\n",
|
| 127 |
+
" print(\" apt-get returned non-zero (likely already installed or non-Debian env):\")\n",
|
| 128 |
+
" print(\" \" + (_apt.stderr or \"\").strip()[-400:])\n",
|
| 129 |
+
"except FileNotFoundError:\n",
|
| 130 |
+
" # No apt-get at all (macOS, non-Debian Linux). Fine if the build deps are\n",
|
| 131 |
+
" # already present; NLE's compilation is verified in the next cell either way.\n",
|
| 132 |
+
" print(\" apt-get not found (non-Debian environment) - skipping system deps.\")\n",
|
| 133 |
+
"\n",
|
| 134 |
+
"print(\"[2/3] Installing NLE (compiles NetHack from source — slow first run)...\")\n",
|
| 135 |
+
"subprocess.run(\n",
|
| 136 |
+
" [sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"nle>=1.2.0\"],\n",
|
| 137 |
+
" check=True,\n",
|
| 138 |
+
")\n",
|
| 139 |
+
"\n",
|
| 140 |
+
"print(\"[3/3] Installing MiniHack + supporting libraries...\")\n",
|
| 141 |
+
"subprocess.run(\n",
|
| 142 |
+
" [\n",
|
| 143 |
+
" sys.executable, \"-m\", \"pip\", \"install\", \"-q\",\n",
|
| 144 |
+
" \"minihack>=1.0.2\",\n",
|
| 145 |
+
" \"torch>=2.4\",\n",
|
| 146 |
+
" \"huggingface_hub>=0.25\",\n",
|
| 147 |
+
" \"polars>=1.0\",\n",
|
| 148 |
+
" \"matplotlib>=3.8\",\n",
|
| 149 |
+
" \"pyyaml>=6.0\",\n",
|
| 150 |
+
" \"gymnasium>=0.29\",\n",
|
| 151 |
+
" \"numpy>=1.26\",\n",
|
| 152 |
+
" ],\n",
|
| 153 |
+
" check=True,\n",
|
| 154 |
+
")\n",
|
| 155 |
+
"print(\"Install step complete.\")"
|
| 156 |
+
],
|
| 157 |
+
"id": "264dea581a6422cd"
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"cell_type": "code",
|
| 161 |
+
"execution_count": null,
|
| 162 |
+
"metadata": {},
|
| 163 |
+
"outputs": [],
|
| 164 |
+
"source": [
|
| 165 |
+
"# ── 2. Verify NLE/MiniHack actually loaded ───────────────────────────────\n",
|
| 166 |
+
"# Fail loudly with a clear message if the C compilation step did not work.\n",
|
| 167 |
+
"try:\n",
|
| 168 |
+
" import nle # noqa: F401\n",
|
| 169 |
+
" import minihack # noqa: F401\n",
|
| 170 |
+
" import gymnasium as gym\n",
|
| 171 |
+
" _env = gym.make(\n",
|
| 172 |
+
" \"MiniHack-Room-Random-5x5-v0\",\n",
|
| 173 |
+
" observation_keys=(\"glyphs\", \"chars\"),\n",
|
| 174 |
+
" )\n",
|
| 175 |
+
" _obs, _info = _env.reset(seed=0)\n",
|
| 176 |
+
" _env.close()\n",
|
| 177 |
+
" print(f\"NLE OK. Glyphs shape: {_obs['glyphs'].shape}\")\n",
|
| 178 |
+
"except Exception as exc:\n",
|
| 179 |
+
" raise RuntimeError(\n",
|
| 180 |
+
" \"NLE/MiniHack failed to import or instantiate. On Colab this almost \"\n",
|
| 181 |
+
" \"always means the C build of NetHack did not finish. Please restart \"\n",
|
| 182 |
+
" \"the runtime (Runtime > Restart runtime) and re-run the install \"\n",
|
| 183 |
+
" \"cell from a clean state.\"\n",
|
| 184 |
+
" ) from exc"
|
| 185 |
+
],
|
| 186 |
+
"id": "2a3dde194076e9be"
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"cell_type": "code",
|
| 190 |
+
"execution_count": null,
|
| 191 |
+
"metadata": {},
|
| 192 |
+
"outputs": [],
|
| 193 |
+
"source": [
|
| 194 |
+
"# ── 3. Download source + checkpoint + ablation assets from HuggingFace ──\n",
|
| 195 |
+
"from huggingface_hub import snapshot_download\n",
|
| 196 |
+
"\n",
|
| 197 |
+
"if HF_REPO_ID == \"UNSET_HF_REPO_ID\":\n",
|
| 198 |
+
" raise RuntimeError(\n",
|
| 199 |
+
" \"HF_REPO_ID is unset: checkpoints are withheld for anonymous \"\n",
|
| 200 |
+
" \"review. Edit the constant in Cell 1 to point at a \"\n",
|
| 201 |
+
" \"public HuggingFace repo containing the source + checkpoint + assets.\"\n",
|
| 202 |
+
" )\n",
|
| 203 |
+
"\n",
|
| 204 |
+
"snapshot_path = snapshot_download(repo_id=HF_REPO_ID, local_dir=SNAPSHOT_DIR)\n",
|
| 205 |
+
"print(f\"Snapshot at: {snapshot_path}\")\n",
|
| 206 |
+
"\n",
|
| 207 |
+
"# Make `import src.*` resolve against the downloaded snapshot.\n",
|
| 208 |
+
"if snapshot_path not in sys.path:\n",
|
| 209 |
+
" sys.path.insert(0, snapshot_path)\n",
|
| 210 |
+
"\n",
|
| 211 |
+
"# Smoke imports — fail here if the snapshot is incomplete.\n",
|
| 212 |
+
"from src.config import load_config\n",
|
| 213 |
+
"from src.models.denoiser import make_model, ModelEMA\n",
|
| 214 |
+
"from src.diffusion.sampling import remdm_sample\n",
|
| 215 |
+
"from src.envs.minihack_env import make_env\n",
|
| 216 |
+
"from src.planners.inference import Evaluator, format_eval_results\n",
|
| 217 |
+
"print(\"Imports OK.\")"
|
| 218 |
+
],
|
| 219 |
+
"id": "d90516ceca2cc1e3"
|
| 220 |
+
},
|
| 221 |
+
{
|
| 222 |
+
"cell_type": "markdown",
|
| 223 |
+
"metadata": {},
|
| 224 |
+
"source": [
|
| 225 |
+
"## Cell 3 — Project overview\n",
|
| 226 |
+
"\n",
|
| 227 |
+
"**Problem.** MiniHack navigation environments are sparse-reward gridworlds with procedurally generated dungeon layouts, locked doors, mazes, and partial observability. Off-the-shelf model-free reinforcement learning baselines (PPO, A2C, DQN, recurrent PPO) do not learn these layouts from scratch within our compute budget, reaching between 0.3% and 4.7% in-distribution after 5M environment steps at default hyperparameters — which is why we train by imitation. They are reported to establish that fact, not as a matched comparison.\n",
|
| 228 |
+
"\n",
|
| 229 |
+
"**Approach.** We treat planning as **discrete masked diffusion over action sequences**. A dual-stream transformer denoiser, conditioned on (i) a 9×9 local glyph crop centred on the agent and (ii) the full 21×79 global dungeon map, generates 64-step action plans by iteratively denoising a sequence of `[MASK]` tokens. At inference time we use **ReMDM** (Remasking Discrete Diffusion Models — Wang et al. 2025): MaskGIT-style progressive unmasking interleaved with stochastic confidence-weighted **remasking**, which lets the model revise low-confidence commitments mid-trajectory rather than baking in early mistakes.\n",
|
| 230 |
+
"\n",
|
| 231 |
+
"**Architecture (`LocalDiffusionPlannerWithGlobal`, ≈5.2M parameters, PyTorch).**\n",
|
| 232 |
+
"\n",
|
| 233 |
+
"```\n",
|
| 234 |
+
"Local stream: 9×9 glyphs → Embed(6000,64) → CNN(64→32→64) → Linear → 1 token (256-D)\n",
|
| 235 |
+
"Global stream: 21×79 glyphs → Embed(6000,32) → CNN(32→32→64) → AdaptivePool(2,4)\n",
|
| 236 |
+
" → Linear(64,256) → 8 spatial tokens (256-D)\n",
|
| 237 |
+
" + auxiliary goal head: mean(global) → MLP → [B,2] (normalised staircase coords)\n",
|
| 238 |
+
" × sigmoid(learnable scalar gate, init logit = −3.0) ← keeps global stream nearly closed early in training\n",
|
| 239 |
+
"Action stream: action_emb(14,256) + timestep_emb(100,256) + position_emb(64,256)\n",
|
| 240 |
+
"Transformer: concat [local(1) + global(8) + actions(64) = 73 tokens]\n",
|
| 241 |
+
" → 4-layer bidirectional TransformerEncoder (256-D, 4 heads, GELU, pre-norm) → last 64 tokens\n",
|
| 242 |
+
"Action head: Linear(256, 12) → action logits (12 movement / interact actions; MASK=12 and PAD=13 are inputs only)\n",
|
| 243 |
+
"```\n",
|
| 244 |
+
"\n",
|
| 245 |
+
"The auxiliary staircase-coordinate head **has no counterpart in the offline baselines**, and the paper lists it as one of four confounds in the imitation comparison (Cell 8).\n",
|
| 246 |
+
"\n",
|
| 247 |
+
"**Diffusion process.** Forward: each token is independently replaced with `[MASK]` with probability σ(t) under a linear schedule. Reverse (inference): K=10 denoising steps with confidence-based progressive unmasking interleaved with stochastic remasking at probability p_remask = η · (1 − r), where η = 0.15 and r is the progress through the chain. DAgger collection uses greedy argmax sampling at K=5 steps. The planner runs in **receding horizon** mode: it commits a 64-action plan, executes the first 16 actions, then replans on the next observation.\n",
|
| 248 |
+
"\n",
|
| 249 |
+
"**Training pipeline.** DAgger online training with **BFS oracle** supervision (`main.py --mode online`). Iteration 0 seeds the replay buffer with three oracle trajectories per training environment as an implicit BC warm-start; subsequent iterations alternate model rollouts → BFS oracle relabelling on the same seed → efficiency-filtered buffer insertion → AdamW gradient steps. The exponentially decaying β starts at 1.0; total budget ≈5.65M env steps with 30 episodes/iteration and 100 grad steps/iteration. Inference uses EMA-shadowed weights. The checkpoint loaded by this notebook is the iter-600 DAgger snapshot from a GPU-H200 run, with all optimiser/scheduler state stripped (the inference path only needs `ema_state_dict`).\n",
|
| 250 |
+
"\n",
|
| 251 |
+
"### The objective under study\n",
|
| 252 |
+
"\n",
|
| 253 |
+
"Fine-tuning runs on the planner's **own rollouts**. Each iteration collects\n",
|
| 254 |
+
"trajectories under the current policy and cuts them into `H`-step windows. Window\n",
|
| 255 |
+
"`i` is assigned the return `R_i` — the reward summed over exactly the `H` steps that\n",
|
| 256 |
+
"window trains on, *not* the episode total broadcast to every window — and receives a\n",
|
| 257 |
+
"scalar weight\n",
|
| 258 |
+
"\n",
|
| 259 |
+
"```\n",
|
| 260 |
+
"A_i = clip( max(R_i, 0) / (µ_batch + ε), c_min, c_max ), [c_min, c_max] = [0.1, 5.0]\n",
|
| 261 |
+
"```\n",
|
| 262 |
+
"\n",
|
| 263 |
+
"The training loss is the ELBO of the denoising objective with each window's term\n",
|
| 264 |
+
"scaled by `A_i`, treated as a constant under a stop-gradient. This is a **clipped\n",
|
| 265 |
+
"return ratio**, in the lineage of reward-weighted regression — it is *not* the\n",
|
| 266 |
+
"advantage-weighted regression weight, which subtracts a baseline and applies an\n",
|
| 267 |
+
"exponential temperature `exp(A/β)`. The paper's conclusions characterise this weight\n",
|
| 268 |
+
"function and do not carry over automatically to the exponential form, which was not\n",
|
| 269 |
+
"tested.\n",
|
| 270 |
+
"\n",
|
| 271 |
+
"### The exact decomposition\n",
|
| 272 |
+
"\n",
|
| 273 |
+
"Write `Ā` for the mean weight and `δ_i = A_i/Ā − 1`, so that `Σ_i δ_i = 0`. The\n",
|
| 274 |
+
"gradient then factors **exactly**, with no approximation:\n",
|
| 275 |
+
"\n",
|
| 276 |
+
"```\n",
|
| 277 |
+
"∇L_RW = Ā · [ ∇L_BC + (1/B) Σ_i δ_i ∇ℓ_i ]\n",
|
| 278 |
+
" ‾‾‾‾‾‾ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\n",
|
| 279 |
+
" imitation g_δ — the return's entire contribution\n",
|
| 280 |
+
"```\n",
|
| 281 |
+
"\n",
|
| 282 |
+
"`Ā` is a scalar that rescales the step size and leaves the direction unchanged, so\n",
|
| 283 |
+
"everything the return does enters through `g_δ`, whose RMS scale is the weights'\n",
|
| 284 |
+
"coefficient of variation `CV_A = sqrt(B/ESS − 1)`. Both terms are measurable at a\n",
|
| 285 |
+
"single parameter point, and both diagnostics cost one batch and no accelerator.\n",
|
| 286 |
+
"\n",
|
| 287 |
+
"### Three contributions (paper headline)\n",
|
| 288 |
+
"\n",
|
| 289 |
+
"1. **Fine-tuning degrades the planner.** On Craftax Classic, across 25 conditions\n",
|
| 290 |
+
" with three seeds each, **no condition recovers the checkpoint it started from**\n",
|
| 291 |
+
" and the plain objective gives up **3.59 of 11.81** points. Collected return\n",
|
| 292 |
+
" falls alongside held-out score (5.21 → 3.73 while eval goes 12.06 → 8.49), so\n",
|
| 293 |
+
" this is not reward hacking under an evaluation mismatch. The conditions that\n",
|
| 294 |
+
" lose least are the ones that barely train: LoRA (11.63), a hard KL trust region\n",
|
| 295 |
+
" (11.53) and head-only updates (11.30).\n",
|
| 296 |
+
"2. **The return term is large and points away from imitation.** `CV_A` is **1.16**\n",
|
| 297 |
+
" on Craftax Classic (effective sample size 437 of a 1024-window batch) and\n",
|
| 298 |
+
" **2.68** on MiniHack (591 of 4608), so the weights do rank windows and the\n",
|
| 299 |
+
" familiar explanation — that sparse returns fail to discriminate — is\n",
|
| 300 |
+
" unavailable. Measured directly, `‖g_δ‖/‖∇L_BC‖ = 0.49 ± 0.01` at cosine\n",
|
| 301 |
+
" **0.02 ± 0.05**, against **0.893 ± 0.010** between two independent noise draws\n",
|
| 302 |
+
" of `∇L_BC` itself. In `D = 9.33 × 10⁶` dimensions a random direction gives\n",
|
| 303 |
+
" `3.3 × 10⁻⁴`, so `g_δ` is structure, not noise.\n",
|
| 304 |
+
"3. **But shrinking it makes things worse.** Advantage clipping confines the weights\n",
|
| 305 |
+
" to [0.8, 1.2] and cuts the return term fivefold, to **0.097 ± 0.003**. It scores\n",
|
| 306 |
+
" **5.06** on Craftax Classic — 3.16 below the unclipped baseline and second worst\n",
|
| 307 |
+
" in the suite. A condition with almost no return term degrades *further* than one\n",
|
| 308 |
+
" with a large one, so the return weighting is not what does the damage. What is\n",
|
| 309 |
+
" left is the data: advantage clipping is also the nearest thing in the suite to\n",
|
| 310 |
+
" unweighted training on the model's own rollouts, and the suite is ordered by how\n",
|
| 311 |
+
" much plasticity each condition allows rather than by any property of the reward.\n",
|
| 312 |
+
"\n",
|
| 313 |
+
"*(Live evaluation in Cell 5; imitation context in Cell 8; ablation evidence in Cells 9–10.)*"
|
| 314 |
+
],
|
| 315 |
+
"id": "14890835349143"
|
| 316 |
+
},
|
| 317 |
+
{
|
| 318 |
+
"cell_type": "markdown",
|
| 319 |
+
"metadata": {},
|
| 320 |
+
"source": [
|
| 321 |
+
"## Cell 4 — Load the pre-trained model\n",
|
| 322 |
+
"\n",
|
| 323 |
+
"We load the architecture from `src/models/denoiser.py` using the project's own `make_model(cfg)` factory, then pour the EMA shadow weights from the stripped checkpoint into it. This is the same EMA-evaluation path that `main.py --mode inference` uses internally."
|
| 324 |
+
],
|
| 325 |
+
"id": "2516d108471c4cc6"
|
| 326 |
+
},
|
| 327 |
+
{
|
| 328 |
+
"cell_type": "code",
|
| 329 |
+
"execution_count": null,
|
| 330 |
+
"metadata": {},
|
| 331 |
+
"outputs": [],
|
| 332 |
+
"source": [
|
| 333 |
+
"import torch\n",
|
| 334 |
+
"\n",
|
| 335 |
+
"# Load the project's authoritative config (the same defaults.yaml the\n",
|
| 336 |
+
"# training run used). All sampling / arch hyperparameters live there.\n",
|
| 337 |
+
"_overrides = {\"device\": INFERENCE_DEVICE} if INFERENCE_DEVICE else {}\n",
|
| 338 |
+
"cfg = load_config(cli_overrides=_overrides)\n",
|
| 339 |
+
"device = torch.device(cfg.device)\n",
|
| 340 |
+
"print(f\"Inference device: {device}\")\n",
|
| 341 |
+
"print(\n",
|
| 342 |
+
" f\"Model arch: n_embd={cfg.n_embd}, n_head={cfg.n_head}, \"\n",
|
| 343 |
+
" f\"n_layer={cfg.n_layer}, n_global_tokens={cfg.n_global_tokens}, \"\n",
|
| 344 |
+
" f\"seq_len={cfg.seq_len}\"\n",
|
| 345 |
+
")\n",
|
| 346 |
+
"print(\n",
|
| 347 |
+
" f\"Diffusion: K_eval={cfg.diffusion_steps_eval}, schedule={cfg.noise_schedule}, \"\n",
|
| 348 |
+
" f\"remask={cfg.remask_strategy}, eta={cfg.eta}, T={cfg.temperature}, top_p={cfg.top_p}\"\n",
|
| 349 |
+
")\n",
|
| 350 |
+
"\n",
|
| 351 |
+
"# Build the architecture and load the stripped EMA checkpoint. The HF repo\n",
|
| 352 |
+
"# ships an inference-only checkpoint that contains only the EMA shadow weights\n",
|
| 353 |
+
"# (≈21 MB) — no optimiser, scheduler, RNG, or curriculum state.\n",
|
| 354 |
+
"ckpt_path = os.path.join(SNAPSHOT_DIR, \"checkpoint_inference.pth\")\n",
|
| 355 |
+
"if not os.path.exists(ckpt_path):\n",
|
| 356 |
+
" raise FileNotFoundError(\n",
|
| 357 |
+
" f\"Expected stripped checkpoint at {ckpt_path}. The HF repo must \"\n",
|
| 358 |
+
" f\"contain checkpoint_inference.pth at its root.\"\n",
|
| 359 |
+
" )\n",
|
| 360 |
+
"\n",
|
| 361 |
+
"model = make_model(cfg).to(device)\n",
|
| 362 |
+
"ckpt = torch.load(ckpt_path, map_location=device, weights_only=False)\n",
|
| 363 |
+
"ema_sd = ckpt[\"ema_state_dict\"] if isinstance(ckpt, dict) and \"ema_state_dict\" in ckpt else ckpt\n",
|
| 364 |
+
"ema = ModelEMA(model, decay=cfg.ema_decay)\n",
|
| 365 |
+
"ema.load_state_dict(ema_sd)\n",
|
| 366 |
+
"ema.apply_to(model)\n",
|
| 367 |
+
"model.eval()\n",
|
| 368 |
+
"\n",
|
| 369 |
+
"n_params = sum(p.numel() for p in model.parameters())\n",
|
| 370 |
+
"print(\n",
|
| 371 |
+
" f\"Loaded EMA weights into {type(model).__name__} \"\n",
|
| 372 |
+
" f\"({n_params:,} parameters ≈ {n_params / 1e6:.2f} M).\"\n",
|
| 373 |
+
")"
|
| 374 |
+
],
|
| 375 |
+
"id": "71470180a5f12e46"
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"cell_type": "markdown",
|
| 379 |
+
"metadata": {},
|
| 380 |
+
"source": [
|
| 381 |
+
"## Cell 5 — Live inference on procedurally generated layouts ⭐\n",
|
| 382 |
+
"\n",
|
| 383 |
+
"We now run the model live on `EPISODES_PER_ENV` episodes per environment, on the 4 in-distribution maps **and** the 3 out-of-distribution maps **and** the optional custom `.des` file. Each episode uses a per-`SEED` RNG so changing `SEED` in Cell 1 produces a fresh batch of procedurally generated layouts that the model has never trained or evaluated on.\n",
|
| 384 |
+
"\n",
|
| 385 |
+
"We call the project's batched evaluator directly (`Evaluator._run_episodes_batched`), which runs all `EPISODES_PER_ENV` rollouts of a given environment in lockstep and batches every replanning forward pass through the GPU. Episodes that fail to construct (e.g. an invalid `.des` file) count as losses individually rather than crashing the pass."
|
| 386 |
+
],
|
| 387 |
+
"id": "717e4b710eba9ccc"
|
| 388 |
+
},
|
| 389 |
+
{
|
| 390 |
+
"cell_type": "code",
|
| 391 |
+
"execution_count": null,
|
| 392 |
+
"metadata": {},
|
| 393 |
+
"outputs": [],
|
| 394 |
+
"source": [
|
| 395 |
+
"import time\n",
|
| 396 |
+
"from pathlib import Path\n",
|
| 397 |
+
"\n",
|
| 398 |
+
"import numpy as np\n",
|
| 399 |
+
"import polars as pl\n",
|
| 400 |
+
"\n",
|
| 401 |
+
"evaluator = Evaluator()\n",
|
| 402 |
+
"\n",
|
| 403 |
+
"\n",
|
| 404 |
+
"def run_live_eval(\n",
|
| 405 |
+
" env_ids: list[str],\n",
|
| 406 |
+
" n_episodes: int,\n",
|
| 407 |
+
" seed: int,\n",
|
| 408 |
+
" des_files: list[str] | None = None,\n",
|
| 409 |
+
") -> dict[str, dict]:\n",
|
| 410 |
+
" \"\"\"Evaluate the model on each env id with seeds derived from *seed*.\n",
|
| 411 |
+
"\n",
|
| 412 |
+
" Mirrors ``Evaluator.evaluate`` but uses ``seed + ep`` so that changing\n",
|
| 413 |
+
" the notebook ``SEED`` constant actually rotates the underlying\n",
|
| 414 |
+
" procedural layouts.\n",
|
| 415 |
+
" \"\"\"\n",
|
| 416 |
+
" targets: list[tuple[str, str | None]] = [(eid, None) for eid in env_ids]\n",
|
| 417 |
+
" if des_files:\n",
|
| 418 |
+
" for p in des_files:\n",
|
| 419 |
+
" with open(p) as fh:\n",
|
| 420 |
+
" targets.append((Path(p).stem, fh.read()))\n",
|
| 421 |
+
"\n",
|
| 422 |
+
" out: dict[str, dict] = {}\n",
|
| 423 |
+
" for env_id, des_content in targets:\n",
|
| 424 |
+
" seeds = [seed + ep for ep in range(n_episodes)]\n",
|
| 425 |
+
" eps = evaluator._run_episodes_batched(\n",
|
| 426 |
+
" model, env_id, n_episodes, cfg, device,\n",
|
| 427 |
+
" seeds=seeds,\n",
|
| 428 |
+
" des_content=des_content,\n",
|
| 429 |
+
" blind_global=False,\n",
|
| 430 |
+
" )\n",
|
| 431 |
+
" wins = sum(1 for r in eps if r[\"won\"])\n",
|
| 432 |
+
" n = max(len(eps), 1)\n",
|
| 433 |
+
" out[env_id] = {\n",
|
| 434 |
+
" \"win_rate\": wins / n,\n",
|
| 435 |
+
" \"wins\": wins,\n",
|
| 436 |
+
" \"avg_reward\": sum(r[\"total_reward\"] for r in eps) / n,\n",
|
| 437 |
+
" \"avg_steps\": sum(r[\"steps\"] for r in eps) / n,\n",
|
| 438 |
+
" \"n_episodes\": len(eps),\n",
|
| 439 |
+
" }\n",
|
| 440 |
+
" return out\n",
|
| 441 |
+
"\n",
|
| 442 |
+
"\n",
|
| 443 |
+
"all_envs = list(ID_ENVS) + list(OOD_ENVS)\n",
|
| 444 |
+
"des_files = [CUSTOM_DES_FILE] if CUSTOM_DES_FILE else None\n",
|
| 445 |
+
"n_total = len(all_envs) * EPISODES_PER_ENV\n",
|
| 446 |
+
"if des_files:\n",
|
| 447 |
+
" n_total += EPISODES_PER_ENV\n",
|
| 448 |
+
"\n",
|
| 449 |
+
"print(\n",
|
| 450 |
+
" f\"Evaluating on {len(all_envs)} registry envs\"\n",
|
| 451 |
+
" + (f\" + {len(des_files)} custom .des\" if des_files else \"\")\n",
|
| 452 |
+
" + f\" × {EPISODES_PER_ENV} episodes (= {n_total} rollouts)...\"\n",
|
| 453 |
+
")\n",
|
| 454 |
+
"t0 = time.time()\n",
|
| 455 |
+
"live_results = run_live_eval(\n",
|
| 456 |
+
" all_envs, EPISODES_PER_ENV, SEED, des_files=des_files,\n",
|
| 457 |
+
")\n",
|
| 458 |
+
"elapsed = time.time() - t0\n",
|
| 459 |
+
"print(f\"Done in {elapsed:.1f}s ({elapsed / max(n_total, 1):.2f}s per rollout).\\n\")\n",
|
| 460 |
+
"\n",
|
| 461 |
+
"print(format_eval_results(live_results, label=\"Live ReMDM Inference\"))"
|
| 462 |
+
],
|
| 463 |
+
"id": "812ffba082dc4a3a"
|
| 464 |
+
},
|
| 465 |
+
{
|
| 466 |
+
"cell_type": "code",
|
| 467 |
+
"execution_count": null,
|
| 468 |
+
"metadata": {},
|
| 469 |
+
"outputs": [],
|
| 470 |
+
"source": [
|
| 471 |
+
"# Tabulate and split-aware aggregate.\n",
|
| 472 |
+
"rows = []\n",
|
| 473 |
+
"for env_id, stats in live_results.items():\n",
|
| 474 |
+
" if env_id in ID_ENVS:\n",
|
| 475 |
+
" split = \"ID\"\n",
|
| 476 |
+
" elif env_id in OOD_ENVS:\n",
|
| 477 |
+
" split = \"OOD\"\n",
|
| 478 |
+
" else:\n",
|
| 479 |
+
" split = \"custom\"\n",
|
| 480 |
+
" rows.append(\n",
|
| 481 |
+
" {\n",
|
| 482 |
+
" \"split\": split,\n",
|
| 483 |
+
" \"env\": env_id,\n",
|
| 484 |
+
" \"win_rate\": round(stats[\"win_rate\"], 3),\n",
|
| 485 |
+
" \"avg_steps\": round(stats[\"avg_steps\"], 1),\n",
|
| 486 |
+
" \"avg_reward\": round(stats[\"avg_reward\"], 2),\n",
|
| 487 |
+
" \"n_episodes\": stats[\"n_episodes\"],\n",
|
| 488 |
+
" }\n",
|
| 489 |
+
" )\n",
|
| 490 |
+
"df_live = pl.DataFrame(rows).sort([\"split\", \"env\"])\n",
|
| 491 |
+
"print(df_live)\n",
|
| 492 |
+
"\n",
|
| 493 |
+
"id_wr = float(np.mean([live_results[e][\"win_rate\"] for e in ID_ENVS if e in live_results]))\n",
|
| 494 |
+
"ood_wr = float(np.mean([live_results[e][\"win_rate\"] for e in OOD_ENVS if e in live_results]))\n",
|
| 495 |
+
"print(\n",
|
| 496 |
+
" f\"\\nMean ID win rate (this run): {id_wr:.2%}\"\n",
|
| 497 |
+
" f\" <- reference (paper Table 6, ReMDM DAgger, 50 episodes/env): 48.5%\"\n",
|
| 498 |
+
")\n",
|
| 499 |
+
"print(\n",
|
| 500 |
+
" f\"Mean OOD win rate (this run): {ood_wr:.2%}\"\n",
|
| 501 |
+
" f\" <- reference (paper Table 6, ReMDM DAgger): 4.7% mean OOD\"\n",
|
| 502 |
+
" f\" (Room-Dark-15x15 12%, Corridor-R5 2%, MazeWalk-45x19 0%)\"\n",
|
| 503 |
+
")\n",
|
| 504 |
+
"print(\n",
|
| 505 |
+
" \"\\nNote: small deviations from the reported numbers are expected -- this run \"\n",
|
| 506 |
+
" f\"uses {EPISODES_PER_ENV} episodes per env (vs 50) and a different seed offset, \"\n",
|
| 507 |
+
" \"so it samples a different (smaller) batch of procedurally generated layouts.\"\n",
|
| 508 |
+
")"
|
| 509 |
+
],
|
| 510 |
+
"id": "631ddcc627be0ac5"
|
| 511 |
+
},
|
| 512 |
+
{
|
| 513 |
+
"cell_type": "markdown",
|
| 514 |
+
"metadata": {},
|
| 515 |
+
"source": [
|
| 516 |
+
"## Cell 6 — Visualise the agent's behaviour ⭐\n",
|
| 517 |
+
"\n",
|
| 518 |
+
"We roll out a single episode on a representative ID environment and capture the dual-stream observations the model actually sees: the **9×9 local glyph crop** (centred on the agent `@`) and the **full 21×79 global dungeon map**. We display the start, mid, and end of the episode side-by-side."
|
| 519 |
+
],
|
| 520 |
+
"id": "57f89da930062058"
|
| 521 |
+
},
|
| 522 |
+
{
|
| 523 |
+
"cell_type": "code",
|
| 524 |
+
"execution_count": null,
|
| 525 |
+
"metadata": {},
|
| 526 |
+
"outputs": [],
|
| 527 |
+
"source": [
|
| 528 |
+
"import random\n",
|
| 529 |
+
"\n",
|
| 530 |
+
"import matplotlib.pyplot as plt\n",
|
| 531 |
+
"import numpy as np\n",
|
| 532 |
+
"\n",
|
| 533 |
+
"random.seed(SEED)\n",
|
| 534 |
+
"np.random.seed(SEED)\n",
|
| 535 |
+
"torch.manual_seed(SEED)\n",
|
| 536 |
+
"\n",
|
| 537 |
+
"VIZ_ENV = ID_ENVS[2] # MiniHack-Corridor-R2-v0 — visually richer than a single room\n",
|
| 538 |
+
"\n",
|
| 539 |
+
"env = make_env(VIZ_ENV, None, cfg)\n",
|
| 540 |
+
"(local_obs, global_obs), _ = env.reset(seed=SEED)\n",
|
| 541 |
+
"\n",
|
| 542 |
+
"trajectory_local: list[np.ndarray] = [local_obs.copy()]\n",
|
| 543 |
+
"trajectory_global: list[np.ndarray] = [global_obs.copy()]\n",
|
| 544 |
+
"actions_taken: list[int] = []\n",
|
| 545 |
+
"plan = None\n",
|
| 546 |
+
"step_in_plan = 0\n",
|
| 547 |
+
"total_reward = 0.0\n",
|
| 548 |
+
"won = False\n",
|
| 549 |
+
"\n",
|
| 550 |
+
"for step in range(200):\n",
|
| 551 |
+
" if step_in_plan == 0 or step_in_plan >= cfg.replan_every:\n",
|
| 552 |
+
" # Replan: full ReMDM denoising over the current local + global obs.\n",
|
| 553 |
+
" local_t = torch.from_numpy(local_obs).long().unsqueeze(0).to(device) # [1, 9, 9]\n",
|
| 554 |
+
" glb_t = torch.from_numpy(global_obs).long().unsqueeze(0).to(device) # [1, 21, 79]\n",
|
| 555 |
+
" plan = remdm_sample(\n",
|
| 556 |
+
" model, local_t, glb_t, cfg, device,\n",
|
| 557 |
+
" physics_aware=False, blind_global=False,\n",
|
| 558 |
+
" )[0].cpu().numpy() # [seq_len]\n",
|
| 559 |
+
" step_in_plan = 0\n",
|
| 560 |
+
"\n",
|
| 561 |
+
" action = int(plan[step_in_plan])\n",
|
| 562 |
+
" step_in_plan += 1\n",
|
| 563 |
+
" actions_taken.append(action)\n",
|
| 564 |
+
" (local_obs, global_obs), reward, term, trunc, info = env.step(action)\n",
|
| 565 |
+
" total_reward += reward\n",
|
| 566 |
+
" trajectory_local.append(local_obs.copy())\n",
|
| 567 |
+
" trajectory_global.append(global_obs.copy())\n",
|
| 568 |
+
" if info.get(\"won\"):\n",
|
| 569 |
+
" won = True\n",
|
| 570 |
+
" if term or trunc:\n",
|
| 571 |
+
" break\n",
|
| 572 |
+
"\n",
|
| 573 |
+
"env.close()\n",
|
| 574 |
+
"n_steps = len(actions_taken)\n",
|
| 575 |
+
"print(\n",
|
| 576 |
+
" f\"{VIZ_ENV} (seed={SEED}): {n_steps} steps, won={won}, total_reward={total_reward:.2f}\"\n",
|
| 577 |
+
")"
|
| 578 |
+
],
|
| 579 |
+
"id": "ef5071a3e18492aa"
|
| 580 |
+
},
|
| 581 |
+
{
|
| 582 |
+
"cell_type": "code",
|
| 583 |
+
"execution_count": null,
|
| 584 |
+
"metadata": {},
|
| 585 |
+
"outputs": [],
|
| 586 |
+
"source": [
|
| 587 |
+
"snapshots = [\n",
|
| 588 |
+
" (\"Start\", 0),\n",
|
| 589 |
+
" (\"Mid\", n_steps // 2),\n",
|
| 590 |
+
" (\"End\", n_steps),\n",
|
| 591 |
+
"]\n",
|
| 592 |
+
"\n",
|
| 593 |
+
"fig, axes = plt.subplots(2, 3, figsize=(13, 7))\n",
|
| 594 |
+
"for col, (label, idx) in enumerate(snapshots):\n",
|
| 595 |
+
" axes[0, col].imshow(trajectory_local[idx], cmap=\"viridis\")\n",
|
| 596 |
+
" axes[0, col].set_title(f\"{label} — local 9×9 (step {idx})\")\n",
|
| 597 |
+
" axes[0, col].set_xticks([])\n",
|
| 598 |
+
" axes[0, col].set_yticks([])\n",
|
| 599 |
+
"\n",
|
| 600 |
+
" axes[1, col].imshow(trajectory_global[idx], cmap=\"viridis\")\n",
|
| 601 |
+
" axes[1, col].set_title(f\"{label} — global 21×79 (step {idx})\")\n",
|
| 602 |
+
" axes[1, col].set_xticks([])\n",
|
| 603 |
+
" axes[1, col].set_yticks([])\n",
|
| 604 |
+
"\n",
|
| 605 |
+
"fig.suptitle(\n",
|
| 606 |
+
" f\"Dual-stream observations during a live rollout on {VIZ_ENV}\",\n",
|
| 607 |
+
" fontsize=12,\n",
|
| 608 |
+
")\n",
|
| 609 |
+
"plt.tight_layout()\n",
|
| 610 |
+
"plt.show()\n",
|
| 611 |
+
"\n",
|
| 612 |
+
"print(\n",
|
| 613 |
+
" \"Each colour above is a NetHack glyph ID (walls, floor, agent @, staircase >, \"\n",
|
| 614 |
+
" \"doors +, etc.). The local crop is the 9×9 window the local CNN stream sees; \"\n",
|
| 615 |
+
" \"the global stream sees the full 21×79 grid and contributes the auxiliary \"\n",
|
| 616 |
+
" \"staircase-coordinate prediction through the goal head.\"\n",
|
| 617 |
+
")"
|
| 618 |
+
],
|
| 619 |
+
"id": "14d3f4ba57c10610"
|
| 620 |
+
},
|
| 621 |
+
{
|
| 622 |
+
"cell_type": "markdown",
|
| 623 |
+
"metadata": {},
|
| 624 |
+
"source": [
|
| 625 |
+
"## Cell 7 — Visualise the ReMDM denoising process ⭐\n",
|
| 626 |
+
"\n",
|
| 627 |
+
"This is the heart of the method. Given a single observation, the planner starts from a sequence of 64 `[MASK]` tokens and over `K = diffusion_steps_eval = 10` reverse-diffusion steps it (a) predicts an action distribution at every position, (b) commits the highest-confidence positions via MaskGIT-style progressive unmasking, and (c) **stochastically remasks** previously committed positions whose confidence is low so they can be re-decoded later. We surface the per-step state by calling `remdm_sample(..., return_analytics=True)`, which returns the full denoising trajectory and per-step diagnostics."
|
| 628 |
+
],
|
| 629 |
+
"id": "936ac1409c9efa8a"
|
| 630 |
+
},
|
| 631 |
+
{
|
| 632 |
+
"cell_type": "code",
|
| 633 |
+
"execution_count": null,
|
| 634 |
+
"metadata": {},
|
| 635 |
+
"outputs": [],
|
| 636 |
+
"source": [
|
| 637 |
+
"# Use a fresh observation from the same env so the visualisation stays focused\n",
|
| 638 |
+
"# on the token-level denoising process, not on action quality.\n",
|
| 639 |
+
"env = make_env(ID_ENVS[2], None, cfg)\n",
|
| 640 |
+
"(local_obs, global_obs), _ = env.reset(seed=SEED)\n",
|
| 641 |
+
"env.close()\n",
|
| 642 |
+
"\n",
|
| 643 |
+
"local_t = torch.from_numpy(local_obs).long().unsqueeze(0).to(device)\n",
|
| 644 |
+
"glb_t = torch.from_numpy(global_obs).long().unsqueeze(0).to(device)\n",
|
| 645 |
+
"\n",
|
| 646 |
+
"seq, path_per_step, conf_track, masked_track = remdm_sample(\n",
|
| 647 |
+
" model, local_t, glb_t, cfg, device,\n",
|
| 648 |
+
" physics_aware=False, blind_global=False,\n",
|
| 649 |
+
" return_analytics=True,\n",
|
| 650 |
+
")\n",
|
| 651 |
+
"K = len(path_per_step)\n",
|
| 652 |
+
"seq_len = cfg.seq_len\n",
|
| 653 |
+
"mask_token = cfg.mask_token\n",
|
| 654 |
+
"\n",
|
| 655 |
+
"# Build a [K+1, seq_len] visualisation: row 0 = the all-MASK initial state,\n",
|
| 656 |
+
"# row k = the sequence after denoising step k.\n",
|
| 657 |
+
"vis = np.full((K + 1, seq_len), mask_token, dtype=np.int64)\n",
|
| 658 |
+
"for k, state in enumerate(path_per_step):\n",
|
| 659 |
+
" vis[k + 1] = state\n",
|
| 660 |
+
"# Encode masked positions as -1 so they get a distinct colour band.\n",
|
| 661 |
+
"display_grid = np.where(vis == mask_token, -1, vis).astype(float)\n",
|
| 662 |
+
"\n",
|
| 663 |
+
"print(\n",
|
| 664 |
+
" f\"Denoising trajectory: K={K} steps, seq_len={seq_len}, \"\n",
|
| 665 |
+
" f\"final masked-token count={int((seq[0] == mask_token).sum().item())} (must be 0)\"\n",
|
| 666 |
+
")"
|
| 667 |
+
],
|
| 668 |
+
"id": "59079c3462bbfaf6"
|
| 669 |
+
},
|
| 670 |
+
{
|
| 671 |
+
"cell_type": "code",
|
| 672 |
+
"execution_count": null,
|
| 673 |
+
"metadata": {},
|
| 674 |
+
"outputs": [],
|
| 675 |
+
"source": [
|
| 676 |
+
"fig, (ax1, ax2) = plt.subplots(\n",
|
| 677 |
+
" 2, 1, figsize=(13, 6),\n",
|
| 678 |
+
" gridspec_kw={\"height_ratios\": [3, 1]},\n",
|
| 679 |
+
")\n",
|
| 680 |
+
"\n",
|
| 681 |
+
"im = ax1.imshow(\n",
|
| 682 |
+
" display_grid, aspect=\"auto\", cmap=\"viridis\",\n",
|
| 683 |
+
" vmin=-1, vmax=cfg.action_dim - 1,\n",
|
| 684 |
+
" interpolation=\"nearest\",\n",
|
| 685 |
+
")\n",
|
| 686 |
+
"ax1.set_yticks(range(K + 1))\n",
|
| 687 |
+
"ax1.set_yticklabels([\"init\"] + [f\"k={k+1}\" for k in range(K)])\n",
|
| 688 |
+
"ax1.set_xlabel(\"token position (0..63)\")\n",
|
| 689 |
+
"ax1.set_ylabel(\"denoising step\")\n",
|
| 690 |
+
"ax1.set_title(\n",
|
| 691 |
+
" f\"ReMDM denoising trajectory ({K} steps) — \"\n",
|
| 692 |
+
" \"dark band (-1) = masked, colours = committed action ids 0..11\"\n",
|
| 693 |
+
")\n",
|
| 694 |
+
"cbar = plt.colorbar(im, ax=ax1, ticks=[-1] + list(range(cfg.action_dim)))\n",
|
| 695 |
+
"cbar.set_label(\"token id (-1 = MASK)\")\n",
|
| 696 |
+
"\n",
|
| 697 |
+
"ax2.plot(range(1, K + 1), conf_track, marker=\"o\", label=\"avg confidence (committed tokens)\")\n",
|
| 698 |
+
"ax2.plot(\n",
|
| 699 |
+
" range(1, K + 1),\n",
|
| 700 |
+
" [m / seq_len for m in masked_track],\n",
|
| 701 |
+
" marker=\"s\",\n",
|
| 702 |
+
" label=\"fraction still masked\",\n",
|
| 703 |
+
")\n",
|
| 704 |
+
"ax2.set_xlabel(\"denoising step k\")\n",
|
| 705 |
+
"ax2.set_ylabel(\"value\")\n",
|
| 706 |
+
"ax2.set_xticks(range(1, K + 1))\n",
|
| 707 |
+
"ax2.legend(loc=\"upper right\")\n",
|
| 708 |
+
"ax2.grid(alpha=0.3)\n",
|
| 709 |
+
"\n",
|
| 710 |
+
"plt.tight_layout()\n",
|
| 711 |
+
"plt.show()\n",
|
| 712 |
+
"\n",
|
| 713 |
+
"print(f\"Final committed plan (first 16 actions): {seq[0, :16].tolist()}\")\n",
|
| 714 |
+
"print(\n",
|
| 715 |
+
" \"Read the heatmap top-to-bottom: row 0 is fully masked, the next rows commit \"\n",
|
| 716 |
+
" \"high-confidence positions while occasionally re-masking low-confidence ones \"\n",
|
| 717 |
+
" \"(the ReMDM 'conf' strategy with eta=0.15), and the last row is the fully \"\n",
|
| 718 |
+
" \"committed plan that gets executed for the next 16 env steps before replanning.\"\n",
|
| 719 |
+
")"
|
| 720 |
+
],
|
| 721 |
+
"id": "ee28eb905ce0956"
|
| 722 |
+
},
|
| 723 |
+
{
|
| 724 |
+
"cell_type": "markdown",
|
| 725 |
+
"metadata": {},
|
| 726 |
+
"source": [
|
| 727 |
+
"## Cell 8 — Imitation learning context (baselines)\n",
|
| 728 |
+
"\n",
|
| 729 |
+
"The table below reports this project's MiniHack baseline runs, under the **imitation protocol**: 50 episodes per environment at inference settings. Baseline rows are means ± std over 3 training seeds; the two ReMDM rows are a single evaluation seed and are read directly from the shipped `results/inference/` artefacts.\n",
|
| 730 |
+
"\n",
|
| 731 |
+
"**Provenance note.** The current paper carries the baseline *configurations* (Table 11, \"MiniHack baseline hyperparameters\") but no baseline *results* table — that comparison was cut when the paper narrowed to the fine-tuning result. The model-free and offline-BC rows below are therefore project context rather than a paper number. The two ReMDM rows *are* paper numbers: the DAgger row is paper Table 6.\n",
|
| 732 |
+
"\n",
|
| 733 |
+
"The four model-free rows are included for **one purpose**: to establish that these layouts are not solvable from scratch within our compute budget, which is why we train by imitation. They use default hyperparameters and are **not a matched comparison**.\n",
|
| 734 |
+
"\n",
|
| 735 |
+
"**This is not an architecture result.** Any gap to the offline CNN+MLP and Decision Transformer confounds four design differences: an auxiliary staircase-prediction head the baselines lack, ten denoising passes per decision against one, chunked replanning against greedy per-step selection, and untuned baselines.\n",
|
| 736 |
+
"\n",
|
| 737 |
+
"**Two evaluation protocols appear in this work and their numbers are not interchangeable.** The direct evaluation of the DAgger checkpoint (this cell, 50 episodes per environment) gives **48.5%** in-distribution — this is paper Table 6. The ablation-harness protocol (Cells 9–10) runs 20 episodes per environment per seed inside the training loop at its own sampling settings, and gives the same checkpoint **47.5%**. The two agree closely here, but no claim crosses between them."
|
| 738 |
+
],
|
| 739 |
+
"id": "e0023c1ce6dbbaee"
|
| 740 |
+
},
|
| 741 |
+
{
|
| 742 |
+
"cell_type": "code",
|
| 743 |
+
"execution_count": null,
|
| 744 |
+
"metadata": {},
|
| 745 |
+
"outputs": [],
|
| 746 |
+
"source": [
|
| 747 |
+
"import polars as pl\n",
|
| 748 |
+
"\n",
|
| 749 |
+
"# Baseline runs under the imitation protocol: 50 episodes per environment at\n",
|
| 750 |
+
"# inference settings. Baseline rows are means +/- std over 3 training seeds.\n",
|
| 751 |
+
"#\n",
|
| 752 |
+
"# PROVENANCE: the current paper keeps the baseline *configurations* (Table 11)\n",
|
| 753 |
+
"# but no baseline *results* table, so the first six rows are project context,\n",
|
| 754 |
+
"# not paper numbers. The two ReMDM rows are read from the shipped\n",
|
| 755 |
+
"# results/inference/*.json artefacts; the DAgger row is paper Table 6.\n",
|
| 756 |
+
"baselines = [\n",
|
| 757 |
+
" {\"method\": \"PPO\", \"params\": \"~0.4 M\", \"id_win_pct\": \"0.3 +/- 0.5\", \"id_steps\": \"396.8 +/- 10.1\", \"ood_win_pct\": \"0.0 +/- 0.0\", \"ood_steps\": \"767.6 +/- 6.0\"},\n",
|
| 758 |
+
" {\"method\": \"A2C\", \"params\": \"~0.4 M\", \"id_win_pct\": \"2.8 +/- 2.3\", \"id_steps\": \"392.9 +/- 8.1\", \"ood_win_pct\": \"0.4 +/- 0.8\", \"ood_steps\": \"763.8 +/- 5.0\"},\n",
|
| 759 |
+
" {\"method\": \"DQN\", \"params\": \"~0.4 M\", \"id_win_pct\": \"3.8 +/- 1.2\", \"id_steps\": \"388.2 +/- 5.2\", \"ood_win_pct\": \"1.3 +/- 0.2\", \"ood_steps\": \"762.7 +/- 1.3\"},\n",
|
| 760 |
+
" {\"method\": \"PPO-RNN\", \"params\": \"~0.5 M\", \"id_win_pct\": \"4.7 +/- 2.9\", \"id_steps\": \"385.4 +/- 13.2\", \"ood_win_pct\": \"0.0 +/- 0.0\", \"ood_steps\": \"766.7 +/- 3.1\"},\n",
|
| 761 |
+
" {\"method\": \"CNN+MLP (Offline BC)\", \"params\": \"~0.6 M\", \"id_win_pct\": \"6.3 +/- 2.5\", \"id_steps\": \"378.8 +/- 6.4\", \"ood_win_pct\": \"0.7 +/- 0.7\", \"ood_steps\": \"764.7 +/- 2.0\"},\n",
|
| 762 |
+
" {\"method\": \"Decision Transformer (Off. BC)\", \"params\": \"~1.0 M\", \"id_win_pct\": \"9.3 +/- 2.1\", \"id_steps\": \"160.7 +/- 4.2\", \"ood_win_pct\": \"1.8 +/- 0.4\", \"ood_steps\": \"196.8 +/- 0.6\"},\n",
|
| 763 |
+
" {\"method\": \"ReMDM (Offline BC)\", \"params\": \"~5.2 M\", \"id_win_pct\": \"73.0\", \"id_steps\": \"107.1\", \"ood_win_pct\": \"6.0\", \"ood_steps\": \"418.5\"},\n",
|
| 764 |
+
" {\"method\": \"ReMDM (DAgger)\", \"params\": \"~5.2 M\", \"id_win_pct\": \"48.5\", \"id_steps\": \"173.9\", \"ood_win_pct\": \"4.7\", \"ood_steps\": \"426.2\"},\n",
|
| 765 |
+
"]\n",
|
| 766 |
+
"df_baselines = pl.DataFrame(baselines)\n",
|
| 767 |
+
"with pl.Config(tbl_rows=10, fmt_str_lengths=40):\n",
|
| 768 |
+
" print(df_baselines)\n",
|
| 769 |
+
"print(\n",
|
| 770 |
+
" \"\\nThe four model-free rows are NOT a matched comparison -- they use default \"\n",
|
| 771 |
+
" \"hyperparameters and are reported only to establish that these layouts are not \"\n",
|
| 772 |
+
" \"solvable from scratch within our budget (0.3-4.7% ID after 5M env steps), \"\n",
|
| 773 |
+
" \"which is why we train by imitation.\\n\"\n",
|
| 774 |
+
" \"\\nThe DAgger row (48.5% ID / 4.7% OOD) is paper Table 6, and it is the \"\n",
|
| 775 |
+
" \"checkpoint every ablation condition in Cells 9-10 fine-tunes. The planner \"\n",
|
| 776 |
+
" \"retains little zero-shot transfer to held-out layouts, so the paper makes no \"\n",
|
| 777 |
+
" \"structural generalisation claim.\"\n",
|
| 778 |
+
")"
|
| 779 |
+
],
|
| 780 |
+
"id": "cbd0984cc1f7787"
|
| 781 |
+
},
|
| 782 |
+
{
|
| 783 |
+
"cell_type": "markdown",
|
| 784 |
+
"metadata": {},
|
| 785 |
+
"source": [
|
| 786 |
+
"## Cell 9 — RL fine-tuning ablation findings (pre-computed)\n",
|
| 787 |
+
"\n",
|
| 788 |
+
"On top of the DAgger checkpoint, we ran a **25-condition suite** (`experiments/rl_finetuning/run_ablations.py --all`) that asks what return-weighted ELBO fine-tuning of the diffusion planner actually does. The 25 conditions are organised into four mechanism groups (A: regularisation, B: training signal, C: architectural freezing, D: data quality / weight computation) plus a `baseline_rl` reference, all initialised from the same checkpoint and run for 500 iterations with 3 seeds. All numbers here use the **ablation-harness protocol**, under which the pretrained checkpoint scores **47.5%** — against 48.5% for the direct evaluation in Cell 8.\n",
|
| 789 |
+
"\n",
|
| 790 |
+
"**In MiniHack the objective takes the checkpoint from 47.5% to 43.8% (±6.1).** Three conditions finish nominally above the checkpoint — `head_only` 49.6%, `gradient_surgery` 48.8%, `layer_ablation_top1` 48.8% — but the best-versus-baseline difference has a bootstrap interval of **[−2.1, +13.7] points at p = 0.40**, so the paper does not read them as improvements.\n",
|
| 791 |
+
"\n",
|
| 792 |
+
"**MiniHack is not a second confirmation, and the paper says so.** The effect here is small relative to a seed standard deviation of 6.1 points. It is reported because it is the same suite run identically and its ordering matches Craftax Classic, where the effect is large and unambiguous (11.81 → 8.22, no condition above the checkpoint). The claim rests on Craftax Classic.\n",
|
| 793 |
+
"\n",
|
| 794 |
+
"**The reward pathway is not the explanation.** `CV_A` averages **2.68** here (effective sample size 591 of a 4,608-window batch) against 1.16 on Craftax Classic, so in both environments the weights rank windows within a batch and the familiar explanation that sparse returns fail to discriminate is unavailable. The direct measurement of the return term was only possible on Craftax Classic — MiniHack needs the NetHack Learning Environment, which would not build on the machine used for that measurement — and there it gives `‖g_δ‖/‖∇L_BC‖ = 0.49 ± 0.01` at cosine `0.02 ± 0.05`. The control that settles it is also Craftax-side: advantage clipping cuts the return term fivefold and scores **3.16 points worse**, so the return weighting is not what does the damage.\n",
|
| 795 |
+
"\n",
|
| 796 |
+
"**What orders the outcome is permitted displacement.** Final KL divergence from the checkpoint correlates with final score at Spearman **−0.56** here and **−0.79** on Craftax Classic (−0.56 and −0.75 with LoRA excluded, which matters because the drift probe reads LoRA's frozen base weights and records near-zero). The sign is the same in both environments now: more displacement, lower score. Group C (architectural freezing) has the highest group mean here (**44.0%**) and Group A the tightest spread (std 0.95 points) — freezing neither destabilises the model nor helps it past the checkpoint.\n",
|
| 797 |
+
"\n",
|
| 798 |
+
"**Group-level summary** (mean ID win rate over each group, Table 7 from the paper):\n",
|
| 799 |
+
"\n",
|
| 800 |
+
"| Group | N | Mean | Δ from Baseline RL | Best | Worst | Std |\n",
|
| 801 |
+
"|---|---|---|---|---|---|---|\n",
|
| 802 |
+
"| Pretrained checkpoint | – | **47.50** | – | – | – | – |\n",
|
| 803 |
+
"| Baseline RL | 1 | 43.75 | – | – | – | 0.00 |\n",
|
| 804 |
+
"| A (Regularisation) | 6 | 42.78 | −0.97 | 44.17 | 41.25 | 0.95 |\n",
|
| 805 |
+
"| B (Training signal) | 7 | 38.57 | −5.18 | 48.75 | 12.08 | 11.64 |\n",
|
| 806 |
+
"| C (Architectural freezing) | 7 | 43.99 | +0.24 | 49.58 | 39.17 | 4.08 |\n",
|
| 807 |
+
"| D (Data quality / weights) | 4 | 38.02 | −5.73 | 44.17 | 32.08 | 4.28 |\n",
|
| 808 |
+
"\n",
|
| 809 |
+
"Every group mean is below the pretrained checkpoint, in both environments.\n",
|
| 810 |
+
"\n",
|
| 811 |
+
"Group B degrades with high variance (std 11.64), driven by the worst condition in the suite: **normalised advantages** at **12.1%** (3.73 on Craftax Classic). It alone mean-centres the weights, making roughly half of them **negative** and violating the non-negativity assumption (A1) the decomposition needs. A negative weight on a cross-entropy term is gradient *ascent* on that sequence's likelihood — unbounded and without a trust region — and its drift lands two to four orders of magnitude beyond any other condition. Supplying the repulsive direction a policy gradient would have does not rescue the objective; it destroys it.\n",
|
| 812 |
+
"\n",
|
| 813 |
+
"**On its own terms.** Craftax Classic collected return falls (5.21 → 3.73) alongside eval score, so the objective is not quietly succeeding at what it optimises. On MiniHack collected return is flat (−0.53 → −0.34, no trend across quartiles) while the training-window win rate rises from 6.3% to 13.5% and eval is flat. Neither environment looks like reward hacking under an evaluation mismatch.\n",
|
| 814 |
+
"\n",
|
| 815 |
+
"**Statistics.** With 3 seeds per arm, an exact two-sided permutation test enumerates C(6,3) = 20 relabellings, so the smallest attainable p-value is 2/20 = **0.10** — a reported p = 0.10 is the most extreme outcome the design can produce, not a null result. The shipped `significance_test.txt` compares the condition furthest from baseline (`normalized_adv`) and reports p at that floor with a bootstrap CI of **[−39.6, −23.8] points**; the paper's **[−2.1, +13.7]** interval is the different, best-versus-baseline comparison. Cell 10 prints the shipped file verbatim so the two are not confused.\n",
|
| 816 |
+
"\n",
|
| 817 |
+
"Each condition also collects gradient-alignment, representation-drift, CKA-similarity, and per-t-bin gradient-norm diagnostics. All training was done **offline** on the GPU-H200 machine — this notebook only displays the saved figures (downloaded with the HF snapshot)."
|
| 818 |
+
],
|
| 819 |
+
"id": "bf4148dfab969b4a"
|
| 820 |
+
},
|
| 821 |
+
{
|
| 822 |
+
"cell_type": "code",
|
| 823 |
+
"execution_count": null,
|
| 824 |
+
"metadata": {},
|
| 825 |
+
"outputs": [],
|
| 826 |
+
"source": [
|
| 827 |
+
"from IPython.display import Image, display\n",
|
| 828 |
+
"\n",
|
| 829 |
+
"assets_dir = os.path.join(SNAPSHOT_DIR, \"ablation_assets\")\n",
|
| 830 |
+
"if not os.path.isdir(assets_dir):\n",
|
| 831 |
+
" raise FileNotFoundError(\n",
|
| 832 |
+
" f\"Expected ablation assets at {assets_dir}. The HF repo must contain \"\n",
|
| 833 |
+
" \"an ablation_assets/ directory with the pre-computed PNGs and CSVs.\"\n",
|
| 834 |
+
" )\n",
|
| 835 |
+
"\n",
|
| 836 |
+
"figures = [\n",
|
| 837 |
+
" (\n",
|
| 838 |
+
" \"final_score_comparison.png\",\n",
|
| 839 |
+
" \"Final ID win rate per condition, sorted, against the pretrained \"\n",
|
| 840 |
+
" \"checkpoint at 47.5% under the harness protocol. Baseline RL falls to \"\n",
|
| 841 |
+
" \"43.8% (+/-6.1). Three conditions -- head_only 49.6, gradient_surgery \"\n",
|
| 842 |
+
" \"48.8, layer_ablation_top1 48.8 -- finish nominally above the \"\n",
|
| 843 |
+
" \"checkpoint, but the best-versus-baseline bootstrap interval is \"\n",
|
| 844 |
+
" \"[-2.1, +13.7] points at p = 0.40, so they are not read as \"\n",
|
| 845 |
+
" \"improvements. The worst condition is normalized_adv at 12.1%, and it \"\n",
|
| 846 |
+
" \"collapses because it mean-centres the weights and makes half of them \"\n",
|
| 847 |
+
" \"negative -- gradient ascent on below-average rollouts -- not because \"\n",
|
| 848 |
+
" \"the reward signal is uninformative. CV_A here is 2.68: the weights do \"\n",
|
| 849 |
+
" \"rank windows.\",\n",
|
| 850 |
+
" ),\n",
|
| 851 |
+
" (\n",
|
| 852 |
+
" \"group_comparison.png\",\n",
|
| 853 |
+
" \"Score distribution by intervention group, all below the pretrained \"\n",
|
| 854 |
+
" \"47.5%. Group C (architectural freezing) has the highest mean at 44.0, \"\n",
|
| 855 |
+
" \"Group A (regularisation) the tightest spread at 42.8 +/- 0.95, and \"\n",
|
| 856 |
+
" \"Groups B (38.6) and D (38.0) sit lowest. Group B's spread (std 11.6) \"\n",
|
| 857 |
+
" \"is carried by the normalized_adv collapse. Freezing parameter subsets \"\n",
|
| 858 |
+
" \"neither destabilises the model nor carries it past the checkpoint.\",\n",
|
| 859 |
+
" ),\n",
|
| 860 |
+
" (\n",
|
| 861 |
+
" \"score_delta_over_baseline_rl.png\",\n",
|
| 862 |
+
" \"Sorted improvement vs the baseline_rl condition. A handful of \"\n",
|
| 863 |
+
" \"conditions beat baseline return-weighted ELBO fine-tuning; on Craftax \"\n",
|
| 864 |
+
" \"Classic, where the effect is unambiguous, none of the 25 reaches the \"\n",
|
| 865 |
+
" \"checkpoint it started from. What orders this ranking is how far each \"\n",
|
| 866 |
+
" \"condition is permitted to displace the parameters (Spearman -0.56 \"\n",
|
| 867 |
+
" \"between final KL and final score here, -0.79 on Craftax Classic), not \"\n",
|
| 868 |
+
" \"how the return is turned into a weight.\",\n",
|
| 869 |
+
" ),\n",
|
| 870 |
+
" (\n",
|
| 871 |
+
" \"per_env_delta.png\",\n",
|
| 872 |
+
" \"Per-environment change from the pretrained checkpoint. The easy rooms \"\n",
|
| 873 |
+
" \"sit near ceiling and move little; the losses concentrate in the \"\n",
|
| 874 |
+
" \"layouts that need longer routing. This mirrors Craftax Classic, where \"\n",
|
| 875 |
+
" \"deep tech-tree achievements are the first to go (tier 3 falls 23% to \"\n",
|
| 876 |
+
" \"10%) while shallow behaviours hold.\",\n",
|
| 877 |
+
" ),\n",
|
| 878 |
+
" (\n",
|
| 879 |
+
" \"gradient_alignment.png\",\n",
|
| 880 |
+
" \"Cosine similarity between the return-weighted RL loss and the BC loss \"\n",
|
| 881 |
+
" \"gradients over fine-tuning. NOTE: the two gradients are taken at \"\n",
|
| 882 |
+
" \"*different parameter points*, so this is a retention diagnostic only \"\n",
|
| 883 |
+
" \"-- it confounds weighting with drift and is unrelated to the \"\n",
|
| 884 |
+
" \"single-parameter-point measurement of ||g_delta||/||grad L_BC|| = \"\n",
|
| 885 |
+
" \"0.49 +/- 0.01 at cosine 0.02 +/- 0.05 quoted above.\",\n",
|
| 886 |
+
" ),\n",
|
| 887 |
+
"]\n",
|
| 888 |
+
"\n",
|
| 889 |
+
"for fname, caption in figures:\n",
|
| 890 |
+
" path = os.path.join(assets_dir, fname)\n",
|
| 891 |
+
" if os.path.exists(path):\n",
|
| 892 |
+
" print(f\"\\n=== {fname} ===\\n{caption}\\n\")\n",
|
| 893 |
+
" display(Image(filename=path))\n",
|
| 894 |
+
" else:\n",
|
| 895 |
+
" print(f\"\\n[missing figure: {fname}]\")"
|
| 896 |
+
],
|
| 897 |
+
"id": "8d35602d5ddf2c60"
|
| 898 |
+
},
|
| 899 |
+
{
|
| 900 |
+
"cell_type": "markdown",
|
| 901 |
+
"metadata": {},
|
| 902 |
+
"source": [
|
| 903 |
+
"## Cell 10 — Ablation results tables\n",
|
| 904 |
+
"\n",
|
| 905 |
+
"The tables below are loaded directly from the saved CSV outputs of the ablation pipeline. `main_results.csv` is the canonical sortable score table; `hypothesis_verdict.csv` attaches each condition to the mechanism hypothesis it tests, so the verdict column reads as a direct answer to *what does return-weighted ELBO fine-tuning of the diffusion planner actually do?*\n",
|
| 906 |
+
"\n",
|
| 907 |
+
"**Read the `Verdict` column carefully.** It is scored against **baseline RL**, not against the pretrained checkpoint. A row marked `IMPROVEMENT` beat the plain return-weighted objective; with three exceptions here (and none at all on Craftax Classic) it did not beat the checkpoint it started from. `Delta_Pretrained` is the column that answers that, and it is negative for 22 of the 25 conditions in MiniHack and for all 25 on Craftax Classic.\n",
|
| 908 |
+
"\n",
|
| 909 |
+
"The collective answer across all 25 conditions is that the outcome does not track how the return is turned into a weight — the Craftax-side control cuts the return term fivefold and scores 3.16 *worse* — but does track how far each condition is permitted to displace the parameters. What that leaves pointing at is the data: fine-tuning on the model's own rollouts, with the return weighting a large but incidental passenger."
|
| 910 |
+
],
|
| 911 |
+
"id": "9cdbeab0b5332cf"
|
| 912 |
+
},
|
| 913 |
+
{
|
| 914 |
+
"cell_type": "code",
|
| 915 |
+
"execution_count": null,
|
| 916 |
+
"metadata": {},
|
| 917 |
+
"outputs": [],
|
| 918 |
+
"source": [
|
| 919 |
+
"import polars as pl\n",
|
| 920 |
+
"\n",
|
| 921 |
+
"main_csv = os.path.join(assets_dir, \"main_results.csv\")\n",
|
| 922 |
+
"verdicts_csv = os.path.join(assets_dir, \"hypothesis_verdict.csv\")\n",
|
| 923 |
+
"\n",
|
| 924 |
+
"df_main = pl.read_csv(main_csv).sort(\"Score\", descending=True)\n",
|
| 925 |
+
"print(\"=== main_results.csv (sorted by Score, descending) ===\")\n",
|
| 926 |
+
"with pl.Config(tbl_rows=30):\n",
|
| 927 |
+
" print(df_main)"
|
| 928 |
+
],
|
| 929 |
+
"id": "75782007e322494c"
|
| 930 |
+
},
|
| 931 |
+
{
|
| 932 |
+
"cell_type": "code",
|
| 933 |
+
"execution_count": null,
|
| 934 |
+
"metadata": {},
|
| 935 |
+
"outputs": [],
|
| 936 |
+
"source": [
|
| 937 |
+
"df_verdicts = pl.read_csv(verdicts_csv).sort(\"Delta_Baseline\", descending=True)\n",
|
| 938 |
+
"print(\"=== hypothesis_verdict.csv (sorted by improvement over baseline_rl) ===\")\n",
|
| 939 |
+
"with pl.Config(tbl_rows=30, fmt_str_lengths=80):\n",
|
| 940 |
+
" print(df_verdicts)"
|
| 941 |
+
],
|
| 942 |
+
"id": "c166b4e53f680bf2"
|
| 943 |
+
},
|
| 944 |
+
{
|
| 945 |
+
"cell_type": "markdown",
|
| 946 |
+
"metadata": {},
|
| 947 |
+
"source": [
|
| 948 |
+
"## Cell 11 — Conclusions\n",
|
| 949 |
+
"\n",
|
| 950 |
+
"**Empirical findings.**\n",
|
| 951 |
+
"\n",
|
| 952 |
+
"1. **The planner learns these layouts; the model-free baselines do not.** The DAgger-trained ReMDM planner reaches **48.5% in-distribution** over 50 episodes per environment (paper Table 6), and the offline-BC variant **73.0%**, against 0.3–4.7% for PPO, A2C, DQN and PPO-RNN trained from scratch. Those four are **not a matched comparison** — they use default hyperparameters and are reported only to establish that the layouts are not solvable from scratch within our budget. Any gap to the offline CNN+MLP and Decision Transformer confounds an auxiliary staircase-prediction head the baselines lack, ten denoising passes per decision against one, chunked replanning against greedy per-step selection, and untuned baselines. **This is not an architecture result**, and the current paper carries these baselines only as configurations (Table 11), not as a results table.\n",
|
| 953 |
+
"\n",
|
| 954 |
+
"2. **Zero-shot OOD transfer is weak, and we make no structural transfer claim.** The DAgger planner reaches **4.7%** mean win rate on the 3 held-out OOD maps against 48.5% in distribution (Room-Dark-15x15 12%, Corridor-R5 2%, MazeWalk-45x19 0%). The offline-BC variant reaches 6.0%. What transfer exists is to a partially observed room, not to longer routing or larger mazes.\n",
|
| 955 |
+
"\n",
|
| 956 |
+
"3. **Return-weighted ELBO fine-tuning degrades the planner — and the reward signal is not why.** In MiniHack the objective takes the checkpoint from **47.5% to 43.8% (±6.1)**; three conditions finish nominally above it, but the best-versus-baseline interval is [−2.1, +13.7] at p = 0.40, so they are not read as improvements. On Craftax Classic, where the effect is large and unambiguous, **no condition of the 25 recovers the checkpoint** and the plain objective gives up **3.59 of 11.81** points. The explanation is *not* that sparse returns fail to discriminate: `CV_A` is **2.68** here and **1.16** on Craftax Classic, and the return term itself measures `‖g_δ‖/‖∇L_BC‖ = 0.49 ± 0.01` at cosine **0.02 ± 0.05** — large and pointing away from imitation, against 0.893 ± 0.010 for `∇L_BC` against itself. **But shrinking it makes things worse.** Advantage clipping cuts the return term fivefold, to 0.097 ± 0.003, and scores **3.16 points below** the unclipped baseline. A condition with almost no return term degrades further than one with a large one.\n",
|
| 957 |
+
"\n",
|
| 958 |
+
"4. **What is left is the data.** The suite is ordered by plasticity — Spearman **−0.56** here and **−0.79** on Craftax Classic between final KL and final score — and advantage clipping, the nearest thing in the suite to unweighted training on the model's own rollouts, sits near the bottom. Together these point at fine-tuning on self-generated rollouts as the damaging ingredient, with the return weighting neither causing the harm nor preventing it. The paper is explicit that this is an inference from a near-substitute: the unweighted-ELBO-on-all-rollouts arm was not run.\n",
|
| 959 |
+
"\n",
|
| 960 |
+
"5. **It is not reward hacking under an evaluation mismatch.** On Craftax Classic collected return falls from **5.21 to 3.73** over the same 500 iterations in which eval score falls from 12.06 to 8.49. On MiniHack collected return is flat (−0.53 → −0.34) while the training-window win rate rises from 6.3% to 13.5% and eval is flat. The objective is not quietly succeeding at what it optimises.\n",
|
| 961 |
+
"\n",
|
| 962 |
+
"**Why the sign constraint is not the whole story.** The objective is a bound only for `A_i ≥ 0`, so it can re-rank sampled behaviour but never push mass away from it, which caps how much *improvement* re-ranking can deliver. That does not explain *degradation*, and it cannot be the whole story, because the same non-negativity holds of reward-weighted and advantage-weighted regression, which work elsewhere. The difference we can point to is the data: those methods are usually applied to a fixed dataset rather than to rollouts fed back through a denoising objective at every step.\n",
|
| 963 |
+
"\n",
|
| 964 |
+
"**A second, separate cost of the surrogate.** Not offered as evidence for the above: the ELBO surrogate is badly conditioned across diffusion time, and markedly worse on the harder environment. On Craftax Classic the gradient norm in the top third of the `t` range is **2.5×** the bottom third with low-`t`/high-`t` cosine similarity **0.06**; on MiniHack the ratio is **1.14** and the cosine **0.52**. Repairing the conditioning does not recover the checkpoint — on Craftax Classic `low_t` (8.12) and `t_curriculum` (8.41) sit at the baseline of 8.22 rather than above it.\n",
|
| 965 |
+
"\n",
|
| 966 |
+
"**What these results do not establish.** MiniHack is **not a second confirmation**: the effect here is small against a 6.1-point seed standard deviation, and the claim rests on Craftax Classic. There is no unweighted-ELBO-on-all-rollouts arm and no continued-DAgger arm. The gradient measurement is at one parameter point, on Craftax Classic only — MiniHack would need the NetHack Learning Environment, which would not build on the machine used for that measurement. The null characterises the clipped return ratio, not the exponential advantage weight `exp(A/β)`. And the gradient-alignment cosine logged by the training harness takes its two gradients at *different* parameter points, so it is a **retention diagnostic only**.\n",
|
| 967 |
+
"\n",
|
| 968 |
+
"**Two cheap diagnostics, worth taking first.** For anyone reaching for this objective: `CV_A` comes free from an effective-sample-size counter and says whether the returns rank anything at all. The ratio `‖g_δ‖/‖∇L_BC‖` costs two backward passes and says how much of the update the reward is responsible for. Ours said 0.49, which looked like a mechanism until the clipping control said otherwise.\n",
|
| 969 |
+
"\n",
|
| 970 |
+
"**Open problem.** Per-step formulations over the denoising chain (d1, DiffPO) admit **signed** advantages, and so change the object being optimised rather than the weighting inside a regression. The obstacle for planners like ours is that those estimators assume monotone unmasking, which ReMDM's inference-time remasking violates. What these results argue for is an estimator that tolerates remasking *and* admits a repulsive direction; **categorical flow matching** (Campbell et al., 2024) is one route to the first half."
|
| 971 |
+
],
|
| 972 |
+
"id": "9bb18fc96b4b459a"
|
| 973 |
+
}
|
| 974 |
+
],
|
| 975 |
+
"metadata": {
|
| 976 |
+
"kernelspec": {
|
| 977 |
+
"display_name": "Python 3",
|
| 978 |
+
"language": "python",
|
| 979 |
+
"name": "python3"
|
| 980 |
+
},
|
| 981 |
+
"language_info": {
|
| 982 |
+
"name": "python",
|
| 983 |
+
"pygments_lexer": "ipython3",
|
| 984 |
+
"version": "3.12.0"
|
| 985 |
+
}
|
| 986 |
+
},
|
| 987 |
+
"nbformat": 4,
|
| 988 |
+
"nbformat_minor": 5
|
| 989 |
+
}
|
experiments/rl_finetuning/outputs/minihack_ablations/diagnosis.md
CHANGED
|
@@ -2,21 +2,21 @@
|
|
| 2 |
|
| 3 |
## Hypothesis Ranking
|
| 4 |
|
| 5 |
-
### 1.
|
| 6 |
|
| 7 |
-
**Description:**
|
| 8 |
|
| 9 |
-
**Evidence:**
|
| 10 |
|
| 11 |
-
**Recommendation:**
|
| 12 |
|
| 13 |
-
### 2.
|
| 14 |
|
| 15 |
-
**Description:**
|
| 16 |
|
| 17 |
-
**Evidence:**
|
| 18 |
|
| 19 |
-
**Recommendation:**
|
| 20 |
|
| 21 |
### 3. Signal Sparsity [*] (0%)
|
| 22 |
|
|
@@ -54,65 +54,65 @@
|
|
| 54 |
|
| 55 |
| Ablation | Group | Score | Delta vs Pretrained |
|
| 56 |
|---|---|---|---|
|
| 57 |
-
| action_diversity | D | 0.
|
| 58 |
-
| advantage_clip | B | 0.
|
| 59 |
-
| attention_only | C | 0.
|
| 60 |
-
| baseline_rl | Baseline | 0.
|
| 61 |
-
| bc_wins | B | 0.
|
| 62 |
-
| entropy_bonus | B | 0.
|
| 63 |
-
| ewc | A | 0.
|
| 64 |
-
| ffn_only | C | 0.
|
| 65 |
-
| frozen_backbone | C | 0.
|
| 66 |
-
| gradient_surgery | B | 0.
|
| 67 |
-
| head_only | C | 0.
|
| 68 |
-
| kl_penalty | A | 0.
|
| 69 |
-
| layer_ablation_top1 | C | 0.
|
| 70 |
-
| layer_ablation_top2 | C | 0.
|
| 71 |
-
| layer_ablation_top3 | C | 0.
|
| 72 |
-
| llrd | A | 0.
|
| 73 |
-
| lora | A | 0.
|
| 74 |
-
| low_t | B | 0.
|
| 75 |
-
| mixed_replay | A | 0.
|
| 76 |
-
| normalized_adv | B | 0.
|
| 77 |
-
| reward_filtering | D | 0.
|
| 78 |
-
| reward_model | D | 0.
|
| 79 |
-
| running_stats | D | 0.
|
| 80 |
-
| t_curriculum | B | 0.
|
| 81 |
-
| trust_region_kl | A | 0.
|
| 82 |
|
| 83 |
| Ablation | Score | Delta vs Baseline | Verdict |
|
| 84 |
|---|---|---|---|
|
| 85 |
-
| action_diversity | 0.
|
| 86 |
-
| advantage_clip | 0.
|
| 87 |
-
| attention_only | 0.
|
| 88 |
-
| baseline_rl | 0.
|
| 89 |
-
| bc_wins | 0.
|
| 90 |
-
| entropy_bonus | 0.
|
| 91 |
-
| ewc | 0.
|
| 92 |
-
| ffn_only | 0.
|
| 93 |
-
| frozen_backbone | 0.
|
| 94 |
-
| gradient_surgery | 0.
|
| 95 |
-
| head_only | 0.
|
| 96 |
-
| kl_penalty | 0.
|
| 97 |
-
| layer_ablation_top1 | 0.
|
| 98 |
-
| layer_ablation_top2 | 0.
|
| 99 |
-
| layer_ablation_top3 | 0.
|
| 100 |
-
| llrd | 0.
|
| 101 |
-
| lora | 0.
|
| 102 |
-
| low_t | 0.
|
| 103 |
-
| mixed_replay | 0.
|
| 104 |
-
| normalized_adv | 0.
|
| 105 |
-
| reward_filtering | 0.
|
| 106 |
-
| reward_model | 0.
|
| 107 |
-
| running_stats | 0.
|
| 108 |
-
| t_curriculum | 0.
|
| 109 |
-
| trust_region_kl | 0.
|
| 110 |
-
|
| 111 |
-
*Pretrained score: 0.
|
| 112 |
-
*Baseline RL score: 0.
|
| 113 |
|
| 114 |
## Aggregate Verdict
|
| 115 |
|
| 116 |
-
Mixed results:
|
| 117 |
|
| 118 |
-
**Gradient alignment** = +0.
|
|
|
|
| 2 |
|
| 3 |
## Hypothesis Ranking
|
| 4 |
|
| 5 |
+
### 1. Gradient Conflict [*] (33%)
|
| 6 |
|
| 7 |
+
**Description:** RL and BC gradients point in conflicting directions.
|
| 8 |
|
| 9 |
+
**Evidence:** 1/3 supporting ablations improved over baseline.
|
| 10 |
|
| 11 |
+
**Recommendation:** Apply PCGrad and investigate t-distribution bias.
|
| 12 |
|
| 13 |
+
### 2. Catastrophic Forgetting [*] (17%)
|
| 14 |
|
| 15 |
+
**Description:** Pretrained representations are corrupted by RL gradients.
|
| 16 |
|
| 17 |
+
**Evidence:** 1/6 supporting ablations improved over baseline.
|
| 18 |
|
| 19 |
+
**Recommendation:** Implement strong parameter regularisation (EWC + LLRD) or use LoRA to restrict update space.
|
| 20 |
|
| 21 |
### 3. Signal Sparsity [*] (0%)
|
| 22 |
|
|
|
|
| 54 |
|
| 55 |
| Ablation | Group | Score | Delta vs Pretrained |
|
| 56 |
|---|---|---|---|
|
| 57 |
+
| action_diversity | D | 0.3750 | -0.1000 |
|
| 58 |
+
| advantage_clip | B | 0.3375 | -0.1375 |
|
| 59 |
+
| attention_only | C | 0.3958 | -0.0792 |
|
| 60 |
+
| baseline_rl | Baseline | 0.4375 | -0.0375 |
|
| 61 |
+
| bc_wins | B | 0.4583 | -0.0167 |
|
| 62 |
+
| entropy_bonus | B | 0.4458 | -0.0292 |
|
| 63 |
+
| ewc | A | 0.4333 | -0.0417 |
|
| 64 |
+
| ffn_only | C | 0.4375 | -0.0375 |
|
| 65 |
+
| frozen_backbone | C | 0.4667 | -0.0083 |
|
| 66 |
+
| gradient_surgery | B | 0.4875 | +0.0125 |
|
| 67 |
+
| head_only | C | 0.4958 | +0.0208 |
|
| 68 |
+
| kl_penalty | A | 0.4417 | -0.0333 |
|
| 69 |
+
| layer_ablation_top1 | C | 0.4875 | +0.0125 |
|
| 70 |
+
| layer_ablation_top2 | C | 0.4042 | -0.0708 |
|
| 71 |
+
| layer_ablation_top3 | C | 0.3917 | -0.0833 |
|
| 72 |
+
| llrd | A | 0.4208 | -0.0542 |
|
| 73 |
+
| lora | A | 0.4333 | -0.0417 |
|
| 74 |
+
| low_t | B | 0.4208 | -0.0542 |
|
| 75 |
+
| mixed_replay | A | 0.4125 | -0.0625 |
|
| 76 |
+
| normalized_adv | B | 0.1208 | -0.3542 |
|
| 77 |
+
| reward_filtering | D | 0.4417 | -0.0333 |
|
| 78 |
+
| reward_model | D | 0.3208 | -0.1542 |
|
| 79 |
+
| running_stats | D | 0.3833 | -0.0917 |
|
| 80 |
+
| t_curriculum | B | 0.4292 | -0.0458 |
|
| 81 |
+
| trust_region_kl | A | 0.4250 | -0.0500 |
|
| 82 |
|
| 83 |
| Ablation | Score | Delta vs Baseline | Verdict |
|
| 84 |
|---|---|---|---|
|
| 85 |
+
| action_diversity | 0.3750 | -0.0625 | COLLAPSE |
|
| 86 |
+
| advantage_clip | 0.3375 | -0.1000 | COLLAPSE |
|
| 87 |
+
| attention_only | 0.3958 | -0.0417 | NEUTRAL |
|
| 88 |
+
| baseline_rl | 0.4375 | +0.0000 | NEUTRAL |
|
| 89 |
+
| bc_wins | 0.4583 | +0.0208 | NEUTRAL |
|
| 90 |
+
| entropy_bonus | 0.4458 | +0.0083 | NEUTRAL |
|
| 91 |
+
| ewc | 0.4333 | -0.0042 | NEUTRAL |
|
| 92 |
+
| ffn_only | 0.4375 | +0.0000 | NEUTRAL |
|
| 93 |
+
| frozen_backbone | 0.4667 | +0.0292 | IMPROVEMENT |
|
| 94 |
+
| gradient_surgery | 0.4875 | +0.0500 | IMPROVEMENT |
|
| 95 |
+
| head_only | 0.4958 | +0.0583 | IMPROVEMENT |
|
| 96 |
+
| kl_penalty | 0.4417 | +0.0042 | NEUTRAL |
|
| 97 |
+
| layer_ablation_top1 | 0.4875 | +0.0500 | IMPROVEMENT |
|
| 98 |
+
| layer_ablation_top2 | 0.4042 | -0.0333 | NEUTRAL |
|
| 99 |
+
| layer_ablation_top3 | 0.3917 | -0.0458 | NEUTRAL |
|
| 100 |
+
| llrd | 0.4208 | -0.0167 | NEUTRAL |
|
| 101 |
+
| lora | 0.4333 | -0.0042 | NEUTRAL |
|
| 102 |
+
| low_t | 0.4208 | -0.0167 | NEUTRAL |
|
| 103 |
+
| mixed_replay | 0.4125 | -0.0250 | NEUTRAL |
|
| 104 |
+
| normalized_adv | 0.1208 | -0.3167 | COLLAPSE |
|
| 105 |
+
| reward_filtering | 0.4417 | +0.0042 | NEUTRAL |
|
| 106 |
+
| reward_model | 0.3208 | -0.1167 | COLLAPSE |
|
| 107 |
+
| running_stats | 0.3833 | -0.0542 | COLLAPSE |
|
| 108 |
+
| t_curriculum | 0.4292 | -0.0083 | NEUTRAL |
|
| 109 |
+
| trust_region_kl | 0.4250 | -0.0125 | NEUTRAL |
|
| 110 |
+
|
| 111 |
+
*Pretrained score: 0.4750*
|
| 112 |
+
*Baseline RL score: 0.4375*
|
| 113 |
|
| 114 |
## Aggregate Verdict
|
| 115 |
|
| 116 |
+
Mixed results: 5/24 ablations collapsed. Check individual verdicts above.
|
| 117 |
|
| 118 |
+
**Gradient alignment** = +0.1107: RL gradient has useful signal.
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/cka_similarity.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_action_diversity.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_advantage_clip.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_attention_only.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_baseline_rl.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_bc_wins.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_entropy_bonus.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_ewc.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_ffn_only.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_frozen_backbone.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_gradient_surgery.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_head_only.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_kl_penalty.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_layer_ablation_top1.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_layer_ablation_top2.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_layer_ablation_top3.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_llrd.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_lora.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_low_t.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_mixed_replay.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_normalized_adv.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_reward_filtering.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_reward_model.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_running_stats.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|
experiments/rl_finetuning/outputs/minihack_ablations/figures/curves_t_curriculum.png
CHANGED
|
Git LFS Details
|
|
Git LFS Details
|