將模型部署到 Azure Functions

了解如何部署預先定型的 ML.NET 機器學習模型,以使用 Azure Functions 無伺服器環境透過 HTTP 進行預測。

必要條件

Azure Functions 範例概觀

此範例是 C# HTTP 觸發程序 Azure Functions 應用程式,該應用程式會使用預先定型的二進位分類模型,將文字的情感分類為正面或負面。 Azure Functions 可讓您輕鬆地在雲端的受控無伺服器環境中大規模執行小段程式碼。 您可以在 GitHub 的 dotnet/machinelearning-samples repository 找到此範例的程式碼。

建立 Azure Functions 專案

  1. 在 Visual Studio 2022 中,開啟 [建立新專案] 對話方塊。

  2. 在 [建立新專案] 對話方塊中,選取 [Azure Functions] 專案範本。

  3. 在 [名稱] 文字方塊中,輸入 "SentimentAnalysisFunctionsApp",然後選取 [下一步] 按鈕。

  4. 在 [其他資訊] 對話方塊中,保留所有預設值,然後選取 [建立] 按鈕。

  5. 安裝 Microsoft.ML NuGet 套件

    1. 在 [方案總管] 中,於您的專案上按一下滑鼠右鍵,然後選取 [管理 NuGet 套件]
    2. 選擇「nuget.org」作為套件來源。
    3. 選取 [瀏覽] 索引標籤。
    4. 搜尋 Microsoft.ML
    5. 選取清單中的套件,然後選取 [安裝] 按鈕。
    6. 選取 [預覽變更] 對話方塊上的 [確定] 按鈕
    7. 如果您同意所列套件的授權條款,請在 [授權接受] 對話方塊上選取 [我接受] 按鈕。

    請遵循相同的步驟來安裝 Microsoft.Extensions.MLMicrosoft.Extensions.DependencyInjectionMicrosoft.Azure.Functions.Extensions NuGet 套件。

將預先定型的模型新增到專案

  1. 在專案中建立名為 MLModels 的目錄以儲存您的建置前模型:在方案總管中,以滑鼠右鍵按一下您的專案,然後選取 [新增 > 資料夾]。 輸入 "MLModels",然後按 Enter。
  2. 將預先建置的模型複製到 MLModels 目錄。
  3. 在 [方案總管] 中,以滑鼠右鍵按一下您預先建置的模型檔案,並選取 [內容]。 在 [進階] 底下,將 [複製到輸出目錄] 的值變更為 [有更新時才複製]

建立 Azure Function 來分析情感

建立用來預測情緒的類別。 將新類別新增至專案:

  1. 在 [方案總管] 中,以滑鼠右鍵按一下專案,然後選取 [新增]>[新增 Azure 函式...]

  2. 在 [新增項目] 對話方塊中,選取 [Azure Function],然後將 [名稱] 欄位變更為 AnalyzeSentiment.cs。 接著,選取 [新增] 按鈕。

  3. 在 [新增 Azure 函式] 對話方塊中,選取 [HTTP 觸發程序],然後從 [授權層級] 下拉式清單中選擇 [匿名]。 然後,選取 [確定] 按鈕。

    AnalyzeSentiment.cs 檔案隨即在程式碼編輯器中開啟。 將下列 using 陳述式新增到 AnalyzeSentiment.cs 的頂端:

    using System;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    using Microsoft.Extensions.ML;
    using SentimentAnalysisFunctionsApp.DataModels;
    

    根據預設,AnalyzeSentiment 類別為 static。 請務必從類別定義移除 static 關鍵字。

    public class AnalyzeSentiment
    {
    
    }
    

建立資料模型

