1. 程式人生 > >使用ML.NET + ASP.NET Core + Docker + Azure Container Instances部署.NET機器學習模型

使用ML.NET + ASP.NET Core + Docker + Azure Container Instances部署.NET機器學習模型

cat ports sed tar enc convert esp asp truct

本文將使用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

設置項目

我們要做的第一件事是為我們的解決方案創建一個文件夾。

mkdir
mlnetacidemo

然後,我們想在新創建的文件夾中創建一個解決方案。

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項目的根目錄中,讓我們創建兩個被調用的類IrisDataIrisPrediction它們將分別定義我們的特性和預測屬性。它們都將用於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項目目錄中。

創建數據模型

我們的模型是使用數據結構構建的IrisDataIrisPrediction用於定義特征以及預測屬性。因此,當我們的模型通過我們的API進行預測時,它也需要引用這些數據類型。因此,我們需要項目內部定義IrisDataIrisPredictionapi類的內容幾乎與model項目中的內容相同,唯一的例外是我們的命名空間從更改modelapi

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機器學習模型