Calibration
Every duel is forecast before it is answered. This page is how those forecasts are scored, and why the obvious metric would have lied.
Prequential by construction
record_duel scores the posterior's and only then
appends the observation. So every forecast is an out-of-sample, one-step-ahead
prediction: the model has never seen the answer it is being scored on.
Why not accuracy
A running count of outcomes is accuracy, and accuracy is not a proper scoring rule. Two failures, and the second is fatal here:
It cannot see sharpness. A model that says 0.51 every time and is right 51% of the time scores identically to one that says 0.99 and is right 51% of the time. The second is wildly overconfident and accuracy cannot tell you.
It is pinned near 50% by the acquisition rule. An information-seeking rule deliberately picks pairs near , because those are the questions worth asking. So the hit rate sits near chance by construction: a perfectly calibrated model looks like a coin flip, and the user concludes it is not learning.
The second point is what makes accuracy harmful rather than merely crude: it penalizes the search for doing its job.
hit_rate is still computed and shown, only so the interface can display
how misleading it is next to the real number.
Brier score and skill
where is the probability the model gave to the option the user actually picked. Lower is better; is what always saying 0.5 scores.
Reported as skill against that baseline:
| skill | Means |
|---|---|
| No better than a coin flip | |
| Perfect and certain | |
| Worse than a coin |
Brier is proper and bounded, and it moves as sharpness improves rather than only as accuracy does, which is the property accuracy lacked.
Log-loss, and what it may not be compared across
Baseline .
Comparable across time for one acquisition rule. Not comparable across acquisition rules: an information-seeking rule serves duels near , which carry the highest log-loss by construction. Comparing two rules on their own self-chosen question sets would score the willingness to ask hard questions as a failure.
check_log_loss is the version for that comparison. See below.
The selection-bias fix
The acquisition function chooses which duels get scored, which means overall skill is measured on a question set the model helped select. That is circular.
So a fraction of duels are drawn uniformly at random and flagged
Forecast::random_check. The app marks them ◇ unbiased probe, and
calibration restricted to those is unbiased:
| Field | Is |
|---|---|
check_n | Number of random-probe forecasts |
check_skill | Brier skill on them — the number without an asterisk |
check_log_loss | Log-loss on them — the only log-loss comparable across rules |
It costs a small share of the query budget and it is the only number here that means what it says unqualified.
The shipped default acquisition is uniform random pairing, so every duel is already
an unbiased sample and check_skill equals overall skill. The probe machinery exists
for the BALD rule, where the distinction is real, and it is one of the reasons
uniform pairing was chosen. See Acquisition.
The reliability diagram
Five buckets over (N_BINS = 5, the most a small session
can fill without every bucket being noise). Each bucket reports:
predicted | Mean forecast in the bucket — the model's claim |
observed | Observed frequency of "A won" — the evidence |
n | How many forecasts landed here |
Plotted, the diagonal is the claim and the dots are the reality. This is the display that makes calibration legible: a single number cannot distinguish "overconfident at the top end" from "underconfident in the middle", and the shape of the failure is what tells you what to do about it.
The app draws a whisker per bucket for how much a bucket that size could wobble by chance, so a dot off the diagonal with a whisker crossing it is not yet evidence of anything.
By provenance
The same scores, split by how the answer was collected:
pub struct ProvenanceScore {
pub provenance: String, // "duel" | "heard_edit" | "self_report"
pub n: usize,
pub brier: f64,
pub log_loss: f64,
pub skill: f64,
}
The comparison this exists for: a hand edit committed through a heard duel and one committed by ticking my edit is better make the same claim in the log, and there is no reason to believe they are equally reliable. Scoring them against forecasts the model made before either answer arrived is the only way to find out which, and it costs one tag.
Empty streams are omitted, so a session that has never committed a hand edit carries exactly one row.
Interpreting it
| Shape | Reading |
|---|---|
| Skill ≈ 0, small | Too early. Correct and expected |
| Skill < 0 with real | Worse than chance — either overfitting a coincidental coordinate, or genuinely inconsistent answers |
| Dots below the diagonal on the right | Overconfident: when it says 80% it is right less often |
| Dots above on the left | Underconfident |
| Skill stuck near 0 with large | The preference is probably not in the feature space |
The user-facing version of this table is in Reading what it learned.
Why the number can look bad
Committing to a forecast before each answer and then reporting the error against a proper scoring rule means the model can publicly fail, and early on it does.
That is what makes the number worth reading later, and it is why the app shows "not beating a coin flip yet" rather than hiding the metric until it flatters.