Fazer previsões com um modelo AutoML ONNX no .NET

Neste artigo, será mostrado como usar um modelo de intercâmbio de rede neural (ONNX) aberto do ML (AutoML) para fazer previsões em um aplicativo de console do C# com ML.NET.

O ML.NET é uma estrutura de aprendizado de máquina de código aberto e de plataforma cruzada para o ecossistema do .NET que permite treinar e consumir modelos de aprendizado de máquina personalizados usando uma abordagem de code-first em C# ou F#, ou por meio de ferramentas de pouco código, como o Model Builder e a CLI do ML.NET. A estrutura é extensível e permite aproveitar outras estruturas de aprendizado de máquina populares, como TensorFlow e ONNX.

O ONNX é um formato open-source para modelos de IA. O ONNX é compatível com a interoperabilidade entre estruturas. Isso significa que você pode treinar um modelo em uma das muitas estruturas de aprendizado de máquina populares, como PyTorch, convertê-lo em formato ONNX e consumir o modelo ONNX em uma estrutura diferente, como ML.NET. Para saber mais, visite o site do ONNX.

Pré-requisitos

Criação de um aplicativo de console em C#

Neste exemplo, é usada a CLI do .NET para compilar o aplicativo, mas é possível fazer as mesmas tarefas usando o Visual Studio. Saiba mais sobre a CLI do .NET.

  1. Abra um terminal e crie um novo aplicativo de console do .NET em C#. Neste exemplo, o nome do aplicativo é AutoMLONNXConsoleApp. Um diretório é criado com esse mesmo nome com o conteúdo do aplicativo.

    dotnet new console -o AutoMLONNXConsoleApp
    
  2. No terminal, navegue até o diretório AutoMLONNXConsoleApp.

    cd AutoMLONNXConsoleApp
    

Adicionar pacotes de software

  1. Instale os pacotes NuGet Microsoft.ML, Microsoft.ML.OnnxRuntimee Microsoft.ML.OnnxTransformer usando a CLI do .NET.

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

    Esses pacotes contêm as dependências necessárias para usar um modelo ONNX em um aplicativo .NET. O ML.NET fornece uma API que usa o runtime do ONNX para previsões.

  2. Abra o arquivo Program.cs e adicione as seguintes diretivas using na parte superior.

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

Adicionar uma referência ao modelo ONNX

Uma maneira do aplicativo de console acessar o modelo ONNX é adicioná-lo ao diretório de saída de compilação. Caso ainda não tenha um modelo, siga este notebook para criar um modelo de exemplo.

Para adicionar uma referência ao arquivo do modelo ONNX em seu aplicativo:

  1. Copie o modelo ONNX para o diretório raiz AutoMLONNXConsoleApp do seu aplicativo.

  2. Abra o arquivo AutoMLONNXConsoleApp.csproj e adicione o conteúdo a seguir dentro do nó Project.

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

    Nesse caso, o nome do arquivo do modelo ONNX é automl-model.onnx.

    (Para saber mais sobre os itens comuns do MSBuild, consulte o Guia do MSBuild.)

  3. Abra o arquivo Program.cs e adicione as linhas a seguir na classe Program.

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

Inicializar MLContext

Dentro do Main método da sua Program classe, crie uma nova instância do MLContext.

MLContext mlContext = new MLContext();

A classe MLContext é um ponto de partida para todas as operações do ML.NET e a inicializar o mlContext cria um ambiente do ML.NET que pode ser compartilhado ao longo do ciclo de vida do modelo. Ele é semelhante, conceitualmente, a DbContext no Entity Framework.

Definir o esquema de dados do modelo

Um modelo espera os dados de entrada e saída em um formato específico. O ML.NET permite definir o formato dos dados por meio de classes. Às vezes, a aparência desse formato pode já ser conhecida. Em casos em que o formato dos dados não é conhecido, é possível usar ferramentas como Netron para inspecionar o modelo ONNX.

O modelo usado neste exemplo usa dados do conjunto de dados do NYC TLC Taxi Trip. Um exemplo dos dados é mostrado na tabela a seguir:

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

Inspecionar o modelo ONNX (opcional)

Use uma ferramenta como Netron para inspecionar as entradas e saídas do modelo.

  1. Abra o Netron.

  2. Na barra de menus superior, selecione Arquivo > Abrir e use o navegador de arquivos para selecionar o modelo.

  3. Seu modelo é aberto. Por exemplo, a estrutura do modelo automl-model.onnx é semelhante ao seguinte:

    Modelo Netron AutoML ONNX

  4. Selecione o último nó na parte inferior do grafo (variable_out1, neste caso) para exibir os metadados do modelo. As entradas e saídas na barra lateral mostram as entradas, as saídas e os tipos de dados esperados pelo modelo. Use essas informações para definir o esquema de entrada e saída do modelo.

Definir esquema de entrada de modelo

