Partager via


Procédure pas à pas de Windows ML

Ce tutoriel court décrit l’utilisation de Windows ML pour exécuter le modèle de classification d’images ResNet-50 sur Windows, en détaillant les étapes d’acquisition et de prétraitement des modèles. L’implémentation implique de sélectionner dynamiquement des fournisseurs d’exécution pour optimiser les performances d’inférence.

Le modèle ResNet-50 est un modèle PyTorch destiné à la classification d’images.

Dans ce tutoriel, vous allez acquérir le modèle ResNet-50 à partir de Hugging Face et le convertir au format QDQ ONNX à l’aide de AI Toolkit.

Ensuite, vous allez charger le modèle, préparer les tenseurs d’entrée et exécuter l’inférence à l’aide des API Windows ML, y compris les étapes de post-traitement pour appliquer softmax et récupérer les principales prédictions.

Acquisition du modèle et prétraitement

Vous pouvez acquérir ResNet-50 à partir de Hugging Face (la plateforme sur laquelle la communauté ML collabore sur des modèles, des jeux de données et des applications). Vous allez convertir ResNet-50 au format ONNX QDQ à l’aide du kit de ressources AI (voir convertir des modèles au format ONNX pour plus d’informations).

L’objectif de cet exemple de code est de tirer parti du runtime Windows ML pour effectuer le travail lourd.

Le runtime Windows ML effectue les tâches suivantes :

  • Chargez le modèle.
  • Sélectionnez dynamiquement le fournisseur d’exécution fourni par IHV (EP) préféré pour le modèle et téléchargez son EP à partir du Microsoft Store, à la demande.
  • Exécutez l’inférence sur le modèle à l’aide de l’EP.

Pour obtenir des informations de référence sur l’API, consultez OrtSessionOptions et 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);

Compilation de l'EP

Si votre modèle n’est pas déjà compilé pour l’EP (qui peut varier en fonction de l’appareil), le modèle doit d’abord être compilé par rapport à cet EP. Il s’agit d’un processus unique. L’exemple de code ci-dessous le gère en compilant le modèle lors de la première exécution, puis en le stockant localement. Les exécutions suivantes du code récupèrent la version compilée et l’exécutent ; ce qui a entraîné des inférences rapides optimisées.

Pour obtenir des informations de référence sur l’API, consultez struct Ort ::ModelCompilationOptions, Ort ::Status struct et 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;

Exécution de l’inférence

L’image d’entrée est convertie au format de données tensoriel, puis l’inférence s’exécute dessus. Bien qu’il s’agit généralement de tout le code qui utilise le runtime ONNX, la différence dans ce cas est qu’il s’agit d’ONNX Runtime directement via Windows ML. La seule exigence est d’ajouter #include <winml/onnxruntime_cxx_api.h> au code.

Voir également Convertir un modèle avec AI Toolkit pour VS Code

Pour obtenir des informations de référence sur l’API, consultez Ort ::Session struct, 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);

Post-traitement

La fonction softmax est appliquée à la sortie brute retournée, et les données d’étiquette sont utilisées pour mapper et imprimer les noms avec les cinq probabilités les plus élevées.

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

Sortie

Voici un exemple de type de sortie à attendre.

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

Exemples de code complets

Les exemples de code complets sont disponibles dans le dépôt GitHub WindowsAppSDK-Samples. Voir WindowsML.