This page shows the chapter notebook with its saved results. Constructed examples do not measure competition gains.
Chapter 3: Building a CV That Matches the Test Set
Try the decision first
When the relationship between features and outcomes changes from season to season, how far does each validation scheme's Brier score sit from the score on the next season?
Write your expectation before opening the worked results. Use the chapter activity to compare supported inputs. To change the calculation itself, run the notebook on GitHub.
Your answers stay on this page until you download them. They are not sent to a server.
Worked notebook and saved results
When the relationship between features and outcomes changes from season to season, how far does each validation scheme's Brier score sit from the score on the next season?
Leave-one-season-out is a respectable group assessment, but its training set contains seasons from after the held-out one. For a competition that forecasts a future season, the estimate you use to choose models then describes a different question from the one the leaderboard asks.
Constructed data with a random-walk relationship; the sizes are properties of this generator, not a competition result. Past-only is closer to the next season, not exact.
The experiment
A constructed league of 8 seasons with 400 games each and 4 features. A logistic model gives win odds from season-specific weights; the weights take a random step from one season to the next, and the size of the step is the control (0 means no drift). Seasons 1 to 7 are development. Both schemes score seasons 5 to 7 (1,200 games, pooled into one Brier score): leave-one-season-out trains on the other six development seasons, past-only trains on earlier seasons only. The reference is the final recipe, fitted on all seven development seasons, scored on 4,000 fresh games of season 8. Every number is the mean of 100 independent histories.
Calculation code
import numpy as np
from sklearn.linear_model import LogisticRegression
from kaggle_companion.activities._common import clean
SEED = 3
REPLICATES = 100 # independent constructed histories; every estimate is their mean
SEASONS, GAMES, FEATURES = 8, 400, 4
FIRST_SCORED = 4 # score seasons 5, 6 and 7 (0-based 4, 5, 6); season 8 is the future
FUTURE_GAMES = 4000
def generate(drift, seed):
"""Seven development seasons and a large season 8. The weights that turn features into win odds
wander from season to season; `drift` is the size of the wander (their length stays constant)."""
rng = np.random.default_rng(seed)
weights = np.zeros((SEASONS, FEATURES))
weights[0] = rng.normal(0, 1.0, FEATURES)
for s in range(1, SEASONS):
step = weights[s - 1] + rng.normal(0, drift, FEATURES)
weights[s] = step * np.linalg.norm(weights[0]) / np.linalg.norm(step)
def games(s, n):
x = rng.normal(size=(n, FEATURES))
win = rng.random(n) < 1 / (1 + np.exp(-x @ weights[s]))
return x, win.astype(int)
dev = [games(s, GAMES) for s in range(SEASONS - 1)]
return dev, games(SEASONS - 1, FUTURE_GAMES)
def fit(seasons):
X = np.vstack([s[0] for s in seasons])
y = np.concatenate([s[1] for s in seasons])
return LogisticRegression(max_iter=500).fit(X, y)
def brier(y, p):
return float(np.mean((y - p) ** 2))
def one_history(drift, seed):
dev, (X_next, y_next) = generate(drift, seed)
held, loso, past = [], [], []
for s in range(FIRST_SCORED, SEASONS - 1):
X, y = dev[s]
held.append(y)
# Leave-one-season-out: train on every other development season, later ones included.
loso.append(fit([d for k, d in enumerate(dev) if k != s]).predict_proba(X)[:, 1])
# Past-only: train on the seasons before s, as a real forecast would.
past.append(fit(dev[:s]).predict_proba(X)[:, 1])
y = np.concatenate(held)
# Truth: the final recipe (all seven development seasons) on the next season, 4,000 fresh games.
truth = brier(y_next, fit(dev).predict_proba(X_next)[:, 1])
return brier(y, np.concatenate(loso)), brier(y, np.concatenate(past)), truth
def run(drift):
runs = np.array([one_history(drift, SEED * 1000 + i) for i in range(REPLICATES)])
loso, past, truth = runs.T
return clean({
"drift": drift,
"loso": float(loso.mean()), "past_only": float(past.mean()), "next_season": float(truth.mean()),
# optimism = Brier on the next season minus the estimate (positive: the estimate looked better than reality)
"optimism": {"loso": float((truth - loso).mean()), "past_only": float((truth - past).mean())},
"per_history": {"loso": (truth - loso).tolist(), "past_only": (truth - past).tolist()},
"loso_more_optimistic": float(np.mean((truth - loso) > (truth - past))),
"replicates": REPLICATES, "scored_games": GAMES * (SEASONS - 1 - FIRST_SCORED), "future_games": FUTURE_GAMES,
})
Measure it across the control
The control is season-to-season drift (standard deviation of the weight step). Each value below runs the whole experiment from the generator up.
Calculation code
import pandas as pd
from kaggle_companion.activities.ch03 import explain
VALUES = [0, 0.25, 0.5, 1.0]
results = {value: run(value) for value in VALUES}
table = pd.DataFrame({str(v): explain(results[v], v)['metrics'] for v in VALUES})
print(table.to_string())0 0.25 0.5 1.0 Leave-one-season-out Brier 0.159 0.167 0.184 0.217 Past-only Brier 0.159 0.170 0.190 0.229 Next-season Brier 0.159 0.173 0.196 0.235 Optimism, leave-one-season-out 0.000 0.006 0.012 0.017 Optimism, past-only 0.000 0.003 0.006 0.006
Look at it
The figure shows the default setting, season-to-season drift (standard deviation of the weight step) = 0.5.
Calculation code
import matplotlib.pyplot as plt
from kaggle_companion.activities.ch03 import draw, NCOLS, HEIGHT
DEFAULT = 0.5
fig, axes = plt.subplots(1, NCOLS, figsize=(6.6 if NCOLS == 1 else 10.4, HEIGHT), layout='constrained')
draw(axes, results[DEFAULT], DEFAULT)
plt.show()What the result says
At drift 0.5 the final recipe scores a Brier of 0.196 on the next season. Leave-one-season-out reports 0.184, so 0.196 - 0.184 = 0.012 of optimism. Past-only reports 0.190, 0.196 - 0.190 = 0.006. Leave-one-season-out is the more optimistic scheme. Leave-one-season-out was the more optimistic in 85% of 100 histories.
- Leave-one-season-out: 0.196 - 0.184 = 0.012 (next season minus estimate).
- Past-only: 0.196 - 0.190 = 0.006.
- Difference between the schemes: 0.190 - 0.184 = 0.006 of Brier.
- Share of histories where leave-one-season-out was more optimistic: 85%.
Apply this to your competition
- Write down whether the test is a future season, a new group or independent rows before choosing a splitter.
- For a future season, train only on earlier seasons whose labels would have been revealed, and report which seasons receive predictions.
- Label leave-one-season-out as a historical grouped check, and compare it with the past-only estimate: a large difference points to drift.
- Expect even past-only validation to be somewhat optimistic when the relationship moves, and keep a margin before trusting a small gain.
Book location: Chapter 3, March Mania as a Special Case.
Apply a lesson from this chapter
Select a lesson whose conditions match your project. State how you would test it and what would make you reject it.
Resample entity-level validation when AUC estimates are noisy
Conditions
- small imbalanced entity labels create uncertain AUC differences
Action
- Proposed adaptation of source observation: Aggregate events to prediction entities and repeat entity-disjoint splits before deciding whether a change matters.
Check
- Proposed verification: Report AUC dispersion and compare it with proposed change magnitude.
Limits
- Repeated splits do not create new independent labels and can remain optimistic after many experiments.
Verify that training and validation loaders refer to different datasets
Conditions
- training scripts load separate train and validation tensors
Action
- Proposed: assert loader dataset identity and split membership before starting optimization.
Check
- Proposed: assert training loader uses train_dataset, validation uses val_dataset and sample IDs are disjoint; assess the loss tensor axes and padded-target handling separately.
Limits
- This is a proposed correction to an observed source assignment. It is not evidence of a valid held-out score or an executed successful training run.
Check local validation’s directional agreement with evaluation early
Conditions
- local validation has not yet been checked against the evaluation distribution
- model selection relies on local validation
Action
- Proposed adaptation of author advice: Compare the direction of a small set of predeclared model changes across local holdout and available external evaluation.
- Proposed adaptation of author advice: Track whether a few materially different changes improve both independent validation and competition feedback.
Check
- Proposed verification: Investigate split mismatch if candidate rankings disagree and retain an untouched local check.
- Proposed verification: Check repeated change directions rather than inferring reliability from one good submission.
Limits
- Leaderboard agreement is descriptive and repeated checking can overfit it; stable behavior is not guaranteed on future datasets.
- Leaderboard correlation is evidence about one contest and cannot prove deployment validity.
Tune inside training resamples before a separate final holdout
Conditions
- several regression model families share a held-out test split
Action
- Proposed: choose hyperparameters with training folds and assess the finalized workflow on the reserved split.
Check
- Proposed: keep final holdout results out of tuning decisions and select predictors rather than outcomes in recipe normalization/correlation steps.
Limits
- The initial split and folds are random stratified splits; temporal or entity dependence is not checked. Recipe1 selects all_numeric rather than explicitly numeric predictors. Repeated cross-family inspection of the holdout can still bias selection.
Reject public-leaderboard-only ensemble gains
Conditions
- forward ensemble selection beats public score but worsens CV
Action
- Proposed conditional application: Compare ensemble selection on nested score-stratified validation rather than selecting only the best public score.
Check
- Proposed check: Choose ensemble weights under nested validation, compare competing ensembles on untouched folds, and log public, local, and final scores separately.
Limits
- The source demonstrates one public/private disagreement; score-stratified folds need auditing for the intended deployment split.
Check fold-count sensitivity before high-fold stacking
Conditions
- small data support a proposed high-fold stack
Action
- Proposed adaptation of source observation: Compare fold counts and score dispersion before using a high-fold stack; nest feature selection in validation as proposed.
Check
- Proposed verification: Evaluate the same model under several splits and inspect fold sample counts and variance.
Limits
- More folds did not automatically mean more reliable CV in this source.
Use component removal to prioritize scientific-model improvements
Conditions
- a scientific pipeline combines physical assumptions and correction stages
Action
- Proposed adaptation of source observation: Compare targeted component removals under a fixed pipeline and prioritize high-impact dependencies.
Check
- Proposed verification: Record the same metric and baseline for each ablation and inspect interactions between PCA and GP components.
Limits
- Reported drops are conditional on the complete pipeline and do not establish independent additive effects.
Match validation exclusions to partially labeled evaluation
Conditions
- sparse-label articles are excluded from scoring
Action
- Proposed conditional application: Exclude those articles from local false-positive accounting to reproduce the official metric.
Check
- Proposed check: Reproduce official article exclusions on known examples, report both official and complete-label metrics, and inspect error families.
Limits
- Matching incomplete labels reproduces the benchmark rather than proving complete extraction quality.
Audit teacher provenance before cross-validating a stack
Conditions
- a meta-model uses outputs from an already trained supervised checkpoint
Action
- Proposed: generate teacher features out of fold or prove teacher training excludes validation recipients.
Check
- Proposed: record the loaded checkpoint training IDs and regenerate fold-safe teacher logits before assessing the stack.
Limits
- Teacher training data are unknown; leakage is a risk requiring evidence, not a proven result. Text length counts truncated token IDs and sentence count is a dot count.
Split simulations before constructing overlapping windows
Conditions
- overlapping windows come from distinct simulation IDs
Action
- Proposed: hold out whole simulation IDs before building training and validation datasets.
Check
- Proposed: assert disjoint simulation IDs across train and validation and inspect errors per held-out simulation.
Limits
- This source shows an implementation, without an isolated gain, execution result, or confirmed final-submission provenance. Historical dependencies and dataset assumptions require local checks.
Evaluate the complete validation iterator
Conditions
- validation may contain more examples than a single batch
Action
- Proposed: iterate until the validation dataset is exhausted and aggregate by processed example count.
Check
- Proposed: assert processed validation IDs equal the manifest and handle the final partial batch explicitly.
Limits
- Complete iteration is proposed correction; the source gives no guarantee its fixed batch is the whole validation set.
Avoid supervised corpus statistics leaking into OOF base models
Conditions
- text preprocessing uses target-derived vectors
Action
- Proposed conditional application: Keep approximate Naive Bayes vectors fold-local rather than adding whole-training labels to out-of-fold predictors.
Check
- Proposed check: Fit label-dependent text statistics only on each fold's training rows and trace validation-label exclusion.
Limits
- Unsupervised and target-derived statistics differ; source did not adopt the rejected whole-data vectors.
Investigate leaderboard disagreement against independent benchmarks
Conditions
- public feedback disagrees with a independently constructed task benchmark
Action
- Proposed adaptation of source observation: Keep the benchmark and investigate disagreement before chasing public score.
Check
- Proposed verification: Compare multiple held-out benchmarks and inspect their distribution overlap with target tasks.
Limits
- This is proposed response to an observed mismatch; the source does not establish the cause or a universally superior split.
Hold out whole events when predicting a new event
Conditions
- observations share event-level conditions and the target is a new event
Action
- Proposed adaptation of source observation: Partition by complete event and build every statistic on the remaining events.
Check
- Proposed verification: Compare event-level error and, as proposed, chronological backtesting where future events are unavailable.
Limits
- Leave-one-event-out can train on future events relative to older tests; chronological checks are proposed for forecasting use.
Rotate overused meta-model folds and investigate score plateaus
Conditions
- many ensemble decisions reuse the same validation predictions
Action
- Proposed adaptation of source observation: Recheck finalists across new fold partitions and investigate unexpected feedback patterns by comparing train/test feature calculations.
Check
- Proposed verification: Verify identical interaction formulas and fold-level score changes on controlled fixtures.
Limits
- New partitions of fixed OOF predictions are not fully independent fresh data; leaderboard diagnosis cannot prove generalization.
Prune candidate reconstructions using past score consistency
Conditions
- a reconstruction puzzle permits lawful use of score observations to constrain candidate assignments
Action
- Proposed adaptation of source observation: Score prior predictions against candidate reconstructions and reject candidates inconsistent with score ranges and order.
Check
- Proposed verification: Verify constraints on known labels and distinguish necessary constraints from a unique solution.
Limits
- Necessary but insufficient conditions; depends on competition rules and feedback behavior, and risks leaderboard overfitting.
Account for skipped validation examples
Conditions
- validation code skips batches with invalid loss
Action
- Proposed: track and report excluded IDs and compute denominators from processed examples.
Check
- Proposed: fail or explicitly report incomplete validation and gather predictions across processes before computing full-set metrics.
Limits
- Accounting and cross-process gathering are proposed corrections. The source skips only NaNs, not all nonfinite values, and has no measured stability benefit.
Audit feedback-driven postprocessing with a paired unprocessed submission
Conditions
- postprocessing uses repeated competition feedback to adjust predictions
Action
- Proposed adaptation of source observation: Treat successful-submission delta adjustment as a competition-specific experiment and retain an untweaked comparator.
Check
- Proposed verification: Proposed: simulate selection on independent held-out data and quantify sensitivity to feedback reuse.
Limits
- High leaderboard-overfitting risk; this is not a general production calibration method.
Maintain comparable folds and restore best checkpoints
Conditions
- heterogeneous image models need comparable OOF predictions
Action
- Proposed adaptation of source observation: Use shared folds and save best checkpoints by the target metric; choose full-fit duration from CV.
Check
- Proposed verification: Audit fold alignment and compare full-fit duration against fold-optimal epochs.
Limits
- Exact durations and schedulers are source-specific and may change with full-data size.
Separate stopping selection from final performance estimation
Conditions
- early stopping chooses an epoch using labeled validation
Action
- Proposed conditional application: Use training, stopping-validation and separate performance-holdout partitions, or a fixed fold-robust epoch count.
Check
- Proposed check: Reserve final untouched scoring partition after choosing epochs; compare selection score with independent estimate.
Limits
- Three-way splits reduce training/scoring sample sizes; early stopping itself is legitimate.