使用ML.NET + ASP.NET Core + Docker + Azure Container Instances部署.NET機器學習模型
本文將使用ML.NET創建機器學習分類模型,通過ASP.NET Core Web API公開它,將其打包到Docker容器中,並通過Azure Container Instances將其部署到雲中。
先決條件
本文假設您對Docker有一定的了解。構建和部署示例應用程序還需要以下軟件/依賴項。重要的是要註意應用程序是在Ubuntu 16.04 PC上構建的,但所有軟件都是跨平臺的,應該適用於任何環境。
- Docker
- Azure CLI
- .NET Core 2.0
- Docker Hub Account
設置項目
我們要做的第一件事是為我們的解決方案創建一個文件夾。
mkdirmlnetacidemo
然後,我們想在新創建的文件夾中創建一個解決方案。
cd mlnetacidemo
dotnet new sln
建立模型
在我們的解決方案文件夾中,我們想要創建一個新的控制臺應用程序,這是我們構建和測試我們的機器學習模型的地方。
設置模型項目
首先,我們要創建項目。從解決方案文件夾輸入:
dotnet new console -o model
現在我們要將這個新項目添加到我們的解決方案中。
dotnet sln mlnetacidemo.sln add model/model.csproj
添加依賴項
由於我們將使用
ML.NET
框架,我們需要將其添加到我們的model
項目中。
cd model
dotnet add package Microsoft.ML
dotnet restore
在我們開始訓練模型之前,我們需要下載我們將用於訓練的數據。我們通過創建一個名為data的目錄並將數據文件下載到那裏來實現。
mkdir data curl -o data/iris.txt https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data
如果我們看一下數據文件,它看起來應該是這樣的:
5.1,3.5,1.4,0.2,Iris-setosa 4.9,3.0,1.4,0.2,Iris-setosa 4.7,3.2,1.3,0.2,Iris-setosa 4.6,3.1,1.5,0.2,Iris-setosa 5.0,3.6,1.4,0.2,Iris-setosa 5.4,3.9,1.7,0.4,Iris-setosa 4.6,3.4,1.4,0.3,Iris-setosa 5.0,3.4,1.5,0.2,Iris-setosa 4.4,2.9,1.4,0.2,Iris-setosa 4.9,3.1,1.5,0.1,Iris-setosa
訓練模型
現在我們已經設置了所有依賴項,現在是構建模型的時候了。我利用了ML.NET入門網站上使用的演示。
定義數據結構
在我們model
項目的根目錄中,讓我們創建兩個被調用的類IrisData
,IrisPrediction
它們將分別定義我們的特性和預測屬性。它們都將用於Microsoft.ML.Runtime.Api
添加屬性屬性。
這是我們IrisData
的樣子:
using Microsoft.ML.Runtime.Api; namespace model { public class IrisData { [Column("0")] public float SepalLength; [Column("1")] public float SepalWidth; [Column("2")] public float PetalLength; [Column("3")] public float PetalWidth; [Column("4")] [ColumnName("Label")] public string Label; } }
同樣,這是IrisPrediction
:
using Microsoft.ML.Runtime.Api; namespace model { public class IrisPrediction { [ColumnName("PredictedLabel")] public string PredictedLabels; } }
構建LearningPipeLine
using Microsoft.ML.Data; using Microsoft.ML; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using Microsoft.ML.Models; using System; using System.Threading.Tasks; namespace model { class Model { public static async Task<PredictionModel<IrisData,IrisPrediction>> Train(LearningPipeline pipeline, string dataPath, string modelPath) { // Load Data pipeline.Add(new TextLoader(dataPath).CreateFrom<IrisData>(separator:‘,‘)); // Transform Data // Assign numeric values to text in the "Label" column, because // only numbers can be processed during model training pipeline.Add(new Dictionarizer("Label")); // Vectorize Features pipeline.Add(new ColumnConcatenator("Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")); // Add Learner pipeline.Add(new StochasticDualCoordinateAscentClassifier()); // Convert Label back to text pipeline.Add(new PredictedLabelColumnOriginalValueConverter() {PredictedLabelColumn = "PredictedLabel"}); // Train Model var model = pipeline.Train<IrisData,IrisPrediction>(); // Persist Model await model.WriteAsync(modelPath); return model; } } }
除了構建LearningPipLine並訓練我們的機器學習模型之外,該模型還序列化並保存在名為model.zip的文件中以供將來使用。
測試我們的模型
現在是時候測試所有內容以確保它正常工作。
using System; using Microsoft.ML; namespace model { class Program { static void Main(string[] args) { string dataPath = "model/data/iris.txt"; string modelPath = "model/model.zip"; var model = Model.Train(new LearningPipeline(),dataPath,modelPath).Result; // Test data for prediction var prediction = model.Predict(new IrisData() { SepalLength = 3.3f, SepalWidth = 1.6f, PetalLength = 0.2f, PetalWidth = 5.1f }); Console.WriteLine($"Predicted flower type is: {prediction.PredictedLabels}"); } } }
全部設定運行。我們可以通過從解決方案目錄輸入以下命令來完成此操作:
dotnet run -p model/model.csproj
運行應用程序後,控制臺上將顯示以下輸出。
Automatically adding a MinMax normalization transform, use ‘norm=Warn‘ or ‘norm=No‘ to turn this behavior off.Using 2 threads to train. Automatically choosing a check frequency of 2.Auto-tuning parameters: maxIterations = 9998. Auto-tuning parameters: L2 = 2.667734E-05. Auto-tuning parameters: L1Threshold (L1/L2) = 0.Using best model from iteration 882. Not training a calibrator because it is not needed. Predicted flower type is: Iris-virginica
公開模型
此外,您會註意到在我們model
項目的根目錄中創建了一個名為model.zip的文件。這個持久化模型現在可以在我們的應用程序之外用於進行預測,我們接下來將通過API執行操作。
一旦構建了機器學習模型,您就希望部署它以便開始進行預測。一種方法是通過REST API。它的核心部分需要做的是接受來自客戶端的數據輸入並回復預測。為了幫助我們這樣做,我們將使用ASP.NET Core API。
設置API項目
我們要做的第一件事是創建項目。
dotnet new webapi -o api
然後我們想將這個新項目添加到我們的解決方案中
dotnet sln mlnetacidemo.sln add api/api.csproj
添加依賴項
因為我們將加載我們的模型並通過我們的API進行預測,所以我們需要將ML.NET
包添加到我們的api
項目中。
cd api
dotnet add package Microsoft.ML
dotnet restore
引用模型
在我們構建機器學習模型的上一步中,它被保存到一個名為的文件中model.zip
。這是我們將在API中引用的文件,以幫助我們進行預測。要在我們的API中引用它,只需將它從模型項目目錄復制到我們的api
項目目錄中。
創建數據模型
我們的模型是使用數據結構構建的IrisData
,IrisPrediction
用於定義特征以及預測屬性。因此,當我們的模型通過我們的API進行預測時,它也需要引用這些數據類型。因此,我們需要在項目內部定義IrisData
和IrisPrediction
類api
。類的內容幾乎與model
項目中的內容相同,唯一的例外是我們的命名空間從更改model
為api
。
using Microsoft.ML.Runtime.Api; namespace api { public class IrisData { [Column("0")] public float SepalLength; [Column("1")] public float SepalWidth; [Column("2")] public float PetalLength; [Column("3")] public float PetalWidth; [Column("4")] [ColumnName("Label")] public string Label; } }
using Microsoft.ML.Runtime.Api; namespace api { public class IrisPrediction { [ColumnName("PredictedLabel")] public string PredictedLabels; } }
構建控制器
現在我們的項目已經建立,是時候添加一個控制器來處理來自客戶端的預測請求了。在Controllers
我們api
項目的目錄中,我們可以創建一個PredictController
使用單個POST
端點調用的新類。該文件的內容應如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.ML; namespace api.Controllers { [Route("api/[controller]")] public class PredictController : Controller { // POST api/predict [HttpPost] public string Post([FromBody] IrisData instance) { var model = PredictionModel.ReadAsync<IrisData,IrisPrediction>("model.zip").Result; var prediction = model.Predict(instance); return prediction.PredictedLabels; } } }
測試API
當我們的predict
控制器完成編碼,就可以來測試它了。從我們mlnetacidemo
解決方案的根目錄中,輸入以下命令。
dotnet run -p api/api.csproj
我們的請求的正文應該類似於下面的代碼段:在POSTMAN或Insomnia等客戶端中,向端點發送HHTP POST請求http://localhost:5000/api/predict
。
{ "SepalLength": 3.3, "SepalWidth": 1.6, "PetalLength": 0.2, "PetalWidth": 5.1, }
打包應用程序
如果成功,返回的輸出應該Iris-virginica
與我們的控制臺應用程序相同。大!現在我們的應用程序已在本地成功運行,現在是時候將它打包到Docker容器中並將其推送到Docker Hub。
創建Dockerfile
在我們的mlnetacidemo
解決方案目錄中,使用以下內容創建一個Dockerfile:
FROM microsoft/dotnet:2.0-sdk AS build WORKDIR /app # copy csproj and restore as distinct layers COPY *.sln . COPY api/*.csproj ./api/ RUN dotnet restore # copy everything else and build app COPY api/. ./api/ WORKDIR /app/api RUN dotnet publish -c release -o out FROM microsoft/aspnetcore:2.0 AS runtime WORKDIR /app COPY api/model.zip . COPY --from=build /app/api/out ./ ENTRYPOINT ["dotnet", "api.dll"]
構建鏡像
我們需要在命令提示符中輸入以下命令。這需要一段時間,因為它需要下載.NET Core SDK和ASP.NET Core運行時Docker鏡像。
docker build -t <DOCKERUSERNAME>/<IMAGENAME>:latest .
本地測試鏡像
我們需要在本地測試我們的鏡像,以確保它可以在雲上運行。為此,我們可以使用該docker run
命令。
docker run -d -p 5000:80 <DOCKERUSERNAME>/<IMAGENAME>:latest
要停止容器,請使用Ctrl + C
。雖然API暴露了端口80,但我們將其綁定到本地端口5000只是為了保持我們先前的API請求不變。向http://localhost:5000/api/predict
適當的主體發送POST請求時,應該再次響應同樣的結果Iris-virginica。
推送到Docker Hub
現在Docker鏡像在本地成功運行,是時候推送到Docker Hub了。同樣,我們使用Docker CLI來執行此操作。
docker login docker push <DOCKERUSERNAME>/<IMAGENAME>:latest
部署到雲
現在,最後一步是向全世界部署和展示我們的機器學習模型和API。我們的部署將通過Azure容器實例進行,因為它幾乎不需要配置或管理服務器。
準備部署清單
盡管可以在命令行中執行部署,但通常最好將所有配置放在文件中以備文檔,並節省時間,而不必每次都輸入參數。使用Azure,我們可以通過JSON文件來實現。
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "containerGroupName": { "type": "string", "defaultValue": "mlnetacicontainergroup", "metadata": { "description": "Container Group name." } } }, "variables": { "containername": "mlnetacidemo", "containerimage": "<DOCKERUSERNAME>/<IMAGENAME>:latest" }, "resources": [ { "name": "[parameters(‘containerGroupName‘)]", "type": "Microsoft.ContainerInstance/containerGroups", "apiVersion": "2018-04-01", "location": "[resourceGroup().location]", "properties": { "containers": [ { "name": "[variables(‘containername‘)]", "properties": { "image": "[variables(‘containerimage‘)]", "resources": { "requests": { "cpu": 1, "memoryInGb": 1.5 } }, "ports": [ { "port": 80 } ] } } ], "osType": "Linux", "ipAddress": { "type": "Public", "ports": [ { "protocol": "tcp", "port": "80" } ] } } } ], "outputs": { "containerIPv4Address": { "type": "string", "value": "[reference(resourceId(‘Microsoft.ContainerInstance/containerGroups/‘, parameters(‘containerGroupName‘))).ipAddress.ip]" } } }
現在我們可以使用這個模板並將其保存到我們mlnetacidemo
解決方案根目錄下的文件azuredeploy.json中。唯一需要改變的是containerimage
的配置,將其替換為您的Docker Hub用戶名和剛剛推送到Docker Hub的鏡像的名稱。
部署
為了部署我們的應用程序,我們需要確保登錄我們的Azure帳戶。要通過Azure CLI執行此操作,請在命令提示符下鍵入:
az login
按照提示登錄。登錄後,是時候為容器創建資源組了。
az group create --name mlnetacidemogroup --location eastus
成功創建組後,就可以部署我們的應用程序了。
az group deployment create --resource-group mlnetacidemogroup --template-file azuredeploy.json
完成後,可以使用以下命令清理資源:
az group delete --name mlnetacidemogroup
為部署初始化需要消耗幾分鐘的時間。如果部署成功,您應該在命令行上看到一些輸出。尋找ContainerIPv4Address主機
,這是可以訪問容器的IP地址,更換URL後再次做一個POST請求到http://<ContainerIPv4Address>/api/predict,
ContainerIPv4Address
是在部署後命令行中找到的值。如果成功,響應內容應該像以前的請求一樣返回Iris-virginica
。
小結
在本文中,我們構建了一個分類機器學習模型,使用ML.NET
該模型預測鳶尾花的分類,給出了四種分類的預測功能,通過ASP.NET Core REST API公開它,將其打包到容器中並使用Azure Container Instances將其部署到雲中。雖然隨著模型的變化,這些操作變得更加復雜,但是目前介紹的內容已經足夠標準化,擴展此示例僅需要進行很少量的修改即可。
使用ML.NET + ASP.NET Core + Docker + Azure Container Instances部署.NET機器學習模型