ONNX Runtime入門示例:在C#中使用ResNet50v2進行影像識別

mingupupup發表於2024-07-02

ONNX Runtime簡介

ONNX Runtime 是一個跨平臺的推理和訓練機器學習加速器。ONNX 執行時推理可以實現更快的客戶體驗和更低的成本,支援來自深度學習框架(如 PyTorch 和 TensorFlow/Keras)以及經典機器學習庫(如 scikit-learn、LightGBM、XGBoost 等)的模型。 ONNX 執行時與不同的硬體、驅動程式和作業系統相容,並透過利用硬體加速器(如果適用)以及圖形最佳化和轉換來提供最佳效能。

image-20240702102515184

ResNet50v2簡介

ResNet50v2 是一種深度卷積神經網路架構,是 ResNet(Residual Network,殘差網路)系列的一部分。ResNet 是由何凱明等人在 2015 年提出的,它透過引入殘差塊(Residual Block)解決了深度神經網路訓練過程中梯度消失和梯度爆炸的問題,使得構建非常深的網路成為可能。ResNet50v2 被廣泛應用於各種計算機視覺任務,如影像分類、目標檢測、影像分割等。由於其深度和強大的特徵學習能力,ResNet50v2 在眾多基準測試中表現出色,是許多研究和應用中的首選模型之一。

示例

這個示例程式碼在

https://github.com/microsoft/onnxruntime/tree/main/csharp/sample/Microsoft.ML.OnnxRuntime.ResNet50v2Sample

fork一份,克隆到本地,在本地開啟這個專案,專案結構如下所示:

image-20240702104856596

依賴的包除了OnnxRuntime還有ImageSharp。

ImageSharp簡介

ImageSharp 是一個新的、功能齊全、完全託管的跨平臺 2D 圖形庫。ImageSharp 旨在簡化影像處理,為您帶來一個非常強大而又非常簡單的 API。

ImageSharp 從頭開始設計,具有靈活性和可擴充套件性。該庫為常見的影像處理操作提供了 API 端點,併為開發其他操作提供了構建塊。

ImageSharp 針對 .NET 8 構建,可用於裝置、雲和嵌入式/IoT 方案。

image-20240702110059615

下載 ResNet50 v2 ONNX 模型,下載地址在:

https://github.com/onnx/models/blob/main/validated/vision/classification/resnet/model/resnet50-v2-7.onnx

讀取路徑

首先,原始碼中是透過程式引數讀取模型的路徑和要測試的影像的路徑,也可以直接賦值:

// Read paths
//string modelFilePath = args[0];
//string imageFilePath = args[1];
string modelFilePath = @"你的路徑\Microsoft.ML.OnnxRuntime.ResNet50v2Sample\resnet50-v2-7.onnx";
string imageFilePath = @"你的路徑\Microsoft.ML.OnnxRuntime.ResNet50v2Sample\獅子.jpg";

讀取影像

接下來,我們將使用跨平臺影像庫 ImageSharp 讀取影像:

 // Read image
 using Image<Rgb24> image = Image.Load<Rgb24>(imageFilePath);

調整影像大小

接下來,我們將影像大小調整為模型期望的適當大小;224 畫素 x 224 畫素:

using Stream imageStream = new MemoryStream();
image.Mutate(x =>
{
    x.Resize(new ResizeOptions
    {
        Size = new Size(224, 224),
        Mode = ResizeMode.Crop
    });
});
image.Save(imageStream, format);

預處理影像

接下來,我們將根據模型的要求對影像進行預處理,具體要求見:

https://github.com/onnx/models/tree/main/validated/vision/classification/resnet#preprocessing

// We use DenseTensor for multi-dimensional access to populate the image data
var mean = new[] { 0.485f, 0.456f, 0.406f };
var stddev = new[] { 0.229f, 0.224f, 0.225f };
DenseTensor<float> processedImage = new(new[] { 1, 3, 224, 224 });
image.ProcessPixelRows(accessor =>
{
    for (int y = 0; y < accessor.Height; y++)
    {
        Span<Rgb24> pixelSpan = accessor.GetRowSpan(y);
        for (int x = 0; x < accessor.Width; x++)
        {
            processedImage[0, 0, y, x] = ((pixelSpan[x].R / 255f) - mean[0]) / stddev[0];
            processedImage[0, 1, y, x] = ((pixelSpan[x].G / 255f) - mean[1]) / stddev[1];
            processedImage[0, 2, y, x] = ((pixelSpan[x].B / 255f) - mean[2]) / stddev[2];
        }
    }
});

在這裡,我們正在建立一個所需大小 (batch-size, channels, height, width) 的張量,訪問畫素值,對其進行預處理,最後將它們分配給適當指示的張量。

設定輸入

接下來,我們將建立模型的輸入:

using var inputOrtValue = OrtValue.CreateTensorValueFromMemory(OrtMemoryInfo.DefaultInstance,
    processedImage.Buffer, new long[] { 1, 3, 224, 224 });

var inputs = new Dictionary<string, OrtValue>
{
    { "data", inputOrtValue }
}

要檢查 ONNX 模型的輸入節點名稱,您可以使用 Netron 視覺化模型並檢視輸入/輸出名稱。在本例中,此模型具有 data 作為輸入節點名稱。

執行推理

接下來,我們將建立一個推理會話並透過它執行輸入:

using var session = new InferenceSession(modelFilePath);
using var runOptions = new RunOptions();
using IDisposableReadOnlyCollection<OrtValue> results = session.Run(runOptions, inputs, session.OutputNames);

後處理輸出

接下來,我們需要對輸出進行後處理以獲得 softmax 向量,因為這不是由模型本身處理的:

var output = results[0].GetTensorDataAsSpan<float>().ToArray();
float sum = output.Sum(x => (float)Math.Exp(x));
IEnumerable<float> softmax = output.Select(x => (float)Math.Exp(x) / sum);

其他型號可能會在輸出之前應用 Softmax 節點,在這種情況下,您不需要此步驟。同樣,您可以使用 Netron 檢視模型輸出。

提取前10個預測結果

IEnumerable<Prediction> top10 = softmax.Select((x, i) => new Prediction { Label = LabelMap.Labels[i], Confidence = x })
                   .OrderByDescending(x => x.Confidence)
                   .Take(10);

列印結果

Console.WriteLine("Top 10 predictions for ResNet50 v2...");
Console.WriteLine("--------------------------------------------------------------");
foreach (var t in top10)
{
    Console.WriteLine($"Label: {t.Label}, Confidence: {t.Confidence}");
}

本例的示例圖片是一隻獅子,如下所示:

image-20240702111735749

檢視預測結果:

image-20240702111809588

在LabelMap類中可以檢視該模型可以識別的物體:

image-20240702112145665

例如cock是公雞的意思,我們可以現場找一張公雞的圖片,檢視效果。

找到的一張公雞圖片如下所示:

image-20240702112326619

修改測試圖片為這種圖片,再次執行,結果如下所示:

image-20240702112443090

成功識別出了公雞。

總結

以上就完成了ONNX Runtime的入門示例,可以根據興趣與需求嘗試使用其他的模型。

參考

1、Image recognition with ResNet50v2 in C# | onnxruntime

2、models/validated/vision/classification/resnet/model/resnet50-v2-7.onnx at main · onnx/models (github.com)

3、microsoft/onnxruntime: ONNX Runtime: cross-platform, high performance ML inferencing and training accelerator (github.com)

4、SixLabors/ImageSharp: 📷 A modern, cross-platform, 2D Graphics library for .NET (github.com)

相關文章