共用方式為


Windows ML 逐步解說

這個簡短的教學課程會逐步解說如何使用 Windows ML 在 Windows 上執行 ResNet-50 影像分類模型,詳細說明模型擷取和前置處理步驟。 實作牽涉到動態選取執行提供者,以獲得最佳推斷效能。

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

在本教學課程中,您將從擁抱臉部取得 ResNet-50 模型,並使用 AI 工具組將其轉換為 QDQ ONNX 格式。

然後,您將載入模型、準備輸入張量,以及使用 Windows ML API 執行推斷,包括套用 softmax 的後續處理步驟,以及擷取最高預測。

取得模型和前置處理

您可以從 Hugging Face 取得 ResNet-50 (ML 社群在模型、數據集和應用程式上共同作業的平臺)。 您將使用 AI 工具組將 ResNet-50 轉換為 QDQ ONNX 格式(如需詳細資訊,請參閱 將模型轉換成 ONNX 格式 )。

此範例程式代碼的目標是利用 Windows ML 執行時間執行繁重的工作。

Windows ML 運行時間會:

  • 載入模型。
  • 依需求動態選擇模型的首選 IHV 提供的執行提供者 (EP),並從 Microsoft 市集下載其 EP。
  • 使用 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");
            
// 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 Runtime 程式碼的常見做法,但此情況的不同之處在於它是透過 Windows ML 直接使用 ONNX Runtime。 唯一的需求是新增 #include <winml/onnxruntime_cxx_api.h> 至程序代碼。

另請參閱 使用適用於 VS Code 的 AI 工具組轉換模型

如需 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