AI與.NET技術(shù)實(shí)現(xiàn)圖像分類模型的部署與調(diào)用的詳細(xì)步驟
引言
人工智能(AI)技術(shù)的迅猛發(fā)展推動(dòng)了各行各業(yè)的數(shù)字化轉(zhuǎn)型。圖像分類,作為計(jì)算機(jī)視覺(jué)領(lǐng)域的核心技術(shù)之一,能夠讓機(jī)器自動(dòng)識(shí)別圖像中的物體、場(chǎng)景或特征,已廣泛應(yīng)用于醫(yī)療診斷、安防監(jiān)控、自動(dòng)駕駛和電子商務(wù)等領(lǐng)域。
與此同時(shí),.NET 平臺(tái)憑借其高效性、跨平臺(tái)能力和強(qiáng)大的 C# 編程語(yǔ)言支持,成為開發(fā)者構(gòu)建企業(yè)級(jí)應(yīng)用的首選技術(shù)棧。將 AI 圖像分類模型與 .NET 技術(shù)結(jié)合,不僅能充分發(fā)揮兩者的優(yōu)勢(shì),還能為開發(fā)者提供一種高效、直觀的實(shí)現(xiàn)方式。
本文將詳細(xì)介紹如何在 .NET 環(huán)境下使用 C# 部署和調(diào)用 AI 圖像分類模型。我們將從環(huán)境搭建、模型選擇,到模型調(diào)用,再到實(shí)際應(yīng)用場(chǎng)景,逐步展開講解,并提供豐富的代碼示例和實(shí)踐指導(dǎo),幫助開發(fā)者快速上手并應(yīng)用到實(shí)際項(xiàng)目中。
準(zhǔn)備工作
在開始實(shí)現(xiàn)圖像分類之前,我們需要準(zhǔn)備必要的開發(fā)環(huán)境和工具。以下是所需的軟件和庫(kù):
- Visual Studio:Visual Studio 2022。
- .NET SDK:安裝 .NET 6.0 或更高版本,確保支持最新的功能和性能優(yōu)化。
- ML.NET:微軟提供的開源機(jī)器學(xué)習(xí)框架,專為 .NET 開發(fā)者設(shè)計(jì),支持模型訓(xùn)練和推理。
- 模型文件:我們將使用預(yù)訓(xùn)練的圖像分類模型 tensorflow_inception_graph.pb。
安裝步驟
創(chuàng)建項(xiàng)目并添加依賴:在命令行中運(yùn)行以下命令,創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序并安裝必要的 NuGet 包:
dotnet new console -n ImageClassificationDemo cd ImageClassificationDemo dotnet add package Microsoft.ML dotnet add package Microsoft.ML.ImageAnalytics dotnet add package Microsoft.ML.TensorFlow dotnet add package SciSharp.TensorFlow.Redist
完成以上步驟后,你的環(huán)境就準(zhǔn)備好了。接下來(lái),我們將選擇一個(gè)合適的圖像分類模型。
圖像分類模型的選擇
圖像分類模型是基于監(jiān)督學(xué)習(xí)的神經(jīng)網(wǎng)絡(luò),其目標(biāo)是將輸入圖像分配到預(yù)定義的類別中。在選擇模型時(shí),我們需要考慮模型的性能、計(jì)算復(fù)雜度和適用場(chǎng)景。以下是幾種常見(jiàn)的圖像分類模型:
- 卷積神經(jīng)網(wǎng)絡(luò)(CNN):如 LeNet、AlexNet 和 VGGNet,適合基本的圖像分類任務(wù),但層數(shù)較深時(shí)可能面臨梯度消失問(wèn)題。
- 殘差網(wǎng)絡(luò)(ResNet):通過(guò)引入殘差連接(skip connections),解決了深層網(wǎng)絡(luò)的訓(xùn)練難題,適用于高精度分類任務(wù)。
- EfficientNet:通過(guò)平衡網(wǎng)絡(luò)深度、寬度和分辨率,提供高效的性能,適合資源受限的場(chǎng)景。
模型訓(xùn)練與導(dǎo)出
考慮到時(shí)間和資源成本,我們將直接使用預(yù)訓(xùn)練的 tensorflow_inception_graph.pb 模型。如果你有自定義需求,可以使用以下步驟訓(xùn)練并導(dǎo)出模型:
- 數(shù)據(jù)準(zhǔn)備:收集并標(biāo)注圖像數(shù)據(jù)集,分為訓(xùn)練集和驗(yàn)證集。
- 訓(xùn)練模型:使用 TensorFlow 或 PyTorch 等框架訓(xùn)練模型。
- 導(dǎo)出模型:利用框架提供的導(dǎo)出工具導(dǎo)出模型。
在本文中,我們選擇 tensorflow_inception_graph.pb 作為示例模型,這是一種由Google開發(fā)的高性能卷積神經(jīng)網(wǎng)絡(luò)(CNN)架構(gòu)。
該模塊通過(guò)并行使用不同大小的卷積核(如1x1、3x3、5x5)和池化層,提取圖像的多尺度特征。這種設(shè)計(jì)提高了模型在圖像分類任務(wù)中的表現(xiàn),同時(shí)保持了計(jì)算效率。支持 1000 個(gè)類別的分類,且可以輕松集成到 .NET 中。
大家可以直接點(diǎn)擊 tensorflow_inception_graph.pb 下載(文章最后也有下載方式)預(yù)訓(xùn)練的模型文件和分類文件,并將其放入項(xiàng)目目錄中。
也可以到github上下載(文章最后也有下載方式),里面的內(nèi)容相對(duì)來(lái)說(shuō)也更豐富些。
在 .NET 中調(diào)用模型
現(xiàn)在,我們進(jìn)入核心部分:在 .NET 中調(diào)用 tensorflow_inception_graph.pb。以下是逐步實(shí)現(xiàn)的過(guò)程。
1. 創(chuàng)建 .NET 項(xiàng)目
使用命令行創(chuàng)建一個(gè)控制臺(tái)應(yīng)用,項(xiàng)目基本結(jié)構(gòu)如下:
ImageClassificationDemo/ ├── ImageClassificationDemo.csproj ├── Program.cs ├── assets/inputs/inception/tensorflow_inception_graph.pb ├── assets/inputs/inception/imagenet_comp_graph_label_strings.txt
2. 定義輸入和輸出數(shù)據(jù)結(jié)構(gòu)
如果在運(yùn)行的時(shí)候報(bào)錯(cuò)說(shuō)找不到模型或者label文件,可以進(jìn)行如下操作:
輸入類中定義數(shù)據(jù)的結(jié)構(gòu)如下,后續(xù)會(huì)使用 TextLoader 加載數(shù)據(jù)時(shí)引用該類型。此處的類名為 ImageNetData:
public class ImageNetData { [LoadColumn(0)] public string ImagePath; [LoadColumn(1)] public string Label; public static IEnumerable<ImageNetData> ReadFromCsv(string file, string folder) { return File.ReadAllLines(file) .Select(x => x.Split('\t')) .Select(x => new ImageNetData { ImagePath = Path.Combine(folder, x[0]), Label = x[1] } ); } } public class ImageNetDataProbability : ImageNetData { public string PredictedLabel; public float Probability { get; set; } }
需要強(qiáng)調(diào)的是,ImageNetData 類中的標(biāo)簽在使用 TensorFlow 模型進(jìn)行評(píng)分時(shí)并沒(méi)有真正使用。而是在測(cè)試預(yù)測(cè)時(shí)使用它,這樣就可以將每個(gè)樣本數(shù)據(jù)的實(shí)際標(biāo)簽與 TensorFlow 模型提供的預(yù)測(cè)標(biāo)簽進(jìn)行比較。
輸出類的結(jié)構(gòu)如下:
public class ImageNetPrediction { [ColumnName(TFModelScorer.InceptionSettings.outputTensorName)] public float[] PredictedLabels; }
Inception 模型還需要幾個(gè)傳入的默認(rèn)參數(shù):
public struct ImageNetSettings { public const int imageHeight = 224; public const int imageWidth = 224; public const float mean = 117; public const bool channelsLast = true; }
3. 定義 estimator 管道
在處理深度神經(jīng)網(wǎng)絡(luò)時(shí),必須使圖像適應(yīng)網(wǎng)絡(luò)期望的格式。這就是圖像被調(diào)整大小然后轉(zhuǎn)換的原因(主要是像素值在所有R,G,B通道上被歸一化)。
var pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: imagesFolder, inputColumnName: nameof(ImageNetData.ImagePath)) .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: ImageNetSettings.imageWidth, imageHeight: ImageNetSettings.imageHeight, inputColumnName: "input")) .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: ImageNetSettings.channelsLast, offsetImage: ImageNetSettings.mean)) .Append(mlContext.Model.LoadTensorFlowModel(modelLocation) .ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput:true));
運(yùn)行代碼后,模型將被成功加載到內(nèi)存中,接下來(lái)我們可以調(diào)用它進(jìn)行圖像分類。
通常情況下,這里經(jīng)常報(bào)的錯(cuò)就是輸入/輸出節(jié)點(diǎn)的名稱不正確,你可以通過(guò) Netron (https://netron.app/)工具查看輸入/輸出節(jié)點(diǎn)的名稱。
因?yàn)檫@兩個(gè)節(jié)點(diǎn)的名稱后面會(huì)在 estimator 的定義中使用:在 inception 網(wǎng)絡(luò)的情況下,輸入張量命名為 ‘input’,輸出命名為 ‘softmax2’。
下圖是通過(guò) Netron 讀取的 tensorflow_inception_graph.pb 模型分析圖:
4. 提取預(yù)測(cè)結(jié)果
填充 estimator 管道
ITransformer model = pipeline.Fit(data); var predictionEngine = mlContext.Model.CreatePredictionEngine<ImageNetData, ImageNetPrediction>(model);
當(dāng)獲得預(yù)測(cè)結(jié)果后,我們會(huì)在屬性中得到一個(gè)浮點(diǎn)數(shù)數(shù)組。數(shù)組中的每個(gè)位置都會(huì)分配到一個(gè)標(biāo)簽。
例如,如果模型有5個(gè)不同的標(biāo)簽,則數(shù)組將為length = 5。數(shù)組中的每個(gè)位置都表示標(biāo)簽在該位置的概率;所有數(shù)組值(概率)的和等于1。
然后,您需要選擇最大的值(概率),并檢查配給了該位置的那個(gè)以填充 estimator 管道標(biāo)簽。
調(diào)用模型進(jìn)行圖像分類
接下來(lái)我們需要編寫代碼來(lái)加載圖像、進(jìn)行預(yù)測(cè)并解析結(jié)果。
1. 準(zhǔn)備素材與分類文件
定義圖像文件夾目錄和圖像分類目錄。以下代碼加載并預(yù)處理圖像:
string assetsRelativePath = @"../../../assets"; string assetsPath = GetAbsolutePath(assetsRelativePath); string tagsTsv = Path.Combine(assetsPath, "inputs", "images", "tags.tsv"); string imagesFolder = Path.Combine(assetsPath, "inputs", "images"); string inceptionPb = Path.Combine(assetsPath, "inputs", "inception", "tensorflow_inception_graph.pb"); string labelsTxt = Path.Combine(assetsPath, "inputs", "inception", "imagenet_comp_graph_label_strings.txt");
2. 加載模型
private PredictionEngine<ImageNetData, ImageNetPrediction> LoadModel(string dataLocation, string imagesFolder, string modelLocation) { ConsoleWriteHeader("Read model"); Console.WriteLine($"Model location: {modelLocation}"); Console.WriteLine($"Images folder: {imagesFolder}"); Console.WriteLine($"Training file: {dataLocation}"); Console.WriteLine($"Default parameters: image size=({ImageNetSettings.imageWidth},{ImageNetSettings.imageHeight}), image mean: {ImageNetSettings.mean}"); var data = mlContext.Data.LoadFromTextFile<ImageNetData>(dataLocation, hasHeader: true); var pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: imagesFolder, inputColumnName: nameof(ImageNetData.ImagePath)) .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: ImageNetSettings.imageWidth, imageHeight: ImageNetSettings.imageHeight, inputColumnName: "input")) .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: ImageNetSettings.channelsLast, offsetImage: ImageNetSettings.mean)) .Append(mlContext.Model.LoadTensorFlowModel(modelLocation). ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput:true)); ITransformer model = pipeline.Fit(data); var predictionEngine = mlContext.Model.CreatePredictionEngine<ImageNetData, ImageNetPrediction>(model); return predictionEngine; }
3. 解析輸出結(jié)果
protected IEnumerable<ImageNetData> PredictDataUsingModel(string testLocation, string imagesFolder, string labelsLocation, PredictionEngine<ImageNetData, ImageNetPrediction> model) { ConsoleWriteHeader("Classify images"); Console.WriteLine($"Images folder: {imagesFolder}"); Console.WriteLine($"Training file: {testLocation}"); Console.WriteLine($"Labels file: {labelsLocation}"); var labels = ReadLabels(labelsLocation); var testData = ImageNetData.ReadFromCsv(testLocation, imagesFolder); foreach (var sample in testData) { var probs = model.Predict(sample).PredictedLabels; var imageData = new ImageNetDataProbability() { ImagePath = sample.ImagePath, Label = sample.Label }; (imageData.PredictedLabel, imageData.Probability) = GetBestLabel(labels, probs); imageData.ConsoleWrite(); yield return imageData; } }
在 Main
方法中調(diào)用,完整代碼如下:
static void Main(string[] args) { string assetsRelativePath = @"../../../assets"; string assetsPath = GetAbsolutePath(assetsRelativePath); string tagsTsv = Path.Combine(assetsPath, "inputs", "images", "tags.tsv"); string imagesFolder = Path.Combine(assetsPath, "inputs", "images"); string inceptionPb = Path.Combine(assetsPath, "inputs", "inception", "tensorflow_inception_graph.pb"); string labelsTxt = Path.Combine(assetsPath, "inputs", "inception", "imagenet_comp_graph_label_strings.txt"); try { TFModelScorer modelScorer = new TFModelScorer(tagsTsv, imagesFolder, inceptionPb, labelsTxt); modelScorer.Score(); } catch (Exception ex) { ConsoleHelpers.ConsoleWriteException(ex.ToString()); } ConsoleHelpers.ConsolePressAnyKey(); }
運(yùn)行程序后,你將看到類似以下的輸出:
其他實(shí)現(xiàn)方式
在實(shí)際應(yīng)用中,我們也可以使用ONNX模型,此處不做額外敘述。由于模型的性能和效率至關(guān)重要,只是提供一些優(yōu)化建議:
- 模型量化:使用 ONNX Runtime 的量化工具,將模型從浮點(diǎn)數(shù)(FP32)轉(zhuǎn)換為整數(shù)(INT8),減少模型大小和推理時(shí)間。
- 硬件加速:結(jié)合 ONNX Runtime 的 GPU 支持,利用 CUDA 或 DirectML 加速推理。
- 批處理:如果需要處理多張圖像,可以將輸入組織為批次(batch),提高吞吐量。例如:
var inputs = new List<ImageInput> { input1, input2, input3 }; var batchPrediction = mlContext.Data.LoadFromEnumerable(inputs); var predictions = model.Transform(batchPrediction);
- 緩存機(jī)制:對(duì)于頻繁使用的模型,保持預(yù)測(cè)引擎的單例實(shí)例,避免重復(fù)加載。
通過(guò)這些優(yōu)化,模型可以在 .NET 環(huán)境中實(shí)現(xiàn)更高的性能,滿足實(shí)時(shí)應(yīng)用的需求。
實(shí)際應(yīng)用場(chǎng)景
圖像分類模型在 .NET 應(yīng)用中有廣泛的用途,以下是幾個(gè)典型場(chǎng)景:
醫(yī)療影像分析
在醫(yī)療系統(tǒng)中,部署圖像分類模型可以輔助醫(yī)生識(shí)別 X 光片或 MRI 圖像中的異常。例如,檢測(cè)肺部結(jié)節(jié)或腫瘤。
智能安防
在監(jiān)控系統(tǒng)中,模型可以實(shí)時(shí)識(shí)別可疑物體或行為,如檢測(cè)闖入者或遺留物品。
電子商務(wù)
在商品管理系統(tǒng)中,自動(dòng)分類上傳的商品圖像,提升搜索和推薦的準(zhǔn)確性。
挑戰(zhàn)與解決方案
- 數(shù)據(jù)隱私:通過(guò)加密傳輸和本地推理保護(hù)用戶數(shù)據(jù)。
- 模型更新:定期從云端下載新模型,并使用版本控制管理。
- 計(jì)算資源:在資源受限的設(shè)備上,使用輕量化模型(如 MobileNet)。
結(jié)論
本文詳細(xì)介紹了如何在 .NET 環(huán)境下使用 C# 部署和調(diào)用 AI 圖像分類模型。從環(huán)境搭建到模型選擇、部署與調(diào)用,再到性能優(yōu)化和應(yīng)用場(chǎng)景,我們提供了一套完整的實(shí)踐指南。通過(guò) ML.NET 和預(yù)測(cè)模式的支持,開發(fā)者可以輕松地將強(qiáng)大的 AI 能力集成到 .NET 應(yīng)用中。
隨著 AI 技術(shù)的不斷進(jìn)步和 .NET 平臺(tái)的持續(xù)發(fā)展,二者的結(jié)合將為開發(fā)者帶來(lái)更多可能性。無(wú)論是構(gòu)建智能桌面應(yīng)用、Web 服務(wù)還是跨平臺(tái)解決方案,圖像分類模型都能為項(xiàng)目增添創(chuàng)新價(jià)值。希望本文能為你的 AI 之旅提供啟發(fā)和幫助!
參考資料
- 素材下載地址: https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip
- Netron工具地址: https://netron.app/
- 224x224圖像素材: https://www.kaggle.com/datasets/abhinavnayak/catsvdogs-transformed/data
- tensorflow教程及模型文件和label文件: https://github.com/martinwicke/tensorflow-tutorial
- Image Classification - Scoring sample: https://github.com/dotnet/machinelearning-samples/blob/main/samples/csharp/getting-started/DeepLearning_ImageClassification_TensorFlow/README.md
- ML.NET 官方文檔: https://dotnet.microsoft.com/apps/machinelearning-ai/ml-dotnet
- ONNX Model Zoo: https://github.com/onnx/models
到此這篇關(guān)于AI與.NET技術(shù)實(shí)現(xiàn)圖像分類模型的部署與調(diào)用的詳細(xì)步驟的文章就介紹到這了,更多相關(guān).net 圖像分類模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ASP.NET MVC使用EPPlus,導(dǎo)出數(shù)據(jù)到Excel中
這篇文章介紹的是怎樣導(dǎo)出數(shù)據(jù)到Excel文件中,大多數(shù)的后端程序都有報(bào)表功能:把顯示在Grid中的數(shù)據(jù)導(dǎo)出到Excel文件中,這篇文章中使用的是EPPlus組件。需要的朋友可以參考借鑒2016-12-12asp.net使用npoi讀取excel模板并導(dǎo)出下載詳解
這篇文章主要介紹了asp.net使用npoi讀取excel模板并導(dǎo)出下載的示例,大家參考使用吧2014-01-01把jQuery的each(callback)方法移植到c#中
jQuery中使用each(callback)方法可以很方便的遍歷集合,如2008-03-03ABP框架中導(dǎo)航菜單的使用及JavaScript API獲取菜單的方法
ABP框架是基于ASP.NET的Web開發(fā)框架,其中包含基本的菜單項(xiàng)可供調(diào)用,特別是自動(dòng)生成的js API使得能夠在客戶端獲取菜單,這里我們就來(lái)看一下ABP框架中導(dǎo)航菜單的使用及JavaScript API獲取菜單的方法2016-06-06.Net中如何將一個(gè)實(shí)例的內(nèi)存二進(jìn)制內(nèi)容讀出來(lái)(超簡(jiǎn)單方法)
這篇文章主要介紹了如何將一個(gè)實(shí)例的內(nèi)存二進(jìn)制內(nèi)容讀出來(lái)(超簡(jiǎn)單方法),接下來(lái)的內(nèi)容中,我們將利用一個(gè)簡(jiǎn)單的方法輸出指定實(shí)例的字節(jié)序列,并此次分析值類型和引用類型實(shí)例在內(nèi)存的布局,需要的朋友可以參考下2023-07-07ASP.NET Core DI手動(dòng)獲取注入對(duì)象的方法
這篇文章主要給大家介紹了關(guān)于ASP.NET Core DI手動(dòng)獲取注入對(duì)象的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11