Eseguire previsioni con un modello ONNX AutoML in .NET

Questo articolo spiega come usare un modello Open Neural Network Exchange (ONNX) di Machine Learning automatizzato (AutoML) per eseguire previsioni in un'applicazione console C# .NET Core con ML.NET.

ML.NET è un framework di apprendimento automatico open source multipiattaforma per l'ecosistema .NET che consente di eseguire il training e usare modelli di apprendimento automatico personalizzati grazie a un approccio Code First in C# o F# e s strumenti a basso impiego di codice come Model Builder e l'interfaccia della riga di comando di ML.NET. Il framework è anche estendibile e consente di sfruttare altri framework di apprendimento automatico più diffusi, ad esempio TensorFlow e ONNX.

ONNX è un formato open source per i modelli di intelligenza artificiale. ONNX supporta l'interoperabilità tra framework. Ciò significa che è possibile eseguire il training di un modello in uno dei tanti framework di apprendimento automatico più diffusi, come PyTorch, convertirlo in formato ONNX e usare il modello ONNX in un framework diverso come ML.NET. Per altre informazioni, vedere il sito Web ONNX.

Prerequisiti

Creare un'applicazione console C#

In questo esempio si usa l'interfaccia della riga di comando di .NET Core per compilare l'applicazione, ma è possibile eseguire le stesse attività usando Visual Studio. Altre informazioni sull'interfaccia della riga di comando di .NET Core.

  1. Aprire un terminale e creare una nuova applicazione console C# .NET Core. In questo esempio il nome dell'applicazione è AutoMLONNXConsoleApp. Viene creata una directory con lo stesso nome contenente il contenuto dell'applicazione.

    dotnet new console -o AutoMLONNXConsoleApp
    
  2. Nel terminale passare alla directory AutoMLONNXConsoleApp.

    cd AutoMLONNXConsoleApp
    

Aggiungere pacchetti software

  1. Installare i pacchetti NuGet Microsoft.ML, Microsoft.ML.OnnxRuntimee Microsoft.ML.OnnxTransformer usando l'interfaccia della riga di comando di .NET Core.

    dotnet add package Microsoft.ML
    dotnet add package Microsoft.ML.OnnxRuntime
    dotnet add package Microsoft.ML.OnnxTransformer
    

    Questi pacchetti contengono le dipendenze necessarie per usare un modello ONNX in un'applicazione .NET. ML.NET fornisce un'API che usa il runtime ONNX per le stime.

  2. Aprire il file Program.cs e aggiungere le istruzioni using seguenti nella parte superiore per fare riferimento ai pacchetti appropriati.

    using System.Linq;
    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms.Onnx;
    

Aggiungere un riferimento al modello ONNX

Un modo per consentire all'applicazione console di accedere al modello ONNX prevede l'aggiunta del modello stesso alla directory di compilazione di output. Per altre informazioni sugli elementi comuni di MSBuild, vedere la guida di MSBuild. Se non si dispone già di un modello, seguire questo notebook per creare un modello di esempio.

Aggiungere un riferimento al file del modello ONNX nell'applicazione

  1. Copiare il modello ONNX nella directory radice AutoMLONNXConsoleApp dell'applicazione.

  2. Aprire il file AutoMLONNXConsoleApp.csproj e aggiungere il contenuto seguente all'interno del nodo Project.

    <ItemGroup>
        <None Include="automl-model.onnx">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
    </ItemGroup>
    

    In questo caso, il nome del file del modello ONNX è automl-model.onnx.

  3. Aprire il file Program.cs e aggiungere la riga seguente all'interno della classe Program.

    static string ONNX_MODEL_PATH = "automl-model.onnx";
    

Inizializzare MLContext

All'interno del metodo Main della classe Program creare una nuova istanza di MLContext.

MLContext mlContext = new MLContext();

La classe MLContext è un punto di partenza per tutte le operazioni ML.NET e l'inizializzazione di mlContext crea un nuovo ambiente ML.NET che può essere condiviso nel ciclo di vita del modello. È concettualmente simile a DbContext in Entity Framework.

Definire lo schema dei dati del modello

