Compartir a través de


Tutorial de Windows ML

Este breve tutorial le guía por el uso de Windows ML para ejecutar el modelo de clasificación de imágenes ResNet-50 en Windows, detallando los pasos de adquisición y preprocesamiento de modelos. La implementación implica seleccionar dinámicamente proveedores de ejecución para optimizar el rendimiento de la inferencia.

El modelo ResNet-50 es un modelo pyTorch diseñado para la clasificación de imágenes.

En este tutorial, obtendrá el modelo ResNet-50 de Hugging Face y lo convertirá a formato ONNX de QDQ usando el AI Toolkit.

A continuación, cargará el modelo, preparará los tensores de entrada y ejecutará la inferencia mediante las API de Windows ML, incluidos los pasos posteriores al procesamiento para aplicar softmax y recuperará las predicciones principales.

Adquisición del modelo y preprocesamiento

Puede adquirir ResNet-50 desde Hugging Face (la plataforma donde la comunidad de ML colabora en modelos, conjuntos de datos y aplicaciones). Convertirá ResNet-50 al formato ONNX de QDQ mediante ai Toolkit (consulte Conversión de modelos al formato ONNX para obtener más información).

El objetivo de este código de ejemplo es aprovechar windows ML runtime para realizar el trabajo pesado.

El entorno de ejecución de Windows ML hará lo siguiente:

  • Cargue el modelo.
  • Seleccione dinámicamente el proveedor de ejecución proporcionado por IHV (EP) preferido para el modelo y descargue su EP desde Microsoft Store a petición.
  • Ejecute inferencia en el modelo mediante el EP.

Para obtener referencia de API, consulte OrtSessionOptions y Microsoft::Windows::AI::MachineLearning::Infrastructure class.

// Create a new instance of EnvironmentCreationOptions
EnvironmentCreationOptions envOptions = new()
{
    logId = "ResnetDemo",
    logLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_ERROR
};

// Pass the options by reference to CreateInstanceWithOptions
OrtEnv ortEnv = OrtEnv.CreateInstanceWithOptions(ref envOptions);

// Use WinML to download and register Execution Providers
Microsoft.Windows.AI.MachineLearning.Infrastructure infrastructure = new();
Console.WriteLine("Ensure EPs are downloaded ...");
await infrastructure.DownloadPackagesAsync();
await infrastructure.RegisterExecutionProviderLibrariesAsync();

//Create Onnx session
Console.WriteLine("Creating session ...");
var sessionOptions = new SessionOptions();
// Set EP Selection Policy
sessionOptions.SetEpSelectionPolicy(ExecutionProviderDevicePolicy.MIN_OVERALL_POWER);

Compilación EP

Si el modelo aún no está compilado para el EP (lo cual puede variar en función del dispositivo), primero se debe compilar para ese EP. Se trata de un proceso único. El código de ejemplo siguiente lo controla compilando el modelo en la primera ejecución y, a continuación, almacenándolo localmente. Las ejecuciones posteriores del código recogen la versión compilada y la ejecutan; lo que da lugar a inferencias rápidas optimizadas.

Para obtener referencia a la API, vea Struct Ort::ModelCompilationOptions, Ort::Status struct y Ort::CompileModel.

// Prepare paths
string executableFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!;
string labelsPath = Path.Combine(executableFolder, "ResNet50Labels.txt");
string imagePath = Path.Combine(executableFolder, "dog.jpg");
            
// TODO: Please use AITK Model Conversion tool to download and convert Resnet, and paste the converted path here
string modelPath = @"";
string compiledModelPath = @"";

