Condividi tramite


Procedura dettagliata di Windows ML

Questa breve esercitazione illustra come usare Windows ML per eseguire il modello di classificazione delle immagini ResNet-50 in Windows, illustrando in dettaglio i passaggi di acquisizione e pre-elaborazione dei modelli. L'implementazione prevede la selezione dinamica dei provider di esecuzione per ottimizzare le prestazioni di inferenza.

Il modello ResNet-50 è un modello PyTorch destinato alla classificazione delle immagini.

In questa esercitazione si acquisirà il modello ResNet-50 da Hugging Face e lo si convertirà in formato QDQ ONNX usando AI Toolkit.

Si caricherà quindi il modello, preparerai i tensori di input ed eseguirai l'inferenza usando le API di Windows ML, inclusi i passaggi di post-elaborazione per applicare softmax e recuperare le stime principali.

Acquisizione del modello e pre-elaborazione

È possibile acquisire ResNet-50 da Hugging Face (la piattaforma in cui la community di ML collabora su modelli, set di dati e app). Si convertirà ResNet-50 in formato QDQ ONNX usando AI Toolkit (vedere Convertire modelli in formato ONNX per altre informazioni).

L'obiettivo di questo codice di esempio è sfruttare il runtime di Windows ML per eseguire le principali operazioni.

Il runtime di Windows ML farà:

  • Caricare il modello.
  • Selezionare dinamicamente il provider di esecuzione fornito da IHV (EP) preferito per il modello e scaricare il relativo EP da Microsoft Store, su richiesta.
  • Eseguire l'inferenza sul modello usando l'EP.

Per informazioni di riferimento sulle API, vedere OrtSessionOptions e la classe ExecutionProviderCatalog .

// 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 Windows ML to download and register Execution Providers
var catalog = Microsoft.Windows.AI.MachineLearning.ExecutionProviderCatalog.GetDefault();
Console.WriteLine("Ensuring and registering execution providers...");
await catalog.EnsureAndRegisterCertifiedAsync();

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

Compilazione EP

Se il modello non è già compilato per l'EP (che può variare a seconda del dispositivo), il modello deve prima essere compilato in base a tale EP. Si tratta di un processo monouso. Il codice di esempio seguente lo gestisce compilando il modello alla prima esecuzione e quindi archiviandolo in locale. Le esecuzioni successive del codice prelevano la versione compilata ed eseguono tale versione; con conseguente inferenza rapida ottimizzata.

Per informazioni di riferimento sulle API, vedere Ort::ModelCompilationOptions struct, Ort::Status struct e 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;

Esecuzione dell'inferenza

L'immagine di input viene convertita in formato di dati tensor e quindi viene eseguita l'inferenza. Sebbene questo sia tipico di tutto il codice che utilizza il runtime ONNX, la differenza in questo caso è che si tratta di ONNX Runtime direttamente tramite Windows ML. L'unico requisito consiste nell'aggiungere #include <winml/onnxruntime_cxx_api.h> al codice.

Vedere anche Convertire un modello con AI Toolkit for VS Code

Per informazioni di riferimento sulle API, vedere Struct Ort::Session, Ort::MemoryInfo struct, Ort::Value struct, Ort::AllocatorWithDefaultOptions struct, Ort::RunOptions struct.

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);

Elaborazione post-produzione

La funzione softmax viene applicata all'output non elaborato restituito, e i dati delle etichette vengono usati per mappare e stampare i nomi con le cinque probabilità più elevate.

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("-------------------------------------------");
}

Risultato

Di seguito è riportato un esempio del tipo di output previsto.

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

Esempi di codice completi

Gli esempi di codice completi sono disponibili nel repository GitHub WindowsAppSDK-Samples. Vedi WindowsML.