Поделиться через


Пошаговое руководство по Windows ML

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

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

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

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

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

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

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

Среда выполнения Windows ML будет:

  • Загрузите модель.
  • Динамически выберите предпочитаемого исполнительного провайдера (EP) IHV для модели и по запросу скачайте его EP из магазина Microsoft.
  • Выполните инференцию модели с помощью 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);

Компиляция 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");
            
// 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;

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

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

См. также статью "Преобразование модели с помощью набора средств ИИ для 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.