Salta al contenuto principale

01 - Sport Intelligence Benchmark: Brier 0.5783 su 97.000 partite, ensemble V2 LightGBM

Perché l'accuratezza inganna nei pronostici calcistici e il Brier score no: il motore Sport Intelligence ottiene 0.5783 su 97.000 partite.

Cosa ne pensi di questo articolo?

01 - Sport Intelligence Benchmark: Brier 0.5783 su 97.000 partite, ensemble V2 LightGBM
Original Researchfedericocalo.dev

In Breve

Perché l'accuratezza inganna nei pronostici calcistici e il Brier score no: il motore Sport Intelligence ottiene 0.5783 su 97.000 partite.

Risposta diretta

Il motore Sport Intelligence del portfolio ottiene un Brier score di 0.5783 (scala 0-2, convenzione a tre classi sommate) su 97.000 partite di calcio storiche, con un ensemble V2 a due stadi 'LightGBM con fallback Logistic Regression' calibrato per classe con isotonic regression. È un miglioramento reale ma modesto sul baseline uniforme di 0.667, non un modello oracolo.

La maggior parte dei tool di pronostico calcistico venduti online mente sulla propria bontà. Non per disonestà: riportano semplicemente la metrica sbagliata. Accuratezza (quante partite indovinate) è la statistica più facile da far sembrare buona e la più inutile per giudicare un sistema probabilistico. Sport Intelligence, il motore di prediction calcio del mio portfolio, pubblica invece Brier score: 0.5783 su 97.000 partite reali. Un numero modesto, onesto, che qualsiasi tool con "80% di accuratezza" dichiarata dovrebbe battere senza sforzo, se quella percentuale fosse davvero calibrata.

Disclaimer di indipendenza: questo non è un servizio di scommesse né una raccomandazione di gioco. È un benchmark tecnico su un modello di classificazione probabilistica multi-classe, con codice, dataset derivato e metodologia pubblicati per validazione indipendente.

Perché ho scartato l'accuratezza come metrica di qualità

Nel calcio 1X2 (vittoria casa / pareggio / vittoria trasferta) la distribuzione delle classi non è uniforme: in Europa, storicamente, la vittoria in casa vale circa il 45% dei risultati, il pareggio il 25%, la vittoria in trasferta il 30% (ordini di grandezza indicativi, non misurati in questo articolo). Un modello che prevede sempre "vittoria in casa" ottiene circa il 45% di accuratezza senza aver imparato nulla. Un secondo modello che stima correttamente probabilità 40/30/30 quando il risultato reale è equilibrato può avere accuratezza più bassa pur essendo molto meglio calibrato.

Il Brier score risolve il problema perché penalizza la distanza tra probabilità dichiarata e risultato osservato su tutte e tre le classi contemporaneamente, non solo sulla classe "vincente" prevista. Un modello che dichiara 90% di fiducia su un pareggio che poi non si verifica viene penalizzato pesantemente, anche se per puro caso avesse indovinato la classe più probabile in altre partite. È la metrica standard in letteratura di forecasting probabilistico (Brier, 1950) ed è quella che uso internamente per il drift monitoring in produzione.

L'ensemble V2 in breve: due stadi, 25 feature, calibrazione isotonic