您必須為輸入資料和預測建立一些類別。 將新類別新增至專案:

  1. 在專案中建立名為 DataModels 的目錄以儲存您的資料模型:在方案總管中,以滑鼠右鍵按一下您的專案,然後選取 [新增 > 資料夾]。 輸入 "DataModels",然後按 Enter。

  2. 在方案總管中,以滑鼠右鍵按一下 DataModels 目錄,然後選取 [新增 > 類別]

  3. 在 [新增項目] 對話方塊中,選取 [類別],然後將 [名稱] 欄位變更為 SentimentData.cs。 接著,選取 [新增] 按鈕。

    SentimentData.cs 檔案隨即在程式碼編輯器中開啟。 將下列 using 陳述式新增至 SentimentData.cs 頂端:

    using Microsoft.ML.Data;
    

    移除現有的類別定義,然後將下列程式碼新增至 SentimentData.cs 檔案:

    public class SentimentData
    {
        [LoadColumn(0)]
        public string SentimentText;
    
        [LoadColumn(1)]
        [ColumnName("Label")]
        public bool Sentiment;
    }
    
  4. 在方案總管中,以滑鼠右鍵按一下 DataModels 目錄,然後選取 [新增 > 類別]

  5. 在 [加入新項目] 對話方塊中,選取 [類別],然後將 [名稱] 欄位變更為 SentimentPrediction.cs。 接著,選取 [新增] 按鈕。 SentimentPrediction.cs 檔案隨即在程式碼編輯器中開啟。 將下列 using 陳述式新增至 SentimentPrediction.cs 頂端:

    using Microsoft.ML.Data;
    

    移除現有的類別定義,然後將下列程式碼新增至 SentimentPrediction.cs 檔案:

    public class SentimentPrediction : SentimentData
    {
    
        [ColumnName("PredictedLabel")]
        public bool Prediction { get; set; }
    
        public float Probability { get; set; }
    
        public float Score { get; set; }
    }
    

    SentimentPrediction 會從 SentimentData 繼承,後者會在 SentimentText 屬性中提供原始資料的存取,以及由模型產生的輸出。

登錄 PredictionEnginePool 服務

若要進行單一預測,您必須建立 PredictionEnginePredictionEngine 不是安全執行緒。 此外,您必須在應用程式內有需要的任何地方建立其執行個體。 隨著應用程式成長,此程序可能會變得無法管理。 為了提升效能和執行緒安全性,請合併使用相依性插入與 PredictionEnginePool 服務,以建立 PredictionEngine 物件的 ObjectPool 供整個應用程式使用。

如果您想要深入了解相依性插入,下列連結提供詳細資訊。

  1. 在 [方案總管] 中,於專案上按一下滑鼠右鍵,然後選取 [新增]>[類別]

  2. 在 [新增項目] 對話方塊中,選取 [類別],然後將 [名稱] 欄位變更為 Startup.cs。 接著,選取 [新增] 按鈕。

  3. 將下列的 using 陳述式新增至 Startup.cs 的頂端:

    using Microsoft.Azure.Functions.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.ML;
    using SentimentAnalysisFunctionsApp;
    using SentimentAnalysisFunctionsApp.DataModels;
    using System.IO;
    using System;
    
  4. 移除 using 陳述式下方的現有程式碼,並新增下列程式碼:

    [assembly: FunctionsStartup(typeof(Startup))]
    namespace SentimentAnalysisFunctionsApp
    {
        public class Startup : FunctionsStartup
        {
    
        }
    }
    
  5. 定義變數來儲存應用程式執行所在的環境,以及模型位於 Startup 類別內的檔案路徑

    private readonly string _environment;
    private readonly string _modelPath;
    
  6. 請在其下方建立建構函式來設定 _environment_modelPath 變數的值。 當應用程式在本機執行時,預設環境為「開發」

    public Startup()
    {
        _environment = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
    
        if (_environment == "Development")
        {
            _modelPath = Path.Combine("MLModels", "sentiment_model.zip");
        }
        else
        {
            string deploymentPath = @"D:\home\site\wwwroot\";
            _modelPath = Path.Combine(deploymentPath, "MLModels", "sentiment_model.zip");
        }
    }
    
  7. 然後,新增名為 Configure 的新方法,以在建構函式下方註冊 PredictionEnginePool 服務。

    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddPredictionEnginePool<SentimentData, SentimentPrediction>()
            .FromFile(modelName: "SentimentAnalysisModel", filePath: _modelPath, watchForChanges: true);
    }
    