Il modello prevede un formato specifico per i dati di input e output. ML.NET consente di definire il formato dei dati tramite classi. A volte il formato dei dati potrebbe essere noto. Se invece non si conosce il formato dei dati, è possibile usare strumenti come Netron per controllare il modello ONNX.

Il modello usato in questo esempio usa i dati del set di dati NYC TLC Taxi Trip. Di seguito è riportato un esempio di dati:

vendor_id rate_code passenger_count trip_time_in_secs trip_distance payment_type fare_amount
VTS 1 1 1140 3.75 CRD 15.5
VTS 1 1 480 2.72 CRD 10.0
VTS 1 1 1680 7.8 CSH 26.5

Esaminare il modello ONNX (facoltativo)

Usare uno strumento come Netron per esaminare gli input e gli output del modello.

  1. Aprire Netron.

  2. Nella barra dei menu in alto selezionare File > Apri e usare il browser file per selezionare il modello.

  3. Verrà aperto il modello. Ad esempio, la struttura del modello automl-model.onnx è simile alla seguente:

    Netron AutoML ONNX Model

  4. Selezionare l'ultimo nodo nella parte inferiore del grafico (variable_out1 in questo caso) per visualizzare i metadati del modello. Gli input e gli output sulla barra laterale mostrano gli input, gli output e i tipi di dati previsti del modello. Usare queste informazioni per definire lo schema di input e output del modello.

Definire lo schema di input del modello

Creare una nuova classe denominata OnnxInput con le proprietà seguenti all'interno del file Program.cs.

public class OnnxInput
{
    [ColumnName("vendor_id")]
    public string VendorId { get; set; }

    [ColumnName("rate_code"),OnnxMapType(typeof(Int64),typeof(Single))]
    public Int64 RateCode { get; set; }

    [ColumnName("passenger_count"), OnnxMapType(typeof(Int64), typeof(Single))]
    public Int64 PassengerCount { get; set; }

    [ColumnName("trip_time_in_secs"), OnnxMapType(typeof(Int64), typeof(Single))]
    public Int64 TripTimeInSecs { get; set; }

    [ColumnName("trip_distance")]
    public float TripDistance { get; set; }

    [ColumnName("payment_type")]
    public string PaymentType { get; set; }
}

Ciascuna proprietà esegue il mapping a una colonna nel set di dati. Le proprietà vengono ulteriormente associate ad attributi mediante annotazioni.

L'attributo ColumnName consente di specificare come ML.NET deve fare riferimento alla colonna quando si opera sui dati. Ad esempio, anche se la proprietà TripDistance segue convenzioni di denominazione .NET standard, il modello conosce solo una colonna o una funzionalità nota come trip_distance. Per risolvere questa discrepanza di denominazione, l'attributo ColumnName esegue il mapping tra la proprietà TripDistance e una colonna o una funzionalità in base al nome trip_distance.

Per i valori numerici, ML.NET opera solo sui tipi valore Single. Tuttavia, il tipo di dati originale di alcune colonne è integer. L'attributo OnnxMapType esegue il mapping dei tipi tra ONNX e ML.NET.

Per altre informazioni sugli attributi dei dati, vedere la guida al caricamento dei dati ML.NET.

Definire lo schema di output del modello

Una volta elaborati i dati, viene generato un output di un determinato formato. Definire lo schema di output dei dati. Creare una nuova classe denominata OnnxOutput con le proprietà seguenti all'interno del file Program.cs.

public class OnnxOutput
{
    [ColumnName("variable_out1")]
    public float[] PredictedFare { get; set; }
}

Analogamente a OnnxInput, usare l'attributo ColumnName per eseguire il mapping tra l'output variable_out1 e un nome più descrittivo PredictedFare.

Definire una pipeline di previsione

