Refresh artefacts and code for the second review release
Browse files- .gitattributes +0 -234
- .gitignore +1 -0
- Craftax_Baselines/Dockerfile +2 -2
- Craftax_Baselines/run_docker.sh +1 -1
- Dockerfile +10 -5
- LICENSE +2 -1
- README.md +194 -146
- checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/commit_success.txt +1 -1
- checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/commit_success.txt +1 -1
- checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/resume_metadata.json +1 -7
- checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/40370176/commit_success.txt +1 -1
- checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/40370176/default/commit_success.txt +1 -1
- checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/resume_metadata.json +0 -6
- checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M/wandb-summary.json +31 -1
- checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M/config.yaml +0 -6
- checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M/wandb-summary.json +76 -1
- configs/defaults.yaml +10 -0
- demo_craftax.ipynb +275 -31
- experiments/README.md +144 -37
- experiments/rl_finetuning/ablations/losses.py +29 -0
- experiments/rl_finetuning/ablations/registry.py +10 -0
- experiments/rl_finetuning/ablations/training.py +10 -7
- experiments/rl_finetuning/analysis/gdelta.py +548 -0
- experiments/rl_finetuning/analysis/plots.py +4 -2
- experiments/rl_finetuning/analysis/tables.py +345 -9
- experiments/rl_finetuning/configs/ablations_final_craftax_classic_gpu_24gb.yaml +2 -2
- experiments/rl_finetuning/configs/ablations_final_craftax_classic_gpu_h200.yaml +1 -1
- experiments/rl_finetuning/configs/ablations_final_craftax_gpu_24gb.yaml +2 -2
- experiments/rl_finetuning/configs/ablations_final_craftax_gpu_h200.yaml +1 -1
- experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_aggregate.json +103 -0
- experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed0.json +74 -0
- experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed1.json +74 -0
- experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed2.json +74 -0
- experiments/rl_finetuning/outputs/craftax_classic_ablations/results.json +6 -11
.gitattributes
CHANGED
|
@@ -33,237 +33,3 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
-
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/ocdbt.process_0/d/503349be2d11cb1bf04a17e7b3e2d1e0 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
-
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/ocdbt.process_0/d/781b85e3ec447139f7d23160643a9c21 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
-
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/ocdbt.process_0/d/92adc334c11103c7741baed0d71e66cb filter=lfs diff=lfs merge=lfs -text
|
| 39 |
-
checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/100000000/default/ocdbt.process_0/d/35a15297533c9b116fa22b2bb1975990 filter=lfs diff=lfs merge=lfs -text
|
| 40 |
-
checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/100000000/default/ocdbt.process_0/d/9778194f691de46821092616cc3a1ff2 filter=lfs diff=lfs merge=lfs -text
|
| 41 |
-
checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M/1000000000/default/ocdbt.process_0/d/369457b7c6608f1adf28eb88024d6b91 filter=lfs diff=lfs merge=lfs -text
|
| 42 |
-
checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M/1000000000/default/ocdbt.process_0/d/6bb110a840637eede93e25f5356236f9 filter=lfs diff=lfs merge=lfs -text
|
| 43 |
-
checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M/1000000000/default/ocdbt.process_0/d/e5a4020f50167115120fe5dac41c20fb filter=lfs diff=lfs merge=lfs -text
|
| 44 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_breakdown.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 50 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 51 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 52 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 53 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 54 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 55 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 56 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 57 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 58 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 59 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 60 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 61 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 62 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 63 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 64 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 65 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 66 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 67 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 68 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 69 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/achievement_collapse_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 70 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/action_dist/js_divergence_comparison.png filter=lfs diff=lfs merge=lfs -text
|
| 71 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/cka_similarity.png filter=lfs diff=lfs merge=lfs -text
|
| 72 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 73 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 74 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 75 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 76 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 77 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 78 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 79 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 80 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 81 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 82 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 83 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 84 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 85 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 86 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 87 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 88 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 89 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 90 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 91 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 92 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 93 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 94 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 95 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 96 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/curves_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 97 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/eval_scores_over_training.png filter=lfs diff=lfs merge=lfs -text
|
| 98 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/final_score_comparison.png filter=lfs diff=lfs merge=lfs -text
|
| 99 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/gradient_alignment.png filter=lfs diff=lfs merge=lfs -text
|
| 100 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/gradient_conflict_map.png filter=lfs diff=lfs merge=lfs -text
|
| 101 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 102 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 103 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 104 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 105 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 106 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 107 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 108 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 109 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 110 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 111 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 112 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 113 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 114 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 115 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 116 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 117 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 118 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 119 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 120 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 121 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 122 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 123 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 124 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 125 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/per_layer_grad_heatmap_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 126 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/representation_drift.png filter=lfs diff=lfs merge=lfs -text
|
| 127 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/score_delta_over_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 128 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 129 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 130 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 131 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 132 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 133 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 134 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 135 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 136 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 137 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 138 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 139 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 140 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 141 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 142 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 143 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 144 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 145 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 146 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 147 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 148 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 149 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 150 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 151 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 152 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_grad_norms_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 153 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_bin_norms_heatmap.png filter=lfs diff=lfs merge=lfs -text
|
| 154 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/t_distribution_analysis.png filter=lfs diff=lfs merge=lfs -text
|
| 155 |
-
experiments/rl_finetuning/outputs/craftax_classic_final_results/analysis/figures/win_rate_and_effective_batch_size.png filter=lfs diff=lfs merge=lfs -text
|
| 156 |
-
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/ocdbt.process_0/d/1b7ef41abe2bf7d133b20680543c1738 filter=lfs diff=lfs merge=lfs -text
|
| 157 |
-
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/ocdbt.process_0/d/8f77d89a5c0896490dcc8e0a7a3e001f filter=lfs diff=lfs merge=lfs -text
|
| 158 |
-
checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/40370176/default/ocdbt.process_0/d/8bf3ebb1b364b9527375993e4a94621d filter=lfs diff=lfs merge=lfs -text
|
| 159 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_breakdown.png filter=lfs diff=lfs merge=lfs -text
|
| 160 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 161 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 162 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 163 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 164 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 165 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 166 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 167 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 168 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 169 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 170 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 171 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 172 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 173 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 174 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 175 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 176 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 177 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 178 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 179 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 180 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 181 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 182 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 183 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 184 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/achievement_collapse_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 185 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/cka_similarity.png filter=lfs diff=lfs merge=lfs -text
|
| 186 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 187 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 188 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 189 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 190 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 191 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 192 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 193 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 194 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 195 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 196 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 197 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 198 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 199 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 200 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 201 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 202 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 203 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 204 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 205 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 206 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 207 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 208 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 209 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 210 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/curves_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 211 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/eval_scores_over_training.png filter=lfs diff=lfs merge=lfs -text
|
| 212 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/final_score_comparison.png filter=lfs diff=lfs merge=lfs -text
|
| 213 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/gradient_alignment.png filter=lfs diff=lfs merge=lfs -text
|
| 214 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/gradient_conflict_map.png filter=lfs diff=lfs merge=lfs -text
|
| 215 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 216 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 217 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 218 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 219 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 220 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 221 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 222 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 223 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 224 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 225 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 226 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 227 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 228 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 229 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 230 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 231 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 232 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 233 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 234 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 235 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 236 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 237 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 238 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 239 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/per_layer_grad_heatmap_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 240 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/representation_drift.png filter=lfs diff=lfs merge=lfs -text
|
| 241 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/score_delta_over_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 242 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_action_diversity.png filter=lfs diff=lfs merge=lfs -text
|
| 243 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_advantage_clip.png filter=lfs diff=lfs merge=lfs -text
|
| 244 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_attention_only.png filter=lfs diff=lfs merge=lfs -text
|
| 245 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_baseline_rl.png filter=lfs diff=lfs merge=lfs -text
|
| 246 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_bc_wins.png filter=lfs diff=lfs merge=lfs -text
|
| 247 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_entropy_bonus.png filter=lfs diff=lfs merge=lfs -text
|
| 248 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_ewc.png filter=lfs diff=lfs merge=lfs -text
|
| 249 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_ffn_only.png filter=lfs diff=lfs merge=lfs -text
|
| 250 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_frozen_backbone.png filter=lfs diff=lfs merge=lfs -text
|
| 251 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_gradient_surgery.png filter=lfs diff=lfs merge=lfs -text
|
| 252 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_head_only.png filter=lfs diff=lfs merge=lfs -text
|
| 253 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_kl_penalty.png filter=lfs diff=lfs merge=lfs -text
|
| 254 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_layer_ablation_top1.png filter=lfs diff=lfs merge=lfs -text
|
| 255 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_layer_ablation_top2.png filter=lfs diff=lfs merge=lfs -text
|
| 256 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_layer_ablation_top3.png filter=lfs diff=lfs merge=lfs -text
|
| 257 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_llrd.png filter=lfs diff=lfs merge=lfs -text
|
| 258 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_lora.png filter=lfs diff=lfs merge=lfs -text
|
| 259 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_low_t.png filter=lfs diff=lfs merge=lfs -text
|
| 260 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_mixed_replay.png filter=lfs diff=lfs merge=lfs -text
|
| 261 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_normalized_adv.png filter=lfs diff=lfs merge=lfs -text
|
| 262 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_reward_filtering.png filter=lfs diff=lfs merge=lfs -text
|
| 263 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_reward_model.png filter=lfs diff=lfs merge=lfs -text
|
| 264 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_running_stats.png filter=lfs diff=lfs merge=lfs -text
|
| 265 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_t_curriculum.png filter=lfs diff=lfs merge=lfs -text
|
| 266 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_grad_norms_trust_region_kl.png filter=lfs diff=lfs merge=lfs -text
|
| 267 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_bin_norms_heatmap.png filter=lfs diff=lfs merge=lfs -text
|
| 268 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/t_distribution_analysis.png filter=lfs diff=lfs merge=lfs -text
|
| 269 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/figures/win_rate_and_effective_batch_size.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
CHANGED
|
@@ -246,3 +246,4 @@ CLI_CHANGES.md
|
|
| 246 |
|
| 247 |
# Claude Code: per-machine settings stay local; the rest of .claude/ is shared
|
| 248 |
.claude/settings.local.json
|
|
|
|
|
|
| 246 |
|
| 247 |
# Claude Code: per-machine settings stay local; the rest of .claude/ is shared
|
| 248 |
.claude/settings.local.json
|
| 249 |
+
results/
|
Craftax_Baselines/Dockerfile
CHANGED
|
@@ -33,9 +33,9 @@ USER duser
|
|
| 33 |
WORKDIR /home/duser
|
| 34 |
|
| 35 |
# Install Python packages
|
| 36 |
-
ENV PATH="
|
| 37 |
RUN python3 -m pip install --upgrade pip
|
| 38 |
ARG REQS
|
| 39 |
RUN pip install $REQS -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html
|
| 40 |
|
| 41 |
-
WORKDIR
|
|
|
|
| 33 |
WORKDIR /home/duser
|
| 34 |
|
| 35 |
# Install Python packages
|
| 36 |
+
ENV PATH="<path omitted for anonymous review>"
|
| 37 |
RUN python3 -m pip install --upgrade pip
|
| 38 |
ARG REQS
|
| 39 |
RUN pip install $REQS -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html
|
| 40 |
|
| 41 |
+
WORKDIR <path omitted for anonymous review>
|
Craftax_Baselines/run_docker.sh
CHANGED
|
@@ -14,7 +14,7 @@ for gpu in $gpus; do
|
|
| 14 |
docker run \
|
| 15 |
--gpus device=$gpu \
|
| 16 |
-e WANDB_API_KEY=$WANDB_API_KEY \
|
| 17 |
-
-v $(pwd):
|
| 18 |
--name craftax_$gpu \
|
| 19 |
--user $(id -u) \
|
| 20 |
--rm \
|
|
|
|
| 14 |
docker run \
|
| 15 |
--gpus device=$gpu \
|
| 16 |
-e WANDB_API_KEY=$WANDB_API_KEY \
|
| 17 |
+
-v $(pwd):<path omitted for anonymous review> \
|
| 18 |
--name craftax_$gpu \
|
| 19 |
--user $(id -u) \
|
| 20 |
--rm \
|
Dockerfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# craftax: vast.ai image.
|
| 2 |
#
|
| 3 |
# Build and push (amd64 is mandatory: vast GPU hosts are x86, an arm64 image
|
| 4 |
# from an Apple Silicon Mac will not run):
|
|
@@ -59,7 +59,12 @@ RUN cp uv.lock /opt/venv/baked-uv.lock
|
|
| 59 |
WORKDIR /workspace
|
| 60 |
|
| 61 |
# --- optional: bake the released checkpoints ------------------------------
|
| 62 |
-
# Left out on purpose. 470 MB unpacks into a large small-file layer,
|
| 63 |
-
# the slowest kind to pull and a likely cause of stalled image pulls.
|
| 64 |
-
#
|
| 65 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# remdm-planner-craftax: vast.ai image.
|
| 2 |
#
|
| 3 |
# Build and push (amd64 is mandatory: vast GPU hosts are x86, an arm64 image
|
| 4 |
# from an Apple Silicon Mac will not run):
|
|
|
|
| 59 |
WORKDIR /workspace
|
| 60 |
|
| 61 |
# --- optional: bake the released checkpoints ------------------------------
|
| 62 |
+
# Left out on purpose. 470 MB on the Hub unpacks into a large small-file layer,
|
| 63 |
+
# which is the slowest kind to pull and a likely cause of stalled image pulls.
|
| 64 |
+
# Fetching them at boot takes under a minute. If you do want them baked, use an
|
| 65 |
+
# isolated env so the pinned huggingface_hub in /opt/venv is not disturbed:
|
| 66 |
+
#
|
| 67 |
+
# RUN HF_HOME=/tmp/hf uvx --from "huggingface_hub[cli]" \
|
| 68 |
+
# hf download AnonMLuser/remdm-planner-craftax \
|
| 69 |
+
# --include "checkpoints/**" --local-dir /opt/checkpoints \
|
| 70 |
+
# && rm -rf /tmp/hf
|
LICENSE
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
MIT License
|
| 2 |
|
| 3 |
-
Copyright (c) 2026 The authors of "Return-Weighted ELBO Fine-Tuning
|
|
|
|
| 4 |
|
| 5 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
| 1 |
MIT License
|
| 2 |
|
| 3 |
+
Copyright (c) 2026 The authors of "Return-Weighted ELBO Fine-Tuning Degrades
|
| 4 |
+
Masked Diffusion Planners"
|
| 5 |
|
| 6 |
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 7 |
of this software and associated documentation files (the "Software"), to deal
|
README.md
CHANGED
|
@@ -38,13 +38,24 @@ checkpoints/
|
|
| 38 |
ablation fine-tunes)
|
| 39 |
ppo_agents/ PPO-RNN experts
|
| 40 |
|
| 41 |
-
experiments/rl_finetuning/outputs/craftax_classic_ablations/
|
| 42 |
figures/ 113 pre-computed PNGs
|
| 43 |
tables/ main_results.csv, group_summary.csv, hypothesis_verdict.csv,
|
| 44 |
-
achievement_summary.csv, gradient_analysis.csv, significance_test.txt,
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
```
|
| 49 |
|
| 50 |
## Headline numbers (Craftax Classic)
|
|
@@ -59,6 +70,39 @@ results/inference/ Single-seed evaluations through the inpainting sampler (see
|
|
| 59 |
No condition of the 25 recovers the checkpoint it started from. Group means are
|
| 60 |
9.74 (A), 7.07 (B), 10.53 (C) and 7.97 (D).
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
## Two evaluation paths — read this before comparing numbers
|
| 63 |
|
| 64 |
Every number in the paper comes from the **ablation harness**
|
|
@@ -66,12 +110,14 @@ Every number in the paper comes from the **ablation harness**
|
|
| 66 |
`sample_plan` with no locked prefix, 50 denoising steps, 8 actions executed per
|
| 67 |
plan, scored as `returned_episode_returns`.
|
| 68 |
|
| 69 |
-
`main.py --mode inference`
|
| 70 |
-
which freezes every executed action as an inpainting prefix
|
| 71 |
-
step. It is a different planner at evaluation time and scores far
|
| 72 |
-
weights (3.26 against 11.81 for the DAgger checkpoint). The
|
| 73 |
-
`results/inference/` were produced by that path and are kept as
|
| 74 |
-
planning-as-inpainting design choice — they are not paper numbers.
|
|
|
|
|
|
|
| 75 |
|
| 76 |
`demo_craftax.ipynb` evaluates through the harness path (Cell 5) and runs the
|
| 77 |
inpainting path only as a clearly labelled ablation (Cell 5b).
|
|
@@ -103,13 +149,13 @@ Then open `demo_craftax.ipynb`, or see the project documentation below.
|
|
| 103 |
|
| 104 |
JAX implementation of **ReMDM** (Remasking Discrete Diffusion Model) for action-sequence planning in [Craftax](https://github.com/MichaelTMatthews/Craftax), a JAX-accelerated, procedurally generated open-world survival game. A bidirectional transformer generates `plan_horizon`-length action plans by iteratively denoising masked token sequences, conditioned on the current symbolic observation. Trained under a pre-trained PPO expert, either offline (behavioural cloning on live rollouts) or online (DAgger).
|
| 105 |
|
| 106 |
-
The sibling repository [`minihack
|
| 107 |
|
| 108 |
## Method
|
| 109 |
|
| 110 |
-
The planner starts from a fully-masked action sequence and iteratively unmasks tokens over `T` denoising steps; ReMDM extends MDLM with remasking strategies that let committed tokens be re-predicted, improving plan coherence.
|
| 111 |
|
| 112 |
-
|
| 113 |
|
| 114 |
## Setup
|
| 115 |
|
|
@@ -117,7 +163,9 @@ Prerequisites: Python 3.12+, [uv](https://docs.astral.sh/uv/). Linux GPU use nee
|
|
| 117 |
|
| 118 |
```bash
|
| 119 |
git clone https://github.com/ANONYMOUS/remdm-planners.git
|
| 120 |
-
cd remdm-
|
|
|
|
|
|
|
| 121 |
|
| 122 |
# Default: CPU-only JAX (macOS, or Linux without a GPU).
|
| 123 |
# Installs the dev group (pytest) too.
|
|
@@ -135,16 +183,17 @@ Extras: `cuda13` and `cuda12` are mutually exclusive and Linux-only. JAX ships G
|
|
| 135 |
## Repo layout
|
| 136 |
|
| 137 |
```
|
| 138 |
-
craftax/
|
| 139 |
-
├── Craftax_Baselines/
|
| 140 |
├── configs/ Experiment configs (defaults.yaml + presets, see Configuration)
|
| 141 |
├── src/ Model, diffusion, planner pipelines
|
| 142 |
├── experiments/
|
| 143 |
│ └── rl_finetuning/ RL fine-tuning ablation suite (run_ablations.py)
|
| 144 |
-
├── scripts/ Param counter, PPO evaluator, HF upload
|
| 145 |
├── tests/ Smoke suite — uv run pytest
|
| 146 |
├── checkpoints/ Gitignored — offline/, online/, ppo_agents/ (see Checkpoints)
|
| 147 |
-
├── results/
|
|
|
|
| 148 |
├── demo_craftax.ipynb Demo notebook
|
| 149 |
├── main.py CLI entry point
|
| 150 |
└── pyproject.toml uv project — deps, cuda12/cuda13 extras, dev group
|
|
@@ -162,7 +211,7 @@ python main.py --mode smoke
|
|
| 162 |
|
| 163 |
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. All training modes need a PPO expert checkpoint.
|
| 164 |
|
| 165 |
-
### Stage 1 — Train the PPO expert (
|
| 166 |
|
| 167 |
```bash
|
| 168 |
cd Craftax_Baselines
|
|
@@ -179,8 +228,6 @@ Rolls out the PPO agent live at each update.
|
|
| 179 |
|
| 180 |
```bash
|
| 181 |
python main.py --mode offline --ppo-checkpoint /path/to/ppo_checkpoint
|
| 182 |
-
python main.py --mode offline --ppo-checkpoint /path/to/ppo_checkpoint \
|
| 183 |
-
--override offline_total_timesteps=100000000
|
| 184 |
```
|
| 185 |
|
| 186 |
### Online DAgger
|
|
@@ -189,8 +236,6 @@ Trained from scratch. Per iteration a mixed expert/learner policy rolls out, the
|
|
| 189 |
|
| 190 |
```bash
|
| 191 |
python main.py --mode online --ppo-checkpoint /path/to/ppo_checkpoint
|
| 192 |
-
python main.py --mode online --ppo-checkpoint /path/to/ppo_checkpoint \
|
| 193 |
-
--override online_total_timesteps=100000000
|
| 194 |
|
| 195 |
# Optional: warm-start from a pre-trained offline checkpoint
|
| 196 |
python main.py --mode online --ppo-checkpoint /path/to/ppo_checkpoint \
|
|
@@ -212,36 +257,43 @@ python main.py --mode collect --ppo-checkpoint /path/to/ppo_checkpoint \
|
|
| 212 |
### Resuming a training run
|
| 213 |
|
| 214 |
```bash
|
| 215 |
-
#
|
| 216 |
python main.py --mode offline --ppo-checkpoint /path/to/ppo_checkpoint \
|
| 217 |
--resume /path/to/completed_offline_checkpoint \
|
| 218 |
--override offline_total_timesteps=200000000
|
| 219 |
-
|
| 220 |
-
# Online. --resume-step / --resume-wandb-run-id override the metadata sidecar.
|
| 221 |
-
python main.py --mode online --ppo-checkpoint /path/to/ppo_checkpoint \
|
| 222 |
-
--resume /path/to/completed_online_checkpoint \
|
| 223 |
-
--override online_total_timesteps=200000000
|
| 224 |
```
|
| 225 |
|
| 226 |
-
The DAgger replay buffer is not persisted; it refills within a few iterations. The cosine LR schedule spans the full `num_updates`,
|
| 227 |
|
| 228 |
-
`--resume` restores the optimiser state, so it needs a checkpoint written by the current AdamW chain; an older one fails loudly
|
| 229 |
|
| 230 |
## Evaluation from a checkpoint
|
| 231 |
|
| 232 |
```bash
|
| 233 |
python main.py --mode inference --checkpoint /path/to/checkpoint --output results/inference/eval.json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
python main.py --mode inference \
|
| 237 |
--config configs/final_craftax_classic_gpu_24gb.yaml \
|
| 238 |
--checkpoint checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M
|
| 239 |
```
|
| 240 |
|
| 241 |
-
Prints mean episode return, completed episodes, steps per second, and per-achievement unlock counts; `--output` also writes them as JSON. Uses historical inpainting: the first `hist_len` plan positions are locked to observed history. Evaluation length is set by the `eval_steps` / `eval_num_envs` config keys.
|
| 242 |
-
|
| 243 |
-
Write eval JSONs into `results/inference/` (created for you): `scripts/hf_upload.py` publishes every JSON it finds there.
|
| 244 |
-
|
| 245 |
Any checkpoint flag (`--checkpoint`, `--ppo-checkpoint`, `--resume`) accepts a W&B artifact reference prefixed `wandb:`; the artifact downloads automatically (location: `wandb_download_dir`, default `./artifacts/`).
|
| 246 |
|
| 247 |
```bash
|
|
@@ -253,7 +305,7 @@ python main.py --mode inference \
|
|
| 253 |
|
| 254 |
### RL baselines
|
| 255 |
|
| 256 |
-
PPO baselines (the expert family: `ppo`, `ppo_rnn`, `ppo_rnd`) train in the
|
| 257 |
|
| 258 |
```bash
|
| 259 |
uv run python scripts/eval_ppo_expert.py \
|
|
@@ -275,7 +327,7 @@ python main.py --mode online --ppo-checkpoint <ppo> --config configs/classic_exp
|
|
| 275 |
|
| 276 |
### RL fine-tuning ablation suite
|
| 277 |
|
| 278 |
-
|
| 279 |
|
| 280 |
```bash
|
| 281 |
python experiments/rl_finetuning/run_ablations.py --list
|
|
@@ -286,26 +338,33 @@ python experiments/rl_finetuning/run_ablations.py \
|
|
| 286 |
--ablations baseline_rl kl_penalty --fast
|
| 287 |
```
|
| 288 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
## Configuration
|
| 290 |
|
| 291 |
One YAML config holds the experiment; the CLI holds the run.
|
| 292 |
|
| 293 |
-
|
| 294 |
-
- **CLI flags**: per-invocation values — `--seed`, `--checkpoint`, `--ppo-checkpoint`, `--data`, `--output`, `--resume*`, `--jit/--no-jit` (disable JIT for debugging).
|
| 295 |
-
- **`--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.
|
| 296 |
|
| 297 |
-
|
|
|
|
|
|
|
| 298 |
|
| 299 |
**`defaults.yaml` is the final Craftax Classic recipe, not a neutral baseline.** Run `main.py` with no `--config` and you get the paper's Classic DAgger run: a 384-dim, 6-layer model over 100M env frames.
|
| 300 |
|
| 301 |
-
**Presets hold only deltas
|
| 302 |
|
| 303 |
> **Schedule keys are denominated in env frames, not update steps.** Six settings — `lr_warmup_frames`, `offline_total_timesteps`, `online_total_timesteps`, `dagger_beta_final`, `dagger_buffer_cycles`, `val_interval_frames` — declare the *hardware-invariant* quantity; `resolve_num_updates()` and `resolve_scaled_hyperparams()` derive the update-step forms the runners consume (`num_updates`, `LR_WARMUP_STEPS`, `DAGGER_BETA_DECAY`, `DAGGER_BUFFER_MAX`, `VAL_INTERVAL`) from them at load. Set the frame-denominated key; the derived ones are outputs, not inputs.
|
| 304 |
|
| 305 |
```bash
|
| 306 |
-
python main.py --mode offline --ppo-checkpoint <ppo> \
|
| 307 |
-
--override lr=1e-4 --override plan_horizon=64 --override
|
| 308 |
-
python main.py --mode offline --ppo-checkpoint <ppo> --no-jit --override num_envs=4
|
| 309 |
```
|
| 310 |
|
| 311 |
| Preset | Purpose |
|
|
@@ -318,29 +377,19 @@ python main.py --mode offline --ppo-checkpoint <ppo> --no-jit --override num_env
|
|
| 318 |
| `configs/classic_exp_d_{100K,250K,850K,3M}_model.yaml` | Craftax Classic model-size scaling sweep |
|
| 319 |
| `configs/craftax_exp_d_{500K,1M,3M,7M}_model.yaml` | Full Craftax model-size scaling sweep |
|
| 320 |
| `configs/final_craftax_classic_{gpu_h200,gpu_24gb}.yaml` | Final Classic DAgger — `num_envs` and `seed` only; the recipe is `defaults.yaml` |
|
| 321 |
-
| `configs/final_craftax_{gpu_h200,gpu_24gb}.yaml` | Final Full Craftax DAgger — the
|
| 322 |
|
| 323 |
-
Within each family the two
|
| 324 |
|
| 325 |
-
|
| 326 |
|
| 327 |
## Checkpoints
|
| 328 |
|
| 329 |
-
With `save_policy: true` (the default), training saves Orbax checkpoints to `policies` (final) and `policies_best` (highest validation return)
|
| 330 |
-
|
| 331 |
-
**Pass the checkpoint directory, not the step subdirectory** — `CheckpointManager` resolves the latest step itself.
|
| 332 |
|
| 333 |
-
Offline checkpoints save at the resolved env-frame budget
|
| 334 |
|
| 335 |
-
`checkpoints/` is gitignored
|
| 336 |
-
|
| 337 |
-
Released weights and the pre-computed ablation outputs are mirrored on the
|
| 338 |
-
Hugging Face Hub at [`AnonMLuser/remdm-planner-craftax`](https://huggingface.co/AnonMLuser/remdm-planner-craftax),
|
| 339 |
-
an anonymous account holding the layout below.
|
| 340 |
-
|
| 341 |
-
```bash
|
| 342 |
-
uv run hf download AnonMLuser/remdm-planner-craftax --include "checkpoints/**" --local-dir .
|
| 343 |
-
```
|
| 344 |
|
| 345 |
| Checkpoint directory | Environment | Role | Trained for |
|
| 346 |
|---|---|---|---|
|
|
@@ -349,41 +398,62 @@ uv run hf download AnonMLuser/remdm-planner-craftax --include "checkpoints/**" -
|
|
| 349 |
| `checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M` | Craftax Classic | PPO-RNN expert | 1e9 env frames |
|
| 350 |
| `checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M` | Full Craftax | PPO-RNN expert | 1e9 env frames |
|
| 351 |
|
| 352 |
-
Full-Craftax diffusion planner checkpoints are not released: no full-Craftax training run has completed
|
| 353 |
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
-
|
| 357 |
-
should be: they are regenerated output, and 244 MB of them was rewritten out of the
|
| 358 |
-
history. `experiments/rl_finetuning/outputs/` and `results/inference/` are gitignored.
|
| 359 |
|
| 360 |
-
|
|
|
|
|
|
|
| 361 |
|
| 362 |
```bash
|
| 363 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
python experiments/rl_finetuning/run_ablations.py --checkpoint $PRETRAINED_CKPT --all
|
| 365 |
```
|
| 366 |
|
| 367 |
-
`scripts/hf_upload_demo.py` reads `outputs/
|
| 368 |
-
from the working copy, so fetch or regenerate
|
| 369 |
-
|
| 370 |
|
| 371 |
-
|
| 372 |
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
|
| 378 |
-
|
| 379 |
-
python
|
| 380 |
-
--
|
| 381 |
-
--
|
| 382 |
```
|
| 383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
### Publishing to the Hub
|
| 385 |
|
| 386 |
-
`scripts/hf_upload.py` rediscovers and uploads
|
| 387 |
|
| 388 |
```bash
|
| 389 |
HF_TOKEN=hf_xxx uv run python scripts/hf_upload.py --repo-id <ANON_HF_REPO_ID> --dry-run
|
|
@@ -391,11 +461,11 @@ HF_TOKEN=hf_xxx uv run python scripts/hf_upload.py --repo-id <ANON_HF_REPO_ID> -
|
|
| 391 |
|
| 392 |
`--dry-run` prints the staged tree and card without uploading; drop it to upload. Also `--inference-results <FILE|DIR> ...` (eval JSONs kept elsewhere), `--private`, `--yes`.
|
| 393 |
|
| 394 |
-
**Checkpoint discovery expects the released layout**, `checkpoints/<role>/<name>/<step>/`
|
| 395 |
|
| 396 |
## Results, citation, licence
|
| 397 |
|
| 398 |
-
Results tables and the full method description are in
|
| 399 |
|
| 400 |
---
|
| 401 |
|
|
@@ -412,7 +482,7 @@ Set via the `env_name` config key.
|
|
| 412 |
|
| 413 |
## Remasking strategies
|
| 414 |
|
| 415 |
-
|
| 416 |
|
| 417 |
| Strategy | Formula | Description |
|
| 418 |
|---|---|---|
|
|
@@ -422,21 +492,18 @@ Controlled by the `remask_strategy` key. All strategies operate on top of the th
|
|
| 422 |
|
| 423 |
## Key hyperparameters
|
| 424 |
|
| 425 |
-
|
|
|
|
| 426 |
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
| `env_name` | `Craftax-Classic-Symbolic-v1` | Craftax environment ID. Use `Craftax-Symbolic-v1` for Full Craftax. |
|
| 430 |
-
| `use_optimistic_resets` | `false` | Use `OptimisticResetVecEnvWrapper` instead of `AutoResetEnvWrapper` |
|
| 431 |
-
| `optimistic_reset_ratio` | 16 | Fraction of envs reset per step when optimistic resets are enabled |
|
| 432 |
|
| 433 |
**Diffusion model**
|
| 434 |
|
| 435 |
| Parameter | Default | Description |
|
| 436 |
|---|---|---|
|
| 437 |
| `plan_horizon` | 32 | Action plan length H |
|
| 438 |
-
| `diffusion_steps` | 15 | Denoising steps T
|
| 439 |
-
| `diffusion_steps_eval` | 10 | Denoising steps T at inference |
|
| 440 |
| `diffusion_schedule` | `cosine` | Noise schedule: `cosine` or `linear` |
|
| 441 |
| `remask_strategy` | `rescale` | Remasking strategy: `rescale`, `cap`, or `conf` |
|
| 442 |
| `train_sigma` | 0.0 | Per-token remasking correction during training (0 = standard MDLM) |
|
|
@@ -447,38 +514,24 @@ Controlled by the `remask_strategy` key. All strategies operate on top of the th
|
|
| 447 |
| `temperature` | 0.5 | Softmax temperature for token sampling |
|
| 448 |
| `top_p` | 0.95 | Nucleus sampling threshold |
|
| 449 |
|
| 450 |
-
**Transformer architecture**
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|---|---|---|
|
| 454 |
-
| `d_model` | 384 | Hidden dimension |
|
| 455 |
-
| `n_heads` | 8 | Attention heads |
|
| 456 |
-
| `n_layers` | 6 | Transformer blocks |
|
| 457 |
-
| `d_ff` | 768 | FFN inner dimension |
|
| 458 |
-
| `obs_encoder_layers` | 2 | MLP layers in the observation encoder |
|
| 459 |
-
| `obs_encoder_width` | 768 | Observation encoder hidden width |
|
| 460 |
-
| `dropout_rate` | 0.1 | Dropout rate (disabled at inference) |
|
| 461 |
|
| 462 |
**Offline training**
|
| 463 |
|
| 464 |
| Parameter | Default | Description |
|
| 465 |
|---|---|---|
|
| 466 |
| `offline_total_timesteps` | 1e8 | Env-frame budget. Derives `num_updates` as `offline_total_timesteps // (num_envs * num_steps)`. |
|
| 467 |
-
| `num_envs` | 1024 | Parallel environments |
|
| 468 |
-
| `
|
| 469 |
-
| `num_minibatches` | 8 | Gradient minibatches per epoch |
|
| 470 |
-
| `update_epochs` | 8 | SGD epochs per update step |
|
| 471 |
| `num_repeats` | 1 | Independent training seeds (vmapped) |
|
| 472 |
| `lr` | 3e-4 | AdamW learning rate (cosine-decayed to 10% over all gradient steps) |
|
| 473 |
| `weight_decay` | 0.0 | Decoupled AdamW decay for core training; 0.0 is Adam exactly (the ablation suite keeps 1e-4) |
|
| 474 |
| `lr_warmup_frames` | 1.6384e6 | Env-frame linear warm-up budget (0 = disabled). Derives `LR_WARMUP_STEPS` in gradient steps. |
|
| 475 |
| `max_grad_norm` | 1.0 | Global gradient clipping norm |
|
| 476 |
| `return_weight_cap` | 5.0 | Clip ceiling for per-window return weights (lower clip fixed at 0.1) |
|
| 477 |
-
| `collect_temperature` | 1.0 | Softmax temperature on PPO logits during live data collection |
|
| 478 |
| `val_interval_frames` | 1e6 | Env-frames between validation rollouts. Derives `VAL_INTERVAL` in update steps. |
|
| 479 |
-
| `val_diffusion_steps` | 50 | Denoising steps during validation rollouts |
|
| 480 |
-
| `val_replan_every` | 4 | Env steps executed per diffusion plan during validation |
|
| 481 |
-
| `val_steps` | 256 | Total env steps per validation rollout |
|
| 482 |
|
| 483 |
**Online DAgger training**
|
| 484 |
|
|
@@ -495,28 +548,24 @@ Controlled by the `remask_strategy` key. All strategies operate on top of the th
|
|
| 495 |
|
| 496 |
| Parameter | Default | Description |
|
| 497 |
|---|---|---|
|
| 498 |
-
| `collect_num_steps`
|
| 499 |
-
| `collect_num_envs` | 128 | Parallel environments during collection |
|
| 500 |
| `ppo_model_type` | `ppo_rnn` | PPO architecture: `ppo`, `ppo_rnn`, or `ppo_rnd` |
|
| 501 |
-
| `
|
| 502 |
-
| `
|
| 503 |
-
| `
|
| 504 |
|
| 505 |
-
**Checkpointing /
|
| 506 |
|
| 507 |
| Parameter | Default | Description |
|
| 508 |
|---|---|---|
|
| 509 |
| `save_policy` | `true` | Save final checkpoint and upload as W&B artifact |
|
| 510 |
-
| `
|
| 511 |
-
| `resume_wandb_run_id` | `null` | Per-run: `--resume-wandb-run-id` (auto-read from metadata) |
|
| 512 |
-
| `resume_step` | `null` | Per-run: `--resume-step` (auto-read from metadata) |
|
| 513 |
| `seed` | `null` | RNG seed (random if null; per-run: `--seed`) |
|
| 514 |
-
| `use_wandb` | `true` | Enable Weights & Biases logging |
|
| 515 |
-
| `wandb_project` | `remdm-planner-craftax` | W&B project name |
|
| 516 |
-
| `wandb_entity` | `null` | W&B entity (null = your default entity) |
|
| 517 |
-
| `wandb_download_dir` | `null` | Download dir for W&B artifacts; null = `./artifacts/` |
|
| 518 |
| `jax_compilation_cache_dir` | `null` | Persistent XLA compilation cache; null = off. See below |
|
| 519 |
|
|
|
|
|
|
|
|
|
|
| 520 |
### Persistent compilation cache
|
| 521 |
|
| 522 |
The whole training run is one `jax.jit`, so every process pays one large
|
|
@@ -533,7 +582,7 @@ python main.py --mode online --ppo-checkpoint <ppo> \
|
|
| 533 |
|
| 534 |
## Environment wrappers
|
| 535 |
|
| 536 |
-
From `Craftax_Baselines/wrappers.py`:
|
| 537 |
|
| 538 |
| Wrapper | Purpose |
|
| 539 |
|---|---|
|
|
@@ -550,30 +599,29 @@ Stack (identical for training and inference): `env -> LogWrapper -> AutoResetEnv
|
|
| 550 |
uv run pytest
|
| 551 |
```
|
| 552 |
|
| 553 |
-
A CPU-only suite,
|
| 554 |
|
| 555 |
| File | Covers |
|
| 556 |
|---|---|
|
| 557 |
-
| `test_smoke_src.py`, `test_smoke_experiments.py` | that things **run**: imports, model from the real config, a gradient step, checkpoint round-trip, samplers, resolvers, every CLI entry point, and all
|
| 558 |
-
| `test_spec_*.py`, `test_method_spec*.py` | that things are **correct**: each canonical statement of the
|
| 559 |
| `test_config.py`, `test_recipe_values.py` | the preset, delta-only, cluster-sibling and poolability rules, and the shipped recipe values |
|
|
|
|
| 560 |
| `test_gpu_agreement.py` | CPU/GPU agreement, skipped without a device |
|
| 561 |
|
| 562 |
## Implementation notes
|
| 563 |
|
| 564 |
-
| Topic | Note
|
| 565 |
-
|---|---
|
| 566 |
-
| JAX purity | `make_train_offline_diffusion` / `make_train_online_dagger` are fully JIT-compatible; env construction and checkpoint I/O sit outside `jax.jit`.
|
| 567 |
-
| Offline data | `--mode offline` rolls out PPO live. `--mode collect` saves an `.npz` for inspection only — re-feeding it to `--mode offline` is unsupported
|
| 568 |
-
| Episode-boundary masking | A window at `(e, t)` is valid only if `dones[e, t+1:t+H-1]` are all `False`.
|
| 569 |
-
| Return weighting | Valid windows are weighted by cumulative reward, normalised by the batch mean, clipped to `[0.1, return_weight_cap]`, and applied as per-sample multipliers before loss reduction.
|
| 570 |
-
| LR schedule | Cosine decay `lr -> lr * 0.1` over all gradient steps. `lr_warmup_frames` prepends linear warm-up, converted
|
| 571 |
-
|
|
| 572 |
-
| DAgger
|
| 573 |
-
| Loss weight clipping | The MDLM SUBS weight `-alpha'(t) / (1 - alpha_t)` is clipped to 1000 for stability as `alpha_t -> 1`.
|
| 574 |
-
|
|
| 575 |
-
|
|
| 576 |
-
|
|
| 577 |
-
|
|
| 578 |
-
| Denoising indexing | Reverse scan runs `step_idx = 0 -> T-1`, mapping to `t = (T - step_idx) / T` (high to low noise). |
|
| 579 |
-
| PPO experts | Training lives entirely in `Craftax_Baselines/`; planner modes only consume checkpoints. Released PPO checkpoints were saved on GPU and fail to restore on a CPU-only machine. |
|
|
|
|
| 38 |
ablation fine-tunes)
|
| 39 |
ppo_agents/ PPO-RNN experts
|
| 40 |
|
| 41 |
+
experiments/rl_finetuning/outputs/craftax_classic_ablations/ The published 25-condition suite
|
| 42 |
figures/ 113 pre-computed PNGs
|
| 43 |
tables/ main_results.csv, group_summary.csv, hypothesis_verdict.csv,
|
| 44 |
+
achievement_summary.csv, gradient_analysis.csv, significance_test.txt,
|
| 45 |
+
gdelta.csv, results.tex, ...
|
| 46 |
+
gdelta/ gdelta_{seed0,seed1,seed2,aggregate}.json <- the return-term measurement
|
| 47 |
+
results.json, diagnosis.md
|
| 48 |
+
|
| 49 |
+
experiments/rl_finetuning/outputs/review_*/ Five review arms, outside the 25-condition suite
|
| 50 |
+
review_anchor_baseline_rl/ same-stack anchor for the four below
|
| 51 |
+
review_run1_bc_all/ unweighted control (every weight fixed at one)
|
| 52 |
+
review_run2_advclip_lr_matched/ advantage_clip at a matched effective step
|
| 53 |
+
review_run4_baseline_lr1e-4/ learning-rate sweep
|
| 54 |
+
review_run4_baseline_lr1e-5/
|
| 55 |
+
|
| 56 |
+
results/inference/ Single-seed evaluations through the inpainting sampler (see below),
|
| 57 |
+
plus PPO-RNN expert evaluations
|
| 58 |
+
results/paper_figures/ The 11 manuscript figures as vector PDF
|
| 59 |
```
|
| 60 |
|
| 61 |
## Headline numbers (Craftax Classic)
|
|
|
|
| 70 |
No condition of the 25 recovers the checkpoint it started from. Group means are
|
| 71 |
9.74 (A), 7.07 (B), 10.53 (C) and 7.97 (D).
|
| 72 |
|
| 73 |
+
### The review arms
|
| 74 |
+
|
| 75 |
+
These are separate runs on a different host and are quoted **against each other**, not
|
| 76 |
+
against the table above: the suite does not reproduce across hosts, and the same
|
| 77 |
+
checkpoint and configuration score the pretrained planner at 11.9754 here against
|
| 78 |
+
11.8084 above, a consistent offset of 1.41%. `baseline_rl` was therefore re-run as a
|
| 79 |
+
same-stack anchor.
|
| 80 |
+
|
| 81 |
+
| Arm | lr | Score |
|
| 82 |
+
|---|---|---|
|
| 83 |
+
| `baseline_rl` — same-stack anchor | 3e-4 | 8.3395 ± 0.1468 |
|
| 84 |
+
| `bc_all` — uniform weights, all rollout windows | 3e-4 | **4.7348 ± 0.1869** |
|
| 85 |
+
| `advantage_clip` — matched effective step | 3.223e-4 | **4.9246 ± 0.2573** |
|
| 86 |
+
| `baseline_rl` | 1e-4 | **10.1486 ± 0.1811** |
|
| 87 |
+
| `baseline_rl` | 1e-5 | **10.8104 ± 0.1024** |
|
| 88 |
+
|
| 89 |
+
Removing the return weighting while keeping the rollouts costs *more* than the
|
| 90 |
+
weighting itself (−3.60 against the anchor). Lowering the learning rate by a factor of
|
| 91 |
+
thirty recovers 68% of the degradation without removing it — 10.8104 still sits 1.1650
|
| 92 |
+
below the same-stack checkpoint, more than ten times the seed sd at that rate.
|
| 93 |
+
Matching the effective step does not close the advantage-clipping gap.
|
| 94 |
+
|
| 95 |
+
The MiniHack counterparts of all three arms were run and are reported in the paper, but
|
| 96 |
+
**their run directories are not part of either release** — only the Craftax Classic ones
|
| 97 |
+
are published here. The MiniHack numbers are quoted in that repo's notebook.
|
| 98 |
+
|
| 99 |
+
### The return term
|
| 100 |
+
|
| 101 |
+
`experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/` holds the direct
|
| 102 |
+
measurement of the return term `g_δ` at the pretrained checkpoint, over 3 rollout seeds
|
| 103 |
+
× 8 noise draws on 1,024-window batches. Reproduce it on a CPU with
|
| 104 |
+
`run_ablations.py --measure-gdelta`. The Eq.-4 correctness residual is 4.8 × 10⁻⁷.
|
| 105 |
+
|
| 106 |
## Two evaluation paths — read this before comparing numbers
|
| 107 |
|
| 108 |
Every number in the paper comes from the **ablation harness**
|
|
|
|
| 110 |
`sample_plan` with no locked prefix, 50 denoising steps, 8 actions executed per
|
| 111 |
plan, scored as `returned_episode_returns`.
|
| 112 |
|
| 113 |
+
`main.py --mode inference` can also take a **different** path,
|
| 114 |
+
`sample_plan_inpainting`, which freezes every executed action as an inpainting prefix
|
| 115 |
+
and replans every step. It is a different planner at evaluation time and scores far
|
| 116 |
+
lower on the same weights (3.26 against 11.81 for the DAgger checkpoint). The
|
| 117 |
+
single-seed JSONs under `results/inference/` were produced by that path and are kept as
|
| 118 |
+
an ablation on the planning-as-inpainting design choice — they are not paper numbers.
|
| 119 |
+
`configs/defaults.yaml` now sets `inference_sampler: sample_plan`, so the two paths
|
| 120 |
+
agree by default.
|
| 121 |
|
| 122 |
`demo_craftax.ipynb` evaluates through the harness path (Cell 5) and runs the
|
| 123 |
inpainting path only as a clearly labelled ablation (Cell 5b).
|
|
|
|
| 149 |
|
| 150 |
JAX implementation of **ReMDM** (Remasking Discrete Diffusion Model) for action-sequence planning in [Craftax](https://github.com/MichaelTMatthews/Craftax), a JAX-accelerated, procedurally generated open-world survival game. A bidirectional transformer generates `plan_horizon`-length action plans by iteratively denoising masked token sequences, conditioned on the current symbolic observation. Trained under a pre-trained PPO expert, either offline (behavioural cloning on live rollouts) or online (DAgger).
|
| 151 |
|
| 152 |
+
The sibling repository [`remdm-planner-minihack`](../remdm-planner-minihack) implements the same method in PyTorch on MiniHack. Both repos share the same CLI, config layout and README structure; commands transfer between them by swapping the repo name and benchmark-specific values.
|
| 153 |
|
| 154 |
## Method
|
| 155 |
|
| 156 |
+
The planner starts from a fully-masked action sequence and iteratively unmasks tokens over `T` denoising steps; ReMDM extends MDLM with remasking strategies that let committed tokens be re-predicted, improving plan coherence.
|
| 157 |
|
| 158 |
+
Two independent training pipelines are compared head-to-head, both fed by one pre-trained PPO expert checkpoint: `--mode offline` behaviour-clones from live expert rollouts, `--mode online` runs DAgger from scratch against expert labels. Either output is scored with `--mode inference`.
|
| 159 |
|
| 160 |
## Setup
|
| 161 |
|
|
|
|
| 163 |
|
| 164 |
```bash
|
| 165 |
git clone https://github.com/ANONYMOUS/remdm-planners.git
|
| 166 |
+
cd remdm-planner-craftax
|
| 167 |
+
# Or, if already cloned without submodules:
|
| 168 |
+
git submodule update --init --recursive
|
| 169 |
|
| 170 |
# Default: CPU-only JAX (macOS, or Linux without a GPU).
|
| 171 |
# Installs the dev group (pytest) too.
|
|
|
|
| 183 |
## Repo layout
|
| 184 |
|
| 185 |
```
|
| 186 |
+
remdm-planner-craftax/
|
| 187 |
+
├── Craftax_Baselines/ Git submodule — PPO expert training and env wrappers
|
| 188 |
├── configs/ Experiment configs (defaults.yaml + presets, see Configuration)
|
| 189 |
├── src/ Model, diffusion, planner pipelines
|
| 190 |
├── experiments/
|
| 191 |
│ └── rl_finetuning/ RL fine-tuning ablation suite (run_ablations.py)
|
| 192 |
+
├── scripts/ Param counter, PPO evaluator, paper figures, HF upload, provisioning
|
| 193 |
├── tests/ Smoke suite — uv run pytest
|
| 194 |
├── checkpoints/ Gitignored — offline/, online/, ppo_agents/ (see Checkpoints)
|
| 195 |
+
├── results/ Gitignored, created on demand — inference/ eval JSONs and
|
| 196 |
+
│ paper_figures/ manuscript PDFs, both published (see Checkpoints)
|
| 197 |
├── demo_craftax.ipynb Demo notebook
|
| 198 |
├── main.py CLI entry point
|
| 199 |
└── pyproject.toml uv project — deps, cuda12/cuda13 extras, dev group
|
|
|
|
| 211 |
|
| 212 |
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. All training modes need a PPO expert checkpoint.
|
| 213 |
|
| 214 |
+
### Stage 1 — Train the PPO expert (submodule)
|
| 215 |
|
| 216 |
```bash
|
| 217 |
cd Craftax_Baselines
|
|
|
|
| 228 |
|
| 229 |
```bash
|
| 230 |
python main.py --mode offline --ppo-checkpoint /path/to/ppo_checkpoint
|
|
|
|
|
|
|
| 231 |
```
|
| 232 |
|
| 233 |
### Online DAgger
|
|
|
|
| 236 |
|
| 237 |
```bash
|
| 238 |
python main.py --mode online --ppo-checkpoint /path/to/ppo_checkpoint
|
|
|
|
|
|
|
| 239 |
|
| 240 |
# Optional: warm-start from a pre-trained offline checkpoint
|
| 241 |
python main.py --mode online --ppo-checkpoint /path/to/ppo_checkpoint \
|
|
|
|
| 257 |
### Resuming a training run
|
| 258 |
|
| 259 |
```bash
|
| 260 |
+
# Same shape for --mode online. --resume also accepts a wandb: artifact reference.
|
| 261 |
python main.py --mode offline --ppo-checkpoint /path/to/ppo_checkpoint \
|
| 262 |
--resume /path/to/completed_offline_checkpoint \
|
| 263 |
--override offline_total_timesteps=200000000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
```
|
| 265 |
|
| 266 |
+
The DAgger replay buffer is not persisted; it refills within a few iterations. The cosine LR schedule spans the full `num_updates`, offset so the LR resumes where it stopped. `resume_step` and `resume_wandb_run_id` come from the metadata sidecar; without one, pass `--resume-step` (or `--resume-wandb-run-id`) explicitly.
|
| 267 |
|
| 268 |
+
`--resume` restores the optimiser state, so it needs a checkpoint written by the current AdamW chain; an older one fails loudly and there is no compatibility path. Use `--checkpoint` instead — parameters only, warm-starting a fresh run.
|
| 269 |
|
| 270 |
## Evaluation from a checkpoint
|
| 271 |
|
| 272 |
```bash
|
| 273 |
python main.py --mode inference --checkpoint /path/to/checkpoint --output results/inference/eval.json
|
| 274 |
+
```
|
| 275 |
+
|
| 276 |
+
Prints steps per second, per-achievement unlock counts, and two returns that must not be quoted against one another; `--output` also writes both as JSON:
|
| 277 |
+
|
| 278 |
+
| Reported | JSON key | Meaning |
|
| 279 |
+
|---|---|---|
|
| 280 |
+
| Mean return, completed episodes | `mean_return_completed_episodes` (with `n_completed_episodes`) | Mean over every episode that terminated inside the rollout — the `returned_episode_returns` statistic the ablation tables and the paper report |
|
| 281 |
+
| Mean return, first life only | `mean_return_first_life`, and `mean_score` for backwards compatibility | Strict single-life return: the first episode of each env only. A harsher statistic |
|
| 282 |
|
| 283 |
+
By default this replans from scratch every `eval_replan` (8) steps, conditioned only on the current observation — the same sampler and cadence as `build_eval_fn` in the ablation harness, so it is the protocol behind the published numbers. Length and width come from `eval_steps` / `eval_num_envs`.
|
| 284 |
+
|
| 285 |
+
`--override inference_sampler=inpainting` switches to the historical-inpainting sampler, which replans every step with the executed actions locked as a fixed prefix, leaving fewer free positions the further into a window it gets. It is kept as an ablation on the planning-as-inpainting design choice and **scores far lower on the same weights**; no published number comes from it.
|
| 286 |
+
|
| 287 |
+
Write eval JSONs into `results/inference/` (created for you): `scripts/hf_upload.py` publishes every JSON it finds there.
|
| 288 |
+
|
| 289 |
+
**Match the config to the checkpoint.** The model is built from the config, not the checkpoint, and a mismatch raises at restore. Every released diffusion checkpoint carries the `defaults.yaml` architecture, so evaluate with the matching `final_*` config, which also sets the right `env_name` and recipe values:
|
| 290 |
+
|
| 291 |
+
```bash
|
| 292 |
python main.py --mode inference \
|
| 293 |
--config configs/final_craftax_classic_gpu_24gb.yaml \
|
| 294 |
--checkpoint checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M
|
| 295 |
```
|
| 296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
Any checkpoint flag (`--checkpoint`, `--ppo-checkpoint`, `--resume`) accepts a W&B artifact reference prefixed `wandb:`; the artifact downloads automatically (location: `wandb_download_dir`, default `./artifacts/`).
|
| 298 |
|
| 299 |
```bash
|
|
|
|
| 305 |
|
| 306 |
### RL baselines
|
| 307 |
|
| 308 |
+
PPO baselines (the expert family: `ppo`, `ppo_rnn`, `ppo_rnd`) train in the `Craftax_Baselines` submodule, see [Training](#stage-1--train-the-ppo-expert-submodule). Evaluate an expert with `scripts/eval_ppo_expert.py`:
|
| 309 |
|
| 310 |
```bash
|
| 311 |
uv run python scripts/eval_ppo_expert.py \
|
|
|
|
| 327 |
|
| 328 |
### RL fine-tuning ablation suite
|
| 329 |
|
| 330 |
+
26 registered ablations (same names as in the minihack repo). See `experiments/README.md`.
|
| 331 |
|
| 332 |
```bash
|
| 333 |
python experiments/rl_finetuning/run_ablations.py --list
|
|
|
|
| 338 |
--ablations baseline_rl kl_penalty --fast
|
| 339 |
```
|
| 340 |
|
| 341 |
+
The same entry point measures the return term of the gradient decomposition at the
|
| 342 |
+
pretrained checkpoint, with no training and no accelerator:
|
| 343 |
+
|
| 344 |
+
```bash
|
| 345 |
+
python experiments/rl_finetuning/run_ablations.py --measure-gdelta --gdelta-seeds 0 1 2 \
|
| 346 |
+
--checkpoint $PRETRAINED_CKPT --results-path $RUN/results.json --output-dir $RUN
|
| 347 |
+
```
|
| 348 |
+
|
| 349 |
## Configuration
|
| 350 |
|
| 351 |
One YAML config holds the experiment; the CLI holds the run.
|
| 352 |
|
| 353 |
+
Precedence, lowest to highest: `configs/defaults.yaml` < `--config` preset < `--override` and run flags. Exactly two config layers — a preset never inherits from another preset.
|
|
|
|
|
|
|
| 354 |
|
| 355 |
+
- **Config files** (`configs/*.yaml`): hyperparameters, model and method settings, ablation definitions.
|
| 356 |
+
- **Run flags**: `--seed`, `--checkpoint`, `--ppo-checkpoint`, `--data`, `--output`, `--resume*`, `--jit/--no-jit` (disable JIT for debugging).
|
| 357 |
+
- **`--override KEY=VALUE`** (repeatable): keys are validated against `defaults.yaml` and cast to the key's type, so a typo is an error, not a silent no-op.
|
| 358 |
|
| 359 |
**`defaults.yaml` is the final Craftax Classic recipe, not a neutral baseline.** Run `main.py` with no `--config` and you get the paper's Classic DAgger run: a 384-dim, 6-layer model over 100M env frames.
|
| 360 |
|
| 361 |
+
**Presets hold only deltas.** A key belongs in a preset only if its value differs from `defaults.yaml`; restating one silently pins the preset when the recipe later moves. `tests/test_config.py` enforces this.
|
| 362 |
|
| 363 |
> **Schedule keys are denominated in env frames, not update steps.** Six settings — `lr_warmup_frames`, `offline_total_timesteps`, `online_total_timesteps`, `dagger_beta_final`, `dagger_buffer_cycles`, `val_interval_frames` — declare the *hardware-invariant* quantity; `resolve_num_updates()` and `resolve_scaled_hyperparams()` derive the update-step forms the runners consume (`num_updates`, `LR_WARMUP_STEPS`, `DAGGER_BETA_DECAY`, `DAGGER_BUFFER_MAX`, `VAL_INTERVAL`) from them at load. Set the frame-denominated key; the derived ones are outputs, not inputs.
|
| 364 |
|
| 365 |
```bash
|
| 366 |
+
python main.py --mode offline --ppo-checkpoint <ppo> --no-jit \
|
| 367 |
+
--override lr=1e-4 --override plan_horizon=64 --override num_envs=4
|
|
|
|
| 368 |
```
|
| 369 |
|
| 370 |
| Preset | Purpose |
|
|
|
|
| 377 |
| `configs/classic_exp_d_{100K,250K,850K,3M}_model.yaml` | Craftax Classic model-size scaling sweep |
|
| 378 |
| `configs/craftax_exp_d_{500K,1M,3M,7M}_model.yaml` | Full Craftax model-size scaling sweep |
|
| 379 |
| `configs/final_craftax_classic_{gpu_h200,gpu_24gb}.yaml` | Final Classic DAgger — `num_envs` and `seed` only; the recipe is `defaults.yaml` |
|
| 380 |
+
| `configs/final_craftax_{gpu_h200,gpu_24gb}.yaml` | Final Full Craftax DAgger — the 8 keys where Full Craftax departs from the Classic recipe, plus `num_envs` and `seed` |
|
| 381 |
|
| 382 |
+
Within each family the two machine configs differ only in `num_envs` and `seed`, guarded by `test_cluster_siblings_differ_only_in_num_envs_and_seed` rather than by the loader. **A Full Craftax hyperparameter change must be made in both `final_craftax_*` files** — with no inheritance those 8 keys are duplicated verbatim in each; a Classic one belongs in `defaults.yaml`.
|
| 383 |
|
| 384 |
+
Key hyperparameters are documented inline in `configs/defaults.yaml`; the [appendix](#key-hyperparameters) tabulates the load-bearing ones. Ablation-suite hyperparameters live in `experiments/rl_finetuning/configs/`, loaded by `run_ablations.py`, not `main.py`.
|
| 385 |
|
| 386 |
## Checkpoints
|
| 387 |
|
| 388 |
+
With `save_policy: true` (the default), training saves Orbax checkpoints to `policies` (final) and `policies_best` (highest validation return) — under `wandb.run.dir` with W&B on, uploaded as `{env_name}-policy` and `{env_name}-policy-best`, and under `{checkpoint_dir}/{mode}/{run_name}/` with W&B off, so a run never discards its weights. Diffusion checkpoints carry a `resume_metadata.json` sidecar recording the producing run's config, which is what `--resume` reads; PPO checkpoints carry `config.yaml` and `wandb-summary.json`.
|
|
|
|
|
|
|
| 389 |
|
| 390 |
+
**Pass the checkpoint directory, not the step subdirectory** — `CheckpointManager` resolves the latest step itself. Offline checkpoints save at the resolved env-frame budget: 99,942,400 for the Classic recipe at 512 envs (1525 updates × 512 × 128).
|
| 391 |
|
| 392 |
+
`checkpoints/` is gitignored; released weights live on the Hub at [`AnonMLuser/remdm-planner-craftax`](https://huggingface.co/AnonMLuser/remdm-planner-craftax), mirroring the layout below.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
|
| 394 |
| Checkpoint directory | Environment | Role | Trained for |
|
| 395 |
|---|---|---|---|
|
|
|
|
| 398 |
| `checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M` | Craftax Classic | PPO-RNN expert | 1e9 env frames |
|
| 399 |
| `checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M` | Full Craftax | PPO-RNN expert | 1e9 env frames |
|
| 400 |
|
| 401 |
+
Full-Craftax diffusion planner checkpoints are not released: no full-Craftax training run has completed; the released Full Craftax expert can still supervise a new run:
|
| 402 |
|
| 403 |
+
```bash
|
| 404 |
+
python main.py --mode online \
|
| 405 |
+
--config configs/final_craftax_gpu_24gb.yaml \
|
| 406 |
+
--ppo-checkpoint checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M
|
| 407 |
+
```
|
| 408 |
+
|
| 409 |
+
```bash
|
| 410 |
+
# All four (~470 MB); narrow the --include glob for a single checkpoint.
|
| 411 |
+
uv run hf download AnonMLuser/remdm-planner-craftax --include "checkpoints/**" --local-dir .
|
| 412 |
+
```
|
| 413 |
+
|
| 414 |
+
**Keep the `--include`.** The Hub repo carries its own `README.md` (the generated model card), `LICENSE` and `.gitattributes`; dropping the glob and pulling into `--local-dir .` overwrites this repository's copies of all three. To fetch everything, add `--exclude "README.md" "LICENSE" ".gitattributes"`, or use a separate `--local-dir`. Publishing is safe either way — `hf_upload.py` stages `LICENSE` and the demo `README.md` from git, not the working tree.
|
| 415 |
|
| 416 |
+
### Experiment outputs
|
|
|
|
|
|
|
| 417 |
|
| 418 |
+
Ablation figures, tables and `results.json` are regenerated output, so
|
| 419 |
+
`experiments/rl_finetuning/outputs/` and `results/inference/` are gitignored. Obtain
|
| 420 |
+
them either way:
|
| 421 |
|
| 422 |
```bash
|
| 423 |
+
# Fetch the published run (figures, tables, results.json, diagnosis.md)
|
| 424 |
+
uv run hf download AnonMLuser/remdm-planner-craftax \
|
| 425 |
+
--include "experiments/rl_finetuning/outputs/**" --local-dir .
|
| 426 |
+
|
| 427 |
+
# Or regenerate from a checkpoint; writes to outputs/{run_id}/
|
| 428 |
python experiments/rl_finetuning/run_ablations.py --checkpoint $PRETRAINED_CKPT --all
|
| 429 |
```
|
| 430 |
|
| 431 |
+
`scripts/hf_upload_demo.py` reads `outputs/craftax_classic_ablations/{figures,tables}`
|
| 432 |
+
from the working copy, so fetch or regenerate first. `demo_craftax.ipynb` needs no local
|
| 433 |
+
copy — it reads them through its own `snapshot_download`.
|
| 434 |
|
| 435 |
+
### Paper figures
|
| 436 |
|
| 437 |
+
Each manuscript figure puts Craftax Classic and MiniHack side by side, so they are built
|
| 438 |
+
by `scripts/paper_figures.py` rather than by the single-environment
|
| 439 |
+
`experiments/rl_finetuning/analysis/plots.py`. It reads *both* repositories'
|
| 440 |
+
`results.json` and emits vector PDF at NeurIPS column width:
|
| 441 |
|
| 442 |
+
```bash
|
| 443 |
+
uv run python scripts/paper_figures.py \
|
| 444 |
+
--minihack-results ../remdm-planner-minihack/experiments/rl_finetuning/outputs/minihack_ablations/results.json \
|
| 445 |
+
--outdir results/paper_figures
|
| 446 |
```
|
| 447 |
|
| 448 |
+
The MiniHack path defaults to that sibling checkout. Pass `--emit-tex-macros` to
|
| 449 |
+
`run_ablations.py` to also write `tables/results.tex`, one `\newcommand` per headline
|
| 450 |
+
quantity, so the manuscript cites generated numbers instead of retyping them. Macros from
|
| 451 |
+
this repository are prefixed `rw` and the sibling suite's `mh`, so both files can be
|
| 452 |
+
`\input` together.
|
| 453 |
+
|
| 454 |
### Publishing to the Hub
|
| 455 |
|
| 456 |
+
`scripts/hf_upload.py` rediscovers and uploads four things, each keeping its repo-relative path: `checkpoints/`, every `experiments/rl_finetuning/outputs/<run>/` holding a `results.json` (with `diagnosis.md`, `tables/`, `figures/`, `gdelta/`), the eval JSONs in `results/inference/`, and the manuscript figure PDFs in `results/paper_figures/`. It drops W&B and hub config keys, shortens absolute paths and regenerates the model card.
|
| 457 |
|
| 458 |
```bash
|
| 459 |
HF_TOKEN=hf_xxx uv run python scripts/hf_upload.py --repo-id <ANON_HF_REPO_ID> --dry-run
|
|
|
|
| 461 |
|
| 462 |
`--dry-run` prints the staged tree and card without uploading; drop it to upload. Also `--inference-results <FILE|DIR> ...` (eval JSONs kept elsewhere), `--private`, `--yes`.
|
| 463 |
|
| 464 |
+
**Checkpoint discovery expects the released layout**, `checkpoints/<role>/<name>/<step>/`. A training run writes elsewhere, so copy its `wandb.run.dir/policies` directory to `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 nest already-published artefacts under `checkpoints/hf/checkpoints/...`.
|
| 465 |
|
| 466 |
## Results, citation, licence
|
| 467 |
|
| 468 |
+
Results tables and the full method description are in *Return-Weighted ELBO Fine-Tuning Degrades Masked Diffusion Planners* (under submission); `demo_craftax.ipynb` reproduces the headline evaluation. Citation to be added on publication. Licence: MIT, see `LICENSE`.
|
| 469 |
|
| 470 |
---
|
| 471 |
|
|
|
|
| 482 |
|
| 483 |
## Remasking strategies
|
| 484 |
|
| 485 |
+
Selected by `remask_strategy`, on top of the three-phase loop controlled by `use_loop`, `t_on` and `t_off`.
|
| 486 |
|
| 487 |
| Strategy | Formula | Description |
|
| 488 |
|---|---|---|
|
|
|
|
| 492 |
|
| 493 |
## Key hyperparameters
|
| 494 |
|
| 495 |
+
`configs/defaults.yaml` is authoritative and commented inline. Tabulated here are the
|
| 496 |
+
keys that change a result, carry a hazard, or are named elsewhere in this README.
|
| 497 |
|
| 498 |
+
**Environment.** `env_name` selects the benchmark: `Craftax-Classic-Symbolic-v1`
|
| 499 |
+
(default) or `Craftax-Symbolic-v1` for Full Craftax.
|
|
|
|
|
|
|
|
|
|
| 500 |
|
| 501 |
**Diffusion model**
|
| 502 |
|
| 503 |
| Parameter | Default | Description |
|
| 504 |
|---|---|---|
|
| 505 |
| `plan_horizon` | 32 | Action plan length H |
|
| 506 |
+
| `diffusion_steps` / `diffusion_steps_eval` | 15 / 10 | Denoising steps T at training and at inference |
|
|
|
|
| 507 |
| `diffusion_schedule` | `cosine` | Noise schedule: `cosine` or `linear` |
|
| 508 |
| `remask_strategy` | `rescale` | Remasking strategy: `rescale`, `cap`, or `conf` |
|
| 509 |
| `train_sigma` | 0.0 | Per-token remasking correction during training (0 = standard MDLM) |
|
|
|
|
| 514 |
| `temperature` | 0.5 | Softmax temperature for token sampling |
|
| 515 |
| `top_p` | 0.95 | Nucleus sampling threshold |
|
| 516 |
|
| 517 |
+
**Transformer architecture.** `d_model` 384, `n_heads` 8, `n_layers` 6, `d_ff` 768,
|
| 518 |
+
`obs_encoder_layers` 2, `obs_encoder_width` 768, `dropout_rate` 0.1 — the shape every
|
| 519 |
+
released checkpoint carries. A checkpoint restores only against a matching config.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 520 |
|
| 521 |
**Offline training**
|
| 522 |
|
| 523 |
| Parameter | Default | Description |
|
| 524 |
|---|---|---|
|
| 525 |
| `offline_total_timesteps` | 1e8 | Env-frame budget. Derives `num_updates` as `offline_total_timesteps // (num_envs * num_steps)`. |
|
| 526 |
+
| `num_envs` / `num_steps` | 1024 / 128 | Parallel environments, and env steps per update; their product is `fpu` |
|
| 527 |
+
| `num_minibatches` / `update_epochs` | 8 / 8 | Gradient minibatches per epoch, and epochs per update |
|
|
|
|
|
|
|
| 528 |
| `num_repeats` | 1 | Independent training seeds (vmapped) |
|
| 529 |
| `lr` | 3e-4 | AdamW learning rate (cosine-decayed to 10% over all gradient steps) |
|
| 530 |
| `weight_decay` | 0.0 | Decoupled AdamW decay for core training; 0.0 is Adam exactly (the ablation suite keeps 1e-4) |
|
| 531 |
| `lr_warmup_frames` | 1.6384e6 | Env-frame linear warm-up budget (0 = disabled). Derives `LR_WARMUP_STEPS` in gradient steps. |
|
| 532 |
| `max_grad_norm` | 1.0 | Global gradient clipping norm |
|
| 533 |
| `return_weight_cap` | 5.0 | Clip ceiling for per-window return weights (lower clip fixed at 0.1) |
|
|
|
|
| 534 |
| `val_interval_frames` | 1e6 | Env-frames between validation rollouts. Derives `VAL_INTERVAL` in update steps. |
|
|
|
|
|
|
|
|
|
|
| 535 |
|
| 536 |
**Online DAgger training**
|
| 537 |
|
|
|
|
| 548 |
|
| 549 |
| Parameter | Default | Description |
|
| 550 |
|---|---|---|
|
| 551 |
+
| `collect_num_steps` / `collect_num_envs` | 1e7 / 128 | Steps to collect, and envs collecting them |
|
|
|
|
| 552 |
| `ppo_model_type` | `ppo_rnn` | PPO architecture: `ppo`, `ppo_rnn`, or `ppo_rnd` |
|
| 553 |
+
| `eval_steps` / `eval_num_envs` | 10000 / 32 | Evaluation length and width (independent of `num_envs`) |
|
| 554 |
+
| `inference_sampler` | `sample_plan` | `sample_plan` (the published protocol) or `inpainting` |
|
| 555 |
+
| `eval_replan` | 8 | Env steps executed per plan under `sample_plan` |
|
| 556 |
|
| 557 |
+
**Checkpointing / logging**
|
| 558 |
|
| 559 |
| Parameter | Default | Description |
|
| 560 |
|---|---|---|
|
| 561 |
| `save_policy` | `true` | Save final checkpoint and upload as W&B artifact |
|
| 562 |
+
| `checkpoint_dir` | `checkpoints` | Where checkpoints land when W&B is off |
|
|
|
|
|
|
|
| 563 |
| `seed` | `null` | RNG seed (random if null; per-run: `--seed`) |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
| `jax_compilation_cache_dir` | `null` | Persistent XLA compilation cache; null = off. See below |
|
| 565 |
|
| 566 |
+
The `resume_*`, `use_wandb` and `wandb_*` keys mirror the run flags documented under
|
| 567 |
+
[Configuration](#configuration).
|
| 568 |
+
|
| 569 |
### Persistent compilation cache
|
| 570 |
|
| 571 |
The whole training run is one `jax.jit`, so every process pays one large
|
|
|
|
| 582 |
|
| 583 |
## Environment wrappers
|
| 584 |
|
| 585 |
+
From `Craftax_Baselines/wrappers.py` (submodule):
|
| 586 |
|
| 587 |
| Wrapper | Purpose |
|
| 588 |
|---|---|
|
|
|
|
| 599 |
uv run pytest
|
| 600 |
```
|
| 601 |
|
| 602 |
+
A CPU-only suite, 14 modules. Tiny synthetic data and a shrunken model throughout — no real checkpoints, datasets or network calls, and nothing written outside `tmp_path`. `conftest.py` forces `JAX_PLATFORMS=cpu` and disables W&B; there are no custom markers.
|
| 603 |
|
| 604 |
| File | Covers |
|
| 605 |
|---|---|
|
| 606 |
+
| `test_smoke_src.py`, `test_smoke_experiments.py` | that things **run**: imports, model from the real config, a gradient step, checkpoint round-trip, samplers, resolvers, every CLI entry point, and all 26 ablations' losses and optimizers |
|
| 607 |
+
| `test_spec_*.py`, `test_method_spec*.py` | that things are **correct**: each canonical statement of the parent workspace's `the spec *.md` pinned against the implementation |
|
| 608 |
| `test_config.py`, `test_recipe_values.py` | the preset, delta-only, cluster-sibling and poolability rules, and the shipped recipe values |
|
| 609 |
+
| `test_gdelta.py`, `test_tex_macros.py` | the `--measure-gdelta` decomposition, and the `--emit-tex-macros` output: definitions only, uniquely named, letters only |
|
| 610 |
| `test_gpu_agreement.py` | CPU/GPU agreement, skipped without a device |
|
| 611 |
|
| 612 |
## Implementation notes
|
| 613 |
|
| 614 |
+
| Topic | Note |
|
| 615 |
+
|---|---|
|
| 616 |
+
| JAX purity | `make_train_offline_diffusion` / `make_train_online_dagger` are fully JIT-compatible; env construction and checkpoint I/O sit outside `jax.jit`. |
|
| 617 |
+
| Offline data | `--mode offline` rolls out PPO live. `--mode collect` saves an `.npz` for inspection only — re-feeding it to `--mode offline` is unsupported. |
|
| 618 |
+
| Episode-boundary masking | A window at `(e, t)` is valid only if `dones[e, t+1:t+H-1]` are all `False`. |
|
| 619 |
+
| Return weighting | Valid windows are weighted by cumulative reward, normalised by the batch mean, clipped to `[0.1, return_weight_cap]`, and applied as per-sample multipliers before loss reduction. |
|
| 620 |
+
| LR schedule | Cosine decay `lr -> lr * 0.1` over all gradient steps. `lr_warmup_frames` prepends linear warm-up, converted as `(frames // fpu) * update_epochs * num_minibatches (* dagger_train_passes online)`. |
|
| 621 |
+
| DAgger sizing | `dagger_sizing()` in `src/planners/common.py` is the single source of truth for `samples_per_update`, buffer capacity and `n_train_passes`. |
|
| 622 |
+
| DAgger aggregation | Ross et al. (2011). A circular buffer accumulates `(obs, expert_plan)` across iterations, with a sliding stride so every visited state contributes a label; the expert receives correct `done` flags so its RNN state resets at episode boundaries. |
|
| 623 |
+
| Loss weight clipping | The MDLM SUBS weight `-alpha'(t) / (1 - alpha_t)` is clipped to 1000 for stability as `alpha_t -> 1`. |
|
| 624 |
+
| Denoising indexing | Reverse scan runs `step_idx = 0 -> T-1`, mapping to `t = (T - step_idx) / T` (high to low noise). |
|
| 625 |
+
| Validation and best checkpoint | Every `val_interval` updates, at `val_diffusion_steps` / `val_replan_every` / `val_steps`. The highest-return parameters are kept alongside the live ones and uploaded as `{env_name}-policy-best`. |
|
| 626 |
+
| W&B namespaces | Centralised in `src/planners/logging.py`: `diffusion/`, `train/`, `env/`, `val/`, `dagger/`. `train/sps` only in modes with live env interaction. |
|
| 627 |
+
| PPO experts | Training lives entirely in `Craftax_Baselines/`; planner modes only consume checkpoints. Released PPO checkpoints were saved on GPU and fail to restore on a CPU-only machine. |
|
|
|
|
|
|
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/commit_success.txt
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
Checkpoint commit was successful to
|
|
|
|
| 1 |
+
Checkpoint commit was successful to policies/99942400
|
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/99942400/default/commit_success.txt
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
Checkpoint commit was successful to
|
|
|
|
| 1 |
+
Checkpoint commit was successful to 99942400.orbax-checkpoint-tmp/default
|
checkpoints/offline/Craftax-Classic-Symbolic-v1-Offline-Diffusion-BC-100M/resume_metadata.json
CHANGED
|
@@ -2,7 +2,6 @@
|
|
| 2 |
"mode": "offline",
|
| 3 |
"update_step": 1525,
|
| 4 |
"total_gradient_steps_completed": 97600,
|
| 5 |
-
"wandb_run_id": null,
|
| 6 |
"config_snapshot": {
|
| 7 |
"ENV_NAME": "Craftax-Classic-Symbolic-v1",
|
| 8 |
"USE_OPTIMISTIC_RESETS": false,
|
|
@@ -58,14 +57,9 @@
|
|
| 58 |
"SAVE_POLICY": true,
|
| 59 |
"CHECKPOINT_DIR": "checkpoints",
|
| 60 |
"RESUME_CHECKPOINT_PATH": null,
|
| 61 |
-
"RESUME_WANDB_RUN_ID": null,
|
| 62 |
"RESUME_STEP": null,
|
| 63 |
-
"JAX_COMPILATION_CACHE_DIR":
|
| 64 |
"SEED": 42,
|
| 65 |
-
"USE_WANDB": true,
|
| 66 |
-
"WANDB_PROJECT": "remdm-planner-craftax",
|
| 67 |
-
"WANDB_ENTITY": "null",
|
| 68 |
-
"WANDB_DOWNLOAD_DIR": null,
|
| 69 |
"MODE": "offline",
|
| 70 |
"JIT": true,
|
| 71 |
"PPO_CHECKPOINT_PATH": "checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M",
|
|
|
|
| 2 |
"mode": "offline",
|
| 3 |
"update_step": 1525,
|
| 4 |
"total_gradient_steps_completed": 97600,
|
|
|
|
| 5 |
"config_snapshot": {
|
| 6 |
"ENV_NAME": "Craftax-Classic-Symbolic-v1",
|
| 7 |
"USE_OPTIMISTIC_RESETS": false,
|
|
|
|
| 57 |
"SAVE_POLICY": true,
|
| 58 |
"CHECKPOINT_DIR": "checkpoints",
|
| 59 |
"RESUME_CHECKPOINT_PATH": null,
|
|
|
|
| 60 |
"RESUME_STEP": null,
|
| 61 |
+
"JAX_COMPILATION_CACHE_DIR": null,
|
| 62 |
"SEED": 42,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
"MODE": "offline",
|
| 64 |
"JIT": true,
|
| 65 |
"PPO_CHECKPOINT_PATH": "checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M",
|
checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/40370176/commit_success.txt
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
Checkpoint commit was successful to
|
|
|
|
| 1 |
+
Checkpoint commit was successful to policies_best/40370176
|
checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/40370176/default/commit_success.txt
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
Checkpoint commit was successful to
|
|
|
|
| 1 |
+
Checkpoint commit was successful to 40370176.orbax-checkpoint-tmp/default
|
checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M/resume_metadata.json
CHANGED
|
@@ -2,7 +2,6 @@
|
|
| 2 |
"mode": "online",
|
| 3 |
"update_step": 1525,
|
| 4 |
"total_gradient_steps_completed": 97600,
|
| 5 |
-
"wandb_run_id": null,
|
| 6 |
"config_snapshot": {
|
| 7 |
"ENV_NAME": "Craftax-Classic-Symbolic-v1",
|
| 8 |
"USE_OPTIMISTIC_RESETS": false,
|
|
@@ -58,14 +57,9 @@
|
|
| 58 |
"SAVE_POLICY": true,
|
| 59 |
"CHECKPOINT_DIR": "checkpoints",
|
| 60 |
"RESUME_CHECKPOINT_PATH": null,
|
| 61 |
-
"RESUME_WANDB_RUN_ID": null,
|
| 62 |
"RESUME_STEP": null,
|
| 63 |
"JAX_COMPILATION_CACHE_DIR": null,
|
| 64 |
"SEED": 42,
|
| 65 |
-
"USE_WANDB": true,
|
| 66 |
-
"WANDB_PROJECT": "remdm-planner-craftax",
|
| 67 |
-
"WANDB_ENTITY": "null",
|
| 68 |
-
"WANDB_DOWNLOAD_DIR": null,
|
| 69 |
"MODE": "online",
|
| 70 |
"JIT": true,
|
| 71 |
"PPO_CHECKPOINT_PATH": "checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M",
|
|
|
|
| 2 |
"mode": "online",
|
| 3 |
"update_step": 1525,
|
| 4 |
"total_gradient_steps_completed": 97600,
|
|
|
|
| 5 |
"config_snapshot": {
|
| 6 |
"ENV_NAME": "Craftax-Classic-Symbolic-v1",
|
| 7 |
"USE_OPTIMISTIC_RESETS": false,
|
|
|
|
| 57 |
"SAVE_POLICY": true,
|
| 58 |
"CHECKPOINT_DIR": "checkpoints",
|
| 59 |
"RESUME_CHECKPOINT_PATH": null,
|
|
|
|
| 60 |
"RESUME_STEP": null,
|
| 61 |
"JAX_COMPILATION_CACHE_DIR": null,
|
| 62 |
"SEED": 42,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
"MODE": "online",
|
| 64 |
"JIT": true,
|
| 65 |
"PPO_CHECKPOINT_PATH": "checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M",
|
checkpoints/ppo_agents/Craftax-Classic-Symbolic-v1-PPO_RNN-1000M/wandb-summary.json
CHANGED
|
@@ -1 +1,31 @@
|
|
| 1 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_runtime": 5855,
|
| 3 |
+
"Achievements/collect_stone": 99.93519592285156,
|
| 4 |
+
"achievements": 19.780298233032227,
|
| 5 |
+
"Achievements/collect_diamond": 18.0168514251709,
|
| 6 |
+
"Achievements/defeat_zombie": 99.22229766845703,
|
| 7 |
+
"Achievements/collect_wood": 100.00000762939453,
|
| 8 |
+
"Achievements/collect_coal": 99.1574935913086,
|
| 9 |
+
"Achievements/defeat_skeleton": 93.77835845947266,
|
| 10 |
+
"Achievements/make_wood_sword": 99.74076843261719,
|
| 11 |
+
"sps": 173326.93437804468,
|
| 12 |
+
"Achievements/place_table": 100.00000762939453,
|
| 13 |
+
"Achievements/place_stone": 99.8703842163086,
|
| 14 |
+
"Achievements/place_plant": 99.74076843261719,
|
| 15 |
+
"_timestamp": 1773173340.4075222,
|
| 16 |
+
"Achievements/make_iron_sword": 97.40765380859375,
|
| 17 |
+
"_step": 1906,
|
| 18 |
+
"episode_return": 19.358198165893555,
|
| 19 |
+
"Achievements/eat_cow": 99.54634094238281,
|
| 20 |
+
"Achievements/collect_sapling": 99.74076843261719,
|
| 21 |
+
"Achievements/make_wood_pickaxe": 100.00000762939453,
|
| 22 |
+
"Achievements/place_furnace": 99.8703842163086,
|
| 23 |
+
"Achievements/collect_drink": 98.57421112060547,
|
| 24 |
+
"episode_length": 341.3253479003906,
|
| 25 |
+
"Achievements/make_stone_sword": 99.54634094238281,
|
| 26 |
+
"Achievements/make_iron_pickaxe": 92.22294616699219,
|
| 27 |
+
"Achievements/wake_up": 83.79779815673828,
|
| 28 |
+
"Achievements/collect_iron": 98.18535614013672,
|
| 29 |
+
"Achievements/eat_plant": 0.3240440785884857,
|
| 30 |
+
"Achievements/make_stone_pickaxe": 99.35191345214844
|
| 31 |
+
}
|
checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M/config.yaml
CHANGED
|
@@ -42,11 +42,5 @@ UPDATE_EPOCHS:
|
|
| 42 |
value: 4
|
| 43 |
USE_OPTIMISTIC_RESETS:
|
| 44 |
value: true
|
| 45 |
-
USE_WANDB:
|
| 46 |
-
value: true
|
| 47 |
VF_COEF:
|
| 48 |
value: 0.5
|
| 49 |
-
WANDB_ENTITY:
|
| 50 |
-
value: null
|
| 51 |
-
WANDB_PROJECT:
|
| 52 |
-
value: null
|
|
|
|
| 42 |
value: 4
|
| 43 |
USE_OPTIMISTIC_RESETS:
|
| 44 |
value: true
|
|
|
|
|
|
|
| 45 |
VF_COEF:
|
| 46 |
value: 0.5
|
|
|
|
|
|
|
|
|
|
|
|
checkpoints/ppo_agents/Craftax-Symbolic-v1-PPO_RNN-1000M/wandb-summary.json
CHANGED
|
@@ -1 +1,76 @@
|
|
| 1 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"Achievements/place_plant": 97.02970123291016,
|
| 3 |
+
"Achievements/collect_drink": 88.11881256103516,
|
| 4 |
+
"Achievements/place_torch": 92.07920837402344,
|
| 5 |
+
"Achievements/defeat_pigman": 0,
|
| 6 |
+
"Achievements/enter_vault": 0,
|
| 7 |
+
"Achievements/defeat_skeleton": 34.158416748046875,
|
| 8 |
+
"Achievements/collect_ruby": 14.356435775756836,
|
| 9 |
+
"Achievements/make_wood_sword": 95.04950714111328,
|
| 10 |
+
"_timestamp": 1774217043.54534,
|
| 11 |
+
"Achievements/defeat_troll": 0,
|
| 12 |
+
"Achievements/defeat_kobold": 0,
|
| 13 |
+
"Achievements/make_stone_sword": 89.1089096069336,
|
| 14 |
+
"Achievements/defeat_necromancer": 0,
|
| 15 |
+
"Achievements/fire_bow": 68.81188201904297,
|
| 16 |
+
"Achievements/collect_stone": 98.51484680175781,
|
| 17 |
+
"Achievements/defeat_gnome_archer": 0,
|
| 18 |
+
"Achievements/cast_iceball": 0,
|
| 19 |
+
"Achievements/enchant_armour": 0,
|
| 20 |
+
"Achievements/make_diamond_sword": 1.9801980257034302,
|
| 21 |
+
"Achievements/make_iron_pickaxe": 6.93069314956665,
|
| 22 |
+
"Achievements/eat_plant": 0.9900990128517151,
|
| 23 |
+
"Achievements/enter_gnomish_mines": 0,
|
| 24 |
+
"Achievements/damage_necromancer": 0,
|
| 25 |
+
"Achievements/find_bow": 82.67326354980469,
|
| 26 |
+
"Achievements/defeat_gnome_warrior": 0,
|
| 27 |
+
"_runtime": 10138,
|
| 28 |
+
"Achievements/enter_graveyard": 0,
|
| 29 |
+
"Achievements/eat_cow": 70.79207611083984,
|
| 30 |
+
"Achievements/drink_potion": 58.41584014892578,
|
| 31 |
+
"Achievements/enter_dungeon": 87.62376403808594,
|
| 32 |
+
"Achievements/eat_bat": 0,
|
| 33 |
+
"Achievements/defeat_deep_thing": 0,
|
| 34 |
+
"Achievements/make_iron_sword": 3.9603960514068604,
|
| 35 |
+
"achievements": 23.94554901123047,
|
| 36 |
+
"Achievements/make_wood_pickaxe": 99.00990295410156,
|
| 37 |
+
"Achievements/eat_snail": 75.7425765991211,
|
| 38 |
+
"Achievements/wake_up": 53.96039581298828,
|
| 39 |
+
"Achievements/cast_fireball": 0,
|
| 40 |
+
"Achievements/defeat_zombie": 44.05940628051758,
|
| 41 |
+
"Achievements/defeat_lizard": 0,
|
| 42 |
+
"Achievements/make_arrow": 84.15841674804688,
|
| 43 |
+
"Achievements/defeat_frost_troll": 0,
|
| 44 |
+
"Achievements/make_diamond_armour": 0,
|
| 45 |
+
"Achievements/enter_fire_realm": 0,
|
| 46 |
+
"Achievements/make_torch": 92.07920837402344,
|
| 47 |
+
"Achievements/enchant_sword": 0,
|
| 48 |
+
"episode_length": 331.6633605957031,
|
| 49 |
+
"Achievements/place_table": 99.50495147705078,
|
| 50 |
+
"Achievements/enter_ice_realm": 0,
|
| 51 |
+
"Achievements/learn_fireball": 0,
|
| 52 |
+
"Achievements/collect_wood": 99.50495147705078,
|
| 53 |
+
"Achievements/collect_sapphire": 12.87128734588623,
|
| 54 |
+
"Achievements/open_chest": 82.67326354980469,
|
| 55 |
+
"Achievements/collect_iron": 65.3465347290039,
|
| 56 |
+
"Achievements/defeat_archer": 0,
|
| 57 |
+
"Achievements/defeat_knight": 0,
|
| 58 |
+
"Achievements/make_iron_armour": 0,
|
| 59 |
+
"Achievements/place_furnace": 98.51484680175781,
|
| 60 |
+
"Achievements/defeat_orc_solider": 60.39603805541992,
|
| 61 |
+
"Achievements/collect_sapling": 97.02970123291016,
|
| 62 |
+
"_step": 15257,
|
| 63 |
+
"Achievements/make_diamond_pickaxe": 3.9603960514068604,
|
| 64 |
+
"Achievements/defeat_orc_mage": 50.99009704589844,
|
| 65 |
+
"Achievements/place_stone": 98.51484680175781,
|
| 66 |
+
"episode_return": 35.05544662475586,
|
| 67 |
+
"Achievements/make_stone_pickaxe": 83.1683120727539,
|
| 68 |
+
"Achievements/enter_troll_mines": 0,
|
| 69 |
+
"Achievements/defeat_fire_elemental": 0,
|
| 70 |
+
"Achievements/learn_iceball": 0,
|
| 71 |
+
"Achievements/collect_diamond": 15.841584205627441,
|
| 72 |
+
"Achievements/collect_coal": 86.63365936279297,
|
| 73 |
+
"Achievements/defeat_ice_elemental": 0,
|
| 74 |
+
"Achievements/enter_sewers": 0,
|
| 75 |
+
"sps": 100008.66166595295
|
| 76 |
+
}
|
configs/defaults.yaml
CHANGED
|
@@ -136,6 +136,16 @@ layer_size: 512
|
|
| 136 |
# ── Inference (--mode inference) ─────────────────────────────────────────────
|
| 137 |
eval_steps: 10000
|
| 138 |
eval_num_envs: 32
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
# ── Checkpointing ────────────────────────────────────────────────────────────
|
| 141 |
save_policy: true
|
|
|
|
| 136 |
# ── Inference (--mode inference) ─────────────────────────────────────────────
|
| 137 |
eval_steps: 10000
|
| 138 |
eval_num_envs: 32
|
| 139 |
+
# Sampler used by --mode inference:
|
| 140 |
+
# sample_plan replan from scratch every eval_replan steps, conditioned only
|
| 141 |
+
# on the current observation. What build_eval_fn does, and the
|
| 142 |
+
# protocol behind every published number.
|
| 143 |
+
# inpainting replan every step with each executed action locked as an
|
| 144 |
+
# inpainting prefix. An ablation on the planning-as-inpainting
|
| 145 |
+
# design choice, not the source of any published number; it
|
| 146 |
+
# scores far lower on the same weights.
|
| 147 |
+
inference_sampler: sample_plan
|
| 148 |
+
eval_replan: 8 # env steps executed per plan (sample_plan)
|
| 149 |
|
| 150 |
# ── Checkpointing ────────────────────────────────────────────────────────────
|
| 151 |
save_policy: true
|
demo_craftax.ipynb
CHANGED
|
@@ -61,10 +61,9 @@
|
|
| 61 |
"# =============================================================================\n",
|
| 62 |
"\n",
|
| 63 |
"# Public HuggingFace repo holding the released artefacts: source, checkpoints\n",
|
| 64 |
-
"# and the pre-computed ablation outputs.
|
| 65 |
-
"#
|
| 66 |
-
"#
|
| 67 |
-
"# or train from source (see README.md).\n",
|
| 68 |
"HF_REPO_ID = \"AnonMLuser/remdm-planner-craftax\"\n",
|
| 69 |
"LOCAL_DIR = \"remdm-planner-craftax\"\n",
|
| 70 |
"\n",
|
|
@@ -264,7 +263,7 @@
|
|
| 264 |
" \"craftax>=1.5.0\",\n",
|
| 265 |
" \"flax>=0.12.6\",\n",
|
| 266 |
" \"optax>=0.2.8\",\n",
|
| 267 |
-
" \"orbax-checkpoint>=0.
|
| 268 |
" \"distrax>=0.1.7\",\n",
|
| 269 |
" \"chex>=0.1.91\",\n",
|
| 270 |
" \"polars>=1.39.3\",\n",
|
|
@@ -293,9 +292,9 @@
|
|
| 293 |
"\n",
|
| 294 |
"if HF_REPO_ID == \"UNSET_HF_REPO_ID\":\n",
|
| 295 |
" raise RuntimeError(\n",
|
| 296 |
-
" \"HF_REPO_ID is unset
|
| 297 |
-
" \"
|
| 298 |
-
" \"
|
| 299 |
" )\n",
|
| 300 |
"\n",
|
| 301 |
"snapshot_path = snapshot_download(repo_id=HF_REPO_ID, local_dir=LOCAL_DIR)\n",
|
|
@@ -537,6 +536,7 @@
|
|
| 537 |
" \"EVAL_STEPS\": EVAL_STEPS,\n",
|
| 538 |
" \"EVAL_NUM_ENVS\": EVAL_NUM_ENVS,\n",
|
| 539 |
" \"DIFFUSION_STEPS_EVAL\": INPAINTING_DIFFUSION_STEPS,\n",
|
|
|
|
| 540 |
" \"USE_WANDB\": False,\n",
|
| 541 |
" \"CHECKPOINT_PATH\": DIFFUSION_ONLINE_CKPT,\n",
|
| 542 |
" }\n",
|
|
@@ -981,12 +981,16 @@
|
|
| 981 |
" **1.16** on Craftax Classic (effective sample size 437 of a 1024-window batch),\n",
|
| 982 |
" so the returns *do* rank windows within a batch and the familiar explanation\n",
|
| 983 |
" that sparse returns fail to discriminate is unavailable. Measured directly at\n",
|
| 984 |
-
" the pretrained parameters, `‖g_δ‖/‖∇L_BC‖ = 0.
|
| 985 |
-
" **0.02 ± 0.05**. A cosine needs a reference
|
| 986 |
-
" independent directions have `cos
|
| 987 |
-
"
|
| 988 |
-
"
|
| 989 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 990 |
"3. **Shrinking the return term makes things worse — the central negative control.**\n",
|
| 991 |
" If the return term drove the degradation, suppressing it should help. Advantage\n",
|
| 992 |
" clipping confines the weights to [0.8, 1.2] and does exactly what it should:\n",
|
|
@@ -994,10 +998,19 @@
|
|
| 994 |
" **0.097 ± 0.003**, a fivefold reduction. Its score is **5.06** — 3.16 below\n",
|
| 995 |
" baseline RL and second worst in the suite. The binary win mask sits between the\n",
|
| 996 |
" two on both axes (`CV_A` 0.77, ratio 0.417 ± 0.025) and scores 7.28, also below\n",
|
| 997 |
-
" baseline. Across these three
|
| 998 |
-
" **
|
|
|
|
| 999 |
" nearly absent degrades further than one whose return term is large, so the\n",
|
| 1000 |
" degradation cannot be attributed to the return weighting.\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1001 |
"\n",
|
| 1002 |
"### What is left is the data\n",
|
| 1003 |
"\n",
|
|
@@ -1147,12 +1160,235 @@
|
|
| 1147 |
" print(fh.read())"
|
| 1148 |
]
|
| 1149 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1150 |
{
|
| 1151 |
"cell_type": "markdown",
|
| 1152 |
"id": "d2dbe7e3e2ab",
|
| 1153 |
"metadata": {},
|
| 1154 |
"source": [
|
| 1155 |
-
"##
|
| 1156 |
"\n",
|
| 1157 |
"1. **Return-weighted ELBO fine-tuning degrades the checkpoint.** From **11.81**,\n",
|
| 1158 |
" baseline RL falls to **8.22 ± 0.14** and none of the 25 conditions ends above\n",
|
|
@@ -1167,21 +1403,27 @@
|
|
| 1167 |
" 3.73** over the same iterations in which eval score falls from **12.06 to\n",
|
| 1168 |
" 8.49**. The objective is failing on its own terms too.\n",
|
| 1169 |
"3. **The reward is not what does the damage.** The return term is real and large:\n",
|
| 1170 |
-
" `‖g_δ‖/‖∇L_BC‖ = 0.
|
| 1171 |
-
" for `∇L_BC` against itself
|
| 1172 |
-
"
|
| 1173 |
-
"
|
| 1174 |
-
" scores **3.16 below** baseline
|
| 1175 |
-
"
|
| 1176 |
-
"
|
|
|
|
|
|
|
| 1177 |
"4. **What is left is the data.** The suite is ordered by plasticity — conditions\n",
|
| 1178 |
" that restrict the update keep more of the checkpoint (Spearman **−0.79**\n",
|
| 1179 |
-
" between final KL and final score) — and the
|
| 1180 |
-
"
|
| 1181 |
-
"
|
| 1182 |
-
"
|
| 1183 |
-
"
|
| 1184 |
-
" a
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1185 |
"\n",
|
| 1186 |
"**Why the sign constraint is not the whole story.** Eq. 3 is a bound only for\n",
|
| 1187 |
"`A_i ≥ 0`, so the objective can re-rank sampled behaviour but never push mass away\n",
|
|
@@ -1221,7 +1463,9 @@
|
|
| 1221 |
"objective: `CV_A` comes free from an effective-sample-size counter and says whether\n",
|
| 1222 |
"the returns rank anything at all. The ratio `‖g_δ‖/‖∇L_BC‖` costs two backward\n",
|
| 1223 |
"passes and says how much of the update the reward is responsible for. Ours said\n",
|
| 1224 |
-
"0.
|
|
|
|
|
|
|
| 1225 |
"\n",
|
| 1226 |
"**Open problem.** Per-step formulations over the denoising chain (d1, DiffPO) admit\n",
|
| 1227 |
"**signed** advantages, and so change the object being optimised rather than the\n",
|
|
|
|
| 61 |
"# =============================================================================\n",
|
| 62 |
"\n",
|
| 63 |
"# Public HuggingFace repo holding the released artefacts: source, checkpoints\n",
|
| 64 |
+
"# and the pre-computed ablation outputs. No authentication required. Point it\n",
|
| 65 |
+
"# at your own Hub repo holding the same layout, or train from source\n",
|
| 66 |
+
"# (see README.md).\n",
|
|
|
|
| 67 |
"HF_REPO_ID = \"AnonMLuser/remdm-planner-craftax\"\n",
|
| 68 |
"LOCAL_DIR = \"remdm-planner-craftax\"\n",
|
| 69 |
"\n",
|
|
|
|
| 263 |
" \"craftax>=1.5.0\",\n",
|
| 264 |
" \"flax>=0.12.6\",\n",
|
| 265 |
" \"optax>=0.2.8\",\n",
|
| 266 |
+
" \"orbax-checkpoint>=0.12\",\n",
|
| 267 |
" \"distrax>=0.1.7\",\n",
|
| 268 |
" \"chex>=0.1.91\",\n",
|
| 269 |
" \"polars>=1.39.3\",\n",
|
|
|
|
| 292 |
"\n",
|
| 293 |
"if HF_REPO_ID == \"UNSET_HF_REPO_ID\":\n",
|
| 294 |
" raise RuntimeError(\n",
|
| 295 |
+
" \"HF_REPO_ID is unset. Edit the constant in Cell 1 to point at a Hub \"\n",
|
| 296 |
+
" \"repo holding the published layout, or train from source \"\n",
|
| 297 |
+
" \"(see README.md).\"\n",
|
| 298 |
" )\n",
|
| 299 |
"\n",
|
| 300 |
"snapshot_path = snapshot_download(repo_id=HF_REPO_ID, local_dir=LOCAL_DIR)\n",
|
|
|
|
| 536 |
" \"EVAL_STEPS\": EVAL_STEPS,\n",
|
| 537 |
" \"EVAL_NUM_ENVS\": EVAL_NUM_ENVS,\n",
|
| 538 |
" \"DIFFUSION_STEPS_EVAL\": INPAINTING_DIFFUSION_STEPS,\n",
|
| 539 |
+
" \"INFERENCE_SAMPLER\": \"inpainting\",\n",
|
| 540 |
" \"USE_WANDB\": False,\n",
|
| 541 |
" \"CHECKPOINT_PATH\": DIFFUSION_ONLINE_CKPT,\n",
|
| 542 |
" }\n",
|
|
|
|
| 981 |
" **1.16** on Craftax Classic (effective sample size 437 of a 1024-window batch),\n",
|
| 982 |
" so the returns *do* rank windows within a batch and the familiar explanation\n",
|
| 983 |
" that sparse returns fail to discriminate is unavailable. Measured directly at\n",
|
| 984 |
+
" the pretrained parameters, `‖g_δ‖/‖∇L_BC‖ = 0.48 ± 0.01` at cosine\n",
|
| 985 |
+
" **0.02 ± 0.05**. A cosine needs a reference. In `D = 9.33 × 10⁶` dimensions two\n",
|
| 986 |
+
" independent directions have `cos ~ N(0, 1/D)`, a standard deviation of\n",
|
| 987 |
+
" `3.3 × 10⁻⁴` — but the seed-level dispersion of the measured cosine is two\n",
|
| 988 |
+
" orders of magnitude larger than that, so **we do not read this column as a test\n",
|
| 989 |
+
" against isotropy**: 0.02 ± 0.05 is consistent with zero at the precision of the\n",
|
| 990 |
+
" seed mean. The informative reference is how well imitation agrees with itself:\n",
|
| 991 |
+
" two independent noise draws of `∇L_BC` give **0.893 ± 0.010**. Against that,\n",
|
| 992 |
+
" `g_δ` is half the imitation gradient in norm and points somewhere imitation\n",
|
| 993 |
+
" does not.\n",
|
| 994 |
"3. **Shrinking the return term makes things worse — the central negative control.**\n",
|
| 995 |
" If the return term drove the degradation, suppressing it should help. Advantage\n",
|
| 996 |
" clipping confines the weights to [0.8, 1.2] and does exactly what it should:\n",
|
|
|
|
| 998 |
" **0.097 ± 0.003**, a fivefold reduction. Its score is **5.06** — 3.16 below\n",
|
| 999 |
" baseline RL and second worst in the suite. The binary win mask sits between the\n",
|
| 1000 |
" two on both axes (`CV_A` 0.77, ratio 0.417 ± 0.025) and scores 7.28, also below\n",
|
| 1001 |
+
" baseline. Across these three the return term's magnitude and final score are\n",
|
| 1002 |
+
" ordered **the same way**, largest term scoring highest — which is the reverse of\n",
|
| 1003 |
+
" what a return-driven degradation predicts. A condition whose return term is\n",
|
| 1004 |
" nearly absent degrades further than one whose return term is large, so the\n",
|
| 1005 |
" degradation cannot be attributed to the return weighting.\n",
|
| 1006 |
+
"4. **Most of the ratio is weight dispersion, not the return signal.** A\n",
|
| 1007 |
+
" shuffled-`δ` null permutes `δ` across the batch, preserving the weight multiset\n",
|
| 1008 |
+
" and `CV_A` while destroying every association between a window's weight and its\n",
|
| 1009 |
+
" own gradient. On Craftax Classic it retains roughly three quarters of the\n",
|
| 1010 |
+
" measured ratio at every transform, at a near-constant **0.36 of `CV_A`**. So on\n",
|
| 1011 |
+
" this benchmark the ratio is largely a function of dispersion and the ordering\n",
|
| 1012 |
+
" above tracks dispersion rather than the return signal specifically. MiniHack\n",
|
| 1013 |
+
" separates the two much further — see its notebook.\n",
|
| 1014 |
"\n",
|
| 1015 |
"### What is left is the data\n",
|
| 1016 |
"\n",
|
|
|
|
| 1160 |
" print(fh.read())"
|
| 1161 |
]
|
| 1162 |
},
|
| 1163 |
+
{
|
| 1164 |
+
"cell_type": "markdown",
|
| 1165 |
+
"metadata": {},
|
| 1166 |
+
"source": [
|
| 1167 |
+
"## 3. The return term, measured directly\n",
|
| 1168 |
+
"\n",
|
| 1169 |
+
"Section 2's conditions vary *how* the return enters the loss. This section measures\n",
|
| 1170 |
+
"*how much* it contributes, at a single parameter point, with no training involved.\n",
|
| 1171 |
+
"\n",
|
| 1172 |
+
"The return-weighted ELBO gradient factors exactly, with no approximation:\n",
|
| 1173 |
+
"\n",
|
| 1174 |
+
"```\n",
|
| 1175 |
+
"∇L_RW = Ā · [ ∇L_BC + (1/B) Σ_i δ_i ∇ℓ_i ] δ_i = A_i/Ā − 1\n",
|
| 1176 |
+
" └────── g_δ ──────┘\n",
|
| 1177 |
+
"```\n",
|
| 1178 |
+
"\n",
|
| 1179 |
+
"`Ā` rescales the step size and leaves the direction unchanged, so everything the\n",
|
| 1180 |
+
"return does enters through `g_δ`. Both terms are computable at one parameter point,\n",
|
| 1181 |
+
"so we compute them: load the checkpoint every ablation starts from, collect one\n",
|
| 1182 |
+
"on-policy batch under the suite's own rollout function, and take `∇L_BC`, `g_δ` and\n",
|
| 1183 |
+
"`∇L_RW` on that batch under a **shared** `(z_t, t)` draw, so the only difference is\n",
|
| 1184 |
+
"the weight vector. 3 rollout seeds × 8 noise draws, 1,024-window batches. It runs in\n",
|
| 1185 |
+
"minutes on a laptop CPU — `run_ablations.py --measure-gdelta`.\n",
|
| 1186 |
+
"\n",
|
| 1187 |
+
"**Correctness check.** Reconstructing `∇L_RW` from the two right-hand terms and\n",
|
| 1188 |
+
"comparing against the directly computed one gives a relative residual below\n",
|
| 1189 |
+
"**5 × 10⁻⁷** on every draw — float32 accumulation over 9.33M parameters, not a\n",
|
| 1190 |
+
"modelling gap.\n",
|
| 1191 |
+
"\n",
|
| 1192 |
+
"The `Shuffled δ` column permutes `δ` across the batch. That preserves the weight\n",
|
| 1193 |
+
"multiset and `CV_A` exactly while destroying every association between a window's\n",
|
| 1194 |
+
"weight and its own gradient, so it bounds how much of the ratio needs that\n",
|
| 1195 |
+
"association at all.\n"
|
| 1196 |
+
]
|
| 1197 |
+
},
|
| 1198 |
+
{
|
| 1199 |
+
"cell_type": "code",
|
| 1200 |
+
"execution_count": null,
|
| 1201 |
+
"metadata": {},
|
| 1202 |
+
"outputs": [],
|
| 1203 |
+
"source": [
|
| 1204 |
+
"# =============================================================================\n",
|
| 1205 |
+
"# CELL 12 — The measured return term (paper Table 2, appendix \"Measuring the\n",
|
| 1206 |
+
"# Return Term\"). Read from the shipped gdelta artefacts, not retyped.\n",
|
| 1207 |
+
"# =============================================================================\n",
|
| 1208 |
+
"\n",
|
| 1209 |
+
"import json\n",
|
| 1210 |
+
"import os\n",
|
| 1211 |
+
"\n",
|
| 1212 |
+
"import polars as pl\n",
|
| 1213 |
+
"\n",
|
| 1214 |
+
"RUN_DIR = \"experiments/rl_finetuning/outputs/craftax_classic_ablations\"\n",
|
| 1215 |
+
"GDELTA_DIR = os.path.join(RUN_DIR, \"gdelta\")\n",
|
| 1216 |
+
"\n",
|
| 1217 |
+
"gdelta_df = pl.read_csv(os.path.join(RUN_DIR, \"tables\", \"gdelta.csv\"))\n",
|
| 1218 |
+
"print(\"Return term at the pretrained checkpoint, 3 seeds x 8 draws:\")\n",
|
| 1219 |
+
"print(\n",
|
| 1220 |
+
" gdelta_df.select(\n",
|
| 1221 |
+
" [\"Transform\", \"CV_A\", \"Ratio\", \"Ratio_Sd\", \"Ratio_Shuffled\", \"Cos\", \"Cos_Sd\",\n",
|
| 1222 |
+
" \"A1_Violated\"]\n",
|
| 1223 |
+
" )\n",
|
| 1224 |
+
")\n",
|
| 1225 |
+
"\n",
|
| 1226 |
+
"with open(os.path.join(GDELTA_DIR, \"gdelta_aggregate.json\")) as fh:\n",
|
| 1227 |
+
" agg = json.load(fh)\n",
|
| 1228 |
+
"\n",
|
| 1229 |
+
"print()\n",
|
| 1230 |
+
"_draws = agg[\"n_draws_per_seed\"]\n",
|
| 1231 |
+
"_draws = _draws[0] if isinstance(_draws, list) and len(set(_draws)) == 1 else _draws\n",
|
| 1232 |
+
"print(f\"seeds x draws : {agg['n_seeds']} x {_draws}\")\n",
|
| 1233 |
+
"print(f\"batch (windows) : {agg['batch']}\")\n",
|
| 1234 |
+
"print(f\"parameters : {agg['n_params']:,}\")\n",
|
| 1235 |
+
"print(f\"Eq.-4 max rel. residual : {agg['eq4_residual_max']:.3e}\")\n",
|
| 1236 |
+
"print(f\"cos(random directions) sd: {agg['random_cos_sd']:.3e}\")\n",
|
| 1237 |
+
"print(\n",
|
| 1238 |
+
" f\"cos(dL_BC, dL_BC') mean : {agg['bc_self_cos_mean']:.3f} \"\n",
|
| 1239 |
+
" f\"+/- {agg['bc_self_cos_std_seeds']:.3f} <- what alignment looks like when a \"\n",
|
| 1240 |
+
" f\"direction IS imitation\"\n",
|
| 1241 |
+
")\n",
|
| 1242 |
+
"\n",
|
| 1243 |
+
"print()\n",
|
| 1244 |
+
"print(\n",
|
| 1245 |
+
" \"Reading it: the ratio and the final score are ordered the SAME way across the\\n\"\n",
|
| 1246 |
+
" \"three transforms where the decomposition is defined (baseline 0.485/8.22,\\n\"\n",
|
| 1247 |
+
" \"bc_wins 0.417/7.28, advantage_clip 0.097/5.06). A return-driven degradation\\n\"\n",
|
| 1248 |
+
" \"predicts the opposite. normalized_adv is excluded: it mean-centres the weights,\\n\"\n",
|
| 1249 |
+
" \"so Abar ~ 0, delta is undefined and (A1) fails -- the objective is no longer a\\n\"\n",
|
| 1250 |
+
" \"bound there, which is the point, not a measurement artefact.\\n\"\n",
|
| 1251 |
+
" \"\\n\"\n",
|
| 1252 |
+
" \"Note the Shuffled column: on Craftax Classic it retains roughly three quarters\\n\"\n",
|
| 1253 |
+
" \"of each measured ratio, at a near-constant 0.36 of CV_A. Most of the ratio is\\n\"\n",
|
| 1254 |
+
" \"weight dispersion rather than the return signal specifically. MiniHack does not\\n\"\n",
|
| 1255 |
+
" \"reproduce that -- there the null costs about five sixths -- so the redundancy is\\n\"\n",
|
| 1256 |
+
" \"a Craftax-side property, not a law.\"\n",
|
| 1257 |
+
")\n"
|
| 1258 |
+
]
|
| 1259 |
+
},
|
| 1260 |
+
{
|
| 1261 |
+
"cell_type": "markdown",
|
| 1262 |
+
"metadata": {},
|
| 1263 |
+
"source": [
|
| 1264 |
+
"## 4. Review arms: the unweighted control and the learning-rate sweep\n",
|
| 1265 |
+
"\n",
|
| 1266 |
+
"The 25-condition suite leaves two accounts of the degradation open, and each has a\n",
|
| 1267 |
+
"run that closes it. These sit **outside** the suite's tables — they are separate\n",
|
| 1268 |
+
"runs on a different host, at three seeds each, shipped under\n",
|
| 1269 |
+
"`experiments/rl_finetuning/outputs/review_*/`.\n",
|
| 1270 |
+
"\n",
|
| 1271 |
+
"**Read them against each other, not against Section 2.** The suite does not\n",
|
| 1272 |
+
"reproduce across hosts: the same checkpoint and configuration score the pretrained\n",
|
| 1273 |
+
"planner at **11.9754** here against **11.8084** in Section 2, and baseline RL at\n",
|
| 1274 |
+
"**8.3395** against **8.2233** — a consistent offset of 1.41%. So `baseline_rl` was\n",
|
| 1275 |
+
"re-run alongside the new arms as a same-stack anchor, and every comparison below is\n",
|
| 1276 |
+
"within the review stack.\n",
|
| 1277 |
+
"\n",
|
| 1278 |
+
"| Arm | What it asks |\n",
|
| 1279 |
+
"|---|---|\n",
|
| 1280 |
+
"| `review_anchor_baseline_rl` | The same-stack anchor every other row is quoted against |\n",
|
| 1281 |
+
"| `review_run1_bc_all` | **Is the weighting the problem, or the data?** Every weight fixed at one, over the same rollout windows — the unweighted control |\n",
|
| 1282 |
+
"| `review_run2_advclip_lr_matched` | **Was the clipping control just a smaller step?** `advantage_clip` re-run with its learning rate scaled by the measured `Ā_base/Ā_clip = 1.0744`, giving 3.223e-4 |\n",
|
| 1283 |
+
"| `review_run4_baseline_lr1e-4` / `_lr1e-5` | **Is the degradation just too large a step?** The suite fine-tunes at the pretraining learning rate of 3e-4 |\n",
|
| 1284 |
+
"\n",
|
| 1285 |
+
"The MiniHack counterparts of all three questions were run too, and the paper reports\n",
|
| 1286 |
+
"them, but **their run directories are not part of this release** — only the Craftax\n",
|
| 1287 |
+
"Classic ones are shipped here. The MiniHack numbers are quoted in its notebook.\n"
|
| 1288 |
+
]
|
| 1289 |
+
},
|
| 1290 |
+
{
|
| 1291 |
+
"cell_type": "code",
|
| 1292 |
+
"execution_count": null,
|
| 1293 |
+
"metadata": {},
|
| 1294 |
+
"outputs": [],
|
| 1295 |
+
"source": [
|
| 1296 |
+
"# =============================================================================\n",
|
| 1297 |
+
"# CELL 13 — Review arms, read from the shipped run directories\n",
|
| 1298 |
+
"# =============================================================================\n",
|
| 1299 |
+
"\n",
|
| 1300 |
+
"import os\n",
|
| 1301 |
+
"\n",
|
| 1302 |
+
"import polars as pl\n",
|
| 1303 |
+
"\n",
|
| 1304 |
+
"OUT = \"experiments/rl_finetuning/outputs\"\n",
|
| 1305 |
+
"REVIEW_RUNS = [\n",
|
| 1306 |
+
" (\"review_anchor_baseline_rl\", \"anchor: baseline_rl @3e-4 (same stack)\"),\n",
|
| 1307 |
+
" (\"review_run1_bc_all\", \"bc_all: uniform weights, all windows\"),\n",
|
| 1308 |
+
" (\"review_run2_advclip_lr_matched\", \"advantage_clip @3.223e-4 (matched step)\"),\n",
|
| 1309 |
+
" (\"review_run4_baseline_lr1e-4\", \"baseline_rl @1e-4\"),\n",
|
| 1310 |
+
" (\"review_run4_baseline_lr1e-5\", \"baseline_rl @1e-5\"),\n",
|
| 1311 |
+
"]\n",
|
| 1312 |
+
"\n",
|
| 1313 |
+
"rows = []\n",
|
| 1314 |
+
"for run, label in REVIEW_RUNS:\n",
|
| 1315 |
+
" csv = os.path.join(OUT, run, \"tables\", \"main_results.csv\")\n",
|
| 1316 |
+
" if not os.path.exists(csv):\n",
|
| 1317 |
+
" print(f\"missing: {csv}\")\n",
|
| 1318 |
+
" continue\n",
|
| 1319 |
+
" df = pl.read_csv(csv)\n",
|
| 1320 |
+
" r = df.row(0, named=True)\n",
|
| 1321 |
+
" rows.append(\n",
|
| 1322 |
+
" {\n",
|
| 1323 |
+
" \"Arm\": label,\n",
|
| 1324 |
+
" \"Condition\": r[\"Method\"],\n",
|
| 1325 |
+
" \"Score\": r[\"Final_Score\"],\n",
|
| 1326 |
+
" \"Sd\": r[\"Score_Std\"],\n",
|
| 1327 |
+
" \"vs_Pretrained\": r[\"Delta_vs_Pretrained\"],\n",
|
| 1328 |
+
" }\n",
|
| 1329 |
+
" )\n",
|
| 1330 |
+
"\n",
|
| 1331 |
+
"review_df = pl.DataFrame(rows)\n",
|
| 1332 |
+
"print(\"Review arms (same-stack pretrained checkpoint = 11.9754):\")\n",
|
| 1333 |
+
"print(review_df)\n",
|
| 1334 |
+
"\n",
|
| 1335 |
+
"anchor = next(r[\"Score\"] for r in rows if r[\"Condition\"] == \"baseline_rl\" and \"anchor\" in r[\"Arm\"])\n",
|
| 1336 |
+
"bc_all = next((r[\"Score\"] for r in rows if r[\"Condition\"] == \"bc_all\"), None)\n",
|
| 1337 |
+
"lr5 = next((r[\"Score\"] for r in rows if \"1e-5\" in r[\"Arm\"]), None)\n",
|
| 1338 |
+
"PRETRAINED_SAME_STACK = 11.9754\n",
|
| 1339 |
+
"\n",
|
| 1340 |
+
"print()\n",
|
| 1341 |
+
"if bc_all is not None:\n",
|
| 1342 |
+
" print(\n",
|
| 1343 |
+
" f\"Unweighted control : bc_all {bc_all:.4f} vs anchor {anchor:.4f} \"\n",
|
| 1344 |
+
" f\"= {bc_all - anchor:+.4f}\"\n",
|
| 1345 |
+
" )\n",
|
| 1346 |
+
" print(\n",
|
| 1347 |
+
" \" Removing the weighting while keeping the rollouts\\n\"\n",
|
| 1348 |
+
" \" degrades the model FURTHER than the weighted objective\\n\"\n",
|
| 1349 |
+
" \" does. The seeds do not overlap. That is the protective\\n\"\n",
|
| 1350 |
+
" \" reading -- the inverse of a return-driven degradation.\\n\"\n",
|
| 1351 |
+
" \" The host offset works against this result, not for it:\\n\"\n",
|
| 1352 |
+
" \" on the published stack bc_all would sit lower still.\\n\"\n",
|
| 1353 |
+
" \" Exact two-sided permutation test: p = 0.10, which is the\\n\"\n",
|
| 1354 |
+
" \" design floor at 3 seeds, not a significant result.\"\n",
|
| 1355 |
+
" )\n",
|
| 1356 |
+
"if lr5 is not None:\n",
|
| 1357 |
+
" shortfall = PRETRAINED_SAME_STACK - lr5\n",
|
| 1358 |
+
" recovered = (lr5 - anchor) / (PRETRAINED_SAME_STACK - anchor)\n",
|
| 1359 |
+
" print()\n",
|
| 1360 |
+
" print(\n",
|
| 1361 |
+
" f\"Learning-rate sweep: 3e-4 {anchor:.4f} -> 1e-4 -> 1e-5 {lr5:.4f}; still \"\n",
|
| 1362 |
+
" f\"{shortfall:.4f} below the same-stack checkpoint,\"\n",
|
| 1363 |
+
" )\n",
|
| 1364 |
+
" print(\n",
|
| 1365 |
+
" f\" having recovered {recovered:.0%} of the default's \"\n",
|
| 1366 |
+
" f\"shortfall. That residual is more than ten times the\"\n",
|
| 1367 |
+
" )\n",
|
| 1368 |
+
" print(\n",
|
| 1369 |
+
" \" seed sd at that rate, and the sd does not fall as the \"\n",
|
| 1370 |
+
" \"step shrinks. Lowering the step by a\"\n",
|
| 1371 |
+
" )\n",
|
| 1372 |
+
" print(\n",
|
| 1373 |
+
" \" factor of thirty buys back two thirds of the \"\n",
|
| 1374 |
+
" \"degradation WITHOUT removing it. 3e-5 was not run.\"\n",
|
| 1375 |
+
" )\n",
|
| 1376 |
+
"print()\n",
|
| 1377 |
+
"print(\n",
|
| 1378 |
+
" \"Matched effective step: advantage_clip at 3.223e-4 reaches 4.9246, leaving it\\n\"\n",
|
| 1379 |
+
" \" 3.4149 below the same-stack anchor, where the unmatched arm\\n\"\n",
|
| 1380 |
+
" \" sat 3.1601 below the published baseline. Equalising the\\n\"\n",
|
| 1381 |
+
" \" effective step does NOT close the clipping gap, so Section 3's\\n\"\n",
|
| 1382 |
+
" \" central negative control was not measuring step size.\"\n",
|
| 1383 |
+
")\n"
|
| 1384 |
+
]
|
| 1385 |
+
},
|
| 1386 |
{
|
| 1387 |
"cell_type": "markdown",
|
| 1388 |
"id": "d2dbe7e3e2ab",
|
| 1389 |
"metadata": {},
|
| 1390 |
"source": [
|
| 1391 |
+
"## 5. Conclusions\n",
|
| 1392 |
"\n",
|
| 1393 |
"1. **Return-weighted ELBO fine-tuning degrades the checkpoint.** From **11.81**,\n",
|
| 1394 |
" baseline RL falls to **8.22 ± 0.14** and none of the 25 conditions ends above\n",
|
|
|
|
| 1403 |
" 3.73** over the same iterations in which eval score falls from **12.06 to\n",
|
| 1404 |
" 8.49**. The objective is failing on its own terms too.\n",
|
| 1405 |
"3. **The reward is not what does the damage.** The return term is real and large:\n",
|
| 1406 |
+
" `‖g_δ‖/‖∇L_BC‖ = 0.48 ± 0.01`, half the imitation gradient in norm, against\n",
|
| 1407 |
+
" `0.893 ± 0.010` for `∇L_BC` against itself. Its cosine with imitation is\n",
|
| 1408 |
+
" `0.02 ± 0.05`, which we read as consistent with zero rather than as a departure\n",
|
| 1409 |
+
" from isotropy. But the central control does not go our way. Advantage clipping\n",
|
| 1410 |
+
" cuts that ratio fivefold to `0.097 ± 0.003` and scores **3.16 below** baseline\n",
|
| 1411 |
+
" RL, second worst in the suite. Across the three transforms where the\n",
|
| 1412 |
+
" decomposition is defined, the return term's magnitude and final score are\n",
|
| 1413 |
+
" ordered the same way — the reverse of what a return-driven degradation\n",
|
| 1414 |
+
" predicts.\n",
|
| 1415 |
"4. **What is left is the data.** The suite is ordered by plasticity — conditions\n",
|
| 1416 |
" that restrict the update keep more of the checkpoint (Spearman **−0.79**\n",
|
| 1417 |
+
" between final KL and final score) — and the unweighted control now exists.\n",
|
| 1418 |
+
" `bc_all` fixes every weight at one over the same rollout windows the weighted\n",
|
| 1419 |
+
" baseline trains on, and it reaches **4.73 ± 0.19** against a same-stack anchor\n",
|
| 1420 |
+
" of **8.34 ± 0.15** (Section 4 below). Removing the weighting while keeping the\n",
|
| 1421 |
+
" rollouts costs *more* than the weighting itself, on both benchmarks. That fits\n",
|
| 1422 |
+
" a **protective** reading of the return weighting rather than a damaging one —\n",
|
| 1423 |
+
" the inverse of the reading this study set out to test. It does not show whether\n",
|
| 1424 |
+
" the rollouts themselves contribute, which would need an arm that does not train\n",
|
| 1425 |
+
" on them, and the pairwise permutation test reaches only `p = 0.10`, the design\n",
|
| 1426 |
+
" floor at three seeds.\n",
|
| 1427 |
"\n",
|
| 1428 |
"**Why the sign constraint is not the whole story.** Eq. 3 is a bound only for\n",
|
| 1429 |
"`A_i ≥ 0`, so the objective can re-rank sampled behaviour but never push mass away\n",
|
|
|
|
| 1463 |
"objective: `CV_A` comes free from an effective-sample-size counter and says whether\n",
|
| 1464 |
"the returns rank anything at all. The ratio `‖g_δ‖/‖∇L_BC‖` costs two backward\n",
|
| 1465 |
"passes and says how much of the update the reward is responsible for. Ours said\n",
|
| 1466 |
+
"0.48, which looked like a mechanism until the clipping control said otherwise.\n",
|
| 1467 |
+
"Take the shuffled-`δ` null alongside it — on Craftax Classic three quarters of\n",
|
| 1468 |
+
"that 0.48 survives destroying the weight/gradient association entirely.\n",
|
| 1469 |
"\n",
|
| 1470 |
"**Open problem.** Per-step formulations over the denoising chain (d1, DiffPO) admit\n",
|
| 1471 |
"**signed** advantages, and so change the object being optimised rather than the\n",
|
experiments/README.md
CHANGED
|
@@ -8,7 +8,7 @@ These scripts are **standalone research code** — they import from `src/` but d
|
|
| 8 |
## `rl_finetuning/` — RL Fine-Tuning Ablation Suite
|
| 9 |
|
| 10 |
Diagnoses why RL fine-tuning of the diffusion model collapses and which interventions fix it.
|
| 11 |
-
Implements **
|
| 12 |
|
| 13 |
**Training data is on-policy.** Each iteration rolls the *current* model out under its EMA weights (`diffusion_steps_collect` denoising steps per plan, `num_steps // plan_horizon` plan cycles) and trains on those windows, weighted by each window's own H-step reward sum. The suite therefore needs no expert: `--ppo-checkpoint` is gone, and only the pretrained diffusion `--checkpoint` is required.
|
| 14 |
|
|
@@ -20,27 +20,30 @@ rl_finetuning/
|
|
| 20 |
├── ablations/
|
| 21 |
│ ├── losses.py # All loss/objective variants as factory functions
|
| 22 |
│ ├── optimizers.py # LLRD, LoRA, gradient surgery, param masking
|
| 23 |
-
│ ├── registry.py # AblationSpec dataclass + REGISTRY (
|
| 24 |
│ └── training.py # make_run_ablation() factory + AblationHistory dataclass
|
| 25 |
├── diagnostics/
|
| 26 |
│ ├── gradient.py # Grad alignment, per-layer norms, surgery metrics
|
| 27 |
│ ├── representation.py # KL drift, CKA similarity, activation norms
|
| 28 |
│ └── timestep.py # t-bin gradient norms, per-t loss decomposition
|
| 29 |
├── analysis/
|
| 30 |
-
│ ├──
|
| 31 |
-
│ ├── plots.py #
|
| 32 |
│ ├── tables.py # Summary tables as polars DataFrames + LaTeX export
|
| 33 |
-
│
|
|
|
|
| 34 |
└── configs/
|
| 35 |
-
├── ablations_default.yaml
|
| 36 |
-
├── ablations_fast.yaml
|
| 37 |
-
├── ablations_final_craftax_classic_gpu_24gb.yaml
|
| 38 |
-
├── ablations_final_craftax_classic_gpu_h200.yaml #
|
| 39 |
-
├── ablations_final_craftax_gpu_24gb.yaml
|
| 40 |
-
└── ablations_final_craftax_gpu_h200.yaml
|
| 41 |
```
|
| 42 |
|
| 43 |
-
|
|
|
|
|
|
|
| 44 |
|
| 45 |
### Config layering
|
| 46 |
|
|
@@ -69,9 +72,12 @@ jax_compilation_cache_dir: /var/tmp/your-user/jax-cache
|
|
| 69 |
|
| 70 |
### Usage
|
| 71 |
|
| 72 |
-
|
| 73 |
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
**Smoke test (2 ablations, fast config):**
|
| 77 |
```bash
|
|
@@ -81,7 +87,7 @@ python experiments/rl_finetuning/run_ablations.py \
|
|
| 81 |
--checkpoint $PRETRAINED_CKPT
|
| 82 |
```
|
| 83 |
|
| 84 |
-
**Full suite (all
|
| 85 |
```bash
|
| 86 |
python experiments/rl_finetuning/run_ablations.py \
|
| 87 |
--config configs/defaults.yaml \
|
|
@@ -94,7 +100,7 @@ python experiments/rl_finetuning/run_ablations.py \
|
|
| 94 |
|
| 95 |
**Full suite against a pinned `final_*` checkpoint:**
|
| 96 |
```bash
|
| 97 |
-
# Craftax Classic,
|
| 98 |
python experiments/rl_finetuning/run_ablations.py \
|
| 99 |
--ablations-config experiments/rl_finetuning/configs/ablations_final_craftax_classic_gpu_24gb.yaml \
|
| 100 |
--all --num-seeds 3 \
|
|
@@ -109,13 +115,6 @@ python experiments/rl_finetuning/run_ablations.py \
|
|
| 109 |
--checkpoint $PRETRAINED_CKPT
|
| 110 |
```
|
| 111 |
|
| 112 |
-
**From a W&B artifact:**
|
| 113 |
-
```bash
|
| 114 |
-
python experiments/rl_finetuning/run_ablations.py \
|
| 115 |
-
--ablations baseline_rl \
|
| 116 |
-
--checkpoint wandb:my-team/remdm-planner-craftax/Craftax-Classic-Symbolic-v1-policy:latest
|
| 117 |
-
```
|
| 118 |
-
|
| 119 |
**Re-plot from saved results (no training):**
|
| 120 |
```bash
|
| 121 |
python experiments/rl_finetuning/run_ablations.py \
|
|
@@ -143,7 +142,8 @@ poolable** with it:
|
|
| 143 |
| `eval_steps` | 1024 | 512 | 1024 | 512 | noisier score |
|
| 144 |
| `mixed_replay_buffer_size` | 20000 | 10000 | 10000 | 10000 | replay horizon |
|
| 145 |
|
| 146 |
-
|
|
|
|
| 147 |
`cka_batch_size`, `per_layer_every`, `repr_drift_every`, `grad_align_every`,
|
| 148 |
`t_analysis_every`) are wall-clock only and do not affect poolability.
|
| 149 |
|
|
@@ -155,11 +155,6 @@ the poolable set. The key set itself is declared once, in
|
|
| 155 |
`run_ablations._RESULT_AFFECTING`, so the classification the tests check and
|
| 156 |
the refusal `--merge` performs are the same policy.
|
| 157 |
|
| 158 |
-
**List all ablations:**
|
| 159 |
-
```bash
|
| 160 |
-
python experiments/rl_finetuning/run_ablations.py --list
|
| 161 |
-
```
|
| 162 |
-
|
| 163 |
### Ablations
|
| 164 |
|
| 165 |
| Group | Name | Tests |
|
|
@@ -177,6 +172,7 @@ python experiments/rl_finetuning/run_ablations.py --list
|
|
| 177 |
| | `advantage_clip` | PPO-style advantage clipping [1-ε, 1+ε] |
|
| 178 |
| | `normalized_adv` | Std-normalised advantages |
|
| 179 |
| | `bc_wins` | Uniform ELBO on win windows (no advantage weighting) |
|
|
|
|
| 180 |
| | `low_t` | ELBO restricted to low-t (fine-detail) regime |
|
| 181 |
| **C: Architecture** | `frozen_backbone` | Train the action head + token embeddings (backbone frozen) |
|
| 182 |
| | `head_only` | Train only the final action projection |
|
|
@@ -209,36 +205,62 @@ experiments/rl_finetuning/outputs/{run_id}/
|
|
| 209 |
│ ├── cka_similarity.png
|
| 210 |
│ ├── t_distribution_analysis.png
|
| 211 |
│ ├── t_bin_grad_norms_{name}.png
|
|
|
|
|
|
|
| 212 |
│ ├── win_rate_and_effective_batch_size.png
|
| 213 |
│ ├── achievement_breakdown.png # Start vs end achievement rates (stacked bars)
|
| 214 |
│ ├── achievement_collapse_{name}.png # Per-ablation achievement heatmap over time
|
| 215 |
│ ├── diagnosis_decision_tree.png
|
| 216 |
-
│ └── action_dist/
|
| 217 |
│ ├── action_freq_{name}.png # Side-by-side pre/post action frequency bars
|
| 218 |
│ ├── transition_matrix_{name}.png # 3-panel heatmap (pre, post, difference)
|
| 219 |
│ ├── action_metrics_{name}.png # 2x2 dashboard (entropy in nats, effective, Gini, divergences)
|
| 220 |
│ └── js_divergence_comparison.png # Cross-ablation JS divergence bar chart
|
|
|
|
|
|
|
|
|
|
| 221 |
└── tables/
|
| 222 |
├── main_results.{csv,tex}
|
| 223 |
├── significance_test.txt # Max-statistic permutation test + p floor + bootstrap CI
|
|
|
|
| 224 |
├── gradient_analysis.{csv,tex}
|
| 225 |
├── t_distribution.{csv,tex}
|
|
|
|
|
|
|
| 226 |
├── forgetting_analysis.{csv,tex}
|
| 227 |
├── hypothesis_verdict.{csv,tex}
|
| 228 |
-
|
|
|
|
|
|
|
| 229 |
```
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
**`results.json` schema:**
|
| 232 |
```json
|
| 233 |
{
|
| 234 |
"pretrained_score": 0.1234,
|
| 235 |
"pretrained_ach_rates": {"achievement_collect_wood": 0.42, ...},
|
| 236 |
-
"config": {"MAX_ITER": 1000, ...},
|
|
|
|
| 237 |
"ablations": {
|
| 238 |
"kl_penalty": {
|
| 239 |
"score": 0.1456, // mean across seeds
|
| 240 |
"score_std": 0.008, // std across seeds (0.0 if num_seeds=1)
|
| 241 |
"all_scores": [0.1456], // per-seed scores
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
"history": { ... } // AblationHistory serialised
|
| 243 |
}
|
| 244 |
}
|
|
@@ -246,14 +268,99 @@ experiments/rl_finetuning/outputs/{run_id}/
|
|
| 246 |
```
|
| 247 |
|
| 248 |
`results.json` is written incrementally after each ablation completes — a partial file with
|
| 249 |
-
N of
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
### W&B logging
|
| 252 |
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
### Diagnostic metrics collected
|
| 259 |
|
|
|
|
| 8 |
## `rl_finetuning/` — RL Fine-Tuning Ablation Suite
|
| 9 |
|
| 10 |
Diagnoses why RL fine-tuning of the diffusion model collapses and which interventions fix it.
|
| 11 |
+
Implements **26 ablations**: a baseline plus four groups (A: Regularisation, B: Training Signal, C: Architecture, D: Data Quality), with a comprehensive diagnostic and analysis pipeline.
|
| 12 |
|
| 13 |
**Training data is on-policy.** Each iteration rolls the *current* model out under its EMA weights (`diffusion_steps_collect` denoising steps per plan, `num_steps // plan_horizon` plan cycles) and trains on those windows, weighted by each window's own H-step reward sum. The suite therefore needs no expert: `--ppo-checkpoint` is gone, and only the pretrained diffusion `--checkpoint` is required.
|
| 14 |
|
|
|
|
| 20 |
├── ablations/
|
| 21 |
│ ├── losses.py # All loss/objective variants as factory functions
|
| 22 |
│ ├── optimizers.py # LLRD, LoRA, gradient surgery, param masking
|
| 23 |
+
│ ├── registry.py # AblationSpec dataclass + REGISTRY (26 ablations)
|
| 24 |
│ └── training.py # make_run_ablation() factory + AblationHistory dataclass
|
| 25 |
├── diagnostics/
|
| 26 |
│ ├── gradient.py # Grad alignment, per-layer norms, surgery metrics
|
| 27 |
│ ├── representation.py # KL drift, CKA similarity, activation norms
|
| 28 |
│ └── timestep.py # t-bin gradient norms, per-t loss decomposition
|
| 29 |
├── analysis/
|
| 30 |
+
│ ├── gdelta.py # Return term g_delta of the decomposition (no training)
|
| 31 |
+
│ ├── plots.py # 16 matplotlib figure generators
|
| 32 |
│ ├── tables.py # Summary tables as polars DataFrames + LaTeX export
|
| 33 |
+
│ ├── report.py # diagnosis.md + decision tree figure
|
| 34 |
+
│ └── action_distribution.py # Pre/post-RL action distribution analysis
|
| 35 |
└── configs/
|
| 36 |
+
├── ablations_default.yaml # Base: all ablation hyperparameters
|
| 37 |
+
├── ablations_fast.yaml # Smoke-test overlay (50 iterations, 16 envs)
|
| 38 |
+
├── ablations_final_craftax_classic_gpu_24gb.yaml # RTX 3090 Ti, seed 42 (reference)
|
| 39 |
+
├── ablations_final_craftax_classic_gpu_h200.yaml # H200, seed 43
|
| 40 |
+
├── ablations_final_craftax_gpu_24gb.yaml # Full Craftax, 24GB, seed 42
|
| 41 |
+
└── ablations_final_craftax_gpu_h200.yaml # Full Craftax, H200, seed 43
|
| 42 |
```
|
| 43 |
|
| 44 |
+
Each `ablations_final_*` matches the `configs/final_*` of the same name.
|
| 45 |
+
|
| 46 |
+
`ablations_default.yaml` carries the released checkpoints' architecture — 384-dim, 8 heads, 6 layers, `d_ff` 768, `plan_horizon` 32, the same for Classic and Full — so the `ablations_final_*` presets need not restate it. A run against a differently-shaped checkpoint must override those keys or the model build fails on a shape mismatch.
|
| 47 |
|
| 48 |
### Config layering
|
| 49 |
|
|
|
|
| 72 |
|
| 73 |
### Usage
|
| 74 |
|
| 75 |
+
`--checkpoint` takes the pretrained diffusion checkpoint from either `--mode offline` or `--mode online`; for DAgger, the final (`{env}-policy`) or best-validation (`{env}-policy-best`) artifact is consumed directly. It also accepts `wandb:team/project/artifact:latest` references, downloaded automatically before training begins.
|
| 76 |
|
| 77 |
+
**List all ablations:**
|
| 78 |
+
```bash
|
| 79 |
+
python experiments/rl_finetuning/run_ablations.py --list
|
| 80 |
+
```
|
| 81 |
|
| 82 |
**Smoke test (2 ablations, fast config):**
|
| 83 |
```bash
|
|
|
|
| 87 |
--checkpoint $PRETRAINED_CKPT
|
| 88 |
```
|
| 89 |
|
| 90 |
+
**Full suite (all 26 ablations):**
|
| 91 |
```bash
|
| 92 |
python experiments/rl_finetuning/run_ablations.py \
|
| 93 |
--config configs/defaults.yaml \
|
|
|
|
| 100 |
|
| 101 |
**Full suite against a pinned `final_*` checkpoint:**
|
| 102 |
```bash
|
| 103 |
+
# Craftax Classic, GPU-24GB hardware (seed 42 checkpoint)
|
| 104 |
python experiments/rl_finetuning/run_ablations.py \
|
| 105 |
--ablations-config experiments/rl_finetuning/configs/ablations_final_craftax_classic_gpu_24gb.yaml \
|
| 106 |
--all --num-seeds 3 \
|
|
|
|
| 115 |
--checkpoint $PRETRAINED_CKPT
|
| 116 |
```
|
| 117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
**Re-plot from saved results (no training):**
|
| 119 |
```bash
|
| 120 |
python experiments/rl_finetuning/run_ablations.py \
|
|
|
|
| 142 |
| `eval_steps` | 1024 | 512 | 1024 | 512 | noisier score |
|
| 143 |
| `mixed_replay_buffer_size` | 20000 | 10000 | 10000 | 10000 | replay horizon |
|
| 144 |
|
| 145 |
+
Values above are post-layering: a config's own value, or what it inherits from
|
| 146 |
+
`ablations_default.yaml`. Differences in diagnostic cadence (`eval_every`, `cka_every`,
|
| 147 |
`cka_batch_size`, `per_layer_every`, `repr_drift_every`, `grad_align_every`,
|
| 148 |
`t_analysis_every`) are wall-clock only and do not affect poolability.
|
| 149 |
|
|
|
|
| 155 |
`run_ablations._RESULT_AFFECTING`, so the classification the tests check and
|
| 156 |
the refusal `--merge` performs are the same policy.
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
### Ablations
|
| 159 |
|
| 160 |
| Group | Name | Tests |
|
|
|
|
| 172 |
| | `advantage_clip` | PPO-style advantage clipping [1-ε, 1+ε] |
|
| 173 |
| | `normalized_adv` | Std-normalised advantages |
|
| 174 |
| | `bc_wins` | Uniform ELBO on win windows (no advantage weighting) |
|
| 175 |
+
| | `bc_all` | Uniform ELBO on all rollout windows (no advantage weighting) |
|
| 176 |
| | `low_t` | ELBO restricted to low-t (fine-detail) regime |
|
| 177 |
| **C: Architecture** | `frozen_backbone` | Train the action head + token embeddings (backbone frozen) |
|
| 178 |
| | `head_only` | Train only the final action projection |
|
|
|
|
| 205 |
│ ├── cka_similarity.png
|
| 206 |
│ ├── t_distribution_analysis.png
|
| 207 |
│ ├── t_bin_grad_norms_{name}.png
|
| 208 |
+
│ ├── t_bin_norms_heatmap.png # Per-t-bin gradient norms, final iteration
|
| 209 |
+
│ ├── group_comparison.png # Boxplot of scores by ablation group
|
| 210 |
│ ├── win_rate_and_effective_batch_size.png
|
| 211 |
│ ├── achievement_breakdown.png # Start vs end achievement rates (stacked bars)
|
| 212 |
│ ├── achievement_collapse_{name}.png # Per-ablation achievement heatmap over time
|
| 213 |
│ ├── diagnosis_decision_tree.png
|
| 214 |
+
│ └── action_dist/ # On by default; --no-action-dist skips it
|
| 215 |
│ ├── action_freq_{name}.png # Side-by-side pre/post action frequency bars
|
| 216 |
│ ├── transition_matrix_{name}.png # 3-panel heatmap (pre, post, difference)
|
| 217 |
│ ├── action_metrics_{name}.png # 2x2 dashboard (entropy in nats, effective, Gini, divergences)
|
| 218 |
│ └── js_divergence_comparison.png # Cross-ablation JS divergence bar chart
|
| 219 |
+
├── gdelta/ # --measure-gdelta only
|
| 220 |
+
│ ├── gdelta_seed{n}.json # Per rollout seed; +/- within is across that seed's draws
|
| 221 |
+
│ └── gdelta_aggregate.json # Across seeds; the dispersion the paper's table prints
|
| 222 |
└── tables/
|
| 223 |
├── main_results.{csv,tex}
|
| 224 |
├── significance_test.txt # Max-statistic permutation test + p floor + bootstrap CI
|
| 225 |
+
├── group_summary.{csv,tex} # Group-level summary table
|
| 226 |
├── gradient_analysis.{csv,tex}
|
| 227 |
├── t_distribution.{csv,tex}
|
| 228 |
+
├── repr_drift.{csv,tex} # KL drift values at the final iteration
|
| 229 |
+
├── per_env.{csv,tex} # Per-achievement rates; needs pretrained_ach_rates
|
| 230 |
├── forgetting_analysis.{csv,tex}
|
| 231 |
├── hypothesis_verdict.{csv,tex}
|
| 232 |
+
├── achievement_summary.{csv,tex} # Per-achievement final unlock rates
|
| 233 |
+
├── gdelta.{csv,tex} # --measure-gdelta only: the decomposition per weight transform
|
| 234 |
+
└── results.tex # --emit-tex-macros only: \newcommand per headline number
|
| 235 |
```
|
| 236 |
|
| 237 |
+
**Action distribution analysis** runs by default and is disabled with
|
| 238 |
+
`--no-action-dist`. The rollout is one vectorised scan over `num_envs`, sized
|
| 239 |
+
from the config rather than by an episode count, so it costs a fraction of a
|
| 240 |
+
training run — which is why the default differs from the minihack twin, where
|
| 241 |
+
the same flag defaults off because MiniHack rollouts are not vectorised. It
|
| 242 |
+
reads each ablation's `final_params`, so it only covers ablations that
|
| 243 |
+
completed.
|
| 244 |
+
|
| 245 |
**`results.json` schema:**
|
| 246 |
```json
|
| 247 |
{
|
| 248 |
"pretrained_score": 0.1234,
|
| 249 |
"pretrained_ach_rates": {"achievement_collect_wood": 0.42, ...},
|
| 250 |
+
"config": {"MAX_ITER": 1000, ...}, // the merged config, keys uppercase
|
| 251 |
+
"merge_provenance": { ... }, // --merge only: inputs + which supplied config
|
| 252 |
"ablations": {
|
| 253 |
"kl_penalty": {
|
| 254 |
"score": 0.1456, // mean across seeds
|
| 255 |
"score_std": 0.008, // std across seeds (0.0 if num_seeds=1)
|
| 256 |
"all_scores": [0.1456], // per-seed scores
|
| 257 |
+
"base_seed": 42, "seeds": [42], // seeding actually used
|
| 258 |
+
"wall_clock_s": 812.4,
|
| 259 |
+
"per_seed_finals": [{...}], // per-seed end-of-run metrics
|
| 260 |
+
"final_ach_rates": {"achievement_collect_wood": 0.40, ...},
|
| 261 |
+
// achievement detail of the same post-loop
|
| 262 |
+
// evaluations that produced all_scores, seed-averaged
|
| 263 |
+
"all_final_ach_rates": [{...}], // per-seed, before averaging
|
| 264 |
"history": { ... } // AblationHistory serialised
|
| 265 |
}
|
| 266 |
}
|
|
|
|
| 268 |
```
|
| 269 |
|
| 270 |
`results.json` is written incrementally after each ablation completes — a partial file with
|
| 271 |
+
N of 26 ablations is fully valid and loadable by `--analyze-only --results-path`.
|
| 272 |
+
|
| 273 |
+
### Measuring the return term (`--measure-gdelta`)
|
| 274 |
+
|
| 275 |
+
Splits the return-weighted ELBO gradient into an imitation term and a return term at a
|
| 276 |
+
single parameter point:
|
| 277 |
+
|
| 278 |
+
```
|
| 279 |
+
grad L_RW = Abar * ( grad L_BC + g_delta ),
|
| 280 |
+
g_delta = (1/B) sum_i delta_i grad l_i, delta_i = A_i/Abar - 1.
|
| 281 |
+
```
|
| 282 |
+
|
| 283 |
+
Loads the pretrained checkpoint, collects one on-policy batch from it, and evaluates
|
| 284 |
+
`grad L_BC`, `g_delta` and `grad L_RW` on that batch at those parameters under a shared
|
| 285 |
+
`(z_t, t)` draw, so the only difference between the three is the weight vector. It repeats
|
| 286 |
+
for the four weighting ablations (`baseline_rl`, `advantage_clip`, `normalized_adv`,
|
| 287 |
+
`bc_wins`) and reports `CV_A`, `Abar`, `ESS/B`, the norm ratio and the cosine, plus a
|
| 288 |
+
shuffled-`delta` null that keeps the weight multiset and destroys its association with each
|
| 289 |
+
window's return. No training and no optimiser step occur; it runs on a laptop CPU.
|
| 290 |
+
|
| 291 |
+
Results land in `gdelta/` under the run's own output directory, beside `results.json`, and
|
| 292 |
+
the aggregate additionally produces `tables/gdelta.{csv,tex}`. With `--emit-tex-macros`,
|
| 293 |
+
the analysis pass picks the aggregate up and emits the measured quantities as `\rwGdelta*`
|
| 294 |
+
macros. Those are kept separate from the `\rwCvA*` macros, which recover `CV_A` from the
|
| 295 |
+
ESS logged during training: the two are measured on different batches and do not agree.
|
| 296 |
+
|
| 297 |
+
Config comes from `--results-path`, so the weight transforms measured are the ones that run
|
| 298 |
+
trained under; without it the standard layering applies.
|
| 299 |
+
|
| 300 |
+
**Reproduction (three rollout seeds, aggregated in one pass):**
|
| 301 |
+
```bash
|
| 302 |
+
python experiments/rl_finetuning/run_ablations.py --measure-gdelta --gdelta-seeds 0 1 2 \
|
| 303 |
+
--checkpoint checkpoints/online/Craftax-Classic-Symbolic-v1-Online-Diffusion-DAgger-100M \
|
| 304 |
+
--results-path experiments/rl_finetuning/outputs/craftax_classic_ablations/results.json \
|
| 305 |
+
--output-dir experiments/rl_finetuning/outputs/craftax_classic_ablations
|
| 306 |
+
```
|
| 307 |
+
|
| 308 |
+
Seeds run on separate machines are aggregated afterwards with `--gdelta-inputs`, the
|
| 309 |
+
counterpart to `--merge`:
|
| 310 |
+
```bash
|
| 311 |
+
python experiments/rl_finetuning/run_ablations.py --run-id gdelta \
|
| 312 |
+
--gdelta-inputs experiments/rl_finetuning/outputs/gdelta/gdelta_seed{0,1,2}.json
|
| 313 |
+
```
|
| 314 |
+
|
| 315 |
+
A single seed's `ratio_std_draws` / `cos_std_draws` are dispersions over that seed's eight
|
| 316 |
+
`(z_t, t)` draws. The aggregate averages the per-seed means and reports the standard
|
| 317 |
+
deviation **across seeds**, which is what the paper's table prints.
|
| 318 |
+
|
| 319 |
+
### CLI reference
|
| 320 |
+
|
| 321 |
+
| Flag | Description |
|
| 322 |
+
|---|---|
|
| 323 |
+
| `--checkpoint PATH` | Pretrained diffusion checkpoint, offline or DAgger (path or `wandb:` artifact) |
|
| 324 |
+
| `--config PATH` | Main pipeline config (default: `configs/defaults.yaml`) |
|
| 325 |
+
| `--ablations-config PATH` | Ablations config, layered on `ablations_default.yaml` (default: `ablations_default.yaml`) |
|
| 326 |
+
| `--all` | Run all 26 ablations |
|
| 327 |
+
| `--ablations NAME [NAME ...]` | Run specific ablations by name |
|
| 328 |
+
| `--list` | Print registered ablations and exit |
|
| 329 |
+
| `--fast` | Smoke-test overlay: `ablations_fast.yaml` applied last (`max_iter` 50, `num_envs` 16) |
|
| 330 |
+
| `--num-seeds N` | Seeds per ablation (overrides `num_seeds`, default 3) |
|
| 331 |
+
| `--seed N` | Base random seed |
|
| 332 |
+
| `--output-dir DIR` | Root output directory (default: `outputs/{run_id}/`) |
|
| 333 |
+
| `--run-id ID` | Run identifier (default: `run_{timestamp}`) |
|
| 334 |
+
| `--analyze-only` | Skip training, regenerate analysis from existing results |
|
| 335 |
+
| `--results-path PATH` | Explicit path to `results.json` (with `--analyze-only` or `--measure-gdelta`) |
|
| 336 |
+
| `--merge PATH [PATH ...]` | Merge multiple `results.json` files and regenerate analysis |
|
| 337 |
+
| `--measure-gdelta` | Measure the return term at the pretrained checkpoint; no training |
|
| 338 |
+
| `--gdelta-seeds N [N ...]` | Rollout seeds to measure (default `0`); the reported +/- is across these |
|
| 339 |
+
| `--gdelta-draws N` | Independent `(z_t, t)` draws per seed (default 8) |
|
| 340 |
+
| `--gdelta-inputs PATH [PATH ...]` | Aggregate per-seed gdelta JSONs from separate machines |
|
| 341 |
+
| `--emit-tex-macros` | Also write `tables/results.tex`, one `\newcommand` per headline number |
|
| 342 |
+
| `--action-dist` / `--no-action-dist` | Pre/post action-distribution analysis (default **on** here; **off** in the minihack twin) |
|
| 343 |
+
| `--use-wandb` / `--no-use-wandb` | Enable/disable W&B logging (overrides `use_wandb`, default `false`) |
|
| 344 |
+
| `--wandb-project NAME` | W&B project (default `remdm-planner-craftax-ablations`) |
|
| 345 |
+
| `--wandb-entity NAME` | W&B entity |
|
| 346 |
+
| `--max-iter N` | Override max training iterations |
|
| 347 |
+
| `--num-envs N` | Override rollout environments per iteration |
|
| 348 |
+
| `--batch-size N` | Override batch size |
|
| 349 |
+
| `--eval-every N` | Override evaluation frequency |
|
| 350 |
+
| `--lr FLOAT` | Override learning rate |
|
| 351 |
+
|
| 352 |
+
There is no `--override`: keys that are not flags are set in the config files.
|
| 353 |
|
| 354 |
### W&B logging
|
| 355 |
|
| 356 |
+
Three metrics per ablation, logged under `ablations/{name}/` against `iteration`:
|
| 357 |
+
`train_loss`, `env_score` (both every logged iteration) and `eval_score` (every
|
| 358 |
+
`eval_every`). They are written after the arm finishes, not during it, and there is
|
| 359 |
+
no `wandb.summary` write.
|
| 360 |
+
|
| 361 |
+
Every other quantity in the table below — gradient alignment, per-layer norms, KL
|
| 362 |
+
drift, CKA, the t-bin norms — is collected into `AblationHistory` and reaches
|
| 363 |
+
`results.json` only. Read those from the run directory, not from W&B.
|
| 364 |
|
| 365 |
### Diagnostic metrics collected
|
| 366 |
|
experiments/rl_finetuning/ablations/losses.py
CHANGED
|
@@ -380,6 +380,35 @@ def make_loss_bc_wins(ctx: LossContext) -> LossFn:
|
|
| 380 |
return loss_fn
|
| 381 |
|
| 382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
def make_loss_low_t(ctx: LossContext) -> LossFn:
|
| 384 |
"""Return-weighted ELBO restricted to t ∈ [ε, t_max_low].
|
| 385 |
|
|
|
|
| 380 |
return loss_fn
|
| 381 |
|
| 382 |
|
| 383 |
+
def make_loss_bc_all(ctx: LossContext) -> LossFn:
|
| 384 |
+
"""Uniform ELBO over every rollout window (BC on self-generated data).
|
| 385 |
+
|
| 386 |
+
The return weighting is dropped outright: ``advantages`` is ignored and
|
| 387 |
+
``compute_loss`` takes its plain batch mean, which is exactly a weight of
|
| 388 |
+
1.0 on every window. The batch is the one ``baseline_rl`` trains on --
|
| 389 |
+
same collection, same windows -- so the pair separates the on-policy data
|
| 390 |
+
distribution from the weighting applied to it, which no other arm does.
|
| 391 |
+
|
| 392 |
+
``bc_wins`` is the nearest neighbour and does not answer this: it keeps
|
| 393 |
+
only the winning windows, so it varies the data as well as the weights.
|
| 394 |
+
|
| 395 |
+
Hypothesis: if this degrades like ``baseline_rl``, fine-tuning on
|
| 396 |
+
self-generated rollouts is the cause and the weighting is incidental.
|
| 397 |
+
|
| 398 |
+
Args:
|
| 399 |
+
ctx: Shared loss context.
|
| 400 |
+
|
| 401 |
+
Returns:
|
| 402 |
+
``LossFn`` averaging uniformly over the whole batch.
|
| 403 |
+
"""
|
| 404 |
+
|
| 405 |
+
def loss_fn(params, acts, obs, valid, rng, advantages):
|
| 406 |
+
del advantages # uniform weights: the point of the arm
|
| 407 |
+
return _core_loss(ctx, params, rng, acts, obs, valid, None)
|
| 408 |
+
|
| 409 |
+
return loss_fn
|
| 410 |
+
|
| 411 |
+
|
| 412 |
def make_loss_low_t(ctx: LossContext) -> LossFn:
|
| 413 |
"""Return-weighted ELBO restricted to t ∈ [ε, t_max_low].
|
| 414 |
|
experiments/rl_finetuning/ablations/registry.py
CHANGED
|
@@ -16,6 +16,7 @@ from experiments.rl_finetuning.ablations.losses import (
|
|
| 16 |
LossFn,
|
| 17 |
make_loss_advantage_clip,
|
| 18 |
make_loss_baseline,
|
|
|
|
| 19 |
make_loss_bc_wins,
|
| 20 |
make_loss_entropy_bonus,
|
| 21 |
make_loss_ewc,
|
|
@@ -303,6 +304,15 @@ REGISTRY: dict[str, AblationSpec] = {
|
|
| 303 |
optimizer_factory=_std_opt,
|
| 304 |
wins_only=True,
|
| 305 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
"low_t": AblationSpec(
|
| 307 |
name="low_t",
|
| 308 |
group="B",
|
|
|
|
| 16 |
LossFn,
|
| 17 |
make_loss_advantage_clip,
|
| 18 |
make_loss_baseline,
|
| 19 |
+
make_loss_bc_all,
|
| 20 |
make_loss_bc_wins,
|
| 21 |
make_loss_entropy_bonus,
|
| 22 |
make_loss_ewc,
|
|
|
|
| 304 |
optimizer_factory=_std_opt,
|
| 305 |
wins_only=True,
|
| 306 |
),
|
| 307 |
+
"bc_all": AblationSpec(
|
| 308 |
+
name="bc_all",
|
| 309 |
+
group="B",
|
| 310 |
+
description="Uniform ELBO on all rollout windows (no advantage weighting)",
|
| 311 |
+
hypothesis="If it degrades like baseline_rl: the self-generated rollouts, "
|
| 312 |
+
"not the return weighting, are the cause",
|
| 313 |
+
loss_factory=make_loss_bc_all,
|
| 314 |
+
optimizer_factory=_std_opt,
|
| 315 |
+
),
|
| 316 |
"low_t": AblationSpec(
|
| 317 |
name="low_t",
|
| 318 |
group="B",
|
experiments/rl_finetuning/ablations/training.py
CHANGED
|
@@ -1549,7 +1549,7 @@ def run_ablation(
|
|
| 1549 |
rng: jax.Array,
|
| 1550 |
wandb_run: Any = None,
|
| 1551 |
output_dir: Any = None,
|
| 1552 |
-
) -> tuple[AblationHistory, float, Any]:
|
| 1553 |
"""Run one complete ablation (Python-level wrapper around make_run_ablation).
|
| 1554 |
|
| 1555 |
Handles EWC Fisher estimation (which requires rollouts before JIT),
|
|
@@ -1572,7 +1572,9 @@ def run_ablation(
|
|
| 1572 |
output_dir: Optional Path for per-iteration checkpoint saving.
|
| 1573 |
|
| 1574 |
Returns:
|
| 1575 |
-
Tuple of ``(history, final_score, final_params)``
|
|
|
|
|
|
|
| 1576 |
"""
|
| 1577 |
logger.info("=" * 60)
|
| 1578 |
logger.info("ABLATION: %s [Group %s]", spec.name, spec.group)
|
|
@@ -1684,13 +1686,14 @@ def run_ablation(
|
|
| 1684 |
final_params = jax.device_get(final_carry.ema_params)
|
| 1685 |
final_score = float(final_info.get("returned_episode_returns", jnp.array(0.0)))
|
| 1686 |
|
| 1687 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1688 |
final_ach = {
|
| 1689 |
k: float(v) / 100.0 for k, v in final_info.items() if "achievement" in k.lower()
|
| 1690 |
}
|
| 1691 |
-
# Overwrite the empty dicts in history with final eval achievements
|
| 1692 |
-
if history.per_achievement_rates:
|
| 1693 |
-
history.per_achievement_rates[-1] = final_ach
|
| 1694 |
|
| 1695 |
logger.info(" [%s] FINAL score: %.4f", spec.name, final_score)
|
| 1696 |
|
|
@@ -1713,4 +1716,4 @@ def run_ablation(
|
|
| 1713 |
}
|
| 1714 |
)
|
| 1715 |
|
| 1716 |
-
return history, final_score, final_params
|
|
|
|
| 1549 |
rng: jax.Array,
|
| 1550 |
wandb_run: Any = None,
|
| 1551 |
output_dir: Any = None,
|
| 1552 |
+
) -> tuple[AblationHistory, float, Any, dict[str, float]]:
|
| 1553 |
"""Run one complete ablation (Python-level wrapper around make_run_ablation).
|
| 1554 |
|
| 1555 |
Handles EWC Fisher estimation (which requires rollouts before JIT),
|
|
|
|
| 1572 |
output_dir: Optional Path for per-iteration checkpoint saving.
|
| 1573 |
|
| 1574 |
Returns:
|
| 1575 |
+
Tuple of ``(history, final_score, final_params, final_ach)``, where
|
| 1576 |
+
``final_ach`` is the per-achievement unlock rate of the same post-loop
|
| 1577 |
+
evaluation that produced ``final_score``.
|
| 1578 |
"""
|
| 1579 |
logger.info("=" * 60)
|
| 1580 |
logger.info("ABLATION: %s [Group %s]", spec.name, spec.group)
|
|
|
|
| 1686 |
final_params = jax.device_get(final_carry.ema_params)
|
| 1687 |
final_score = float(final_info.get("returned_episode_returns", jnp.array(0.0)))
|
| 1688 |
|
| 1689 |
+
# Per-achievement detail of the *same* evaluation that produced
|
| 1690 |
+
# `final_score`. It is returned separately rather than written over the
|
| 1691 |
+
# last entry of `history.per_achievement_rates`: that entry belongs to the
|
| 1692 |
+
# last in-loop evaluation, a different draw, and overwriting it made the
|
| 1693 |
+
# achievement tables and the headline score disagree by construction.
|
| 1694 |
final_ach = {
|
| 1695 |
k: float(v) / 100.0 for k, v in final_info.items() if "achievement" in k.lower()
|
| 1696 |
}
|
|
|
|
|
|
|
|
|
|
| 1697 |
|
| 1698 |
logger.info(" [%s] FINAL score: %.4f", spec.name, final_score)
|
| 1699 |
|
|
|
|
| 1716 |
}
|
| 1717 |
)
|
| 1718 |
|
| 1719 |
+
return history, final_score, final_params, final_ach
|
experiments/rl_finetuning/analysis/gdelta.py
ADDED
|
@@ -0,0 +1,548 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Measure the return term g_delta of the return-weighted ELBO decomposition.
|
| 2 |
+
|
| 3 |
+
Implements the measurement behind Eq. 8 and Table 6 of the paper. Writing the
|
| 4 |
+
per-window weight as A_i and the batch mean as Abar, the training gradient
|
| 5 |
+
decomposes exactly as
|
| 6 |
+
|
| 7 |
+
grad L_RW = Abar * ( grad L_BC + g_delta ),
|
| 8 |
+
g_delta = (1/B) sum_i delta_i grad l_i, delta_i = A_i/Abar - 1,
|
| 9 |
+
|
| 10 |
+
so g_delta carries the entire directional contribution of the return and Abar
|
| 11 |
+
is the scalar step-size rescaling. :func:`measure` loads a pretrained
|
| 12 |
+
checkpoint, collects one on-policy batch from it, and evaluates grad L_BC,
|
| 13 |
+
g_delta and grad L_RW on that batch at those parameters under a shared
|
| 14 |
+
(z_t, t) draw, so the only difference between the three is the weight vector.
|
| 15 |
+
It repeats for every weight transform the ablation suite uses, and reports
|
| 16 |
+
three references the cosine column needs:
|
| 17 |
+
|
| 18 |
+
* the random-direction null, cos ~ N(0, 1/sqrt(D)) for D parameters;
|
| 19 |
+
* cos(grad L_BC, grad L_BC) across two independent noise draws, which is the
|
| 20 |
+
value a direction attains when it *is* the imitation direction;
|
| 21 |
+
* the shuffled-delta null, which permutes delta across the batch. This keeps
|
| 22 |
+
the multiset of weights and hence CV_A, and destroys the association
|
| 23 |
+
between a window's weight and that window's gradient. Any part of the
|
| 24 |
+
measured ratio and cosine that survives the shuffle is batch
|
| 25 |
+
heterogeneity, not return signal.
|
| 26 |
+
|
| 27 |
+
No training and no optimiser step occur. Runs on CPU in a few minutes.
|
| 28 |
+
|
| 29 |
+
The per-draw standard deviations a single seed reports are *within* one rollout
|
| 30 |
+
seed. The figure Table 6 prints is the standard deviation across rollout seeds,
|
| 31 |
+
which :func:`aggregate` computes over the per-seed records.
|
| 32 |
+
|
| 33 |
+
Driven by ``run_ablations.py --measure-gdelta``; see :func:`run_gdelta`.
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
from __future__ import annotations
|
| 37 |
+
|
| 38 |
+
import logging
|
| 39 |
+
from pathlib import Path
|
| 40 |
+
|
| 41 |
+
import numpy as np
|
| 42 |
+
import orjson
|
| 43 |
+
|
| 44 |
+
logger = logging.getLogger(__name__)
|
| 45 |
+
|
| 46 |
+
# The variant -> ablation mapping this module assumes. Each entry names the
|
| 47 |
+
# registry key, the loss factory that key must still use, and its wins_only
|
| 48 |
+
# flag. verify_registry() fails loudly if the suite drifts away from it, so a
|
| 49 |
+
# registry edit cannot silently desynchronise the measurement.
|
| 50 |
+
REGISTRY_RULES = {
|
| 51 |
+
"baseline_clipped_ratio": ("baseline_rl", "make_loss_baseline", False),
|
| 52 |
+
"advantage_clip": ("advantage_clip", "make_loss_advantage_clip", False),
|
| 53 |
+
"normalized_adv": ("normalized_adv", "make_loss_normalized_adv", False),
|
| 54 |
+
"bc_wins": ("bc_wins", "make_loss_bc_wins", True),
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
BASELINE = "baseline_clipped_ratio"
|
| 58 |
+
|
| 59 |
+
GDELTA_DIRNAME = "gdelta"
|
| 60 |
+
AGGREGATE_FILENAME = "gdelta_aggregate.json"
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class RegistryDriftError(RuntimeError):
|
| 64 |
+
"""The ablation registry no longer matches the variants measured here."""
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def verify_registry() -> None:
|
| 68 |
+
"""Fail if the ablation registry no longer matches the assumed variants."""
|
| 69 |
+
from experiments.rl_finetuning.ablations.registry import REGISTRY
|
| 70 |
+
|
| 71 |
+
for variant, (name, factory, wins_only) in REGISTRY_RULES.items():
|
| 72 |
+
spec = REGISTRY.get(name)
|
| 73 |
+
if spec is None:
|
| 74 |
+
raise RegistryDriftError(
|
| 75 |
+
f"registry has no ablation {name!r}; variant {variant!r} is stale"
|
| 76 |
+
)
|
| 77 |
+
if spec.loss_factory.__name__ != factory:
|
| 78 |
+
raise RegistryDriftError(
|
| 79 |
+
f"ablation {name!r} now uses {spec.loss_factory.__name__}, "
|
| 80 |
+
f"not {factory}; variant {variant!r} measures a weighting the "
|
| 81 |
+
"trainer no longer applies"
|
| 82 |
+
)
|
| 83 |
+
if bool(spec.wins_only) != wins_only:
|
| 84 |
+
raise RegistryDriftError(
|
| 85 |
+
f"ablation {name!r} has wins_only={spec.wins_only}, expected "
|
| 86 |
+
f"{wins_only}; variant {variant!r} is stale"
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def build_variants(adv, returns, cfg, batch):
|
| 91 |
+
"""The weight vectors the suite's four weighting ablations apply.
|
| 92 |
+
|
| 93 |
+
``bc_wins`` is the binary win mask rescaled by ``B / n_wins``, which is the
|
| 94 |
+
vector ``make_loss_bc_wins`` builds internally from the mask that
|
| 95 |
+
``_compute_advantages(wins_only=True)`` hands it. The rescaling is what
|
| 96 |
+
turns ``compute_loss``'s batch mean into a uniform mean over the winners,
|
| 97 |
+
so it is part of the weight the trainer applies, not a normalisation added
|
| 98 |
+
here.
|
| 99 |
+
"""
|
| 100 |
+
import jax.numpy as jnp
|
| 101 |
+
|
| 102 |
+
eps = cfg["ADV_CLIP_EPS"]
|
| 103 |
+
win = (returns > cfg["WIN_THRESHOLD"]).astype(jnp.float32)
|
| 104 |
+
n_win = win.sum()
|
| 105 |
+
return {
|
| 106 |
+
BASELINE: adv,
|
| 107 |
+
"advantage_clip": jnp.clip(adv, 1.0 - eps, 1.0 + eps),
|
| 108 |
+
"normalized_adv": (adv - adv.mean()) / (adv.std() + 1e-8),
|
| 109 |
+
"bc_wins": win * jnp.where(n_win > 0, batch / jnp.maximum(n_win, 1.0), 0.0),
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def centred_delta(weights):
|
| 114 |
+
"""Return ``(delta, Abar, a1_holds)`` for a weight vector.
|
| 115 |
+
|
| 116 |
+
(A1) of the paper asks for non-negative weights with a strictly positive
|
| 117 |
+
mean. Where it holds, ``delta_i = A_i/Abar - 1`` and ``mean(delta) == 0``.
|
| 118 |
+
Where it does not -- ``normalized_adv`` mean-centres, so its weights are
|
| 119 |
+
signed and its mean vanishes -- the ratio is meaningless, and the caller is
|
| 120 |
+
told rather than handed a number that divides by a value near zero.
|
| 121 |
+
"""
|
| 122 |
+
import jax.numpy as jnp
|
| 123 |
+
|
| 124 |
+
wbar = float(jnp.mean(weights))
|
| 125 |
+
scale = float(jnp.max(jnp.abs(weights))) + 1e-12
|
| 126 |
+
a1_holds = bool(jnp.all(weights >= 0.0)) and wbar > 1e-6 * scale
|
| 127 |
+
if not a1_holds:
|
| 128 |
+
return weights - wbar, wbar, False
|
| 129 |
+
return weights / wbar - 1.0, wbar, True
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def against_bc(g, g_bc, norm_bc):
|
| 133 |
+
"""``(||g|| / ||g_BC||, cos(g, g_BC))`` for one gradient against imitation."""
|
| 134 |
+
import jax.numpy as jnp
|
| 135 |
+
|
| 136 |
+
norm = float(jnp.linalg.norm(g))
|
| 137 |
+
return norm / norm_bc, float(jnp.dot(g, g_bc) / (norm * norm_bc + 1e-12))
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def effective_sample_size(weights) -> float:
|
| 141 |
+
"""ESS as a fraction of the batch: (sum A)^2 / (B sum A^2)."""
|
| 142 |
+
import jax.numpy as jnp
|
| 143 |
+
|
| 144 |
+
total = float(jnp.sum(weights))
|
| 145 |
+
sq = float(jnp.sum(weights ** 2))
|
| 146 |
+
if sq <= 0.0:
|
| 147 |
+
return float("nan")
|
| 148 |
+
return total ** 2 / (sq * weights.shape[0])
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def restore_params(net, rng, obs_dim, plan_horizon, ckpt: str):
|
| 152 |
+
"""Restore Orbax parameters with an explicit single-device sharding.
|
| 153 |
+
|
| 154 |
+
``model.load_checkpoint`` builds its restore target with ``jax.eval_shape``,
|
| 155 |
+
whose leaves carry no sharding. Orbax >= 0.12 rejects that when the
|
| 156 |
+
checkpoint was written under a different device topology, which is the
|
| 157 |
+
common case for a checkpoint trained on GPU and inspected on CPU. Attaching
|
| 158 |
+
a concrete sharding here is the only difference from the library path.
|
| 159 |
+
"""
|
| 160 |
+
import jax
|
| 161 |
+
import orbax.checkpoint as ocp
|
| 162 |
+
|
| 163 |
+
from src.planners.model import _validate_restored_tree, abstract_params
|
| 164 |
+
|
| 165 |
+
sharding = jax.sharding.SingleDeviceSharding(jax.devices()[0])
|
| 166 |
+
abstract = abstract_params(net, rng, obs_dim, plan_horizon)
|
| 167 |
+
restore_args = jax.tree.map(
|
| 168 |
+
lambda x: ocp.ArrayRestoreArgs(sharding=sharding, dtype=x.dtype), abstract
|
| 169 |
+
)
|
| 170 |
+
with ocp.CheckpointManager(str(Path(ckpt).resolve())) as mgr:
|
| 171 |
+
step = mgr.latest_step()
|
| 172 |
+
restored = mgr.restore(
|
| 173 |
+
step,
|
| 174 |
+
args=ocp.args.PyTreeRestore(
|
| 175 |
+
item={"params": abstract},
|
| 176 |
+
restore_args={"params": restore_args},
|
| 177 |
+
partial_restore=True,
|
| 178 |
+
),
|
| 179 |
+
)
|
| 180 |
+
params = restored["params"]
|
| 181 |
+
_validate_restored_tree(params, abstract, ckpt)
|
| 182 |
+
return params, step
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def aggregate(blobs: list[dict], inputs: list[str] | None = None) -> dict:
|
| 186 |
+
"""Combine per-seed records, reporting the standard deviation across seeds.
|
| 187 |
+
|
| 188 |
+
Each input contributes one number per variant per column -- its own mean
|
| 189 |
+
over the ``(z_t, t)`` draws. The dispersion reported here is over those
|
| 190 |
+
per-seed means, which is the quantity the paper's table claims.
|
| 191 |
+
|
| 192 |
+
Args:
|
| 193 |
+
blobs: Per-seed records, as returned by :func:`measure`.
|
| 194 |
+
inputs: Optional provenance paths recorded in the output.
|
| 195 |
+
|
| 196 |
+
Returns:
|
| 197 |
+
The aggregate record.
|
| 198 |
+
"""
|
| 199 |
+
labels = inputs if inputs is not None else [
|
| 200 |
+
f"seed{b.get('seed', i)}" for i, b in enumerate(blobs)
|
| 201 |
+
]
|
| 202 |
+
for blob, label in zip(blobs, labels, strict=True):
|
| 203 |
+
if blob.get("aggregate"):
|
| 204 |
+
raise ValueError(f"{label} is already an aggregate")
|
| 205 |
+
|
| 206 |
+
names = list(blobs[0]["variants"])
|
| 207 |
+
for blob, label in zip(blobs, labels, strict=True):
|
| 208 |
+
if list(blob["variants"]) != names:
|
| 209 |
+
raise ValueError(f"{label} has a different variant set")
|
| 210 |
+
|
| 211 |
+
def across(values):
|
| 212 |
+
arr = np.array(values, dtype=float)
|
| 213 |
+
return float(arr.mean()), float(arr.std())
|
| 214 |
+
|
| 215 |
+
out = {
|
| 216 |
+
"aggregate": True,
|
| 217 |
+
"inputs": [str(p) for p in labels],
|
| 218 |
+
"seeds": [int(b["seed"]) for b in blobs],
|
| 219 |
+
"n_seeds": len(blobs),
|
| 220 |
+
"n_draws_per_seed": [int(b["n_draws"]) for b in blobs],
|
| 221 |
+
"n_params": int(blobs[0]["n_params"]),
|
| 222 |
+
"random_cos_sd": float(blobs[0]["random_cos_sd"]),
|
| 223 |
+
"batch": int(blobs[0]["batch"]),
|
| 224 |
+
"eq4_residual_max": float(max(b["eq4_residual_max"] for b in blobs)),
|
| 225 |
+
"variants": {},
|
| 226 |
+
}
|
| 227 |
+
out["bc_self_cos_mean"], out["bc_self_cos_std_seeds"] = across(
|
| 228 |
+
[b["bc_self_cos_mean"] for b in blobs]
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
columns = [
|
| 232 |
+
"cv_a", "abar", "abar_ratio_to_baseline", "ess_fraction",
|
| 233 |
+
"ratio_mean", "cos_mean", "ratio_shuffled_mean", "cos_shuffled_mean",
|
| 234 |
+
]
|
| 235 |
+
for name in names:
|
| 236 |
+
rec = {"a1_violated": any(b["variants"][name]["a1_violated"] for b in blobs)}
|
| 237 |
+
for col in columns:
|
| 238 |
+
mean, std = across([b["variants"][name][col] for b in blobs])
|
| 239 |
+
stem = col[:-5] if col.endswith("_mean") else col
|
| 240 |
+
rec[f"{stem}_mean"] = mean
|
| 241 |
+
rec[f"{stem}_std_seeds"] = std
|
| 242 |
+
out["variants"][name] = rec
|
| 243 |
+
return out
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def print_aggregate(agg: dict) -> None:
|
| 247 |
+
print(f"\naggregate over {agg['n_seeds']} rollout seeds {agg['seeds']}, "
|
| 248 |
+
f"{agg['n_draws_per_seed']} draws each; +/- is ACROSS SEEDS")
|
| 249 |
+
print(f"cos(grad L_BC, grad L_BC) = {agg['bc_self_cos_mean']:.3f} "
|
| 250 |
+
f"+/- {agg['bc_self_cos_std_seeds']:.3f}")
|
| 251 |
+
print(f"Eq. 4 identity, max relative residual = {agg['eq4_residual_max']:.2e}\n")
|
| 252 |
+
header = (f"{'weight transform':26s} {'CV_A':>7s} {'Abar':>8s} {'Abar/base':>10s} "
|
| 253 |
+
f"{'ESS/B':>7s} {'ratio':>16s} {'cos':>16s} "
|
| 254 |
+
f"{'ratio(shuf)':>16s} {'cos(shuf)':>16s}")
|
| 255 |
+
print(header)
|
| 256 |
+
for name, rec in agg["variants"].items():
|
| 257 |
+
flag = " [(A1) violated]" if rec["a1_violated"] else ""
|
| 258 |
+
print(f"{name:26s} {rec['cv_a_mean']:7.3f} {rec['abar_mean']:8.3f} "
|
| 259 |
+
f"{rec['abar_ratio_to_baseline_mean']:10.3f} "
|
| 260 |
+
f"{rec['ess_fraction_mean']:7.3f} "
|
| 261 |
+
f"{rec['ratio_mean']:9.3f} +/-{rec['ratio_std_seeds']:.3f} "
|
| 262 |
+
f"{rec['cos_mean']:+9.3f} +/-{rec['cos_std_seeds']:.3f} "
|
| 263 |
+
f"{rec['ratio_shuffled_mean']:9.3f} +/-{rec['ratio_shuffled_std_seeds']:.3f} "
|
| 264 |
+
f"{rec['cos_shuffled_mean']:+9.3f} +/-{rec['cos_shuffled_std_seeds']:.3f}"
|
| 265 |
+
f"{flag}")
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def measure(
|
| 269 |
+
config: dict,
|
| 270 |
+
ckpt: str,
|
| 271 |
+
*,
|
| 272 |
+
seed: int = 0,
|
| 273 |
+
n_draws: int = 8,
|
| 274 |
+
num_envs: int | None = None,
|
| 275 |
+
batch_size: int | None = None,
|
| 276 |
+
) -> dict:
|
| 277 |
+
"""Measure the decomposition at one rollout seed.
|
| 278 |
+
|
| 279 |
+
Args:
|
| 280 |
+
config: UPPERCASE config dict; the run's own, so the weight
|
| 281 |
+
transforms match the ones it trained under.
|
| 282 |
+
ckpt: Orbax checkpoint directory of the pretrained planner.
|
| 283 |
+
seed: Rollout seed. Also fixes the ``(z_t, t)`` draws.
|
| 284 |
+
n_draws: Independent ``(z_t, t)`` draws to average over.
|
| 285 |
+
num_envs: Override ``NUM_ENVS``; ``None`` keeps the config value.
|
| 286 |
+
batch_size: Override ``BATCH_SIZE``; ``None`` keeps the config value.
|
| 287 |
+
|
| 288 |
+
Returns:
|
| 289 |
+
The per-seed record.
|
| 290 |
+
"""
|
| 291 |
+
import jax
|
| 292 |
+
import jax.numpy as jnp
|
| 293 |
+
|
| 294 |
+
from experiments.rl_finetuning.ablations.training import (
|
| 295 |
+
_compute_advantages,
|
| 296 |
+
build_rollout_fn,
|
| 297 |
+
)
|
| 298 |
+
from src.diffusion.loss import compute_loss
|
| 299 |
+
from src.diffusion.schedules import SCHEDULE_MAP
|
| 300 |
+
from src.planners.env import make_env
|
| 301 |
+
from src.planners.model import build_model, make_apply_fns
|
| 302 |
+
|
| 303 |
+
verify_registry()
|
| 304 |
+
|
| 305 |
+
cfg = dict(config)
|
| 306 |
+
if num_envs is not None:
|
| 307 |
+
cfg["NUM_ENVS"] = num_envs
|
| 308 |
+
if batch_size is not None:
|
| 309 |
+
cfg["BATCH_SIZE"] = batch_size
|
| 310 |
+
|
| 311 |
+
env, env_params = make_env(cfg, cfg["NUM_ENVS"])
|
| 312 |
+
num_actions = env.action_space(env_params).n
|
| 313 |
+
obs_dim = env.observation_space(env_params).shape[0]
|
| 314 |
+
cfg["NUM_ACTIONS"] = num_actions
|
| 315 |
+
schedule_fn, schedule_deriv_fn = SCHEDULE_MAP[cfg.get("DIFFUSION_SCHEDULE", "cosine")]
|
| 316 |
+
|
| 317 |
+
net = build_model(cfg, num_actions)
|
| 318 |
+
apply_eval, apply_train = make_apply_fns(net)
|
| 319 |
+
|
| 320 |
+
rng = jax.random.PRNGKey(seed)
|
| 321 |
+
# A separate stream for the shuffled-delta null, so adding the control
|
| 322 |
+
# leaves every draw of the real measurement bit-for-bit unchanged.
|
| 323 |
+
perm_rng = jax.random.PRNGKey(seed + 10_000)
|
| 324 |
+
rng, key = jax.random.split(rng)
|
| 325 |
+
params, step = restore_params(net, key, obs_dim, cfg["PLAN_HORIZON"], ckpt)
|
| 326 |
+
n_params = sum(int(np.prod(x.shape)) for x in jax.tree.leaves(params))
|
| 327 |
+
random_cos_sd = 1.0 / np.sqrt(n_params)
|
| 328 |
+
print(f"checkpoint step {step}, D = {n_params/1e6:.2f}M, "
|
| 329 |
+
f"random-cosine null sd = {random_cos_sd:.2e}", flush=True)
|
| 330 |
+
|
| 331 |
+
# ---- one on-policy batch from the pretrained policy ----
|
| 332 |
+
collect = build_rollout_fn(env, env_params, apply_eval, cfg, obs_dim)
|
| 333 |
+
rng, key = jax.random.split(rng)
|
| 334 |
+
obs0, state0 = env.reset(key, env_params)
|
| 335 |
+
done0 = jnp.zeros(cfg["NUM_ENVS"], dtype=bool)
|
| 336 |
+
rng, key = jax.random.split(rng)
|
| 337 |
+
_, _, _, _, f_obs, f_acts, f_valid, f_ret, _ = collect(
|
| 338 |
+
params, state0, obs0, done0, key
|
| 339 |
+
)
|
| 340 |
+
adv, _, _ = _compute_advantages(
|
| 341 |
+
f_ret,
|
| 342 |
+
cfg["RETURN_WEIGHT_FLOOR"],
|
| 343 |
+
cfg["RETURN_WEIGHT_CAP"],
|
| 344 |
+
wins_only=False,
|
| 345 |
+
win_thresh=cfg["WIN_THRESHOLD"],
|
| 346 |
+
use_running_stats=False,
|
| 347 |
+
ema_decay=0.99,
|
| 348 |
+
running_mean=jnp.array(0.0),
|
| 349 |
+
running_std=jnp.array(1.0),
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
batch = min(cfg["BATCH_SIZE"], f_obs.shape[0])
|
| 353 |
+
rng, key = jax.random.split(rng)
|
| 354 |
+
idx = jax.random.permutation(key, f_obs.shape[0])[:batch]
|
| 355 |
+
obs_b, act_b, val_b = f_obs[idx], f_acts[idx], f_valid[idx]
|
| 356 |
+
adv_b, ret_b = adv[idx], f_ret[idx]
|
| 357 |
+
print(f"batch {batch} windows, win rate {float((ret_b > cfg['WIN_THRESHOLD']).mean()):.3f}",
|
| 358 |
+
flush=True)
|
| 359 |
+
|
| 360 |
+
variants = build_variants(adv_b, ret_b, cfg, batch)
|
| 361 |
+
|
| 362 |
+
def flatten(tree):
|
| 363 |
+
return jnp.concatenate([x.ravel() for x in jax.tree.leaves(tree)])
|
| 364 |
+
|
| 365 |
+
def gradient(weights, key):
|
| 366 |
+
def loss(p):
|
| 367 |
+
value, _ = compute_loss(
|
| 368 |
+
apply_train, p, key, act_b, obs_b, val_b, num_actions,
|
| 369 |
+
schedule_fn, schedule_deriv_fn,
|
| 370 |
+
sigma_t=cfg.get("TRAIN_SIGMA", 0.0),
|
| 371 |
+
label_smoothing=cfg.get("LABEL_SMOOTHING", 0.0),
|
| 372 |
+
advantages=weights,
|
| 373 |
+
)
|
| 374 |
+
return value
|
| 375 |
+
return flatten(jax.grad(loss)(params))
|
| 376 |
+
|
| 377 |
+
deltas = {}
|
| 378 |
+
stats = {}
|
| 379 |
+
for name, weights in variants.items():
|
| 380 |
+
delta, wbar, a1_holds = centred_delta(weights)
|
| 381 |
+
deltas[name] = delta
|
| 382 |
+
stats[name] = {
|
| 383 |
+
"cv_a": float(jnp.sqrt(jnp.mean(delta ** 2))),
|
| 384 |
+
"abar": wbar,
|
| 385 |
+
"ess_fraction": effective_sample_size(weights),
|
| 386 |
+
"a1_violated": not a1_holds,
|
| 387 |
+
}
|
| 388 |
+
base_abar = stats[BASELINE]["abar"]
|
| 389 |
+
for rec in stats.values():
|
| 390 |
+
rec["abar_ratio_to_baseline"] = rec["abar"] / base_abar
|
| 391 |
+
|
| 392 |
+
acc = {name: {"ratio": [], "cos": [], "ratio_shuf": [], "cos_shuf": []}
|
| 393 |
+
for name in variants}
|
| 394 |
+
bc_self, residuals = [], []
|
| 395 |
+
for draw in range(n_draws):
|
| 396 |
+
rng, key = jax.random.split(rng)
|
| 397 |
+
g_bc = gradient(None, key)
|
| 398 |
+
norm_bc = float(jnp.linalg.norm(g_bc))
|
| 399 |
+
|
| 400 |
+
rng, key2 = jax.random.split(rng)
|
| 401 |
+
g_bc2 = gradient(None, key2) # same objective, independent noise draw
|
| 402 |
+
bc_self.append(
|
| 403 |
+
float(jnp.dot(g_bc, g_bc2) / (norm_bc * jnp.linalg.norm(g_bc2) + 1e-12))
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
+
for name, weights in variants.items():
|
| 407 |
+
delta = deltas[name]
|
| 408 |
+
g_delta = gradient(delta, key)
|
| 409 |
+
ratio, cos = against_bc(g_delta, g_bc, norm_bc)
|
| 410 |
+
acc[name]["ratio"].append(ratio)
|
| 411 |
+
acc[name]["cos"].append(cos)
|
| 412 |
+
|
| 413 |
+
# Shuffled-delta null: same multiset of weights, same CV_A, the
|
| 414 |
+
# association with each window's own return destroyed. The (z_t, t)
|
| 415 |
+
# key is the one the real measurement used, so the two differ only
|
| 416 |
+
# in which window carries which weight.
|
| 417 |
+
perm_rng, perm_key = jax.random.split(perm_rng)
|
| 418 |
+
shuffled = jax.random.permutation(perm_key, delta)
|
| 419 |
+
ratio_s, cos_s = against_bc(gradient(shuffled, key), g_bc, norm_bc)
|
| 420 |
+
acc[name]["ratio_shuf"].append(ratio_s)
|
| 421 |
+
acc[name]["cos_shuf"].append(cos_s)
|
| 422 |
+
|
| 423 |
+
if name == BASELINE:
|
| 424 |
+
# Eq. 4 identity check: grad L_RW == Abar * (grad L_BC + g_delta)
|
| 425 |
+
g_rw = gradient(weights, key)
|
| 426 |
+
residuals.append(float(
|
| 427 |
+
jnp.linalg.norm(g_rw - stats[name]["abar"] * (g_bc + g_delta))
|
| 428 |
+
/ (jnp.linalg.norm(g_rw) + 1e-12)
|
| 429 |
+
))
|
| 430 |
+
print(f" draw {draw + 1}/{n_draws}", flush=True)
|
| 431 |
+
|
| 432 |
+
out = {
|
| 433 |
+
"aggregate": False,
|
| 434 |
+
"checkpoint_step": int(step),
|
| 435 |
+
"n_params": int(n_params),
|
| 436 |
+
"random_cos_sd": float(random_cos_sd),
|
| 437 |
+
"batch": int(batch),
|
| 438 |
+
"seed": seed,
|
| 439 |
+
"n_draws": n_draws,
|
| 440 |
+
"bc_self_cos_mean": float(np.mean(bc_self)),
|
| 441 |
+
"bc_self_cos_std": float(np.std(bc_self)),
|
| 442 |
+
"eq4_residual_max": float(np.max(residuals)),
|
| 443 |
+
"variants": {},
|
| 444 |
+
}
|
| 445 |
+
print(f"\ncos(grad L_BC, grad L_BC) across draws = "
|
| 446 |
+
f"{np.mean(bc_self):.3f} +/- {np.std(bc_self):.3f} [same-objective reference]")
|
| 447 |
+
print(f"random-direction null: cos ~ N(0, {random_cos_sd:.2e})")
|
| 448 |
+
print(f"Eq. 4 identity, max relative residual = {np.max(residuals):.2e}")
|
| 449 |
+
print(f"+/- below is across the {n_draws} (z_t, t) draws of this one seed, "
|
| 450 |
+
"not across seeds\n")
|
| 451 |
+
print(f"{'weight transform':26s} {'CV_A':>7s} {'Abar':>8s} {'Abar/base':>10s} "
|
| 452 |
+
f"{'ESS/B':>7s} {'ratio':>16s} {'cos':>16s} "
|
| 453 |
+
f"{'ratio(shuf)':>16s} {'cos(shuf)':>16s}")
|
| 454 |
+
for name, rec in acc.items():
|
| 455 |
+
ratio, cos = np.array(rec["ratio"]), np.array(rec["cos"])
|
| 456 |
+
ratio_s, cos_s = np.array(rec["ratio_shuf"]), np.array(rec["cos_shuf"])
|
| 457 |
+
out["variants"][name] = {
|
| 458 |
+
**stats[name],
|
| 459 |
+
"ratio_mean": float(ratio.mean()), "ratio_std_draws": float(ratio.std()),
|
| 460 |
+
"cos_mean": float(cos.mean()), "cos_std_draws": float(cos.std()),
|
| 461 |
+
"ratio_shuffled_mean": float(ratio_s.mean()),
|
| 462 |
+
"ratio_shuffled_std": float(ratio_s.std()),
|
| 463 |
+
"cos_shuffled_mean": float(cos_s.mean()),
|
| 464 |
+
"cos_shuffled_std": float(cos_s.std()),
|
| 465 |
+
}
|
| 466 |
+
flag = " [(A1) violated]" if stats[name]["a1_violated"] else ""
|
| 467 |
+
print(f"{name:26s} {stats[name]['cv_a']:7.3f} {stats[name]['abar']:8.3f} "
|
| 468 |
+
f"{stats[name]['abar_ratio_to_baseline']:10.3f} "
|
| 469 |
+
f"{stats[name]['ess_fraction']:7.3f} "
|
| 470 |
+
f"{ratio.mean():9.3f} +/-{ratio.std():.3f} "
|
| 471 |
+
f"{cos.mean():+9.3f} +/-{cos.std():.3f} "
|
| 472 |
+
f"{ratio_s.mean():9.3f} +/-{ratio_s.std():.3f} "
|
| 473 |
+
f"{cos_s.mean():+9.3f} +/-{cos_s.std():.3f}{flag}")
|
| 474 |
+
return out
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
def _write(path: Path, blob: dict) -> None:
|
| 478 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 479 |
+
path.write_bytes(orjson.dumps(blob, option=orjson.OPT_INDENT_2))
|
| 480 |
+
logger.info("Wrote %s", path)
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def load_gdelta_aggregate(output_dir: Path) -> dict | None:
|
| 484 |
+
"""The aggregate record for a run, or ``None`` if it was never measured."""
|
| 485 |
+
path = Path(output_dir) / GDELTA_DIRNAME / AGGREGATE_FILENAME
|
| 486 |
+
if not path.is_file():
|
| 487 |
+
return None
|
| 488 |
+
return orjson.loads(path.read_bytes())
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
def run_gdelta(
|
| 492 |
+
config: dict,
|
| 493 |
+
ckpt: str,
|
| 494 |
+
output_dir: Path,
|
| 495 |
+
*,
|
| 496 |
+
seeds: list[int],
|
| 497 |
+
n_draws: int = 8,
|
| 498 |
+
num_envs: int | None = None,
|
| 499 |
+
batch_size: int | None = None,
|
| 500 |
+
) -> dict:
|
| 501 |
+
"""Measure every seed and write the per-seed and aggregate records.
|
| 502 |
+
|
| 503 |
+
Artifacts land in ``output_dir/gdelta/``, beside the run's ``results.json``,
|
| 504 |
+
so one run directory holds the suite's scores and the gradient measurement
|
| 505 |
+
taken at the checkpoint they started from.
|
| 506 |
+
|
| 507 |
+
Args:
|
| 508 |
+
config: UPPERCASE config dict.
|
| 509 |
+
ckpt: Orbax checkpoint directory of the pretrained planner.
|
| 510 |
+
output_dir: The run's root output directory.
|
| 511 |
+
seeds: Rollout seeds to measure.
|
| 512 |
+
n_draws: Independent ``(z_t, t)`` draws per seed.
|
| 513 |
+
num_envs: Override ``NUM_ENVS``; ``None`` keeps the config value.
|
| 514 |
+
batch_size: Override ``BATCH_SIZE``; ``None`` keeps the config value.
|
| 515 |
+
|
| 516 |
+
Returns:
|
| 517 |
+
The aggregate record.
|
| 518 |
+
"""
|
| 519 |
+
gdelta_dir = Path(output_dir) / GDELTA_DIRNAME
|
| 520 |
+
blobs, labels = [], []
|
| 521 |
+
for seed in seeds:
|
| 522 |
+
logger.info("Measuring g_delta at rollout seed %d", seed)
|
| 523 |
+
blob = measure(
|
| 524 |
+
config,
|
| 525 |
+
ckpt,
|
| 526 |
+
seed=seed,
|
| 527 |
+
n_draws=n_draws,
|
| 528 |
+
num_envs=num_envs,
|
| 529 |
+
batch_size=batch_size,
|
| 530 |
+
)
|
| 531 |
+
path = gdelta_dir / f"gdelta_seed{seed}.json"
|
| 532 |
+
_write(path, blob)
|
| 533 |
+
blobs.append(blob)
|
| 534 |
+
labels.append(str(path))
|
| 535 |
+
|
| 536 |
+
agg = aggregate(blobs, labels)
|
| 537 |
+
_write(gdelta_dir / AGGREGATE_FILENAME, agg)
|
| 538 |
+
print_aggregate(agg)
|
| 539 |
+
return agg
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
def aggregate_files(paths: list[str], output_dir: Path) -> dict:
|
| 543 |
+
"""Aggregate per-seed records written elsewhere, e.g. by another machine."""
|
| 544 |
+
blobs = [orjson.loads(Path(p).read_bytes()) for p in paths]
|
| 545 |
+
agg = aggregate(blobs, [str(p) for p in paths])
|
| 546 |
+
_write(Path(output_dir) / GDELTA_DIRNAME / AGGREGATE_FILENAME, agg)
|
| 547 |
+
print_aggregate(agg)
|
| 548 |
+
return agg
|
experiments/rl_finetuning/analysis/plots.py
CHANGED
|
@@ -791,10 +791,12 @@ def plot_achievement_breakdown(
|
|
| 791 |
names, values in [0, 1]).
|
| 792 |
output_dir: Output directory for the figure.
|
| 793 |
"""
|
| 794 |
-
# Collect ablations
|
|
|
|
|
|
|
| 795 |
valid: list[tuple[str, dict[str, float], dict[str, float]]] = []
|
| 796 |
for name, res in results.items():
|
| 797 |
-
rates = res["history"].per_achievement_rates
|
| 798 |
if len(rates) >= 2:
|
| 799 |
valid.append((name, rates[0], rates[-1]))
|
| 800 |
|
|
|
|
| 791 |
names, values in [0, 1]).
|
| 792 |
output_dir: Output directory for the figure.
|
| 793 |
"""
|
| 794 |
+
# Collect ablations with at least two *populated* eval checkpoints. Empty
|
| 795 |
+
# entries are checkpoints where the scan could not return variable-key
|
| 796 |
+
# dicts, not evaluations that unlocked nothing.
|
| 797 |
valid: list[tuple[str, dict[str, float], dict[str, float]]] = []
|
| 798 |
for name, res in results.items():
|
| 799 |
+
rates = [r for r in res["history"].per_achievement_rates if r]
|
| 800 |
if len(rates) >= 2:
|
| 801 |
valid.append((name, rates[0], rates[-1]))
|
| 802 |
|
experiments/rl_finetuning/analysis/tables.py
CHANGED
|
@@ -15,6 +15,10 @@ import polars as pl
|
|
| 15 |
|
| 16 |
from experiments.rl_finetuning.ablations.registry import REGISTRY
|
| 17 |
from experiments.rl_finetuning.ablations.training import AblationHistory
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
logger = logging.getLogger(__name__)
|
| 20 |
|
|
@@ -100,6 +104,26 @@ def baseline_rl_score_of(results: dict[str, dict], pretrained_score: float) -> f
|
|
| 100 |
return results.get("baseline_rl", {}).get("score", pretrained_score)
|
| 101 |
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
def _latex_escape(text: str) -> str:
|
| 104 |
"""Escape LaTeX special characters in a string.
|
| 105 |
|
|
@@ -267,13 +291,13 @@ def write_significance_test(results: dict[str, dict], out_dir: Path) -> None:
|
|
| 267 |
f"bootstrap 95% CI of the difference (10000 resamples, seed 0): "
|
| 268 |
f"[{lo:.4f}, {hi:.4f}]\n"
|
| 269 |
+ (
|
| 270 |
-
f"arms excluded from the max (seed count != {n_b}): "
|
| 271 |
-
f"{', '.join(dropped)}\n"
|
| 272 |
if dropped
|
| 273 |
else ""
|
| 274 |
)
|
| 275 |
)
|
| 276 |
|
|
|
|
| 277 |
def make_main_results_table(
|
| 278 |
results: dict[str, dict],
|
| 279 |
pretrained_score: float,
|
|
@@ -369,6 +393,47 @@ def make_gradient_analysis_table(
|
|
| 369 |
return pl.DataFrame(rows)
|
| 370 |
|
| 371 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 372 |
def make_t_distribution_table(
|
| 373 |
results: dict[str, dict],
|
| 374 |
) -> pl.DataFrame:
|
|
@@ -592,8 +657,7 @@ def make_per_env_table(
|
|
| 592 |
"""
|
| 593 |
ablation_finals: dict[str, dict[str, float]] = {}
|
| 594 |
for name, res in results.items():
|
| 595 |
-
|
| 596 |
-
ablation_finals[name] = rates[-1] if rates else {}
|
| 597 |
|
| 598 |
all_keys: list[str] = sorted(
|
| 599 |
set(pretrained_ach_rates) | {k for d in ablation_finals.values() for k in d}
|
|
@@ -645,9 +709,7 @@ def make_hypothesis_verdict_table(
|
|
| 645 |
result = verdict(score, baseline_rl_score, pretrained_score)
|
| 646 |
conclusion = {
|
| 647 |
"IMPROVEMENT": "Hypothesis SUPPORTED — this intervention helps",
|
| 648 |
-
"COLLAPSE": (
|
| 649 |
-
"Hypothesis REFUTED — intervention did not prevent collapse"
|
| 650 |
-
),
|
| 651 |
"NEUTRAL": "Inconclusive — no significant change",
|
| 652 |
}[result]
|
| 653 |
|
|
@@ -685,8 +747,7 @@ def make_achievement_table(
|
|
| 685 |
# Collect final achievement rates for every ablation.
|
| 686 |
ablation_finals: dict[str, dict[str, float]] = {}
|
| 687 |
for name, res in results.items():
|
| 688 |
-
|
| 689 |
-
ablation_finals[name] = rates[-1] if rates else {}
|
| 690 |
|
| 691 |
# Union of all achievement keys.
|
| 692 |
all_keys: list[str] = sorted(
|
|
@@ -709,11 +770,262 @@ def make_achievement_table(
|
|
| 709 |
return pl.DataFrame(rows)
|
| 710 |
|
| 711 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
def generate_summary_tables(
|
| 713 |
results: dict[str, dict],
|
| 714 |
pretrained_score: float,
|
| 715 |
output_dir: Path,
|
| 716 |
pretrained_ach_rates: dict[str, float] | None = None,
|
|
|
|
|
|
|
| 717 |
) -> dict[str, pl.DataFrame]:
|
| 718 |
"""Generate all summary tables and save to output_dir/tables/.
|
| 719 |
|
|
@@ -727,6 +1039,15 @@ def generate_summary_tables(
|
|
| 727 |
pretrained_ach_rates: Optional per-achievement unlock rates for the pretrained
|
| 728 |
baseline (keys = achievement name, values in [0, 1]).
|
| 729 |
When provided, a per-achievement summary table is generated.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 730 |
|
| 731 |
Returns:
|
| 732 |
Dict mapping table name -> polars DataFrame.
|
|
@@ -818,5 +1139,20 @@ def generate_summary_tables(
|
|
| 818 |
label="tab:per_env",
|
| 819 |
)
|
| 820 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 821 |
logger.info("All tables saved to %s", tables_dir)
|
| 822 |
return tables
|
|
|
|
| 15 |
|
| 16 |
from experiments.rl_finetuning.ablations.registry import REGISTRY
|
| 17 |
from experiments.rl_finetuning.ablations.training import AblationHistory
|
| 18 |
+
from experiments.rl_finetuning.analysis.gdelta import (
|
| 19 |
+
REGISTRY_RULES,
|
| 20 |
+
load_gdelta_aggregate,
|
| 21 |
+
)
|
| 22 |
|
| 23 |
logger = logging.getLogger(__name__)
|
| 24 |
|
|
|
|
| 104 |
return results.get("baseline_rl", {}).get("score", pretrained_score)
|
| 105 |
|
| 106 |
|
| 107 |
+
def final_ach_rates(res: dict) -> dict[str, float]:
|
| 108 |
+
"""Per-achievement rates of the evaluation that produced ``score``.
|
| 109 |
+
|
| 110 |
+
``final_ach_rates`` is the detail of the post-loop evaluation the headline
|
| 111 |
+
score comes from, so tables built on it reconcile with the score by
|
| 112 |
+
construction. Suite runs from before that field existed wrote the same
|
| 113 |
+
evaluation over the last in-loop history entry; those are read from there.
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
res: One entry of a results dict.
|
| 117 |
+
|
| 118 |
+
Returns:
|
| 119 |
+
``{achievement: rate}``, empty when neither source has any.
|
| 120 |
+
"""
|
| 121 |
+
if res.get("final_ach_rates"):
|
| 122 |
+
return res["final_ach_rates"]
|
| 123 |
+
rates = res["history"].per_achievement_rates
|
| 124 |
+
return rates[-1] if rates else {}
|
| 125 |
+
|
| 126 |
+
|
| 127 |
def _latex_escape(text: str) -> str:
|
| 128 |
"""Escape LaTeX special characters in a string.
|
| 129 |
|
|
|
|
| 291 |
f"bootstrap 95% CI of the difference (10000 resamples, seed 0): "
|
| 292 |
f"[{lo:.4f}, {hi:.4f}]\n"
|
| 293 |
+ (
|
| 294 |
+
f"arms excluded from the max (seed count != {n_b}): {', '.join(dropped)}\n"
|
|
|
|
| 295 |
if dropped
|
| 296 |
else ""
|
| 297 |
)
|
| 298 |
)
|
| 299 |
|
| 300 |
+
|
| 301 |
def make_main_results_table(
|
| 302 |
results: dict[str, dict],
|
| 303 |
pretrained_score: float,
|
|
|
|
| 393 |
return pl.DataFrame(rows)
|
| 394 |
|
| 395 |
|
| 396 |
+
def make_gdelta_table(agg: dict) -> pl.DataFrame:
|
| 397 |
+
"""The gradient-decomposition table: one row per weight transform.
|
| 398 |
+
|
| 399 |
+
Columns follow the paper's ``tab:gdelta``. Every ``+/-`` is the standard
|
| 400 |
+
deviation ACROSS ROLLOUT SEEDS, which is what :func:`gdelta.aggregate`
|
| 401 |
+
computes; a single seed's dispersion over its own ``(z_t, t)`` draws is a
|
| 402 |
+
different and smaller quantity and is not reported here.
|
| 403 |
+
|
| 404 |
+
``normalized_adv`` mean-centres its weights, so (A1) fails and its ratio
|
| 405 |
+
divides by a mean near zero. The row is kept with an explicit flag rather
|
| 406 |
+
than dropped, because its absence would read as an omission.
|
| 407 |
+
|
| 408 |
+
Args:
|
| 409 |
+
agg: The aggregate record from ``gdelta/gdelta_aggregate.json``.
|
| 410 |
+
|
| 411 |
+
Returns:
|
| 412 |
+
Polars DataFrame.
|
| 413 |
+
"""
|
| 414 |
+
rows = []
|
| 415 |
+
for name, rec in agg["variants"].items():
|
| 416 |
+
rows.append(
|
| 417 |
+
{
|
| 418 |
+
"Transform": name,
|
| 419 |
+
"CV_A": round(rec["cv_a_mean"], 3),
|
| 420 |
+
"Abar": round(rec["abar_mean"], 3),
|
| 421 |
+
"Abar_Ratio_Baseline": round(rec["abar_ratio_to_baseline_mean"], 3),
|
| 422 |
+
"ESS_Fraction": round(rec["ess_fraction_mean"], 3),
|
| 423 |
+
"Ratio": round(rec["ratio_mean"], 3),
|
| 424 |
+
"Ratio_Sd": round(rec["ratio_std_seeds"], 3),
|
| 425 |
+
"Cos": round(rec["cos_mean"], 3),
|
| 426 |
+
"Cos_Sd": round(rec["cos_std_seeds"], 3),
|
| 427 |
+
"Ratio_Shuffled": round(rec["ratio_shuffled_mean"], 3),
|
| 428 |
+
"Ratio_Shuffled_Sd": round(rec["ratio_shuffled_std_seeds"], 3),
|
| 429 |
+
"Cos_Shuffled": round(rec["cos_shuffled_mean"], 3),
|
| 430 |
+
"Cos_Shuffled_Sd": round(rec["cos_shuffled_std_seeds"], 3),
|
| 431 |
+
"A1_Violated": bool(rec["a1_violated"]),
|
| 432 |
+
}
|
| 433 |
+
)
|
| 434 |
+
return pl.DataFrame(rows)
|
| 435 |
+
|
| 436 |
+
|
| 437 |
def make_t_distribution_table(
|
| 438 |
results: dict[str, dict],
|
| 439 |
) -> pl.DataFrame:
|
|
|
|
| 657 |
"""
|
| 658 |
ablation_finals: dict[str, dict[str, float]] = {}
|
| 659 |
for name, res in results.items():
|
| 660 |
+
ablation_finals[name] = final_ach_rates(res)
|
|
|
|
| 661 |
|
| 662 |
all_keys: list[str] = sorted(
|
| 663 |
set(pretrained_ach_rates) | {k for d in ablation_finals.values() for k in d}
|
|
|
|
| 709 |
result = verdict(score, baseline_rl_score, pretrained_score)
|
| 710 |
conclusion = {
|
| 711 |
"IMPROVEMENT": "Hypothesis SUPPORTED — this intervention helps",
|
| 712 |
+
"COLLAPSE": ("Hypothesis REFUTED — intervention did not prevent collapse"),
|
|
|
|
|
|
|
| 713 |
"NEUTRAL": "Inconclusive — no significant change",
|
| 714 |
}[result]
|
| 715 |
|
|
|
|
| 747 |
# Collect final achievement rates for every ablation.
|
| 748 |
ablation_finals: dict[str, dict[str, float]] = {}
|
| 749 |
for name, res in results.items():
|
| 750 |
+
ablation_finals[name] = final_ach_rates(res)
|
|
|
|
| 751 |
|
| 752 |
# Union of all achievement keys.
|
| 753 |
all_keys: list[str] = sorted(
|
|
|
|
| 770 |
return pl.DataFrame(rows)
|
| 771 |
|
| 772 |
|
| 773 |
+
_DIGIT_WORDS = (
|
| 774 |
+
"Zero",
|
| 775 |
+
"One",
|
| 776 |
+
"Two",
|
| 777 |
+
"Three",
|
| 778 |
+
"Four",
|
| 779 |
+
"Five",
|
| 780 |
+
"Six",
|
| 781 |
+
"Seven",
|
| 782 |
+
"Eight",
|
| 783 |
+
"Nine",
|
| 784 |
+
)
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
def _macro_name(*parts: str) -> str:
|
| 788 |
+
"""A TeX-legal control sequence name built from arbitrary identifiers.
|
| 789 |
+
|
| 790 |
+
``\\newcommand`` names may contain letters only, so underscores and
|
| 791 |
+
hyphens become word boundaries and digits are spelled out:
|
| 792 |
+
``layer_ablation_top1`` -> ``LayerAblationTopOne``.
|
| 793 |
+
"""
|
| 794 |
+
out = []
|
| 795 |
+
for part in parts:
|
| 796 |
+
for word in str(part).replace("-", "_").split("_"):
|
| 797 |
+
if not word:
|
| 798 |
+
continue
|
| 799 |
+
chars = [_DIGIT_WORDS[int(c)] if c.isdigit() else c for c in word]
|
| 800 |
+
joined = "".join(chars)
|
| 801 |
+
out.append(joined[0].upper() + joined[1:])
|
| 802 |
+
return "".join(out)
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
def _cv_a(ess: float, batch: int) -> float:
|
| 806 |
+
"""Weight dispersion recovered from an effective sample size."""
|
| 807 |
+
if not batch or ess <= 0:
|
| 808 |
+
return float("nan")
|
| 809 |
+
return float(np.sqrt(max(batch / ess - 1.0, 0.0)))
|
| 810 |
+
|
| 811 |
+
|
| 812 |
+
def write_gdelta_table(agg: dict, output_dir: Path) -> pl.DataFrame:
|
| 813 |
+
"""Write ``tables/gdelta.{csv,tex}`` for one aggregate record."""
|
| 814 |
+
df = make_gdelta_table(agg)
|
| 815 |
+
_save_table(
|
| 816 |
+
df,
|
| 817 |
+
Path(output_dir) / "tables" / "gdelta",
|
| 818 |
+
caption="Return-term decomposition per weight transform.",
|
| 819 |
+
label="tab:gdelta",
|
| 820 |
+
)
|
| 821 |
+
return df
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
def _define_gdelta_macros(agg: dict, define) -> None:
|
| 825 |
+
"""Emit the measured decomposition quantities as ``\\newcommand``s.
|
| 826 |
+
|
| 827 |
+
Each variant is tagged by its ABLATION name, not its gdelta variant name,
|
| 828 |
+
so ``baseline_clipped_ratio`` becomes ``BaselineRl`` and sits beside the
|
| 829 |
+
``Score`` and ``Ess`` macros for the same condition. The ``Gdelta`` infix
|
| 830 |
+
keeps these clear of the ESS-derived ``CvA`` macros, which measure a
|
| 831 |
+
different thing on different batches.
|
| 832 |
+
"""
|
| 833 |
+
define("GdeltaNSeeds", value=str(int(agg["n_seeds"])))
|
| 834 |
+
define("GdeltaNParams", value=f"{agg['n_params'] / 1e6:.2f}")
|
| 835 |
+
define("GdeltaRandomCosSd", value=f"{agg['random_cos_sd']:.1e}")
|
| 836 |
+
define("GdeltaBatch", value=str(int(agg["batch"])))
|
| 837 |
+
define("GdeltaBcSelfCos", value=f"{agg['bc_self_cos_mean']:.3f}")
|
| 838 |
+
define("GdeltaBcSelfCosSd", value=f"{agg['bc_self_cos_std_seeds']:.3f}")
|
| 839 |
+
define("GdeltaEqFourResidual", value=f"{agg['eq4_residual_max']:.1e}")
|
| 840 |
+
|
| 841 |
+
for variant, rec in agg["variants"].items():
|
| 842 |
+
rule = REGISTRY_RULES.get(variant)
|
| 843 |
+
tag = _macro_name(rule[0] if rule else variant)
|
| 844 |
+
define("GdeltaCvA", tag=tag, value=f"{rec['cv_a_mean']:.2f}")
|
| 845 |
+
define("GdeltaAbar", tag=tag, value=f"{rec['abar_mean']:.3f}")
|
| 846 |
+
define(
|
| 847 |
+
"GdeltaAbarRatio", tag=tag,
|
| 848 |
+
value=f"{rec['abar_ratio_to_baseline_mean']:.3f}",
|
| 849 |
+
)
|
| 850 |
+
define("GdeltaEss", tag=tag, value=f"{rec['ess_fraction_mean']:.2f}")
|
| 851 |
+
define("GdeltaRatio", tag=tag, value=f"{rec['ratio_mean']:.3f}")
|
| 852 |
+
define("GdeltaRatioSd", tag=tag, value=f"{rec['ratio_std_seeds']:.3f}")
|
| 853 |
+
define("GdeltaCos", tag=tag, value=f"{rec['cos_mean']:.3f}")
|
| 854 |
+
define("GdeltaCosSd", tag=tag, value=f"{rec['cos_std_seeds']:.3f}")
|
| 855 |
+
define(
|
| 856 |
+
"GdeltaRatioShuf", tag=tag,
|
| 857 |
+
value=f"{rec['ratio_shuffled_mean']:.3f}",
|
| 858 |
+
)
|
| 859 |
+
define(
|
| 860 |
+
"GdeltaRatioShufSd", tag=tag,
|
| 861 |
+
value=f"{rec['ratio_shuffled_std_seeds']:.3f}",
|
| 862 |
+
)
|
| 863 |
+
define("GdeltaCosShuf", tag=tag, value=f"{rec['cos_shuffled_mean']:.3f}")
|
| 864 |
+
define(
|
| 865 |
+
"GdeltaCosShufSd", tag=tag,
|
| 866 |
+
value=f"{rec['cos_shuffled_std_seeds']:.3f}",
|
| 867 |
+
)
|
| 868 |
+
|
| 869 |
+
|
| 870 |
+
def write_tex_macros(
|
| 871 |
+
results: dict[str, dict],
|
| 872 |
+
pretrained_score: float,
|
| 873 |
+
out_path: Path,
|
| 874 |
+
config: dict | None = None,
|
| 875 |
+
prefix: str = "rw",
|
| 876 |
+
scale: float = 1.0,
|
| 877 |
+
gdelta: dict | None = None,
|
| 878 |
+
) -> Path:
|
| 879 |
+
"""Write ``\\newcommand`` definitions for every headline quantity.
|
| 880 |
+
|
| 881 |
+
The paper workspace expects generated numbers to reach the manuscript
|
| 882 |
+
through macros so each is traceable to the run that produced it, rather
|
| 883 |
+
than being retyped as a literal.
|
| 884 |
+
|
| 885 |
+
Each quantity is emitted at the number of decimals the manuscript prints
|
| 886 |
+
it with, not at a uniform significant-figure count. A macro is a literal
|
| 887 |
+
substitution: ``%.4g`` would render ``\\rwGroupMeanA`` as ``9.744`` where
|
| 888 |
+
the paper prints ``9.74``, silently changing the displayed precision at
|
| 889 |
+
every site. Scores, standard deviations and group statistics take two
|
| 890 |
+
decimals; effective sample size takes none, which is how Section 6.4
|
| 891 |
+
quotes it.
|
| 892 |
+
|
| 893 |
+
Args:
|
| 894 |
+
results: Dict mapping ablation_name -> result entry.
|
| 895 |
+
pretrained_score: Pretrained model eval score.
|
| 896 |
+
out_path: File to write, conventionally ``results.tex``.
|
| 897 |
+
config: The run's config, read for ``BATCH_SIZE`` so
|
| 898 |
+
:math:`\\mathrm{CV}_A` and ESS can be emitted.
|
| 899 |
+
prefix: Macro-name prefix, keeping these out of the way of
|
| 900 |
+
LaTeX's own names. Two suites sharing one
|
| 901 |
+
``results.tex`` need different prefixes.
|
| 902 |
+
gdelta: Optional aggregate record from
|
| 903 |
+
``gdelta/gdelta_aggregate.json``. When present, the
|
| 904 |
+
measured decomposition quantities are emitted under a
|
| 905 |
+
``Gdelta`` infix. They are deliberately NOT merged
|
| 906 |
+
into the ``CvA`` macros above: those recover
|
| 907 |
+
:math:`\\mathrm{CV}_A` from the ESS logged during
|
| 908 |
+
training, whereas these are measured on the
|
| 909 |
+
measurement batches at the pretrained checkpoint. The
|
| 910 |
+
two differ, and the manuscript quotes both.
|
| 911 |
+
scale: Multiplier on score-like quantities before
|
| 912 |
+
formatting, for a suite whose metric is a fraction
|
| 913 |
+
the paper reports as a percentage. Never applied to
|
| 914 |
+
ESS or :math:`\\mathrm{CV}_A`, which are unitless.
|
| 915 |
+
|
| 916 |
+
Returns:
|
| 917 |
+
The path written.
|
| 918 |
+
"""
|
| 919 |
+
cfg = config or {}
|
| 920 |
+
batch = int(cfg.get("BATCH_SIZE") or cfg.get("batch_size") or 0)
|
| 921 |
+
|
| 922 |
+
def score(value: float, decimals: int = 2) -> str:
|
| 923 |
+
return f"{float(value) * scale:.{decimals}f}"
|
| 924 |
+
|
| 925 |
+
lines = [
|
| 926 |
+
"% Generated by experiments/rl_finetuning/analysis/tables.py.",
|
| 927 |
+
"% Do not hand-edit: regenerate with --emit-tex-macros.",
|
| 928 |
+
"",
|
| 929 |
+
]
|
| 930 |
+
defined: dict[str, str] = {}
|
| 931 |
+
|
| 932 |
+
def define(*parts: str, value: str, tag: str = "") -> None:
|
| 933 |
+
"""Emit one ``\\newcommand``, refusing to redefine a name.
|
| 934 |
+
|
| 935 |
+
The mangling in :func:`_macro_name` is lossy -- ``top1`` and
|
| 936 |
+
``top_one`` both become ``TopOne`` -- so two conditions can collide.
|
| 937 |
+
Silently overwriting would put a number under a name that reads as
|
| 938 |
+
another condition's, which is exactly the failure the macro
|
| 939 |
+
convention exists to prevent.
|
| 940 |
+
"""
|
| 941 |
+
macro = f"{prefix}{''.join(parts)}{tag}"
|
| 942 |
+
if macro in defined:
|
| 943 |
+
raise ValueError(
|
| 944 |
+
f"macro name collision: \\{macro} would be defined twice "
|
| 945 |
+
f"(existing value {defined[macro]!r}, new value {value!r}). "
|
| 946 |
+
f"_macro_name() mangles distinct identifiers onto one TeX "
|
| 947 |
+
f"control sequence; rename the condition or extend the rule."
|
| 948 |
+
)
|
| 949 |
+
defined[macro] = value
|
| 950 |
+
lines.append(f"\\newcommand{{\\{macro}}}{{{value}}}")
|
| 951 |
+
|
| 952 |
+
define("PretrainedScore", value=score(pretrained_score))
|
| 953 |
+
if batch:
|
| 954 |
+
define("BatchSize", value=str(batch))
|
| 955 |
+
|
| 956 |
+
within_seed_var: list[float] = []
|
| 957 |
+
baseline = (
|
| 958 |
+
float(results["baseline_rl"]["score"]) if "baseline_rl" in results else None
|
| 959 |
+
)
|
| 960 |
+
for name in sorted(results):
|
| 961 |
+
res = results[name]
|
| 962 |
+
tag = _macro_name(name)
|
| 963 |
+
define("Score", tag=tag, value=score(res["score"]))
|
| 964 |
+
# Magnitude only: the manuscript carries the sign as $-$ / $+$.
|
| 965 |
+
define(
|
| 966 |
+
"DeltaPretrained",
|
| 967 |
+
tag=tag,
|
| 968 |
+
value=score(abs(float(res["score"]) - pretrained_score)),
|
| 969 |
+
)
|
| 970 |
+
if baseline is not None:
|
| 971 |
+
define(
|
| 972 |
+
"DeltaBaseline",
|
| 973 |
+
tag=tag,
|
| 974 |
+
value=score(abs(float(res["score"]) - baseline)),
|
| 975 |
+
)
|
| 976 |
+
define("ScoreSd", tag=tag, value=score(res.get("score_std", 0.0)))
|
| 977 |
+
scores = [float(s) for s in res.get("all_scores", [])]
|
| 978 |
+
if len(scores) >= 2:
|
| 979 |
+
within_seed_var.append(float(np.var(scores, ddof=1)))
|
| 980 |
+
|
| 981 |
+
ess_series = res["history"].effective_batch_size
|
| 982 |
+
if ess_series:
|
| 983 |
+
ess = float(np.mean(ess_series))
|
| 984 |
+
define("Ess", tag=tag, value=f"{ess:.0f}")
|
| 985 |
+
if batch:
|
| 986 |
+
cv = float(np.mean([_cv_a(e, batch) for e in ess_series if e > 0]))
|
| 987 |
+
define("CvA", tag=tag, value=f"{cv:.2f}")
|
| 988 |
+
|
| 989 |
+
# Pooled within-condition sd across seeds: the seed noise a single
|
| 990 |
+
# condition's score carries, not the spread between conditions.
|
| 991 |
+
if within_seed_var:
|
| 992 |
+
pooled = float(np.sqrt(np.mean(within_seed_var)))
|
| 993 |
+
define("PooledSeedSd", value=score(pooled))
|
| 994 |
+
|
| 995 |
+
group_df = make_group_summary_table(results)
|
| 996 |
+
for row in group_df.iter_rows(named=True):
|
| 997 |
+
tag = _macro_name(row["Group"])
|
| 998 |
+
for col, suffix in (
|
| 999 |
+
("Mean", "GroupMean"),
|
| 1000 |
+
("Best", "GroupBest"),
|
| 1001 |
+
("Worst", "GroupWorst"),
|
| 1002 |
+
("StdDev", "GroupSd"),
|
| 1003 |
+
):
|
| 1004 |
+
define(suffix, tag=tag, value=score(row[col]))
|
| 1005 |
+
define("GroupN", tag=tag, value=str(int(row["N"])))
|
| 1006 |
+
if baseline is not None:
|
| 1007 |
+
define(
|
| 1008 |
+
"GroupDelta",
|
| 1009 |
+
tag=tag,
|
| 1010 |
+
value=score(abs(float(row["Mean"]) - baseline)),
|
| 1011 |
+
)
|
| 1012 |
+
|
| 1013 |
+
if gdelta:
|
| 1014 |
+
_define_gdelta_macros(gdelta, define)
|
| 1015 |
+
|
| 1016 |
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
| 1017 |
+
out_path.write_text("\n".join(lines) + "\n")
|
| 1018 |
+
logger.info("Saved %s (%d macros)", out_path, len(defined))
|
| 1019 |
+
return out_path
|
| 1020 |
+
|
| 1021 |
+
|
| 1022 |
def generate_summary_tables(
|
| 1023 |
results: dict[str, dict],
|
| 1024 |
pretrained_score: float,
|
| 1025 |
output_dir: Path,
|
| 1026 |
pretrained_ach_rates: dict[str, float] | None = None,
|
| 1027 |
+
emit_tex_macros: bool = False,
|
| 1028 |
+
config: dict | None = None,
|
| 1029 |
) -> dict[str, pl.DataFrame]:
|
| 1030 |
"""Generate all summary tables and save to output_dir/tables/.
|
| 1031 |
|
|
|
|
| 1039 |
pretrained_ach_rates: Optional per-achievement unlock rates for the pretrained
|
| 1040 |
baseline (keys = achievement name, values in [0, 1]).
|
| 1041 |
When provided, a per-achievement summary table is generated.
|
| 1042 |
+
emit_tex_macros: Also write ``tables/results.tex``, one
|
| 1043 |
+
``\\newcommand`` per headline quantity, for the
|
| 1044 |
+
manuscript to cite instead of a literal.
|
| 1045 |
+
config: The run's config, needed by the macro file for
|
| 1046 |
+
``BATCH_SIZE``.
|
| 1047 |
+
|
| 1048 |
+
A ``gdelta/gdelta_aggregate.json`` in ``output_dir``, written by
|
| 1049 |
+
``run_ablations.py --measure-gdelta``, additionally produces
|
| 1050 |
+
``tables/gdelta.{csv,tex}`` and the measured decomposition macros.
|
| 1051 |
|
| 1052 |
Returns:
|
| 1053 |
Dict mapping table name -> polars DataFrame.
|
|
|
|
| 1139 |
label="tab:per_env",
|
| 1140 |
)
|
| 1141 |
|
| 1142 |
+
# Present only once the gradient measurement has been run for this run
|
| 1143 |
+
# directory; its absence is the normal state and not an error.
|
| 1144 |
+
gdelta_agg = load_gdelta_aggregate(output_dir)
|
| 1145 |
+
if gdelta_agg is not None:
|
| 1146 |
+
tables["gdelta"] = write_gdelta_table(gdelta_agg, output_dir)
|
| 1147 |
+
|
| 1148 |
+
if emit_tex_macros:
|
| 1149 |
+
write_tex_macros(
|
| 1150 |
+
results,
|
| 1151 |
+
pretrained_score,
|
| 1152 |
+
tables_dir / "results.tex",
|
| 1153 |
+
config=config,
|
| 1154 |
+
gdelta=gdelta_agg,
|
| 1155 |
+
)
|
| 1156 |
+
|
| 1157 |
logger.info("All tables saved to %s", tables_dir)
|
| 1158 |
return tables
|
experiments/rl_finetuning/configs/ablations_final_craftax_classic_gpu_24gb.yaml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
# =============================================================================
|
| 2 |
-
# RL Fine-Tuning Ablation Suite — Craftax Classic,
|
| 3 |
# Matches checkpoint produced by: configs/final_craftax_classic_gpu_24gb.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 42)
|
| 5 |
-
# Target hardware: RTX 3090 Ti (24 GB VRAM)
|
| 6 |
# That shape is the ablations_default.yaml architecture, so it is inherited
|
| 7 |
# rather than restated here. This file holds only its own deltas; every
|
| 8 |
# other key comes from the base, which is layered on automatically.
|
|
|
|
| 1 |
# =============================================================================
|
| 2 |
+
# RL Fine-Tuning Ablation Suite — Craftax Classic, GPU-24GB hardware
|
| 3 |
# Matches checkpoint produced by: configs/final_craftax_classic_gpu_24gb.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 42)
|
| 5 |
+
# Target hardware: GPU-24GB RTX 3090 Ti (24 GB VRAM)
|
| 6 |
# That shape is the ablations_default.yaml architecture, so it is inherited
|
| 7 |
# rather than restated here. This file holds only its own deltas; every
|
| 8 |
# other key comes from the base, which is layered on automatically.
|
experiments/rl_finetuning/configs/ablations_final_craftax_classic_gpu_h200.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# =============================================================================
|
| 2 |
-
# RL Fine-Tuning Ablation Suite — Craftax Classic, H200 hardware
|
| 3 |
# Matches checkpoint produced by: configs/final_craftax_classic_gpu_h200.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 43)
|
| 5 |
# Target hardware: H200 8 GB partition
|
|
|
|
| 1 |
# =============================================================================
|
| 2 |
+
# RL Fine-Tuning Ablation Suite — Craftax Classic, GPU-H200 hardware
|
| 3 |
# Matches checkpoint produced by: configs/final_craftax_classic_gpu_h200.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 43)
|
| 5 |
# Target hardware: H200 8 GB partition
|
experiments/rl_finetuning/configs/ablations_final_craftax_gpu_24gb.yaml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
# =============================================================================
|
| 2 |
-
# RL Fine-Tuning Ablation Suite — Full Craftax,
|
| 3 |
# Matches checkpoints produced by: configs/final_craftax_gpu_24gb.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 42)
|
| 5 |
-
# Target hardware:
|
| 6 |
# That shape is the ablations_default.yaml architecture, so it is inherited
|
| 7 |
# rather than restated here. This file holds only its own deltas; every
|
| 8 |
# other key comes from the base, which is layered on automatically.
|
|
|
|
| 1 |
# =============================================================================
|
| 2 |
+
# RL Fine-Tuning Ablation Suite — Full Craftax, GPU-24GB hardware
|
| 3 |
# Matches checkpoints produced by: configs/final_craftax_gpu_24gb.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 42)
|
| 5 |
+
# Target hardware: GPU-24GB reference machine (GPU model unrecorded) (24 GB VRAM)
|
| 6 |
# That shape is the ablations_default.yaml architecture, so it is inherited
|
| 7 |
# rather than restated here. This file holds only its own deltas; every
|
| 8 |
# other key comes from the base, which is layered on automatically.
|
experiments/rl_finetuning/configs/ablations_final_craftax_gpu_h200.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# =============================================================================
|
| 2 |
-
# RL Fine-Tuning Ablation Suite — Full Craftax, H200 hardware
|
| 3 |
# Matches checkpoints produced by: configs/final_craftax_gpu_h200.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 43)
|
| 5 |
# Target hardware: H200 8 GB partition
|
|
|
|
| 1 |
# =============================================================================
|
| 2 |
+
# RL Fine-Tuning Ablation Suite — Full Craftax, GPU-H200 hardware
|
| 3 |
# Matches checkpoints produced by: configs/final_craftax_gpu_h200.yaml
|
| 4 |
# (d_model=384, n_heads=8, n_layers=6, d_ff=768, plan_horizon=32, seed 43)
|
| 5 |
# Target hardware: H200 8 GB partition
|
experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_aggregate.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"aggregate": true,
|
| 3 |
+
"inputs": [
|
| 4 |
+
"experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed0.json",
|
| 5 |
+
"experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed1.json",
|
| 6 |
+
"experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed2.json"
|
| 7 |
+
],
|
| 8 |
+
"seeds": [
|
| 9 |
+
0,
|
| 10 |
+
1,
|
| 11 |
+
2
|
| 12 |
+
],
|
| 13 |
+
"n_seeds": 3,
|
| 14 |
+
"n_draws_per_seed": [
|
| 15 |
+
8,
|
| 16 |
+
8,
|
| 17 |
+
8
|
| 18 |
+
],
|
| 19 |
+
"n_params": 9334289,
|
| 20 |
+
"random_cos_sd": 0.000327310078675855,
|
| 21 |
+
"batch": 1024,
|
| 22 |
+
"eq4_residual_max": 4.80532605706685e-7,
|
| 23 |
+
"variants": {
|
| 24 |
+
"baseline_clipped_ratio": {
|
| 25 |
+
"a1_violated": false,
|
| 26 |
+
"cv_a_mean": 0.9854568839073181,
|
| 27 |
+
"cv_a_std_seeds": 0.018878114308338592,
|
| 28 |
+
"abar_mean": 1.0235402981440227,
|
| 29 |
+
"abar_std_seeds": 0.010061440877455132,
|
| 30 |
+
"abar_ratio_to_baseline_mean": 1.0,
|
| 31 |
+
"abar_ratio_to_baseline_std_seeds": 0.0,
|
| 32 |
+
"ess_fraction_mean": 0.507413487651479,
|
| 33 |
+
"ess_fraction_std_seeds": 0.009639155208801934,
|
| 34 |
+
"ratio_mean": 0.48452565418889765,
|
| 35 |
+
"ratio_std_seeds": 0.006983698107912942,
|
| 36 |
+
"cos_mean": 0.023888170331095655,
|
| 37 |
+
"cos_std_seeds": 0.055179809801403155,
|
| 38 |
+
"ratio_shuffled_mean": 0.3560985313002038,
|
| 39 |
+
"ratio_shuffled_std_seeds": 0.01911878846489955,
|
| 40 |
+
"cos_shuffled_mean": -0.05353597580688074,
|
| 41 |
+
"cos_shuffled_std_seeds": 0.025540160726829325
|
| 42 |
+
},
|
| 43 |
+
"advantage_clip": {
|
| 44 |
+
"a1_violated": false,
|
| 45 |
+
"cv_a_mean": 0.2014569491147995,
|
| 46 |
+
"cv_a_std_seeds": 0.0004557291260598919,
|
| 47 |
+
"abar_mean": 0.9526590506235758,
|
| 48 |
+
"abar_std_seeds": 0.009015712817429673,
|
| 49 |
+
"abar_ratio_to_baseline_mean": 0.9307592739286493,
|
| 50 |
+
"abar_ratio_to_baseline_std_seeds": 0.003565955741888492,
|
| 51 |
+
"ess_fraction_mean": 0.9609977946615301,
|
| 52 |
+
"ess_fraction_std_seeds": 0.00016941930355432837,
|
| 53 |
+
"ratio_mean": 0.09726429819029546,
|
| 54 |
+
"ratio_std_seeds": 0.0032068849286760316,
|
| 55 |
+
"cos_mean": -0.002687329212979724,
|
| 56 |
+
"cos_std_seeds": 0.06825983346564395,
|
| 57 |
+
"ratio_shuffled_mean": 0.07327280714136783,
|
| 58 |
+
"ratio_shuffled_std_seeds": 0.002498156230349314,
|
| 59 |
+
"cos_shuffled_mean": -0.03329448491179695,
|
| 60 |
+
"cos_shuffled_std_seeds": 0.052956390835973914
|
| 61 |
+
},
|
| 62 |
+
"normalized_adv": {
|
| 63 |
+
"a1_violated": true,
|
| 64 |
+
"cv_a_mean": 0.9999999205271403,
|
| 65 |
+
"cv_a_std_seeds": 7.434005313662571e-8,
|
| 66 |
+
"abar_mean": -1.0322158535321554e-8,
|
| 67 |
+
"abar_std_seeds": 3.960053117034971e-8,
|
| 68 |
+
"abar_ratio_to_baseline_mean": -1.0236111820250936e-8,
|
| 69 |
+
"abar_ratio_to_baseline_std_seeds": 3.8875732176752924e-8,
|
| 70 |
+
"ess_fraction_mean": 1.6747491186726309e-15,
|
| 71 |
+
"ess_fraction_std_seeds": 9.26030088934509e-16,
|
| 72 |
+
"ratio_mean": 0.4919759005782294,
|
| 73 |
+
"ratio_std_seeds": 0.016081065492987393,
|
| 74 |
+
"cos_mean": 0.023888198743710138,
|
| 75 |
+
"cos_std_seeds": 0.05517983637811973,
|
| 76 |
+
"ratio_shuffled_mean": 0.357488476116716,
|
| 77 |
+
"ratio_shuffled_std_seeds": 0.020395976400501593,
|
| 78 |
+
"cos_shuffled_mean": 0.05980315369864305,
|
| 79 |
+
"cos_shuffled_std_seeds": 0.04091382822120188
|
| 80 |
+
},
|
| 81 |
+
"bc_wins": {
|
| 82 |
+
"a1_violated": false,
|
| 83 |
+
"cv_a_mean": 0.7747447292009989,
|
| 84 |
+
"cv_a_std_seeds": 0.021514876051124374,
|
| 85 |
+
"abar_mean": 1.0,
|
| 86 |
+
"abar_std_seeds": 1.7547132305955582e-7,
|
| 87 |
+
"abar_ratio_to_baseline_mean": 0.9770956797109048,
|
| 88 |
+
"abar_ratio_to_baseline_std_seeds": 0.009621438085986775,
|
| 89 |
+
"ess_fraction_mean": 0.624999973036458,
|
| 90 |
+
"ess_fraction_std_seeds": 0.013053179236684295,
|
| 91 |
+
"ratio_mean": 0.4170324476063991,
|
| 92 |
+
"ratio_std_seeds": 0.024545794473638916,
|
| 93 |
+
"cos_mean": 0.1420223995228298,
|
| 94 |
+
"cos_std_seeds": 0.09300934862546119,
|
| 95 |
+
"ratio_shuffled_mean": 0.2839805669591658,
|
| 96 |
+
"ratio_shuffled_std_seeds": 0.007360032818748407,
|
| 97 |
+
"cos_shuffled_mean": 0.014593212322021524,
|
| 98 |
+
"cos_shuffled_std_seeds": 0.056382858532646296
|
| 99 |
+
}
|
| 100 |
+
},
|
| 101 |
+
"bc_self_cos_mean": 0.892872134844462,
|
| 102 |
+
"bc_self_cos_std_seeds": 0.009867953837605352
|
| 103 |
+
}
|
experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed0.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"aggregate": false,
|
| 3 |
+
"checkpoint_step": 40370176,
|
| 4 |
+
"n_params": 9334289,
|
| 5 |
+
"random_cos_sd": 0.000327310078675855,
|
| 6 |
+
"batch": 1024,
|
| 7 |
+
"seed": 0,
|
| 8 |
+
"n_draws": 8,
|
| 9 |
+
"bc_self_cos_mean": 0.8886517286300659,
|
| 10 |
+
"bc_self_cos_std": 0.013864377043282333,
|
| 11 |
+
"eq4_residual_max": 4.80532605706685e-7,
|
| 12 |
+
"variants": {
|
| 13 |
+
"baseline_clipped_ratio": {
|
| 14 |
+
"cv_a": 0.9587592482566833,
|
| 15 |
+
"abar": 1.0352622270584106,
|
| 16 |
+
"ess_fraction": 0.5210452873754741,
|
| 17 |
+
"a1_violated": false,
|
| 18 |
+
"abar_ratio_to_baseline": 1.0,
|
| 19 |
+
"ratio_mean": 0.4929142524131889,
|
| 20 |
+
"ratio_std_draws": 0.062149263585443426,
|
| 21 |
+
"cos_mean": -0.05232078512199223,
|
| 22 |
+
"cos_std_draws": 0.12823055031892652,
|
| 23 |
+
"ratio_shuffled_mean": 0.3702785550029438,
|
| 24 |
+
"ratio_shuffled_std": 0.04985673247875491,
|
| 25 |
+
"cos_shuffled_mean": -0.07264717968791956,
|
| 26 |
+
"cos_shuffled_std": 0.1375778261666584
|
| 27 |
+
},
|
| 28 |
+
"advantage_clip": {
|
| 29 |
+
"cv_a": 0.2019623965024948,
|
| 30 |
+
"abar": 0.9651404619216919,
|
| 31 |
+
"ess_fraction": 0.9608097961720935,
|
| 32 |
+
"a1_violated": false,
|
| 33 |
+
"abar_ratio_to_baseline": 0.9322666631661406,
|
| 34 |
+
"ratio_mean": 0.10178494489910378,
|
| 35 |
+
"ratio_std_draws": 0.010863613185205834,
|
| 36 |
+
"cos_mean": -0.08856321198982187,
|
| 37 |
+
"cos_std_draws": 0.09348871621494285,
|
| 38 |
+
"ratio_shuffled_mean": 0.07580454134313921,
|
| 39 |
+
"ratio_shuffled_std": 0.006146976066602656,
|
| 40 |
+
"cos_shuffled_mean": -0.08668288914486766,
|
| 41 |
+
"cos_shuffled_std": 0.10151970823488679
|
| 42 |
+
},
|
| 43 |
+
"normalized_adv": {
|
| 44 |
+
"cv_a": 0.9999999403953552,
|
| 45 |
+
"abar": -2.1653249859809875e-8,
|
| 46 |
+
"ess_fraction": 4.688632853842156e-16,
|
| 47 |
+
"a1_violated": true,
|
| 48 |
+
"abar_ratio_to_baseline": -2.0915715162655282e-8,
|
| 49 |
+
"ratio_mean": 0.5141167874551692,
|
| 50 |
+
"ratio_std_draws": 0.06482259643725917,
|
| 51 |
+
"cos_mean": -0.05232076484389836,
|
| 52 |
+
"cos_std_draws": 0.12823054295881925,
|
| 53 |
+
"ratio_shuffled_mean": 0.38073488366371927,
|
| 54 |
+
"ratio_shuffled_std": 0.024812852770472734,
|
| 55 |
+
"cos_shuffled_mean": 0.06606726971222088,
|
| 56 |
+
"cos_shuffled_std": 0.16606576384474356
|
| 57 |
+
},
|
| 58 |
+
"bc_wins": {
|
| 59 |
+
"cv_a": 0.7458090782165527,
|
| 60 |
+
"abar": 0.9999998211860657,
|
| 61 |
+
"ess_fraction": 0.6425777397495268,
|
| 62 |
+
"a1_violated": false,
|
| 63 |
+
"abar_ratio_to_baseline": 0.9659386723955539,
|
| 64 |
+
"ratio_mean": 0.3823743698913481,
|
| 65 |
+
"ratio_std_draws": 0.04196826406011548,
|
| 66 |
+
"cos_mean": 0.02126681269146502,
|
| 67 |
+
"cos_std_draws": 0.12931455515444024,
|
| 68 |
+
"ratio_shuffled_mean": 0.2905509104827731,
|
| 69 |
+
"ratio_shuffled_std": 0.0079822831305291,
|
| 70 |
+
"cos_shuffled_mean": 0.0010593719780445099,
|
| 71 |
+
"cos_shuffled_std": 0.22469821243454166
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
}
|
experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed1.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"aggregate": false,
|
| 3 |
+
"checkpoint_step": 40370176,
|
| 4 |
+
"n_params": 9334289,
|
| 5 |
+
"random_cos_sd": 0.000327310078675855,
|
| 6 |
+
"batch": 1024,
|
| 7 |
+
"seed": 1,
|
| 8 |
+
"n_draws": 8,
|
| 9 |
+
"bc_self_cos_mean": 0.8834625333547592,
|
| 10 |
+
"bc_self_cos_std": 0.028890878138939563,
|
| 11 |
+
"eq4_residual_max": 4.458028683984594e-7,
|
| 12 |
+
"variants": {
|
| 13 |
+
"baseline_clipped_ratio": {
|
| 14 |
+
"cv_a": 0.9988502860069275,
|
| 15 |
+
"abar": 1.0246647596359253,
|
| 16 |
+
"ess_fraction": 0.5005752960258663,
|
| 17 |
+
"a1_violated": false,
|
| 18 |
+
"abar_ratio_to_baseline": 1.0,
|
| 19 |
+
"ratio_mean": 0.48484595625152527,
|
| 20 |
+
"ratio_std_draws": 0.03739208589573637,
|
| 21 |
+
"cos_mean": 0.047454304061830044,
|
| 22 |
+
"cos_std_draws": 0.06307783640351931,
|
| 23 |
+
"ratio_shuffled_mean": 0.36894560309691693,
|
| 24 |
+
"ratio_shuffled_std": 0.022733560664731905,
|
| 25 |
+
"cos_shuffled_mean": -0.017437556954973843,
|
| 26 |
+
"cos_shuffled_std": 0.13464438960797637
|
| 27 |
+
},
|
| 28 |
+
"advantage_clip": {
|
| 29 |
+
"cv_a": 0.20085792243480682,
|
| 30 |
+
"abar": 0.94867342710495,
|
| 31 |
+
"ess_fraction": 0.9612204281808505,
|
| 32 |
+
"a1_violated": false,
|
| 33 |
+
"abar_ratio_to_baseline": 0.9258378588544649,
|
| 34 |
+
"ratio_mean": 0.0953185879622219,
|
| 35 |
+
"ratio_std_draws": 0.011684367167521479,
|
| 36 |
+
"cos_mean": 0.002065446664346382,
|
| 37 |
+
"cos_std_draws": 0.07558667393060121,
|
| 38 |
+
"ratio_shuffled_mean": 0.0741409312015546,
|
| 39 |
+
"ratio_shuffled_std": 0.0062286893856330055,
|
| 40 |
+
"cos_shuffled_mean": 0.03888408502098173,
|
| 41 |
+
"cos_shuffled_std": 0.14181727110818865
|
| 42 |
+
},
|
| 43 |
+
"normalized_adv": {
|
| 44 |
+
"cv_a": 0.9999998211860657,
|
| 45 |
+
"abar": 4.284083843231201e-8,
|
| 46 |
+
"ess_fraction": 1.835337984556805e-15,
|
| 47 |
+
"a1_violated": true,
|
| 48 |
+
"abar_ratio_to_baseline": 4.18096143440454e-8,
|
| 49 |
+
"ratio_mean": 0.4854039954925514,
|
| 50 |
+
"ratio_std_draws": 0.037435128863097536,
|
| 51 |
+
"cos_mean": 0.04745421721599996,
|
| 52 |
+
"cos_std_draws": 0.06307784333867371,
|
| 53 |
+
"ratio_shuffled_mean": 0.3606536861227143,
|
| 54 |
+
"ratio_shuffled_std": 0.0351022186659039,
|
| 55 |
+
"cos_shuffled_mean": 0.10648557799868286,
|
| 56 |
+
"cos_shuffled_std": 0.06018093926588145
|
| 57 |
+
},
|
| 58 |
+
"bc_wins": {
|
| 59 |
+
"cv_a": 0.7973600029945374,
|
| 60 |
+
"abar": 0.9999999403953552,
|
| 61 |
+
"ess_fraction": 0.6113279738392736,
|
| 62 |
+
"a1_violated": false,
|
| 63 |
+
"abar_ratio_to_baseline": 0.9759288889281859,
|
| 64 |
+
"ratio_mean": 0.4326711384712623,
|
| 65 |
+
"ratio_std_draws": 0.03118598407923515,
|
| 66 |
+
"cos_mean": 0.1572374363313429,
|
| 67 |
+
"cos_std_draws": 0.14007506866888625,
|
| 68 |
+
"ratio_shuffled_mean": 0.287686680066821,
|
| 69 |
+
"ratio_shuffled_std": 0.014448778378290157,
|
| 70 |
+
"cos_shuffled_mean": -0.04669254180043936,
|
| 71 |
+
"cos_shuffled_std": 0.2434607700636855
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
}
|
experiments/rl_finetuning/outputs/craftax_classic_ablations/gdelta/gdelta_seed2.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"aggregate": false,
|
| 3 |
+
"checkpoint_step": 40370176,
|
| 4 |
+
"n_params": 9334289,
|
| 5 |
+
"random_cos_sd": 0.000327310078675855,
|
| 6 |
+
"batch": 1024,
|
| 7 |
+
"seed": 2,
|
| 8 |
+
"n_draws": 8,
|
| 9 |
+
"bc_self_cos_mean": 0.9065021425485611,
|
| 10 |
+
"bc_self_cos_std": 0.0077027198757505366,
|
| 11 |
+
"eq4_residual_max": 4.4017934897055966e-7,
|
| 12 |
+
"variants": {
|
| 13 |
+
"baseline_clipped_ratio": {
|
| 14 |
+
"cv_a": 0.9987611174583435,
|
| 15 |
+
"abar": 1.010693907737732,
|
| 16 |
+
"ess_fraction": 0.5006198795530965,
|
| 17 |
+
"a1_violated": false,
|
| 18 |
+
"abar_ratio_to_baseline": 1.0,
|
| 19 |
+
"ratio_mean": 0.47581675390197886,
|
| 20 |
+
"ratio_std_draws": 0.050013706533038056,
|
| 21 |
+
"cos_mean": 0.07653099205344915,
|
| 22 |
+
"cos_std_draws": 0.1371696455986107,
|
| 23 |
+
"ratio_shuffled_mean": 0.3290714358007507,
|
| 24 |
+
"ratio_shuffled_std": 0.017993567005007666,
|
| 25 |
+
"cos_shuffled_mean": -0.07052319077774882,
|
| 26 |
+
"cos_shuffled_std": 0.14557688155890267
|
| 27 |
+
},
|
| 28 |
+
"advantage_clip": {
|
| 29 |
+
"cv_a": 0.20155052840709686,
|
| 30 |
+
"abar": 0.9441632628440857,
|
| 31 |
+
"ess_fraction": 0.9609631596316461,
|
| 32 |
+
"a1_violated": false,
|
| 33 |
+
"abar_ratio_to_baseline": 0.9341732997653425,
|
| 34 |
+
"ratio_mean": 0.09468936170956067,
|
| 35 |
+
"ratio_std_draws": 0.0089181304684684,
|
| 36 |
+
"cos_mean": 0.07843577768653631,
|
| 37 |
+
"cos_std_draws": 0.15751443556107037,
|
| 38 |
+
"ratio_shuffled_mean": 0.06987294887940967,
|
| 39 |
+
"ratio_shuffled_std": 0.005245661765416722,
|
| 40 |
+
"cos_shuffled_mean": -0.05208465061150491,
|
| 41 |
+
"cos_shuffled_std": 0.1682371637969602
|
| 42 |
+
},
|
| 43 |
+
"normalized_adv": {
|
| 44 |
+
"cv_a": 1.0,
|
| 45 |
+
"abar": -5.21540641784668e-8,
|
| 46 |
+
"ess_fraction": 2.720046086076872e-15,
|
| 47 |
+
"a1_violated": true,
|
| 48 |
+
"abar_ratio_to_baseline": -5.1602234642142926e-8,
|
| 49 |
+
"ratio_mean": 0.4764069187869675,
|
| 50 |
+
"ratio_std_draws": 0.05007575024715252,
|
| 51 |
+
"cos_mean": 0.07653114385902882,
|
| 52 |
+
"cos_std_draws": 0.13716963854788167,
|
| 53 |
+
"ratio_shuffled_mean": 0.3310768585637146,
|
| 54 |
+
"ratio_shuffled_std": 0.02154253356764448,
|
| 55 |
+
"cos_shuffled_mean": 0.006856613385025412,
|
| 56 |
+
"cos_shuffled_std": 0.19543885350694304
|
| 57 |
+
},
|
| 58 |
+
"bc_wins": {
|
| 59 |
+
"cv_a": 0.7810651063919067,
|
| 60 |
+
"abar": 1.000000238418579,
|
| 61 |
+
"ess_fraction": 0.6210942055205736,
|
| 62 |
+
"a1_violated": false,
|
| 63 |
+
"abar_ratio_to_baseline": 0.989419477808975,
|
| 64 |
+
"ratio_mean": 0.4360518344565869,
|
| 65 |
+
"ratio_std_draws": 0.025181532407221242,
|
| 66 |
+
"cos_mean": 0.24756294954568148,
|
| 67 |
+
"cos_std_draws": 0.08224887485750225,
|
| 68 |
+
"ratio_shuffled_mean": 0.2737041103279033,
|
| 69 |
+
"ratio_shuffled_std": 0.02088922719350182,
|
| 70 |
+
"cos_shuffled_mean": 0.08941280678845942,
|
| 71 |
+
"cos_shuffled_std": 0.19656693288770055
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
}
|
experiments/rl_finetuning/outputs/craftax_classic_ablations/results.json
CHANGED
|
@@ -26,11 +26,11 @@
|
|
| 26 |
},
|
| 27 |
"merge_provenance": {
|
| 28 |
"inputs": [
|
| 29 |
-
"
|
| 30 |
-
"
|
| 31 |
-
"
|
| 32 |
],
|
| 33 |
-
"config_from": "
|
| 34 |
},
|
| 35 |
"config": {
|
| 36 |
"ENV_NAME": "Craftax-Classic-Symbolic-v1",
|
|
@@ -87,14 +87,9 @@
|
|
| 87 |
"SAVE_POLICY": true,
|
| 88 |
"CHECKPOINT_DIR": "checkpoints",
|
| 89 |
"RESUME_CHECKPOINT_PATH": null,
|
| 90 |
-
"RESUME_WANDB_RUN_ID": null,
|
| 91 |
"RESUME_STEP": null,
|
| 92 |
-
"JAX_COMPILATION_CACHE_DIR":
|
| 93 |
"SEED": null,
|
| 94 |
-
"USE_WANDB": true,
|
| 95 |
-
"WANDB_PROJECT": "remdm-planner-craftax-ablations",
|
| 96 |
-
"WANDB_ENTITY": "null",
|
| 97 |
-
"WANDB_DOWNLOAD_DIR": null,
|
| 98 |
"MAX_ITER": 500,
|
| 99 |
"BATCH_SIZE": 1024,
|
| 100 |
"DIFFUSION_STEPS_COLLECT": 5,
|
|
@@ -132,7 +127,7 @@
|
|
| 132 |
"REWARD_MODEL_LR": 0.001,
|
| 133 |
"REWARD_MODEL_TRAIN_STEPS": 50,
|
| 134 |
"NUM_SEEDS": 3,
|
| 135 |
-
"CHECKPOINT_PATH": "
|
| 136 |
"NUM_ACTIONS": 17
|
| 137 |
},
|
| 138 |
"ablations": {
|
|
|
|
| 26 |
},
|
| 27 |
"merge_provenance": {
|
| 28 |
"inputs": [
|
| 29 |
+
"gpu_24gb_0/results.json",
|
| 30 |
+
"gpu_24gb_1/results.json",
|
| 31 |
+
"gpu_24gb_2/results.json"
|
| 32 |
],
|
| 33 |
+
"config_from": "gpu_24gb_0/results.json"
|
| 34 |
},
|
| 35 |
"config": {
|
| 36 |
"ENV_NAME": "Craftax-Classic-Symbolic-v1",
|
|
|
|
| 87 |
"SAVE_POLICY": true,
|
| 88 |
"CHECKPOINT_DIR": "checkpoints",
|
| 89 |
"RESUME_CHECKPOINT_PATH": null,
|
|
|
|
| 90 |
"RESUME_STEP": null,
|
| 91 |
+
"JAX_COMPILATION_CACHE_DIR": null,
|
| 92 |
"SEED": null,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
"MAX_ITER": 500,
|
| 94 |
"BATCH_SIZE": 1024,
|
| 95 |
"DIFFUSION_STEPS_COLLECT": 5,
|
|
|
|
| 127 |
"REWARD_MODEL_LR": 0.001,
|
| 128 |
"REWARD_MODEL_TRAIN_STEPS": 50,
|
| 129 |
"NUM_SEEDS": 3,
|
| 130 |
+
"CHECKPOINT_PATH": "ablation_src/policy-best-v2",
|
| 131 |
"NUM_ACTIONS": 17
|
| 132 |
},
|
| 133 |
"ablations": {
|