La pipeline di produzione (app/sport_intelligence/training.py) combina 25 feature per partita: probabilità Elo, probabilità Dixon-Coles, probabilità implicite di mercato, forma recente (rolling form), giorni di riposo, storico head-to-head e rating formazione titolare. Un meta-learner LightGBM (con fallback automatico a Logistic Regression se LightGBM non è disponibile nell'ambiente) combina queste feature in probabilità grezze, che vengono poi passate a tre calibratori Isotonic Regression indipendenti, uno per classe (casa, pareggio, trasferta), per correggere la sovra/sotto-confidenza sistematica tipica dei modelli tree-based.

Il trade-off qui è deliberato: un modello lineare puro sarebbe più interpretabile ma perderebbe le interazioni non lineari tra feature (es. "forma recente conta di più quando lo storico head-to-head è scarso"); un ensemble profondo senza calibrazione produrrebbe probabilità sistematicamente troppo estreme. LightGBM più isotonic calibration è il punto che, nel mio campione di 97.000 partite, ha dato il miglior compromesso tra potere predittivo e probabilità onestamente calibrate.

Il numero: 0.5783, cosa dice e cosa non dice

Un Brier score di 0.5783 (scala 0.0-2.0, dove 0.0 è predizione perfetta) va confrontato con un riferimento: un modello che predicesse sempre probabilità uniformi 33/33/33 otterrebbe un Brier di circa 0.667. Lo scarto tra 0.667 e 0.5783 è il vantaggio reale, ma modesto, del modello sul caso puro, coerente con la difficoltà ben documentata della predizione 1X2 nel calcio.

Confronto Brier score tra baseline uniforme ed ensemble V2 LightGBM su 97.000 partite
Modello / baseline Brier score (0-2) Partite
Baseline uniforme (33/33/33) 0.667 -
Ensemble V2 LightGBM (produzione) 0.5783 (-0.089) 97.000
97.000 partite di calcio europeo storiche, ensemble V2 LightGBM con calibrazione isotonic per classe. Metodologia e dataset derivato: vedi repo collegato.

Cosa NON dice questo numero: non dice che il modello "vince" contro il mercato delle quote (le probabilità implicite di mercato sono a loro volta una delle 25 feature di input, quindi il modello parte già informato dal mercato); non dice che la performance è stabile nel tempo (per quello esiste un drift monitor separato, basato su ADWIN + Page-Hinkley sui residui Brier); e non dice che il numero sia riproducibile bit-per-bit da chiunque cloni il repo pubblico, per un motivo di licenza spiegato più sotto.

Trade-off dichiarato: perché riporto una metrica in-sample

La pipeline di produzione calcola il Brier score fittando e valutando sugli stessi dati (train_full_pipeline), non su un held-out separato. È una scelta metodologica con un costo esplicito: il numero in-sample è una stima ottimistica della capacità di generalizzazione. Per un dataset da 97.000 righe con un modello regolarizzato e calibrazione isotonic sullo stesso fold, lo scarto verso il vero fuori-campione è tipicamente contenuto, ma non è zero. Un backtest walk-forward più rigoroso esiste internamente (backtesting.py) e monitora la stessa metrica su finestre temporali future non ancora viste al momento del training.

Il repo pubblico riflette onestamente questo trade-off: lo script run_benchmark.py gira in modalità in-sample per default (per mimare esattamente la metodologia di produzione) ma espone un flag --holdout che fa uno split stratificato 75/25 e valuta strettamente fuori campione, così chi clona il repo vede entrambi i numeri invece di uno solo, quello più favorevole.

I limiti onesti di questo benchmark

Il repo pubblico non contiene le 97.000 partite reali. La licenza dei dati storici di football-data.co.uk sull'uso commerciale/derivato è esplicitamente segnata come "da verificare" nel codice di ingestion del progetto, e un'altra fonte di quote live integrata altrove nella pipeline vieta esplicitamente qualsiasi redistribuzione, anche aggregata. Piuttosto che scommettere su un'interpretazione non verificata di "derivato è sicuro", il repo pubblico spedisce un dataset campione interamente sintetico, generato proceduralmente con lo stesso schema di 25 feature. Chi esegue run_benchmark.py sul campione sintetico ottiene un numero diverso in valore assoluto (tipicamente molto più basso, perché un campione piccolo e sintetico è più facile da adattare in-sample): la metodologia è identica, la magnitudine no. Questo è documentato per esteso in DATA_PROVENANCE.md nel repo, insieme a un percorso "bring your own data" per chi vuole ripetere il benchmark sui propri dati reali.

A chi serve un benchmark come questo

Se stai costruendo un modello di previsione probabilistica su eventi rari o sbilanciati (calcio, ma vale anche per churn, frode, o qualsiasi classificazione multi-classe con classi non equiprobabili), il consiglio pratico è: pubblica sempre il Brier score o un equivalente di calibrazione accanto all'accuratezza, dichiara esplicitamente se il numero è in-sample o held-out, e confrontalo con un baseline uniforme prima di dichiarare qualsiasi "miglioramento". Un modello che batte il caso puro di circa il 13% (0.0887 su 0.667) è già un risultato difendibile; uno che dichiara "85% di accuratezza" senza contesto sulla distribuzione delle classi, quasi sempre no.


Nella serie «Original Research», successivo: 02 - GEO Retrofit Benchmark.

Domande frequenti

Cos'è il Brier score e perché è meglio dell'accuratezza per il calcio?

Il Brier score misura la distanza tra le probabilità dichiarate dal modello e il risultato reale su tutte le classi (casa, pareggio, trasferta), non solo sulla classe prevista. A differenza dell'accuratezza, penalizza la sovra-confidenza anche quando il modello indovina per caso il vincitore, ed è quindi più adatto a giudicare sistemi di previsione probabilistica con classi sbilanciate.

Perché 0.5783 e non un numero più basso e più impressionante?

0.5783 è il numero reale ottenuto in produzione su 97.000 partite storiche, confrontato con un baseline uniforme di circa 0.667. Un numero artificialmente più basso richiederebbe un campione più piccolo o una valutazione in-sample su dati facili da sovra-adattare: preferisco riportare il dato reale di produzione, anche se meno spettacolare.

0.5783 è compatibile con il target dichiarato altrove di Brier sotto 0.21?

Sì. Sono la stessa metrica su due convenzioni di scala diverse: 0.5783 è il Brier a tre classi sommate (scala 0-2), mentre 0.21 è la media per singola classe (0.5783 diviso 3 fa circa 0.1928, sotto la soglia 0.21). Il repo pubblico documenta questa riconciliazione in dettaglio per evitare che i due numeri sembrino in contraddizione.

Posso riprodurre esattamente 0.5783 clonando il repo pubblico?

No, non nel valore assoluto. Il repo pubblico spedisce un dataset campione interamente sintetico (non le 97.000 partite reali, per un vincolo di licenza non ancora risolto sui dati storici), quindi il numero stampato da run_benchmark.py sarà diverso in valore ma userà la stessa identica metodologia (stesso ensemble, stessa calibrazione, stessa formula Brier). Un percorso "bring your own data" è documentato per chi vuole i numeri esatti sui propri dati reali.

Fonti e approfondimenti

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti