An NBA home-win prediction project with a leakage-safe, season-by-season evaluation pipeline and a small C++ logistic-regression inference runtime.
The checked-in walk-forward evaluation trains a standardized logistic-regression model on completed prior seasons and evaluates the next season. Across seasons ending in 2019, 2020, 2021, and 2022, weighted by games, it reports:
| Metric | Result |
|---|---|
| Accuracy | 63.62% |
| Always-home baseline accuracy | 55.86% |
| Brier score | 0.2226 |
| Log loss | 0.6360 |
| 10-bin expected calibration error | 3.56% |
See RESULTS.md for fold boundaries, methodology, and limitations.
The machine-readable source of truth is
results/walk_forward_logistic_regression.json.
Python/src/nba_predictor/features.py emits each feature row before updating
either team's season record, rolling ten-game statistics, or rest state with the
game being predicted. The evaluator uses only regular-season games from
data/game.csv, derives the season-ending year from the dataset's stable
season_id (2YYYY becomes YYYY+1), and validates each ID against its game
date. The 2019--20 season explicitly includes the July/August 2020 bubble, while
December 2020 begins the next season. Evaluation starts at the season ending in
2002 and never trains on a game dated on or after a fold's test period.
Run the evaluation:
uv run --extra dev python Python/scripts/evaluate_walk_forward.pyRun the safety tests:
uv run --extra dev pytest -qCPP_Predictor/ loads a previously exported logistic-regression model and
scaler, validates ten fixture predictions against their Python-exported values,
and measures in-process inference. On an Apple M5 MacBook Air, four independent
default runs produced a median-of-medians of 24.705M predictions/s (about 0.0405
microseconds/prediction); within-run throughput CV was 0.12--0.39%.
The default benchmark performs 500,000 warmup predictions, calibrates enough
work for at least 0.75 seconds per trial, and reports the median of seven trials
plus min/max throughput and dispersion. It cycles through all fixtures and
consumes every probability in a volatile checksum so the compiler cannot remove
the work. Full methodology and limitations are in
CPP_Predictor/BENCHMARK.md, with structured
results in results/cpp_benchmark_apple_m5.json.
make -C CPP_Predictor clean all
(cd CPP_Predictor && ./nba_predictor)The current C++ model artifacts were produced by the original notebook's random stratified-split workflow. They are useful for runtime parity only; they are not the model used for the walk-forward performance result above. Re-exporting the time-aware model into the C++ artifact format is a separate deployment task. The benchmark is an in-process single-thread microbenchmark and excludes model loading, feature construction, file I/O, and service overhead.
data/game.csv Raw NBA game log
Python/src/nba_predictor/ Pre-game features and evaluation logic
Python/scripts/evaluate_walk_forward.py
Python/tests/ Leakage and fold-boundary tests
results/ Versioned evaluation artifact
CPP_Predictor/ C++11 inference runtime and fixtures
Python/jupyter notebook/ Original exploratory notebook