Una pipeline in ML.NET è in genere una serie di trasformazioni concatenate che operano sui dati di input per produrre un output. Per altre informazioni sulle trasformazioni dei dati, vedere la guida alla trasformazione dei dati ML.NET.

  1. Creare un nuovo metodo denominato GetPredictionPipeline all'interno della classe Program

    static ITransformer GetPredictionPipeline(MLContext mlContext)
    {
    
    }
    
  2. Definire il nome delle colonne di input e output. Aggiungere il codice seguente all'interno del metodo GetPredictionPipeline.

    var inputColumns = new string []
    {
        "vendor_id", "rate_code", "passenger_count", "trip_time_in_secs", "trip_distance", "payment_type"
    };
    
    var outputColumns = new string [] { "variable_out1" };
    
  3. Definire la pipeline. Un oggetto IEstimator fornisce un progetto di operazioni, input e schemi di output della pipeline.

    var onnxPredictionPipeline =
        mlContext
            .Transforms
            .ApplyOnnxModel(
                outputColumnNames: outputColumns,
                inputColumnNames: inputColumns,
                ONNX_MODEL_PATH);
    

    In questo caso, ApplyOnnxModel è l'unica trasformazione nella pipeline, che accetta i nomi delle colonne di input e output, nonché il percorso del file di modello ONNX.

  4. Un'interfaccia IEstimator definisce solo il set di operazioni da applicare ai dati. Ciò che opera sui dati è noto come ITransformer. Usare il metodo Fit per crearne uno da onnxPredictionPipeline.

    var emptyDv = mlContext.Data.LoadFromEnumerable(new OnnxInput[] {});
    
    return onnxPredictionPipeline.Fit(emptyDv);
    

    Il metodo Fit prevede IDataView come input per eseguire le operazioni. IDataView è un modo per rappresentare i dati in ML.NET usando un formato tabulare. Poiché in questo caso la pipeline viene usata solo per le previsioni, è possibile fornire un valore IDataView vuoto per fornire all'interfaccia ITransformer le informazioni sullo schema di input e output necessarie. L'interfaccia ITransformer montata viene quindi restituita per un ulteriore uso nell'applicazione.

    Suggerimento

    In questo esempio la pipeline viene definita e usata all'interno della stessa applicazione. È tuttavia consigliabile usare applicazioni separate per definire e usare la pipeline per eseguire le previsioni. In ML.NET le pipeline possono essere serializzate e salvate per un ulteriore uso in altre applicazioni degli utenti finali .NET. ML.NET supporta diverse destinazioni di distribuzione, ad esempio applicazioni desktop, servizi Web, applicazioni WebAssembly* e molte altre ancora. Per altre informazioni sul salvataggio delle pipeline, vedere la guida ML.NET relativa al salvataggio e al caricamento di modelli con training.

    *WebAssembly è supportato solo in .NET Core 5 o versione successiva

  5. All'interno del metodo Main eseguire una chiamata al metodo GetPredictionPipeline con i parametri obbligatori.

    var onnxPredictionPipeline = GetPredictionPipeline(mlContext);
    

Usare il modello per effettuare previsioni

A questo punto si dispone di una pipeline e sarà pertanto possibile usarla per eseguire previsioni. ML.NET fornisce un'API utile per eseguire previsioni su una singola istanza di dati denominata PredictionEngine.

  1. All'interno del metodo Main creare una classe PredictionEngine usando il metodo CreatePredictionEngine.

    var onnxPredictionEngine = mlContext.Model.CreatePredictionEngine<OnnxInput, OnnxOutput>(onnxPredictionPipeline);
    
  2. Creare un input di dati di test.

    var testInput = new OnnxInput
    {
        VendorId = "CMT",
        RateCode = 1,
        PassengerCount = 1,
        TripTimeInSecs = 1271,
        TripDistance = 3.8f,
        PaymentType = "CRD"
    };
    
  3. Usare la classe predictionEngine per eseguire previsioni in base ai nuovi dati testInput usando il metodo Predict.

    var prediction = onnxPredictionEngine.Predict(testInput);
    
  4. Restituire il risultato della previsione nella console.

    Console.WriteLine($"Predicted Fare: {prediction.PredictedFare.First()}");
    
  5. Usare l'interfaccia della riga di comando di .NET Core per eseguire l'applicazione.

    dotnet run
    

    Il risultato sarà simile all'output seguente:

    Predicted Fare: 15.621523
    

Per altre informazioni sull'esecuzione di previsioni in ML.NET, vedere la guida all'uso di un modello per eseguire previsioni.

Passaggi successivi