概括而言,此程式碼會在應用程式要求時自動初始化物件和服務以供稍後使用,而不必手動執行。

機器學習模型不是靜態的。 當新的定型資料可供使用時,模型會重新定型並重新部署。 將最新版本的模型放入應用程式的方法之一,就是重新啟動或重新部署您的應用程式。 不過,這會導致應用程式停機。 此 PredictionEnginePool 服務提供一種機制來重新載入更新後的模型,而不需重新啟動或重新部署您的應用程式。

watchForChanges 參數設定為 true,並 PredictionEnginePool 啟動接聽檔案系統變更通知的 FileSystemWatcher,然後在檔案發生變更時引發事件。 這會提示 PredictionEnginePool 自動重新載入模型。

模型是由 modelName 參數所識別,讓每個應用程式可以在變更時重新載入多個模型。

提示

或者,您可以在使用遠端儲存的模型時使用 FromUri 方法。 FromUri 不會監看檔案變更事件,而是輪詢遠端位置是否有變更。 輪詢間隔預設為 5 分鐘。 您可以根據應用程式的需求增加或減少輪詢間隔。 在下列程式碼範例中,PredictionEnginePool 會每隔一分鐘輪詢儲存在指定 URI 的模型。

builder.Services.AddPredictionEnginePool<SentimentData, SentimentPrediction>()
  .FromUri(
      modelName: "SentimentAnalysisModel",
      uri:"https://github.com/dotnet/samples/raw/main/machine-learning/models/sentimentanalysis/sentiment_model.zip",
      period: TimeSpan.FromMinutes(1));

將模型載入函式

將下列程式碼插入 AnalyzeSentiment 類別:

public AnalyzeSentiment(PredictionEnginePool<SentimentData, SentimentPrediction> predictionEnginePool)
{
    _predictionEnginePool = predictionEnginePool;
}

此程式碼會透過將 PredictionEnginePool 傳遞到您透過相依性插入所取得的函式建構函式來指派它。

使用模型來進行預測

使用下列程式碼取代 AnalyzeSentiment 類別中 Run 方法的現有實作:

public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    // Parse HTTP Request Body
    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    SentimentData data = JsonConvert.DeserializeObject<SentimentData>(requestBody);

    //Make Prediction
    SentimentPrediction prediction = _predictionEnginePool.Predict(modelName: "SentimentAnalysisModel", example: data);

    //Convert prediction to string
    string sentiment = Convert.ToBoolean(prediction.Prediction) ? "Positive" : "Negative";

    //Return Prediction
    return new OkObjectResult(sentiment);
}

Run 方法執行時,來自 HTTP 請求的傳入資料會還原序列化,並用來作為 PredictionEnginePool 的輸入。 接著會呼叫 Predict 方法,以使用在 Startup 類別中註冊的 SentimentAnalysisModel 進行預測,並在成功時將結果傳回給使用者。

在本機進行測試

一切都設定好後,就可以開始測試應用程式:

  1. 執行應用程式

  2. 開啟 Powershell 並在提示字元中輸入下列程式碼,其中 PORT 是正在執行您應用程式的連接埠。 一般來說,連接埠是 7071。

    Invoke-RestMethod "http://localhost:<PORT>/api/AnalyzeSentiment" -Method Post -Body (@{SentimentText="This is a very bad steak"} | ConvertTo-Json) -ContentType "application/json"
    

    如果成功,輸出看起來應該類似下列文字:

    Negative
    

恭喜! 您已成功提供您的模型,以使用 Azure Function 在網際網路上進行預測。

後續步驟