Руководство по Windows ML (машинному обучению)

В этом кратком руководстве описывается использование Windows ML для запуска модели классификации изображений ResNet-50 на Windows, с детальным описанием шагов по приобретению и предварительной обработке модели. Реализация включает динамический выбор поставщиков выполнения для оптимизированной производительности вывода.

Модель ResNet-50 — это модель PyTorch, предназначенная для классификации изображений.

В этом руководстве вы получите модель ResNet-50 из Hugging Face и преобразуйте ее в формат QDQ ONNX с помощью набора средств Foundry.

Затем вы загрузите модель, подготовите входные тензоры и запустите вывод с помощью API-интерфейсов машинного обучения Windows, включая этапы после обработки для применения softmax и получения лучших прогнозов.

Получение модели и предварительная обработка

Вы можете получить ResNet-50 из Hugging Face (платформа, в которой сообщество машинного обучения работает над моделями, наборами данных и приложениями). Вы преобразуете ResNet-50 в формат QDQ ONNX с помощью набора средств Foundry (см. дополнительные сведения о преобразовании моделей в формат ONNX ).

Цель этого примера кода — использовать среду выполнения машинного обучения Windows для выполнения сложных вычислений.

Среда выполнения машинного обучения Windows будет:

  • Загрузите модель.
  • Динамически выберите предпочитаемый поставщик выполнения IHV (EP) для модели и скачайте его EP из Microsoft Store по запросу.
  • Выполните инференцию модели с помощью EP.

Справочник по API см. в разделе OrtSessionOptions и класс 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);

Замечание

В этом руководстве используется вариант настройки поставщиков выполнения одним вызовом. Если вашему приложению необходимо проверить доступные поставщики, управлять скачиванием или зарегистрировать конкретный поставщик, ознакомьтесь с Install Windows EPs и Register Windows EPs.

Компиляция EP

Если модель еще не скомпилирована для EP (которая может отличаться в зависимости от устройства), сначала необходимо скомпилировать модель в соответствии с этим EP. Это одноразовый процесс. Приведенный ниже пример кода обрабатывает его путем компиляции модели во время первого запуска, а затем локального хранения. Последующие запуски кода выбирают скомпилированную версию и выполняют это; что приводит к оптимизации быстрых выводов.

Справочные сведения об API см. в Ort::ModelCompilationOptions struct, Ort::Status struct и 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");
            
// Set the paths to your converted ONNX model
// Use Foundry Toolkit to download and convert ResNet-50 to ONNX format: https://code.visualstudio.com/docs/intelligentapps/modelconversion
string modelPath = Path.Combine(executableFolder, "resnet50-v2-7.onnx");
string compiledModelPath = Path.Combine(executableFolder, "resnet50-v2-7-compiled.onnx");

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

Выполнение вывода

Входное изображение преобразуется в тензорный формат данных, а затем на нём выполняется инференс. Хотя это характерно для всего кода, использующего ONNX Runtime, в этом случае разница в том, что используется ONNX Runtime напрямую через Windows ML. Единственное требование заключается в добавлении #include <winml/onnxruntime_cxx_api.h> в код.

Кроме того, см . раздел "Преобразование модели с помощью средств Foundry Toolkit для VS Code"

Справочник по API см. в разделе 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);

Последующая обработка

Функция softmax применяется к возвращаемым необработанным выходным данным, а данные меток используются для сопоставления и печати имен с пятью самыми высокими вероятностями.

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

Выходные данные

Ниже приведен пример ожидаемого типа выходных данных.

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

Полные примеры кода

Полные примеры кода доступны в репозитории WindowsAppSDK-Samples GitHub. См. WindowsML.