Cycling model — version analysis

Cycling result predictor — version analysis

Two neural model generations (2024–2025), each scored on the same held-out race and compared. Notes on what changed and what was fixed during cleanup.

eval: Giro 2026 (held out of all training) · metric NDCG@5 = per-stage ranking of the real top finishers, higher is better · AUC = top-10 hit rate
both scored through one shared scorer · coverage differs (v1 183/184 riders, v2 123/184), so the gap is indicative, not exact.
v1 — dense_multi 2024 · encoder–decoder
NDCG@5 0.21 · AUC 0.60
encoder · rider's season
20×28 numerickeras-OCR img · 1499
→
LSTM40·30·15
→
latent
↓  latent repeated × 21 stages  ↓
decoder · per stage
latent21×21 stagestage img
→
TD dense
→
softmax(4)finish band
Architecture
Per-rider encoder–decoder. Encoder: last 20 races × 28 numeric features + stage images (keras-OCR → 1499-dim vectors) → LSTM → repeat over 21 stages. Decoder: 21 stages × 21 features + images → 4-class softmax per stage (finish band: 1–25 / 26–75 / 76–125 / 126+). ~8.3M params.
Notable preds
Giro 2026 GC top picks: Gall, Vingegaard, Ciccone, Hindley, Bernal, Kuss — right rider type (climbers), ranked loosely. Images were masked (2026 not in the 2024 store), so this is a floor.
AI cleanup
  • One-word typo ('one_day' vs 'one_day_race') threw inside a try/except that swallowed it, silently dropping 13 decoder features. Fixing it restored the 21-feature decoder and let the original checkpoint load.
  • Mask mismatch: layer used -1000, padding used -9999 (loss ignored the wrong value).
  • Removed leftover ipdb breakpoints.
v2 — image-encoder 2025 · encoder–decoder
NDCG@5 0.31 · AUC 0.74
encoder · rider's season
28 numericautoencoder latent · 100
→
LSTMfocal loss
→
latent
↓  latent repeated × 21 stages  ↓
decoder · per stage
latentstage featsstage img
→
TD dense
→
sigmoid(1)top-10 y/n
Architecture
Same LSTM encoder–decoder skeleton. Two changes: images pre-encoded by trained autoencoders (100-dim latents) instead of keras-OCR vectors; output switched from 4-class softmax to binary sigmoid (top-10 yes/no) with focal loss for class imbalance.
Changed from v1
4-class → binary target · keras-OCR 1499-dim tower → autoencoder 100-dim latents · added focal loss.
Notable preds
GC top picks: Arensman, Storer, Gee, Caruso, Rubio. Scores came out nearly flat (weak favorite separation); per-stage signal was real (AUC 0.74) but the GC aggregation compressed.
AI cleanup
Mask values already unified to -9999 in this codebase (the v1 bug was gone). Main work was pointing the pipeline at encoded latents and handling missing 2026 image coverage (123/184 riders).
scored 2026-07 · out-of-sample · v1 = the actual 2024 checkpoint (20240627-155334) · full method in BENCHMARK.md · back to the digest