Correct the exploiter budget: it was a quarter of the target, not half
The exploiters ran 32.5M learner actions, not the 65.5M I recorded -- they train one seat, so a 250x1024 run yields half of what the same shape gives the both-seat self-play trainer. Against targets trained on 131M and 122.6M, that makes the attacker roughly 4x underfunded. Which means the absolute number does not support "ours is only 22.8% exploitable". It supports exactly one claim: at a matched budget, league gives up more. Whether the ordering survives a properly funded attacker is now the open question, so the script takes --updates and --batch-games to run it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBQKgvBbxbheiTF1AVy1Sh
This commit is contained in:
@@ -8,6 +8,7 @@ frozen policy could not defend.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
@@ -77,6 +78,12 @@ def _final_score(cfg, exploiter_params, frozen, matches: int) -> dict:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--updates", type=int, default=250, help="exploiter training updates")
|
||||
parser.add_argument("--batch-games", type=int, default=1024)
|
||||
parser.add_argument("--tag", default="", help="suffix for the run dir, to keep runs apart")
|
||||
args = parser.parse_args()
|
||||
|
||||
match_cfg = load_config("configs/jax_ppo/match-selfplay.yaml")
|
||||
old_cfg = load_config("configs/jax_ppo/balanced.yaml")
|
||||
|
||||
@@ -94,10 +101,10 @@ def main() -> None:
|
||||
for name, frozen in targets.items():
|
||||
print(f"\n===== training an exploiter against: {name} =====", flush=True)
|
||||
cfg = load_config("configs/jax_ppo/match-selfplay.yaml")
|
||||
cfg.ppo.batch_games = 1024
|
||||
cfg.run.total_updates = 250
|
||||
cfg.run.log_every = 25
|
||||
slug = name.split()[0]
|
||||
cfg.ppo.batch_games = args.batch_games
|
||||
cfg.run.total_updates = args.updates
|
||||
cfg.run.log_every = max(1, args.updates // 10)
|
||||
slug = name.split()[0] + args.tag
|
||||
state = train_exploiter(cfg, frozen, Path(f"runs/jax-ppo-match/exploit-{slug}"))
|
||||
result = _final_score(cfg, state.params, frozen, MATCHES)
|
||||
rows.append({"target": name, **result})
|
||||
@@ -110,7 +117,7 @@ def main() -> None:
|
||||
ci = f"[{row['wilson_low']:.3f}, {row['wilson_high']:.3f}]"
|
||||
print(f"{row['target']:<28}{row['exploiter_win_rate']:>20.4f}{ci:>22}")
|
||||
print("\nhigher = the frozen policy had more to farm. 0.5 = nothing found.")
|
||||
Path("runs/jax-ppo-match/exploitability.json").write_text(json.dumps(rows, indent=2))
|
||||
Path(f"runs/jax-ppo-match/exploitability{args.tag}.json").write_text(json.dumps(rows, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user