fix: add top_n cutoff to ELO tournament (default top 10 generations)
This commit is contained in:
1 parent
cd9d2eebd8
commit
cb38081a8b
2 files changed
+40
-7
No files matched your search
+25
-1
@@ -16,6 +16,7 @@ Reuses `run_match`, `LearnedPolicy`, `HeuristicPolicy`,
|
||||
Usage:
|
||||
python -m ml.eval.elo_tracker
|
||||
python -m ml.eval.elo_tracker --episodes 50 --output data/elo_history.json
|
||||
python -m ml.eval.elo_tracker --top-n 10
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -285,12 +286,17 @@ def run_generation_tournament(
|
||||
episodes_per_matchup: int = 50,
|
||||
playstyle: str = "midrange",
|
||||
k_factor: float = DEFAULT_K,
|
||||
top_n: int = 10,
|
||||
) -> EloResult:
|
||||
"""
|
||||
Discover generation checkpoints and run an ELO tournament.
|
||||
|
||||
Looks for `gen-*/best_policy.pt` under checkpoint_dir.
|
||||
Also includes heuristic and random baselines.
|
||||
|
||||
Args:
|
||||
top_n: Only include the N most recent gen-* checkpoints.
|
||||
0 means include all (unbounded). Default is 10.
|
||||
"""
|
||||
policies: Dict[str, object] = {
|
||||
"heuristic": HeuristicPolicy(),
|
||||
@@ -299,7 +305,20 @@ def run_generation_tournament(
|
||||
|
||||
ckpt_path = Path(checkpoint_dir)
|
||||
if ckpt_path.exists():
|
||||
for gen_dir in sorted(ckpt_path.glob("gen-*")):
|
||||
gen_dirs = sorted(ckpt_path.glob("gen-*"))
|
||||
|
||||
# Apply top_n cutoff — keep only the latest N generation dirs
|
||||
if top_n > 0 and len(gen_dirs) > top_n:
|
||||
skipped = len(gen_dirs) - top_n
|
||||
logger.info(
|
||||
"top_n=%d: skipping %d older generation(s), using %s … %s",
|
||||
top_n, skipped,
|
||||
gen_dirs[-top_n].name,
|
||||
gen_dirs[-1].name,
|
||||
)
|
||||
gen_dirs = gen_dirs[-top_n:]
|
||||
|
||||
for gen_dir in gen_dirs:
|
||||
model_path = gen_dir / "best_policy.pt"
|
||||
if model_path.exists():
|
||||
name = gen_dir.name # e.g., "gen-001"
|
||||
@@ -363,6 +382,10 @@ def main():
|
||||
"--k-factor", type=float, default=32,
|
||||
help="ELO K-factor (default: 32)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--top-n", type=int, default=10,
|
||||
help="Only include the N most recent gen checkpoints (0 = all, default: 10)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -373,6 +396,7 @@ def main():
|
||||
episodes_per_matchup=args.episodes,
|
||||
playstyle=args.playstyle,
|
||||
k_factor=args.k_factor,
|
||||
top_n=args.top_n,
|
||||
)
|
||||
|
||||
# Print results
|
||||
|
||||
+15
-6
@@ -1097,7 +1097,7 @@ _elo_tournament_state = _EloTournamentState()
|
||||
_elo_tournament_lock = threading.Lock()
|
||||
|
||||
|
||||
def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
|
||||
def _run_elo_tournament_pipeline(episodes: int, playstyle: str, top_n: int):
|
||||
"""Run ELO tournament in a background thread."""
|
||||
try:
|
||||
import sys
|
||||
@@ -1108,7 +1108,7 @@ def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
|
||||
with _elo_tournament_lock:
|
||||
_elo_tournament_state.running = True
|
||||
_elo_tournament_state.phase = "running"
|
||||
_elo_tournament_state.message = "Running ELO tournament..."
|
||||
_elo_tournament_state.message = f"Running ELO tournament (top {top_n} generations)..."
|
||||
_elo_tournament_state.error = None
|
||||
_elo_tournament_state.result = None
|
||||
|
||||
@@ -1121,6 +1121,7 @@ def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
|
||||
checkpoint_dir=ckpt_dir,
|
||||
episodes_per_matchup=episodes,
|
||||
playstyle=playstyle,
|
||||
top_n=top_n,
|
||||
)
|
||||
|
||||
# Save to ELO history
|
||||
@@ -1155,29 +1156,37 @@ def _run_elo_tournament_pipeline(episodes: int, playstyle: str):
|
||||
|
||||
@router.post("/api/ml/elo/tournament")
|
||||
async def ml_start_elo_tournament(request: FastAPIRequest):
|
||||
"""Start an ELO-rated tournament across all generation checkpoints."""
|
||||
"""Start an ELO-rated tournament across generation checkpoints.
|
||||
|
||||
Body params:
|
||||
episodes (int): Games per matchup pair (default 50)
|
||||
playstyle (str): Game playstyle (default midrange)
|
||||
topN (int): Max number of most-recent gen checkpoints to include
|
||||
(default 10, 0 = unlimited)
|
||||
"""
|
||||
if _elo_tournament_state.running:
|
||||
raise HTTPException(409, "ELO tournament already in progress")
|
||||
|
||||
body = await request.json() if await request.body() else {}
|
||||
episodes = body.get("episodes", 50)
|
||||
playstyle = body.get("playstyle", "midrange")
|
||||
top_n = body.get("topN", 10)
|
||||
|
||||
with _elo_tournament_lock:
|
||||
_elo_tournament_state.running = True
|
||||
_elo_tournament_state.phase = "starting"
|
||||
_elo_tournament_state.message = "Initializing ELO tournament..."
|
||||
_elo_tournament_state.message = f"Initializing ELO tournament (top {top_n} generations)..."
|
||||
_elo_tournament_state.result = None
|
||||
_elo_tournament_state.error = None
|
||||
|
||||
thread = threading.Thread(
|
||||
target=_run_elo_tournament_pipeline,
|
||||
args=(episodes, playstyle),
|
||||
args=(episodes, playstyle, top_n),
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
|
||||
return {"status": "started", "episodes": episodes}
|
||||
return {"status": "started", "episodes": episodes, "top_n": top_n}
|
||||
|
||||
|
||||
@router.get("/api/ml/elo/tournament/status")
|
||||
|
||||
Reference in new issue
Block a user