L’importanza dei dati
Ogni problema di classificazione o predizione basato su Machine Learning non può prescindere dalla disponibilità di grosse quantità di dati peraltro opportunamente organizzati e strutturati. I semplici Big Data — ammesso che siano davvero Big e davvero Data — non bastano.
Ogni operazione richiede un livello di focus estremo nell’analisi del problema alla radice, nella mappatura di esso su un algoritmo più o meno standard — o combinazione di vari algoritmi — e nella strutturazione dei dati da cui emergerà il modello finale pronto per andare in produzione.
La quantità di dati ragionevolmente necessaria per condurre in porto un progetto di Machine Learning è notevole: almeno dell’ordine dei gigabyte e a volte persino un ordine più su. Va da sé che il software che sostiene il processo di training, e il sottostante ambiente runtime, devono essere in grado di gestire lettura ed elaborazione di tali grandi quantità di dati in modo preciso ed efficace.
Questo è uno dei problemi di fondo che spesso portano a considerare ambienti di sviluppo di soluzioni Machine Learning diversi dal classico Python. In questo articolo vedremo la API di ML.NET per il caricamento e l’elaborazione dei dataset anche di grandi dimensioni.
Tipologie di dataset
Nella maggior parte dei progetti di Machine Learning si ha disponibilità di un solo insieme di dati che viene quindi utilizzato in larghissima parte per il training. Solo una frazione di esso — tipicamente, ma non necessariamente, un terzo — viene usato per testare il modello risultante e tirarne fuori delle metriche appropriate che consentano di darne una valutazione possibilmente oggettiva. Più in generale, però, esistono tre differenti tipologie di dataset: training dataset, validation set e test set.
Training dataset
Il training dataset fa riferimento ai soli dati usati per allenare il modello prima che esso venga messo in funzione in un ambiente di produzione. È il dataset che contiene il maggior numero di dati ed è anche il dataset che determina la qualità del risultato finale, che sia classificazione o predizione.
È fondamentale, infatti, che i dati siano simili nei valori e nella distribuzione ai dati che il modello elaborerà in produzione. Ove i dati riscontrati in produzione fossero significativamente diversi — per peso, valori e distribuzione — da quelli di training, allora la performance del modello risulterebbe scadente.
Validation set
Il validation set fa riferimento ai dati su cui il modello viene valutato nella fase di training. In termini di software testing si potrebbe associare l’idea del validation set a quella degli unit test o anche degli integration test. Il validation set è spesso solo un sottoinsieme del training set, anche se non è infrequente che si tratti di un dataset indipendente e persino di dimensioni paragonabili.
Lo scopo principale del validation set è quello di permettere il fine tuning degli iperparametri dell’algoritmo che definisce il modello finale. Ciascun algoritmo ha una serie di parametri — tipicamente numero massimo di iterazioni e soglia di tolleranza dell’errore — che possono essere modificati in modo da produrre sperabilmente risultati migliori la volta successiva.
Molto spesso infatti i valori di questi iperparametri sono valori default o addirittura valori scelti a caso. Verificando la resa del modello a fronte di un validation set, il team può decidere come modificare gli iperparametri prima di procedere ad una nuova seduta di training.
Test set
Il test set serve per una validazione definitiva del modello prodotto dalla fase di training. Il test set dà la misura di come il modello funzionerà nel mondo reale. Va da sé che, per essere efficace, soprattutto il contenuto del test set deve coincidere con i valori e la distribuzione dei valori nel mondo reale, persino più di quelli del validation set. Il ruolo del test set paragonato al ruolo dei test nel mondo software è confrontabile con gli acceptance tests.
Caricamento dei Dati in ML.NET
Il punto di ingresso nella libreria ML.NET è l’oggetto MLContext. Una sua istanza viene utilizzata più o meno come si userebbe l’oggetto che rappresenta la connessione a un database ed è anche il punto di ingresso in ciascuna pipeline di Machine Learning che si crea con ML.NET. Ecco come crearne un’istanza.
var mlContext = new MLContext();
Una sola istanza è condivisa da tutti gli oggetti che in qualche modo partecipano al workflow da cui risulterà il modello finale pienamente allenato. Più in generale un’istanza di MLContext serve anche per caricare in produzione un modello già allenato e agisce come il contenitore centralizzato di qualsiasi operazione di Machine Learning, che si tratti di semplice caricamento di dati, feature engineering, training, validazione o persistenza.
L’interfaccia IDataView
La libreria ML.NET utilizza una specifica interfaccia per il caricamento dei dati: l’interfaccia IDataView. In particolare, si tratta di un cursore specificamente disegnato per muoversi agevolmente anche su milioni di righe e terabyte di dati.
Tutti gli algoritmi integrati nella libreria ML.NET caricano i dati tramite i metodi dell’interfaccia. La libreria fornisce nativamente alcuni loader di dati compatibili con l’interfaccia. In particolare, ve n’è uno per i formati di testo CSV e TSV, per file binari e per sorgenti dati compatibili con IEnumerable. Esiste anche la possibilità di caricare dati tramite un database loader che si interfaccia con le sorgenti dati per cui esiste un provider ADO.NET: praticamente tutti i database relazionali.
A proposito di IDataView è interessante notare come gli oggetti che la implementano abbiano una cache di memoria e metodi per salvare su disco il contenuto. Ecco un esempio di come caricare dati da un file di testo. La proprietà Data esposta dal MLContext è di tipo DataOperationsCatalog.
var mlContext = new MLContext(); // Load data into the pipeline var dataView = mlContext.Data.LoadFromTextFile<ModelInput>(INPUT_FILE);
In particolare, l’esempio carica il contenuto del file di testo — si assume del tipo CSV o TSV — nelle proprietà pubbliche della classe ModelInput. Trattandosi di una sorgente dati posizionale, il mapping avviene o per ordine di definizione delle proprietà — la prima colonna va sulla prima proprietà definita e così via — o tramite la posizione assegnata alla proprietà della classe ModelInput attraverso l’attribute LoadColumn(position).
L’effettivo caricamento del dato non avviene con l’esecuzione del metodo LoadFromTextFile ma solamente con l’esecuzione dell’algoritmo di training.
Ulteriori trasformazioni
Fino a questo punto abbiamo assunto che i dati memorizzati nel file caricato non abbiano bisogno di ulteriori trasformazioni. È uno scenario possibile, ma non certamente unico. È possibile, infatti, che i dati siano parcheggiati in un data warehouse in un formato strutturato ma intermedio e non direttamente compatibile con le necessità del training. Pertanto, è possibile applicare, nel codice che esegue il training, trasformazioni dedicate.
ML.NET fornisce strumenti appositi. Tipiche trasformazioni sono l’aggiunta di nuove colonne — per esempio, colonne calcolate — e la conversione di stringhe e valori enumerati in numeri reali. Un altro esempio può essere la rimozione di alcune righe — per esempio, picchi di valori — fatta temporaneamente senza sporcare la sorgente originale e solo per provare a migliorare la resa del training.
Ecco un esempio di aggiunta di una colonna Label che duplica i valori della colonna FareAmount.
mlContext.Transforms.CopyColumns("Label", "FareAmount");
La modifica dei dati allo scopo di training è operazione che può avvenire direttamente sulla sorgente (file, warehouse) e diventare persistente oppure può essere fatta in memoria usando le API di ML.NET.
Conclusioni
È ovvio che il Machine Learning abbia bisogno di grandi quantità di dati e che tali dati, oltre ad essere raccolti e collezionati in qualche modo, debbano anche essere ripuliti ed elaborati in memoria. Le popolarissime librerie Python per la maggior parte permettono il solo caricamento brutale in memoria con il risultato che, per dataset di parecchi GB, la memoria della macchina si azzera e, se si è in cloud, i costi aumentano.
ML.NET nasce invece con il preciso obiettivo di minimizzare questo problema e lo fa tramite un’interfaccia intelligente cursor-style che tiene la complessità in spazio al minimo possibile.
Da oltre 25 anni, Dino Esposito è un punto di riferimento internazionale nel contesto delle tecnologie web e, in generale, dello sviluppo software, soprattutto su piattaforma Microsoft.
Autore di oltre 20 libri e almeno un migliaio di articoli, ha contribuito ad avviare la digital transformation nel mondo del tennis professionistico e attualmente si occupa di intelligenza artificiale e machine learning nell’ambito di energia, ambiente e smart cities.
https://www.linkedin.com/in/dinoesposito
https://www.instagram.com/_youbiquitous