Files
coorl-lost-cities/docs/research/deep-cfr-baseline-2000-analysis.md
T
coolguyandClaude Opus 4.7 004b913a7b Rename bot family, curate analyze plots, tier evaluation cadence
Three coordinated hygiene changes; none target the diagnosed
selection-bias bottleneck. They make the codebase honestly reflect the
pure-self-play stance and reduce dashboard noise.

Bot rename (drop the unhelpful safe_ prefix; suffixes describe behaviour):
- safe_heuristic_loose -> heuristic_aggressive
- safe_heuristic       -> heuristic_balanced
- safe_heuristic_strict -> heuristic_cautious
- noisy_safe           -> heuristic_noisy
- passive_discard      -> discard_only

Class renames in bots/: SafeHeuristicBot -> HeuristicBot,
SafeHeuristicParams -> HeuristicParams, PassiveDiscardBot -> DiscardOnlyBot,
plus loose/strict parameter constants. Backwards compatibility was dropped
intentionally per user instruction; no aliases. Active configs, docs,
scripts, tests updated. Archive directories (configs/archive,
docs/archive, runs/archive) left intact and may still reference old
names per their read-only policy. The src/.../bots/passive.py module was
renamed to discard_only.py via git mv.

Analyze plot curation (deep_cfr/analyze.py):
- Added analysis_00_core.png as the canonical daily dashboard with 10
  heuristic-free metrics (loss/{advantage,strategy}; vs heuristic_cautious:
  avg_score_diff0, win_rate0, avg_opened_colors, positive_expedition_rate,
  bonus_expedition_rate, score_per_opened_color, policy_entropy; vs random:
  win_rate0).
- Removed analysis_05_open_quality.png (bad/weak/good open rates,
  recoverable score) and analysis_07_calibration.png (calibration gap,
  recoverable mean) - both relied on the heuristic recoverable_score
  classifier already dropped from inputs.
- Removed SELECTIVITY_PLOTS and plot_selectivity (heuristic-laden).
- SUMMARY_EVAL_METRICS no longer includes bad_open_rate or
  calibration_gap.
- PlotSpec gained an opponents allowlist so the new core section can pin
  a specific opponent per panel without restructuring plot_section.

Tiered evaluation cadence (EvaluationConfig):
- Added extended_opponents and extended_eval_every (default 0 = disabled).
- opponents_for_iteration(iteration) returns the core list every
  eval_every and appends extended_opponents (de-duplicated) when
  iteration is also a multiple of extended_eval_every.
- default.yaml now uses 3 core opponents (random, discard_only,
  heuristic_cautious) every 5 iterations and 3 extended opponents
  (heuristic_balanced, heuristic_aggressive, heuristic_noisy) every 50
  iterations. random is the floor sanity. discard_only is the
  zero-pit detector / absolute-score reference (its score is always 0,
  so eval/discard_only/avg_score_diff0 directly equals the model's raw
  average score). heuristic_cautious is the ceiling and the
  archive-comparable benchmark used in the prior diagnostic sections.

Net eval cost reduction: roughly 50% (3 opponents x every 5 iter, plus
6 opponents x every 50 iter, vs the prior 6 x every 5).

Documented in docs/plans/deep-cfr-selectivity.md section 9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:32:55 +09:00

208 lines
9.2 KiB
Markdown