Crie uma nova classe chamada OnnxInput com as propriedades a seguir dentro do arquivo 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; }
}

Cada uma das propriedades é mapeada para uma coluna no conjunto de dados. As propriedades são anotadas com mais detalhes com atributos.

O atributo ColumnName permite especificar como o ML.NET deve referenciar a coluna ao operar com os dados. Por exemplo, embora a propriedade TripDistance siga as convenções de nomenclatura padrão do .NET, o modelo só conhece uma coluna ou um recurso conhecido como trip_distance. Para resolver essa discrepância de nomenclatura, o atributo ColumnName mapeia a propriedade TripDistance para uma coluna ou recurso de nome trip_distance.

Para valores numéricos, o ML.NET só opera com tipos de valor Single. No entanto, o tipo de dados original de algumas colunas são inteiros. O atributo OnnxMapType mapeia tipos entre o ONNX e o ML.NET.

Para saber mais sobre atributos de dados, consulte o Guia de dados de carga do ML.NET.

Definir o esquema de saída do modelo

Depois que os dados são processados, ele produz uma saída com um determinado formato. Definir o esquema de saída de dados. Crie uma nova classe chamada OnnxOutput com as propriedades a seguir dentro do arquivo Program.cs.

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

Semelhante ao OnnxInput, use o atributo ColumnName para mapear a saída variable_out1 para um nome mais descritivo PredictedFare.

Definir um pipeline de previsão

Um pipeline no ML.NET normalmente é uma série de transformações encadeadas que operam com os dados de entrada para produzir uma saída. Para saber mais sobre transformações de dados, consulte o Guia de transformação de dados do ML.NET.

  1. Criar um novo método chamado GetPredictionPipeline dentro da classe Program

    static ITransformer GetPredictionPipeline(MLContext mlContext)
    {
    
    }
    
  2. Defina o nome das colunas de entrada e saída. Adicione o seguinte código dentro do método 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. Definir o pipeline. Um IEstimator fornece um blueprint das operações e os esquemas de entrada e de saída do pipeline.

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

    Nesse caso, ApplyOnnxModel é a única transformação no pipeline, que usa os nomes das colunas de entrada e saída, bem como o caminho para o arquivo de modelo ONNX.

  4. Um IEstimator só define o conjunto de operações a serem aplicadas aos dados. O que opera com os dados é conhecido como um ITransformer. Use o método Fit para criar um do onnxPredictionPipeline.

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

    O método Fit espera um IDataView como entrada para executar as operações. Um IDataView é uma maneira de representar dados no ML.NET usando um formato tabular. Como nesse caso o pipeline é usado apenas para previsões, é possível fornecer um IDataView vazio para fornecer ITransformer, as informações necessárias de esquema de entrada e saída. O ITransformer ajustado é então retornado para uso posterior no aplicativo.

    Dica

    Neste exemplo, o pipeline é definido e usado dentro do mesmo aplicativo. No entanto, é recomendável usar aplicativos separados para definir e usar o pipeline para fazer previsões. No ML.NET, os pipelines podem ser serializados e salvos para uso adicional em outros aplicativos de usuário final do .NET. O ML.NET dá suporte a vários destinos de implantação, como aplicativos de área de trabalho, serviços Web, aplicativos WebAssembly e muito mais. Para saber mais sobre como salvar pipelines, consulte o Guia de modelos treinados para salvar e carregar ML.NET.

  5. Dentro do método Main, chame o método GetPredictionPipeline com os parâmetros necessários.

    var onnxPredictionPipeline = GetPredictionPipeline(mlContext);
    

Usar o modelo para fazer previsões

Agora que já tem um pipeline, é hora de usá-lo para fazer previsões. O ML.NET fornece uma API de conveniência para fazer previsões em uma única instância de dados chamada PredictionEngine.

  1. Dentro do método Main, crie um PredictionEngine usando o método CreatePredictionEngine.

    var onnxPredictionEngine = mlContext.Model.CreatePredictionEngine<OnnxInput, OnnxOutput>(onnxPredictionPipeline);
    
  2. Criar um banco de dados de teste.

    var testInput = new OnnxInput
    {
        VendorId = "CMT",
        RateCode = 1,
        PassengerCount = 1,
        TripTimeInSecs = 1271,
        TripDistance = 3.8f,
        PaymentType = "CRD"
    };
    
  3. Use o predictionEngine para fazer previsões com base nos novos testInput dados usando o método Predict.

    var prediction = onnxPredictionEngine.Predict(testInput);
    
  4. Gere o resultado de sua previsão para o console.

    Console.WriteLine($"Predicted Fare: {prediction.PredictedFare.First()}");
    
  5. Use a CLI do .NET para executar o aplicativo.

    dotnet run
    

    O resultado deve ser semelhante à saída a seguir:

    Predicted Fare: 15.621523
    

Para saber mais sobre como fazer previsões no ML.NET, confira Usar um modelo para fazer previsões.

Próximas etapas