
Dati, dati ovunque! Le nostre applicazioni estraggono dati dai servizi più disparati e, a loro volta, ne producono di nuovi. Dati da persistere e…poi? La griglia, il salvataggio in un formato leggibile da Excel, qualche plot: questi sono i probabili punti di arrivo della nostra fase di raccolta, analisi e produzione. Tipicamente, noi sviluppatori ci fermiamo al bottone “Esporta Excel”, cercando magari di trovare una maniera furba per trasformare le sofisticate collezioni di oggetti che descrivono il dominio in questo formato arcaico che appiattisce tutto su una riga, ripetuta magari milioni di volte.
C’è poi un mantra, che sentiamo ripetere ovunque: più dati abbiamo meglio possiamo analizzarli per scoprire strutture (pattern) nascoste che ci conducano alla risoluzione di problemi reali. Il problema è che questi pattern sono troppo complessi da trovare per l’occhio umano. Questo è il Machine Learning: uno strumento per trovare pattern e generare codice che ci aiuti a riconoscerli in nuovi dati, magari che stanno arrivando in tempo reale dalla nostra applicazione. L’ obiettivo, quindi, è tornare alla nostra applicazione e renderla più intelligente, consentendole, ad esempio, di anticipare problemi in una catena di montaggio industriale o scovare malware nelle nostre e-mail.
Tutto molto bello, ma c’è la convinzione in giro che il Machine Learning sia una sorta di lavatrice programmabile: butti dentro i panni sporchi (dati), scegli un programma, aggiungi uno o più detersivi, premi un tasto di avvio ti allontani per un po’, ritorni e il tuo model è bello e pronto. “Hai aggiunto le reti neurali? La prossima volta mettine un pochino di più!”
L’ intero processo è riassunto in questa immagine, capace di spaventare anche i più coraggiosi. (fonte: Introducing Azure Machine Learning)

“Basta conoscere un po’ di tecniche statistiche per analizzare i dati”. Questa è la seconda convinzione semplicistica che ascoltate in giro. Mentre tutti abbiamo un’idea di come si calcoli una media (vero?), sono pochi quelli che ricordano il teorema di Bayes.
Accettata l’idea che bisogna tornare a studiare, il terzo problema è che tutti i corsi partono da dataset che cercano di analizzare problemi classici: le mance dei taxi a New York, i prezzi a metro quadro degli appartamenti, la correlazione tra il peso di un nascituro e i dati disponibili sulla madre, etc. La creazione dei modelli fluisce in maniera più o meno naturale e sembra che tutto funzioni come il processo di lavaggio di una lavatrice. Poi passiamo ai nostri dati e non arriviamo ad alcun risultato che abbia senso.
Internet è pieno di articoli sulle correlazioni più bizzarre e senza senso (ad esempio qui). Il più interessante è quello in cui si conclude che gli sviluppatori che usano lo space guadagnino di più rispetto a quelli che usano il tab (un’analisi più approfondita di questi dati è contenuta in questo articolo di Evelina Gabasova).

In questo articolo (e nei prossimi), partirò da alcuni dati raccolti in questi ultimi mesi e relativi a partite di calcio e giocatori.
L’ idea è concentrarsi sulla fase di pre-processamento dei dati: ossia tutto ciò che serve a creare dei dataset puliti e ottimizzati da dare in pasto ai famosi algoritmi che conducano a un modello predittivo da usare su nuovi dati.
Una riga del nostro file di partenza contiene le seguenti informazioni relative a un match giocato da un singolo giocatore:

Nel dettaglio, abbiamo l’età del calciatore nel giorno del match, i minuti giocati, il suo ruolo (ad esempio, DC è un difensore centrale), il risultato della partita, le squadre coinvolte, numero di gol, assist, autogol, etc.
Questi dati, provenienti da un semplice scraping di pagine html, sono validi? Sono nella forma più corretta per un’analisi statistica?
Per cercare di capirlo, utilizzerò un servizio cloud, Azure Machine Learning. Lo scopo è semplificare un insieme di operazioni apparentemente triviali ma in grado di portare via tempo e risorse. L’indirizzo da cui partire è il seguente: https://studio.azureml.net
Una volta registrati, ci troviamo di fronte a questa schermata:

Abbiamo la possibilità di caricare sul portale un dataset in formato csv, importandolo dal nostro computer.


Una volta caricato il dataset sul portale, posso creare un notebook Jupyter per studiarlo. Si tratta di un’interfaccia interattiva per l’analisi dei dati basata su uno standard open source.
Il portale Azure offre la scelta tra R, Python 2 e Python 3 come linguaggi per inserire i comandi. Nel mio caso ho scelto R.