# Deep CFR 512x3 2000-Iteration Baseline Analysis
**Last verified:** 2026-05-08, commit `aa898ed`
**Source run:** `runs/2026-05-08_051124_baseline-512x3-2000-dense-eval`
## Question
Does the 512x3 2000-iteration baseline show useful learning signals and
Lost Cities selectivity, and what should be improved before treating it as the
canonical comparison anchor?
## Short Answer
The run shows real optimization progress in the advantage networks, but the
policy that is evaluated does not improve. The main failure mode is not simply
"opens too many colors." By late training, the model often opens slightly fewer
colors against strong heuristic opponents, but the expeditions it opens are
lower quality and become negative more often. This looks more like passive or
degenerate average-policy learning than successful selectivity.
Use this run as evidence for the next diagnostics, not as a final baseline for
model-size decisions. Before running another long model-size comparison, separate
current regret-matching policy quality from average-strategy-network quality and
audit the interleaved outcome-sampling target behavior.
## Run Context
The source run used the old pre-deterministic baseline settings:
- `network.hidden_size=512`, `network.num_layers=3`
- `run.seed=79`
- `run.max_iterations=2000`
- `evaluation.eval_every=10`
- `checkpoint.save_every=100`
- `traversal.num_workers=8`
- `traversal.scheduler=interleaved`
- `traversal.sampling_mode=outcome`
- `traversal.outcome_unsampled_regret=zero`
- `run.deterministic=false`
It is therefore not the canonical reproducibility baseline after the deterministic
traversal changes. Its value is diagnostic: it is long enough to show how the
current 512x3 setup behaves after memory warm-up and many strategy updates.
## Evidence
Advantage fitting improves, but strategy fitting does not produce a better
evaluated policy.
| Metric | Early | Late | Interpretation |
| --- | ---: | ---: | --- |
| `loss/advantage` mean | ~2403 at iters 1-100 | ~807 at iters 1501-2000 | advantage networks fit their targets better |
| `loss/strategy` mean | ~1.53 at iters 1-100 | ~1.61 at iters 1501-2000 | average policy training does not clearly improve |
| `memory/advantage` | full by iter 101-500 | full | both player buffers saturated at 2M each |
| `memory/strategy` | full by iter 101-500 | full | strategy buffer saturated at 2M |
Evaluation performance peaks early and then degrades.
| Opponent | First 20 evals `win_rate0` | Last 20 evals `win_rate0` | First 20 evals score diff | Last 20 evals score diff |
| --- | ---: | ---: | ---: | ---: |
| random | 0.808 | 0.665 | +35.1 | +12.4 |
| discard_only | 0.026 | 0.012 | -30.8 | -43.0 |
| heuristic_balanced | 0.070 | 0.011 | -68.7 | -94.2 |
| heuristic_aggressive | 0.078 | 0.014 | -69.3 | -95.0 |
| heuristic_cautious | 0.072 | 0.011 | -57.6 | -85.0 |
| heuristic_noisy | 0.120 | 0.033 | -49.4 | -78.8 |
The best strict-heuristic point appears around iteration 70:
- `eval/heuristic_cautious/win_rate0=0.15`
- `eval/heuristic_cautious/avg_score_diff0=-41.09`
The final point at iteration 2000 is worse:
- `eval/heuristic_cautious/win_rate0=0.02`
- `eval/heuristic_cautious/avg_score_diff0=-81.64`
Selectivity does not meaningfully emerge. Against `heuristic_cautious`:
| Metric | First 20 evals | Last 20 evals | Interpretation |
| --- | ---: | ---: | --- |
| `avg_opened_colors` | 4.31 | 3.92 | opens slightly fewer colors |
| `score_per_opened_color` | -8.09 | -15.41 | opened expeditions become worse |
| `positive_expedition_rate` | 0.142 | 0.046 | fewer opened expeditions end positive |
| `negative_expedition_rate` | 0.834 | 0.947 | almost every opened expedition is negative |
| `bad_open_rate` | 0.905 | 0.908 | first-open quality does not improve |
The open-quality metrics are generated by `src/coolrl_lost_cities/games/classic/deep_cfr/evaluate.py:124`
and classify first-open actions around `src/coolrl_lost_cities/games/classic/deep_cfr/evaluate.py:546`.
A "good" open has non-negative visible recoverable score; a "bad" open has no
bonus path and negative recoverable score.
Traversal gets deeper and slower late in training:
- `traversal/nodes` mean rises from ~119k at iters 1-100 to ~199k at iters
1501-2000.
- `traversal/avg_endpoint_depth` rises from ~212 to ~354.
- `time/iteration_seconds` rises from ~12.0s to ~14.4s.
- late node-limit cutoffs appear, but remain small on average: about 1.0% of
endpoints in iters 1501-2000.
This suggests the policy is changing game length and trajectory shape, but not
toward higher-quality scoring.
## Interpretation
The strongest signal is a current-vs-average policy split. Deep CFR trains
advantage networks to define a current regret-matching policy, but the evaluated
agent is the strategy network, which approximates the average policy. In this
run, advantage loss improves while strategy loss and eval quality do not. That
points to either:
1. the current policy is improving but the average strategy network or strategy
memory is failing to represent it, or
2. the advantage networks are fitting targets that do not translate into better
game play under the current traversal distribution.
The current metrics cannot distinguish those two cases. The next diagnostic
should evaluate the same checkpoint under both policy views:
- current advantage networks + regret matching,
- average strategy network.
There is also a target-consistency issue worth auditing before a new long run.
The recursive Cython traversal honors `outcome_unsampled_regret=zero` by setting
unsampled legal targets to zero at
`src/coolrl_lost_cities/games/classic/deep_cfr/traversal.pyx:930`. The
interleaved traversal currently sets every legal target to `-node_value` before
overwriting the sampled action at
`src/coolrl_lost_cities/games/classic/deep_cfr/interleaved_traversal.py:386`.
That makes the interleaved path behave like the `negative_node_value` variant
even when the resolved config says `zero`.
Theoretically, this is a state-wise constant shift and regret matching is
invariant to action-independent constants. With function approximation and MSE,
however, it changes training dynamics and should be treated as an experimental
variable unless explicitly made consistent.
## Deep CFR Improvement Candidates
### 1. Evaluate current policy vs average policy first
This is the highest-value diagnostic. If current regret-matching policy is
materially stronger than the strategy network, focus on strategy memory,
strategy weighting, and average-policy fitting. If both are weak, focus on
advantage targets, exploration, and representation.
### 2. Make interleaved outcome targets match the config
Either implement `outcome_unsampled_regret` in the interleaved scheduler or
change the default/config documentation to state that interleaved outcome
sampling uses the baseline-subtracted target. The former is cleaner because it
lets `zero` vs `negative_node_value` become a controlled ablation.
### 3. Run a lower-variance traversal ablation
Outcome sampling is correct but high variance for Lost Cities entry decisions.
Before changing model size, test whether increasing `traversals_per_player`
improves strict-heuristic score and expedition quality. A useful paired
ablation would keep model/config fixed and change only traversal count, for
example 280 vs 560 per player.
### 4. Add first-open regret diagnostics
The current bad-open metric is outcome-facing and heuristic-visible. It tells us
that opened expeditions are bad by recoverable score, but not whether the
counterfactual regret target said the open action was bad at the decision point.
Add diagnostics over advantage-memory targets by action class:
- first-open action targets,
- non-open alternatives in the same information state,
- color/action class buckets,
- target quantiles, not only means.
If bad first-open actions have negative advantage targets but the policy still
opens, suspect representation or strategy fitting. If their targets are not
negative, suspect exploration distribution or the metric/objective mismatch.
### 5. Treat model-size scaling as a follow-up, not the first fix
The earlier 1024x6 run showed a better 200-iteration signal than 512x3, but this
2000-iteration baseline shows a more basic policy-quality issue. Scaling capacity
may help representation, but it will not tell us whether the average-policy
training path is currently suppressing or distorting a better current policy.
## Recommended Next Plan
1. Implement or script current-vs-average evaluation for a saved checkpoint.
2. Run it on `iteration_00070.pt`, `iteration_00200.pt`, `iteration_01000.pt`,
and `iteration_02000.pt` from the source run.
3. Audit and fix interleaved `outcome_unsampled_regret` handling.
4. Establish a new deterministic 512x3 baseline only after the target behavior
is intentional and documented.
5. Then compare 512x3 vs 1024x6 under matched deterministic settings and W&B
grouping.
## Practical Baseline Rule
Do not promote `baseline-512x3-2000-dense-eval` as the canonical baseline. It is
better classified as a diagnostic pre-determinism long run. The next canonical
baseline should be rerun with:
- deterministic traversal enabled,
- `evaluation.eval_every=5`,
- `checkpoint.save_every=50`,
- explicit W&B group/job type,
- and target semantics that match the resolved config.