// Compile the model if not already compiled
bool isCompiled = File.Exists(compiledModelPath);
if (!isCompiled)
{
    Console.WriteLine("No compiled model found. Compiling model ...");
    using (var compileOptions = new OrtModelCompilationOptions(sessionOptions))
    {
        compileOptions.SetInputModelPath(modelPath);
        compileOptions.SetOutputModelPath(compiledModelPath);
        compileOptions.CompileModel();
        isCompiled = File.Exists(compiledModelPath);
        if (isCompiled)
        {
            Console.WriteLine("Model compiled successfully!");
        }
        else
        {
            Console.WriteLine("Failed to compile the model. Will use original model.");
        }
    }
}
else
{
    Console.WriteLine("Found precompiled model.");
}
var modelPathToUse = isCompiled ? compiledModelPath : modelPath;

Ejecución de la inferencia

La imagen de entrada se convierte en formato de datos tensor y, a continuación, la inferencia se ejecuta en ella. Aunque esto es típico de todo el código que usa el entorno de ejecución de ONNX, la diferencia en este caso es que es ONNX Runtime directamente a través de Windows ML. El único requisito es agregar #include <win_onnxruntime_cxx_api.h> al código.

Consulte también Conversión de un modelo con AI Toolkit para VS Code

Para obtener referencia de API, vea estructura Ort::Session, estructura Ort::MemoryInfo, estructura Ort::Value, estructura Ort::AllocatorWithDefaultOptions, estructura Ort::RunOptions.

using var session = new InferenceSession(modelPathToUse, sessionOptions);

Console.WriteLine("Preparing input ...");
// Load and preprocess image
var input = await PreprocessImageAsync(await LoadImageFileAsync(imagePath));
// Prepare input tensor
var inputName = session.InputMetadata.First().Key;
var inputTensor = new DenseTensor<float>(
    input.ToArray(),          // Use the DenseTensor<float> directly
    new[] { 1, 3, 224, 224 }, // Shape of the tensor
    false                     // isReversedStride should be explicitly set to false
);

// Bind inputs and run inference
var inputs = new List<NamedOnnxValue>
{
    NamedOnnxValue.CreateFromTensor(inputName, inputTensor)
};

Console.WriteLine("Running inference ...");
var results = session.Run(inputs);
for (int i = 0; i < 40; i++)
{
    results = session.Run(inputs);
}

// Extract output tensor
var outputName = session.OutputMetadata.First().Key;
var resultTensor = results.First(r => r.Name == outputName).AsEnumerable<float>().ToArray();

// Load labels and print results
var labels = LoadLabels(labelsPath);
PrintResults(labels, resultTensor);

Posprocesamiento

La función softmax se aplica a la salida sin procesar devuelta, y los datos de etiqueta se usan para asociar e imprimir los nombres con las cinco probabilidades más altas.

private static void PrintResults(IList<string> labels, IReadOnlyList<float> results)
{
    // Apply softmax to the results
    float maxLogit = results.Max();
    var expScores = results.Select(r => MathF.Exp(r - maxLogit)).ToList(); // stability with maxLogit
    float sumExp = expScores.Sum();
    var softmaxResults = expScores.Select(e => e / sumExp).ToList();

    // Get top 5 results
    IEnumerable<(int Index, float Confidence)> topResults = softmaxResults
        .Select((value, index) => (Index: index, Confidence: value))
        .OrderByDescending(x => x.Confidence)
        .Take(5);

    // Display results
    Console.WriteLine("Top Predictions:");
    Console.WriteLine("-------------------------------------------");
    Console.WriteLine("{0,-32} {1,10}", "Label", "Confidence");
    Console.WriteLine("-------------------------------------------");

    foreach (var result in topResults)
    {
        Console.WriteLine("{0,-32} {1,10:P2}", labels[result.Index], result.Confidence);
    }

    Console.WriteLine("-------------------------------------------");
}

Salida

Este es un ejemplo del tipo de salida que se va a esperar.

285, Egyptian cat with confidence of 0.904274
281, tabby with confidence of 0.0620204
282, tiger cat with confidence of 0.0223081
287, lynx with confidence of 0.00119624
761, remote control with confidence of 0.000487919

Ejemplos de código completos

Los ejemplos de código completos están disponibles en el repositorio de GitHub aquí.