Il comando head mostra le prime righe del nostro dataset. Di seguito, invece, il risultato del comando summary(dat) che per ogni colonna mostra un riassunto statistico:

Vediamo che diversi dati hanno senso. Ad esempio, year va da un minimo di 2006 ad un massimo di 2019. minutes assume valori da 1 a 120 (evidentemente qualche partita è andata ai tempi supplementari). age va da 15,3 anni fino a un massimo di 32,55. La colonna month ha un valore minimo uguale a 0 mentre dovrebbe variare tra 1 e 12: ciò indica l’immancabile bug da correggere nell’ estrazione e conversione dei dati.
Scopriamo, invece, che altre variabili non hanno alcun senso, dal punto di vista di un’analisi statistica. Ad esempio, result è una proprietà che assume tre possibili valori: W (win), L (loss), D (deuce). In gergo si dice che result è una proprietà categorica. La maggior parte degli algoritmi di Machine Learning si aspetta dati numerici in ingresso.
Quindi dobbiamo trovare una maniera di trasformare result in una proprietà numerica. La procedura più diffusa è chiamata one-hot encoding. Piuttosto che rimpiazzare il valore categorico con uno numerico (arbitrario), definiamo tre nuove variabili numeriche che chiameremo, ad esempio, is_win, is_loss, is_deuce. Ciascuna di esse potrà assumere un valore che è 0 oppure 1.
Un’analoga procedura deve essere applicata al ruolo del giocatore. Purtroppo, i ruoli sono 12 e quindi la procedura di normalizzazione è alquanto tediosa. Ho pensato, per questa serie di articoli, di considerare solo 3 ruoli: is_defender (difensore), is_midfield (centrocampista) e is_forward (attaccante). Quest’analisi di normalizzazione mi ha portato a scoprire anche che nei dati iniziali mancavano le informazioni relative ai portieri (nella repository troverete il file json originale). Inoltre, in diverse righe manca completamente l’informazione sul ruolo.
Non esiste una formula esatta per gestire i dati mancanti perché la soluzione dipende principalmente dal contesto e dalla natura dei dati. Un’opzione è semplicemente rimuovere le righe in cui il dato è mancante. Tuttavia, questa soluzione non è molto popolare perché porta a perdere dati, soprattutto se c’è un pattern nella mancanza dei dati.
Altra tecnica diffusa è quella di rimpiazzare il valore nullo con la nostra migliore stima di quello che potrebbe essere il valore corretto. Nel nostro caso, potremmo andare a consultare un database delle partite giocate dal calciatore, capire in che ruolo ha giocato in esse e prendere il valore massimo. Per semplicità, mi sono limitato ad aggiungere una proprietà is_norole.
Come vedete, un semplice esempio può nascondere insidie e costringerci a scelte delle quali magari in seguito pentirci amaramente. I dati non sono mai perfetti!
La proprietà name che conserva il nome del calciatore probabilmente non ha senso a meno che non siate interessati a ricostruire la carriera di un singolo giocatore (ma in quel caso si tratterebbe di un problema di visualizzazione di dati e non di analisi). Escluderei anche i nomi delle squadre, lasciando solo l’informazione se il giocatore appartenga alla squadra di casa (is_home con due possibili valori: 0 e 1).
Gli algoritmi di Machine Learning traggono vantaggio anche dalla normalizzazione delle variabili numeriche che varino in un intervallo ben preciso. Ad esempio, minutes può essere espresso come un numero compreso tra 0 e 1 dividendolo per 120 (la durata massima della partita). Applichiamo lo stesso metodo a age, dividendolo per 16425 (numero ottenuto moltiplicando 45 anni * 365): un’altra scelta!
Il risultato finale del nostro primo tentativo di normalizzazione dei dati può essere caricato nuovamente su Azure Machine Learning Studio.


Possiamo ora caricare i dati relativi ad altre squadre. La repository Github dove troverete tutti i file è la seguente: https://github.com/sorrentmutie/FootballDataForMachineLearning
Nel complesso abbiamo 50770 righe di dati. Possiamo ritenerci soddisfatti? “Caricare la lavatrice”? In gergo, quelle che noi sviluppatori chiamiamo proprietà, sono dette feature. Abbiamo delle buone feature? Quando possiamo dire di avere una buona feature? La risposta è nei seguenti cinque punti:
- la feature deve essere pertinente all’obiettivo finale;
- deve essere nota nel momento della predizione;
- deve essere numerica con un’ampiezza che abbia significato;
- dobbiamo disporre di un buon numero di esempi;
- portare l’intuizione umana nel problema.
Come potete intuire, la strada è ancora lunga. Ma per oggi, ci fermiamo qui e continueremo il nostro viaggio nel prossimo articolo.