Contattaci

AI e ML.NET

  • Data: 10 Aprile 2019
  • Autore: Marco Milani
  • Categorie

  • Giuneco Tech

    Il nostro settore richiede uno studio continuo e una forte attenzione all’innovazione. Incentiviamo quindi dei programmi formativi annuali per tutti i componenti del team, con ore dedicate (durante l’orario di lavoro) e serate formative sia online che in presenza. Sponsorizziamo eventi, sia come partner che semplicemente come partecipanti, e scriviamo articoli su quello che abbiamo imparato per essere, a nostra volta, dei divulgatori.

    Vai alla sezione Team
  • Nei sistemi moderni il termine Machine Learning è sempre più presente, grazie ai suoi molteplici campi di applicazione e alla creazione di modelli sempre più affidabili. Spesso la definizione è utilizzata come sinonimo di intelligenza artificiale, anche se, come vedremo, non è del tutto corretto.

    Cos’è l’intelligenza artificiale

    L’intelligenza artificiale è una delle più recenti discipline dell’informatica: la sua origine infatti risale agli anni 50 e il termine stesso fu coniato nel 1956 dal matematico statunitense John McCarthy.

    L’intelligenza artificiale è suddivisa in un grande numero di sottodiscipline: alcune aree, come l’apprendimento e la percezione, hanno un campo di azione generale, mentre altre si occupano di problemi specifici come teorie dei giochi, dimostrazione di teoremi matematici, guida di veicoli autonomi o diagnosi di malattie.

    La disciplina è stata sviluppata attraverso queste quattro aree:

    • Pensare Umanamente
    • Pensare Razionalmente
    • Agire Umanamente
    • Agire Razionalmente

    Negli sviluppi incentrati sugli esseri umani l’intelligenza artificiale si avvicina alle scienze empiriche, richiedendo ipotesi e osservazioni sul comportamento umano; un approccio razionale, invece, sfrutta una combinazione di matematica e ingegneria.

    Si definisce quindi razionale un sistema che, date le proprie conoscenze dell’ambiente, è poi in grado di effettuare “la scelta giusta”; dove per la scelta giusta si intende quella soluzione che permette di ottenere il migliore risultato atteso.

    Per la ricerca della scelta giusta sono stati implementati e descritti svariati algoritmi decisionali (ad esempio, A*), con altrettante metodologie di ottimizzazione e riduzione di calcoli (Pruning).

    Una metodologia di ricerca della scelta ottimale è il Machine Learning, che studia algoritmi capaci di migliorare automaticamente le proprie performance attraverso l’esperienza e l’apprendimento.

    Apprendimento

    Il Machine Learning (apprendimento automatico), nel seguito anche ML, è l’abilità delle macchine di apprendere senza algoritmi specifici per la risoluzione di un problema. La definizione più accreditata è quella fornita da Tom M. Mitchell:

    «si dice che un programma apprende dall’esperienza E con riferimento a alcune classi di compiti T e con misurazione della performance P, se le sue performance nel compito T, come misurato da P, migliorano con l’esperienza E».

    È chiaro quindi che c’è apprendimento quando le prestazioni del programma migliorano dopo lo svolgimento di un compito. Ma quando il Machine Learning è importante per lo sviluppo di sistemi?

    • Quando non è possibile prevedere tutte le configurazioni in cui un sistema si può trovare.
    • Nel caso in cui non è possibile prevedere tutti i cambiamenti di stato nel tempo.
    • Quando si sviluppano sistemi per cui l’apprendimento automatico si è rivelato essere più efficace di un approccio tradizionale (ad esempio, Gioco degli scacchi e riconoscimento oggetti).

    L’esperienza che un algoritmo di ML può avere durante il processo di apprendimento può essere raggruppata in due categorie:

    • Algoritmi Supervisionati
      • I dataset usati contengono molte feature (vettore di dati raccolti) e hanno target (o “label” risultato atteso) o label
    • Algoritmi non Supervisionati
      • I dataset usati contengono feature ma nessun target perché questi sono incentrati sull’apprendimento delle proprietà delle strutture dati.

    Nel problema di classificazione dell’esempio successivo utilizzeremo un algoritmo supervisionato in quanto la classe di destinazione di ogni record è nota. Se il dato di classe non fosse noto, potremmo utilizzare un algoritmo non supervisionato, riuscendo a raggruppare i record in cluster di dati.

    Gli obiettivi di un algoritmo di apprendimento possono essere molteplici:

    • Regressione:
      • Dati valori di input predire un valore di output. (price prediction)
    • Classificazione:
      • Dati valori di input dati predire categorie o classi dati.
    • Riduzione della dimensionalità:
      • Ridurre le dimensioni dei dati preservando più informazioni possibili.
    • Detenzione di anomalie:
      • Ricercare elementi atipici all’interno di set di dati o di eventi (rilevamento frodi carta di credito).
    • Sintesi e campionamento:
      • Generare nuovi esempi simili a quelli dei dataset.

    Ma come possiamo integrare un algoritmo di Machine Learning all’interno della nostra applicazione?

    ML.NET (versione usata 0.8.0)

    ML.NET è un framework cross platform, sviluppato da Microsoft Research, per integrare soluzioni di Machine Learning all’interno di applicazioni .NET ed è ampiamente usato all’interno di servizi Microsoft come Windows, Bing e Azure.

    Nei suoi primi rilasci il framework era incentrato sulla risoluzione di problemi di classificazione, analisi sentimento e previsioni. Inoltre esponeva i suoi componenti core di trasformazione dati, algoritmi di apprendimento e strutture dati di dominio.

    Presumibilmente, le prossime versioni implementeranno nuove funzionalità come rilevamento di anomalie, analisi di log, suggerimenti di ottimizzazione estendendo note librerie di deep learning come TensorFlow o Caffe2 e librerie di Machine Learning generiche come Accord.NET.

    ML.NET espone tutte le funzionalità necessarie alla creazione di un modello ML:

    • Caricamento dei dati
      • Text (csv, txt)
      • Parquet
      • Binary
      • IEnumerable<T>
      • File sets
    • Trasformazione
      • Trasformazioni testuali
      • Cambiamento nello schema
      • Gestione dati mancanti
      • Codifica categorie
      • Normalizzazione
      • Selezione dei dati rilevanti in un training set
      • NGRAM
    • Algoritmi
      • Linear (e.g. SymSGD, SDCA)
      • Boosted Trees (e.g. FastTree, LightGBM)
      • K-Means
      • SVM
      • Averaged Perceptron
    • Addestramento
    • Valutazione modello
    • Distribuzione

    Un punto fondamentale durante l’implementazione di un algoritmo è la valutazione del modello per determinarne la capacità di risoluzione del problema. Nel caso in cui la valutazione non dia risultati soddisfacenti è necessario variare i parametri dell’algoritmo o provarne uno diverso.

    Di seguito, un diagramma che rappresenta il flusso di implementazione di un algoritmo di machine learning.

    Esempio: Red Wine Quality Predictor

    L’obiettivo dell’esempio è quello di creare un modello di classificazione per determinare la qualità di un vino chiamato “Vinho Verde” a partire da un’analisi chimica.

    Per il training di questo modello è stato utilizzato un algoritmo di classificazione chiamato:

    Stochastic Dual Coordinate Ascent

    I sorgenti sono disponibili su Github:

    https://github.com/markjackmilian/RWQPredictor

    Il training set utilizzato è invece disponibile qui:

    https://archive.ics.uci.edu/ml/datasets/Wine+Quality

    Come primo passo occorre creare un’applicazione console .NET Core, aggiungendo un riferimento al pacchetto nuget “Microsoft.ML” (versione 0.8).

    Successivamente si definiscono due classi di dominio:

    • WineData
      • Rappresenta una singola analisi chimica
    • WinePrediction
      • Rappresenta il risultato predetto della qualità del vino
    public class WineData
     {
         public float FixedAcidity { get; set; }
         public float VolatileAcidity { get; set; }
         public float CitricAcidity { get; set; }
         public float ResidualSugar { get; set; }
         public float Chlorides { get; set; }
         public float FreeSulfuDioxide { get; set; }
         public float TotalSulfuDioxide { get; set; }
         public float Density { get; set; }
         public float Ph { get; set; }
         public float Sulphates { get; set; }
         public float Alcohol { get; set; }
     }
     
     public class WinePrediction
     {
         public float[] Score { get; set; }
     }

    Il passo seguente consiste nello scaricare il training set dall’archivio UCI Machine Learning Repository,  estrapolando alcune righe di analisi per verificare la bontà dell’allenamento.

    Si creano poi due elementi per testare il livello di predizione su campioni non presenti nel training.

    public class WineDataExamples
     {
         public static WineData Test1 => new WineData()
         {
             FixedAcidity = 6.6f,
             VolatileAcidity = 0.5f,
             CitricAcidity = 0.01f,
             ResidualSugar = 1.5f,
             Chlorides = 0.06f,
             FreeSulfuDioxide = 17,
             TotalSulfuDioxide = 26,
             Density = 0.9952f,
             Ph = 3.4f,
             Sulphates = 0.58f,
             Alcohol = 9.8f
         };
         
         public static WineData Test2 => new WineData()
         {
             FixedAcidity = 7.6f,
             VolatileAcidity = 0.49f,
             CitricAcidity = 0.33f,
             ResidualSugar = 1.9f,
             Chlorides = 0.074f,
             FreeSulfuDioxide = 27,
             TotalSulfuDioxide = 85,
             Density = 0.99706f,
             Ph = 3.41f,
             Sulphates = 0.58f,
             Alcohol = 9f
         };
         
     }

    Sono state incluse, (apportando qualche modifica per il dominio di implementazione) alcune classi dal repository github degli esempi di ML.NET:

    https://github.com/dotnet/machinelearning-samples

    L’implementazione segue l’impostazione degli esempi del repository Github, dividendo il flusso in due metodi:

    • BuildTrainEvaluateAndSaveModel()
      • Legge i dati, crea la pipeline, allena il modello e lo persiste
    • TestSomePredictions()
      • Inizializza un modello leggendo la rete appena creata e in seguito la testa utilizzando elementi non presenti nel training set.
    static async Task Main(string[] args)
    {
        try
        {
            // Create MLContext to be shared across the model creation workflow objects 
            // Set a random seed for repeatable/deterministic results across multiple trainings.
            var mlContext = new MLContext(DateTime.Now.Millisecond);
    
            // Read data, create model and save it
            BuildTrainEvaluateAndSaveModel(mlContext);
    
            // Test prediction
            TestSomePredictions(mlContext);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
    

    All’interno del primo metodo viene creato un oggetto “TextLoader”, che rappresenta la definizione dello schema dei dati in ingresso (Train e TestData) definendo separatore, presenza di riga di header e le colonne con tipo dato. Per informazioni circa i tipi dato facciamo riferimento al seguente link:

    https://github.com/dotnet/machinelearning/blob/master/docs/code/IDataViewTypeSystem.md

    public static TextLoader CreateTextLoader(MLContext mlContext)
    {
        return  mlContext.Data.TextReader(new TextLoader.Arguments()
        {
            Separator = ";",
            HasHeader = true,
            Column = new[]
            {
                new TextLoader.Column("FixedAcidity", DataKind.R4, 0),
                new TextLoader.Column("VolatileAcidity", DataKind.R4, 1),
                new TextLoader.Column("CitricAcidity", DataKind.R4, 2),
                new TextLoader.Column("ResidualSugar", DataKind.R4, 3),
                new TextLoader.Column("Chlorides", DataKind.R4, 4),
                new TextLoader.Column("FreeSulfuDioxide", DataKind.R4, 5),
                new TextLoader.Column("TotalSulfuDioxide", DataKind.R4, 6),
                new TextLoader.Column("Density", DataKind.R4, 7),
                new TextLoader.Column("Ph", DataKind.R4, 8),
                new TextLoader.Column("Sulphates", DataKind.R4, 9),
                new TextLoader.Column("Alcohol", DataKind.R4, 10),
                
                new TextLoader.Column("Label", DataKind.R4, 11),
    
            }
        });
    }
    

    Successivamente si crea la “pipeline” necessaria alla gestione e trasformazione dei dati in modo che il modello possa utilizzarli in fase di training.

    Nell’esempio proposto quindi la pipeline si limita a creare un vettore “Features” dalle colonne dei dati di analisi chimica del vino.

    var dataProcessPipeline = mlContext.Transforms.Concatenate("Features","FixedAcidity","VolatileAcidity", "CitricAcidity","ResidualSugar","Chlorides","FreeSulfuDioxide","TotalSulfuDioxide", "Density", "Ph", "Sulphates","Alcohol" );

    È possibile poi creare il modello e avviare il training.

    Var modelBuilder = new Common.ModelBuilder<WineData, WinePrediction>(mlContext, dataProcessPipeline);
        var trainer = mlContext.MulticlassClassification.Trainers.StochasticDualCoordinateAscent(labelColumn: “Label”, featureColumn: “Features”);
        modelBuilder.AddTrainer(trainer);
    
    modelBuilder.Train(trainingDataView);

    La fase successiva consiste quindi nel testare la bontà dell’apprendimento usando i dati di controllo:

    Var metrics = modelBuilder.EvaluateMultiClassClassificationModel(testDataView, “Label”);

    Come ultimo passaggio, viene salvato lo stato del modello in modo da poterlo riutilizzare all’interno di altre applicazioni.

    modelBuilder.SaveModelAsFile(ModelPath);

    Di seguito il metodo completo:

    private static void BuildTrainEvaluateAndSaveModel(MLContext mlContext)
    {
         // Loading configuration
        var textLoader = WineLoaderFactory.CreateTextLoader(mlContext);
        var trainingDataView = textLoader.Read(TrainDataPath);
        var testDataView = textLoader.Read(TestDataPath);
    
        // Create pipeline
        var dataProcessPipeline = mlContext.Transforms.Concatenate("Features", "FixedAcidity",
                                                                               "VolatileAcidity",
                                                                               "CitricAcidity",
                                                                               "ResidualSugar" ,
                                                                               "Chlorides" ,
                                                                               "FreeSulfuDioxide" ,
                                                                               "TotalSulfuDioxide" ,
                                                                               "Density" ,
                                                                               "Ph",
                                                                               "Sulphates" ,
                                                                               "Alcohol" );
    
        //  Set the training algorithm                        
        var modelBuilder = new Common.ModelBuilder<WineData, WinePrediction>(mlContext, dataProcessPipeline);
        var trainer = mlContext.MulticlassClassification.Trainers.StochasticDualCoordinateAscent(labelColumn: "Label", featureColumn: "Features");
        modelBuilder.AddTrainer(trainer);
    
        Console.WriteLine("=============== Training the model ===============");
        modelBuilder.Train(trainingDataView);
    
        Console.WriteLine("===== Evaluating Model's accuracy with Test data =====");
        var metrics = modelBuilder.EvaluateMultiClassClassificationModel(testDataView, "Label");
        Common.ConsoleHelper.PrintMultiClassClassificationMetrics(trainer.ToString(), metrics);
    
        Console.WriteLine("=============== Saving the model to a file ===============");
        modelBuilder.SaveModelAsFile(ModelPath);
    }
    

    Il metodo successivo mostra come caricare e interrogare il modello precedentemente allenato.

    private static void TestSomePredictions(MLContext mlContext)
    {
        var modelScorer = new Common.ModelScorer<WineData, WinePrediction>(mlContext);
        modelScorer.LoadModelFromZipFile(ModelPath);
    
        var prediction = modelScorer.PredictSingle(WineDataExamples.Test1);
        Console.WriteLine($"Expected: 6");
        for (int i = 0; i < prediction.Score.Length; i++)
            Console.WriteLine($"{i}:  {prediction.Score[i]:0.####}");
        
        prediction = modelScorer.PredictSingle(WineDataExamples.Test2);
        Console.WriteLine($"Expected: 5");
        for (int i = 0; i < prediction.Score.Length; i++)
            Console.WriteLine($"{i}:  {prediction.Score[i]:0.####}");
    }
    

    L’esecuzione del programma produce il seguente output:

    Il risultato della classificazione mostra le probabilità che il vino rientri nella categoria/qualità.

    Il primo esempio si aspetta una qualità “6” e possiamo vedere che la probabilità più alta è proprio sulla categoria 6 (0.5204), mentre il secondo esempio è di categoria “5” e anche in questo caso il modello mostra la probabilità più alta proprio in corrispondenza della categoria 5 (0.8858).

    In entrambi i casi il modello creato è stato in grado di prevedere correttamente la categoria attesa, dimostrando come sia possibile creare un proprio modello di machine learning mediante ML.NET.