Come ho vinto al Fantacalcio italiano ⚽ usando l’apprendimento automatico
How I won Italian Fantasy Football ⚽ using machine learning

Decifrando il codice del Fantacalcio attraverso il potere dell’IA
Come ingegnere meccanico con un forte interesse per la programmazione e l’informatica, mi sono appassionato al mondo del machine learning e dell’intelligenza artificiale alcuni anni fa. Riconoscendo il loro potenziale in diverse discipline ingegneristiche, ho intrapreso un viaggio per studiare il machine learning. Tuttavia, nonostante l’acquisizione di conoscenze teoriche, ho faticato a trovare modi pratici per applicare e mettere in pratica le mie nuove competenze. Sebbene fossero disponibili set di dati preconfezionati, non fornivano l’esperienza completa di raccolta e elaborazione dei dati. Poi, mi è venuto un pensiero: perché non applicare il machine learning per aiutarmi a vincere al fantacalcio?
Introduzione al Fantacalcio

Il Fantacalcio è un gioco molto popolare tra i tifosi di calcio italiani. I partecipanti si formano in gruppi e competono durante tutto l’anno in base alle prestazioni dei giocatori reali nella Serie A, la massima serie del calcio italiano. Prima dell’inizio della stagione, i partecipanti tengono un’asta per selezionare le loro rose di più di 20 giocatori. Dopo ogni giornata di Serie A, i giocatori ricevono voti in base alle loro prestazioni, con bonus aggiuntivi per gol e assist. Questi voti accumulati e i bonus determinano i punteggi dei partecipanti. Uno degli aspetti cruciali del gioco è la selezione di una formazione settimanale di giocatori e prendere decisioni su chi far giocare regolarmente e chi mettere in panchina.
Obiettivo del mio lavoro
Il principale obiettivo del mio algoritmo di machine learning sarebbe quello di predire il voto e il fanta-voto (voto più bonus) dei giocatori della Serie A in base alla partita della loro squadra. Il calcio è un gioco intrinsecamente incerto, poiché è impossibile garantire se un giocatore segnerà o meno. Tuttavia, alcuni giocatori hanno una maggiore probabilità di segnare rispetto ad altri e le loro prestazioni possono variare in base alla squadra avversaria. Il mio obiettivo era trovare un metodo oggettivo per determinare quale giocatore avesse una probabilità maggiore di offrire una prestazione migliore in qualsiasi giornata di Serie A.
Avviso: sezioni come questa verranno utilizzate nell’articolo per fornire esempi reali dal Fantacalcio, per illustrare i concetti discussi. Se non sei familiare con il gioco o i giocatori della Serie A, puoi tranquillamente saltare queste sezioni.

Raccolta e elaborazione dei dati
Una volta scaricato l’archivio dei voti dal Fantacalcio, il passo successivo era raccogliere un set completo di caratteristiche per addestrare l’algoritmo di machine learning. Per costruire questo dataset, ho trovato che fbref.com fosse una risorsa preziosa, fornendo un modo conveniente per estrarre statistiche sia per i giocatori che per le squadre della Serie A. Il sito offriva un’ampia gamma di statistiche meticolosamente raccolte, che comprendevano vari indicatori come i gol attesi, i contrasti, i passaggi e il numero medio di occasioni create. L’abbondanza di dati dettagliati disponibili su FBRef ha facilitato notevolmente il processo di assemblaggio di un robusto set di caratteristiche per addestrare l’algoritmo di machine learning.


