共用方式為


Windows ML 攻略

這段簡短教學將帶你使用 Windows ML 在 Windows 上執行 ResNet-50 影像分類模型,詳細說明模型擷取與前處理步驟。 實作牽涉到動態選取執行提供者,以獲得最佳推斷效能。

ResNet-50 模型是適用於影像分類的 PyTorch 模型。

在這個教學中,你將從 Hugging Face 取得 ResNet-50 模型,並使用 Foundry 工具包將其轉換成 QDQ ONNX 格式。

接著你會載入模型、準備輸入張量,並使用 Windows 機器學習 API 執行推論,包括後處理步驟以套用 softmax,並取得頂尖預測值。

取得模型和前置處理

您可以從 Hugging Face 取得 ResNet-50 (ML 社群在模型、數據集和應用程式上共同作業的平臺)。 你可以使用 Foundry 工具包將 ResNet-50 轉換成 QDQ ONNX 格式(詳情請參見「 轉換為 ONNX 格式 」)。

這個範例程式碼的目標是利用 Windows 機器學習執行環境來完成繁重的工作。

Windows ML 執行環境將:

  • 載入模型。
  • 動態選擇該模型所需的 IHV 提供的執行提供者(EP),並視需要從 Microsoft Store 下載該執行提供者。
  • 使用 EP 在模型上執行推斷。

API 參考,請參見 OrtSessionOptionsExecutionProviderCatalog 類別。

// 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 結構Ort::Status 結構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 執行時的程式碼的典型做法,但這次的差別在於它是直接透過 Windows ML 使用 ONNX 執行時。 唯一的需求是新增 #include <winml/onnxruntime_cxx_api.h> 至程序代碼。

另請參閱 VS Code 的「用 Foundry 工具包轉換模型

如需 API 參考,請參閱 Ort::Session 結構Ort::MemoryInfo 結構Ort::Value 結構、 Ort::AllocatorWithDefaultOptions 結構、 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);

後製處理

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