L’approccio che ho adottato prevedeva la costruzione di un dataset composto da oltre 50 caratteristiche per ogni giocatore. Questo dataset combina le statistiche medie elaborate del giocatore, unite alle statistiche della sua squadra e alle statistiche della squadra avversaria per una determinata giornata di campionato. Gli output target per ogni riga del dataset erano il voto del giocatore e il voto fantacalcio. Per costruire il dataset, ho preso in considerazione le ultime tre stagioni della Serie A.
Per affrontare la sfida delle statistiche non affidabili per i giocatori con poco tempo di gioco nella stagione, ho utilizzato tre strategie:
- Media pesata con le statistiche della stagione precedente.
- In assenza di dati storici affidabili, le statistiche del giocatore sono state mediate con quelle del giocatore medio in un ruolo simile.
- Ho utilizzato una lista predefinita per mediare parzialmente le statistiche di un giocatore con quelle di un giocatore precedente della stessa squadra che ha ricoperto un ruolo simile.
Ad esempio, le prestazioni del debuttante del Napoli Kim potrebbero essere confrontate con le prestazioni precedenti di Koulibaly, o le prestazioni di Thauvin potrebbero essere valutate in relazione al suo predecessore, Deulofeu (ma questo si è rivelato essere errato).

Definizione e addestramento dell’algoritmo
Per rendere le cose più interessanti e i risultati più piacevoli da visualizzare, l’algoritmo di machine learning è stato progettato per andare oltre le semplici previsioni del voto e del voto fantacalcio. Invece, è stato adottato un approccio probabilistico, sfruttando TensorFlow e TensorFlow Probability per costruire una rete neurale in grado di generare una distribuzione di probabilità. In particolare, la rete prevedeva i parametri di una distribuzione di probabilità sinusoidale-arcoseno. Questa scelta è stata fatta per tener conto della naturale asimmetria nella distribuzione del voto delle prestazioni dei giocatori. Ad esempio, nel caso di un giocatore offensivo, anche se il suo voto fantacalcio medio potrebbe essere intorno a 6,5, l’algoritmo riconosceva che un voto di 10 (che indica una prestazione eccezionale, come segnare un gol) sarebbe molto più probabile che si verifichi rispetto a un voto di 4 (che rappresenta una prestazione sottotono rara).

L’architettura della rete neurale profonda utilizzata per questa attività comprendeva più strati densi, ognuno utilizzando la funzione di attivazione sigmoide. Per prevenire l’overfitting e migliorare la generalizzazione, sono state utilizzate tecniche di regolarizzazione come Dropout e Early Stopping. Dropout disabilita casualmente una frazione delle unità della rete neurale durante l’addestramento, mentre Early Stopping interrompe il processo di addestramento se la perdita di validazione smette di migliorare. La funzione di perdita scelta per addestrare il modello era il Negative Log Likelihood, che misura la discrepanza tra la distribuzione di probabilità prevista e i risultati effettivi.
Di seguito è riportato un frammento di codice scritto per la costruzione della rete neurale:
callback = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience = 10)neg_log_likelihood = lambda x, rv_x: -rv_x.log_prob(x)inputs = tfk.layers.Input(shape=(X_len,), name="input")x = tfk.layers.Dropout(0.2)(inputs)x = tfk.layers.Dense(16, activation="relu") (x)x = tfk.layers.Dropout(0.2)(x)x = tfk.layers.Dense(16, activation="relu") (x)prob_dist_params = 4def prob_dist(t): return tfp.distributions.SinhArcsinh(loc=t[..., 0], scale=1e-3 + tf.math.softplus(t[..., 1]), skewness = t[..., 2], tailweight = tailweight_min + tailweight_range * tf.math.sigmoid(t[..., 3]), allow_nan_stats = False)x1 = tfk.layers.Dense(8, activation="sigmoid")(x)x1 = tfk.layers.Dense(prob_dist_params, activation="linear")(x1)out_1 = tfp.layers.DistributionLambda(prob_dist)(x1)x2 = tfk.layers.Dense(8, activation="sigmoid")(x)x2 = tfk.layers.Dense(prob_dist_params, activation="linear")(x2)out_2 = tfp.layers.DistributionLambda(prob_dist)(x2)modelb = tf.keras.Model(inputs, [out_1, out_2])modelb.compile(optimizer=tf.keras.optimizers.Nadam(learning_rate = 0.001), loss=neg_log_likelihood)modelb.fit(X_train.astype('float32'), [y_train[:, 0].astype('float32'), y_train[:, 1].astype('float32')], validation_data = (X_test.astype('float32'), [y_test[:, 0].astype('float32'), y_test[:, 1].astype('
Utilizzo della rete neurale per le previsioni
L’algoritmo addestrato offriva previsioni sulla distribuzione di probabilità per il voto di un giocatore e per il voto fantacalcio. Considerando le statistiche medie del giocatore, le informazioni sulla squadra, i dati sull’avversario e i fattori casa/trasferta, era in grado di prevedere le prestazioni del giocatore per le future partite di Serie A. Attraverso il post-processing delle distribuzioni di probabilità, era possibile ottenere una previsione numerica del voto atteso e un massimo potenziale di voto, semplificando la selezione della formazione nel Fantacalcio.

Utilizzando la tecnica del Monte Carlo, le distribuzioni di probabilità di ciascun giocatore venivano utilizzate per prevedere il voto totale atteso di una formazione. Il metodo del Monte Carlo prevede l’esecuzione di molteplici simulazioni casuali per stimare gli esiti potenziali. Ecco fatto! Avevo tutti gli strumenti che mi permettevano di scegliere la migliore formazione dalla mia rosa del Fantacalcio per ogni giornata di Serie A.

Dove l’algoritmo ha avuto successo
Come metrica aggiuntiva, ho confrontato i voti attesi con le mie aspettative soggettive e ho trovato i risultati soddisfacenti. L’algoritmo si è dimostrato particolarmente efficace nella variante Mantra del Fantacalcio, che prevede che i giocatori assumano più ruoli simili al calcio reale, dalla difesa centrale e laterale agli esterni e agli attaccanti. La selezione della formazione ottimale tra i moduli disponibili presentava una sfida complessa, poiché non era sempre il caso che i giocatori offensivi superassero quelli difensivi.
Inoltre, utilizzando l’algoritmo per prevedere la selezione di una squadra di Serie A statisticamente media come avversario, è stato utile per prepararsi all’asta di mercato di gennaio. Mi ha permesso di individuare giocatori sottovalutati che potevano essere stati sottovalutati dalle opinioni popolari.
Giocatori come El Shaarawy e Orsolini sono esempi notevoli di giocatori che si sono distinti particolarmente nelle fasi finali della stagione di Serie A. L’algoritmo ha previsto che le loro prestazioni fossero al livello di altri centrocampisti di alto livello già alla fine di gennaio.

Dove l’algoritmo ha fallito o potrebbe essere migliorato
Il punto debole dell’algoritmo risiede nella previsione delle prestazioni dei portieri. È stata sviluppata una rete neurale separata, utilizzando diverse caratteristiche e aggiungendo la probabilità di tenere la porta inviolata come output. Tuttavia, i risultati non sono stati soddisfacenti, probabilmente a causa del numero limitato di portieri (solo uno per squadra) rispetto ai giocatori di campo. Ciò ha comportato un dataset meno diversificato, aumentando il rischio di overfitting.
Inoltre, l’algoritmo considerava solo le statistiche medie di ciascun giocatore durante la stagione. Sebbene questo approccio fosse sufficiente, l’inclusione dei dati delle due o tre partite precedenti del giocatore prima di una determinata giornata potrebbe migliorare la capacità dell’algoritmo di valutare la sua forma attuale. Ciò fornirebbe una valutazione più completa delle recenti prestazioni del giocatore.
Tutto il lavoro pubblico
Puoi trovare il codice scritto per questo progetto, così come i risultati generati per diverse giornate di Serie A su Github. Ho intenzione di apportare ulteriori miglioramenti per la prossima stagione ogni volta che il tempo lo permette. Se hai domande o hai bisogno di chiarimenti, non esitare a contattarmi.