将模型部署到 Azure FunctionsDeploy a model to Azure Functions

了解如何通过 Azure Functions 无服务器环境部署预先训练的 ML.NET 机器学习模型,以便通过 HTTP 进行预测。Learn how to deploy a pre-trained ML.NET machine learning model for predictions over HTTP through an Azure Functions serverless environment.

备注

此示例运行 PredictionEnginePool 服务的预览版本。This sample runs a preview version of the PredictionEnginePool service.

先决条件Prerequisites

Azure Functions 示例概述Azure Functions sample overview

此示例是 C# HTTP 触发器 Azure Functions 应用程序 ,它使用预先定型的二进制分类模型将文本的情绪分类为消极或积极。This sample is a C# HTTP Trigger Azure Functions application that uses a pretrained binary classification model to categorize the sentiment of text as positive or negative. Azure Functions 提供了一种简便的方法,可以在云中托管的无服务器环境中大规模运行少量代码。Azure Functions provides an easy way to run small pieces of code at scale on a managed serverless environment in the cloud. 若要获取此示例的代码,请参阅 GitHub 上的 dotnet/machinelearning-samples 存储库。The code for this sample can be found on the dotnet/machinelearning-samples repository on GitHub.

创建 Azure Functions 项目Create Azure Functions project

  1. 打开 Visual Studio 2017。Open Visual Studio 2017. 从菜单栏中选择“文件” > “新建” > “项目” 。Select File > New > Project from the menu bar. 在“新项目” 对话框中,依次选择“Visual C#” 节点和“云” 节点。In the New Project dialog, select the Visual C# node followed by the Cloud node. 然后,选择“Azure Functions” 项目模板。Then select the Azure Functions project template. 在“名称” 文本框中,键入“SentimentAnalysisFunctionsApp”,再选择“确定” 按钮。In the Name text box, type "SentimentAnalysisFunctionsApp" and then select the OK button.

  2. 在“新项目” 对话框中,打开项目选项上方的下拉列表,并选择“Azure Functions v2 (.NET Core)” 。In the New Project dialog, open the dropdown above the project options and select Azure Functions v2 (.NET Core). 然后,依次选择“Http 触发器” 项目和“确定” 按钮。Then, select the Http trigger project and then select the OK button.

  3. 在项目中创建“MLModels” 目录,用于保存模型:Create a directory named MLModels in your project to save your model:

    在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” > “新文件夹” 。In Solution Explorer, right-click on your project and select Add > New Folder. 键入“MLModels”,再按 Enter。Type "MLModels" and hit Enter.

  4. 安装 Microsoft.ML NuGet 包 版本 1.3.1 :Install the Microsoft.ML NuGet Package version 1.3.1:

    在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。In Solution Explorer, right-click on your project and select Manage NuGet Packages. 将“nuget.org”选择为“包源”,选择“浏览”选项卡并搜索“Microsoft.ML” ,在列表中选择该包,然后选择“安装” 按钮。Choose "nuget.org" as the Package source, select the Browse tab, search for Microsoft.ML, select that package in the list, and select the Install button. 选择“预览更改” 对话框上的“确定” 按钮,如果你同意所列包的许可条款,则选择“接受许可” 对话框上的“我接受” 按钮。Select the OK button on the Preview Changes dialog and then select the I Accept button on the License Acceptance dialog if you agree with the license terms for the packages listed.

  5. 安装 Microsoft.Azure.Functions.Extensions NuGet 包 :Install the Microsoft.Azure.Functions.Extensions NuGet Package:

    在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。In Solution Explorer, right-click on your project and select Manage NuGet Packages. 选择“nuget.org”作为包源,然后选择“浏览”选项卡并搜索“Microsoft.Azure.Functions.Extensions”,在列表中选择该包,再选择“安装”按钮 。Choose "nuget.org" as the Package source, select the Browse tab, search for Microsoft.Azure.Functions.Extensions, select that package in the list, and select the Install button. 选择“预览更改” 对话框上的“确定” 按钮,如果你同意所列包的许可条款,则选择“接受许可” 对话框上的“我接受” 按钮。Select the OK button on the Preview Changes dialog and then select the I Accept button on the License Acceptance dialog if you agree with the license terms for the packages listed.

  6. 安装 0.15.1 版本的 Microsoft.Extensions.ML NuGet 包 :Install the Microsoft.Extensions.ML NuGet Package version 0.15.1:

    在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。In Solution Explorer, right-click on your project and select Manage NuGet Packages. 选择“nuget.org”作为包源,然后选择“浏览”选项卡并搜索“Microsoft.Extensions.ML”,在列表中选择包,再选择“安装”按钮 。Choose "nuget.org" as the Package source, select the Browse tab, search for Microsoft.Extensions.ML, select that package in the list, and select the Install button. 选择“预览更改” 对话框上的“确定” 按钮,如果你同意所列包的许可条款,则选择“接受许可” 对话框上的“我接受” 按钮。Select the OK button on the Preview Changes dialog and then select the I Accept button on the License Acceptance dialog if you agree with the license terms for the packages listed.

  7. 安装 1.0.31 版本的 Microsoft.NET.Sdk.Functions NuGet 包 :Install the Microsoft.NET.Sdk.Functions NuGet Package version 1.0.31:

    在“解决方案资源管理器”中,右键单击项目,然后选择“管理 NuGet 包” 。In Solution Explorer, right-click on your project and select Manage NuGet Packages. 选择“nuget.org”作为包源,然后选择“已安装”选项卡并搜索“Microsoft.NET.Sdk.Functions”,在列表中选择该包,再从“版本”下拉列表中选择“1.0.31”,并选择“更新”按钮 。 Choose "nuget.org" as the Package source, select the Installed tab, search for Microsoft.NET.Sdk.Functions, select that package in the list, select 1.0.31 from the Version dropdown, and select the Update button. 选择“预览更改” 对话框上的“确定” 按钮,如果你同意所列包的许可条款,则选择“接受许可” 对话框上的“我接受” 按钮。Select the OK button on the Preview Changes dialog and then select the I Accept button on the License Acceptance dialog if you agree with the license terms for the packages listed.

将预先训练的模型添加到项目Add pre-trained model to project

  1. 将预生成的模型复制到 MLModels 文件夹。Copy your pre-built model to the MLModels folder.
  2. 在“解决方案资源管理器”中,右键单击预生成的模型文件,并选择“属性” 。In Solution Explorer, right-click your pre-built model file and select Properties. 在“高级”下,将“复制到输出目录”的值更改为“如果较新则复制” 。Under Advanced, change the value of Copy to Output Directory to Copy if newer.

创建 Azure 函数用于分析情绪Create Azure Function to analyze sentiment

创建情绪预测类。Create a class to predict sentiment. 向项目添加一个新类:Add a new class to your project:

  1. 在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” > “新项” 。In Solution Explorer, right-click the project, and then select Add > New Item.

  2. 在“添加新项” 对话框中,选择“Azure 函数” ,并将“名称” 字段更改为“AnalyzeSentiment.cs” 。In the Add New Item dialog box, select Azure Function and change the Name field to AnalyzeSentiment.cs. 然后,选择“添加” 按钮。Then, select the Add button.

  3. 在“新 Azure 函数” 对话框中,选择“Http 触发器” 。In the New Azure Function dialog box, select Http Trigger. 然后,选择“确定” 按钮。Then, select the OK button.

    此时,AnalyzeSentiment.cs 文件在代码编辑器中打开。The AnalyzeSentiment.cs file opens in the code editor. 将以下 using 语句添加到 AnalyzeSentiment.cs 的顶部:Add the following using statement to the top of 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 类为 staticBy default, the AnalyzeSentiment class is static. 确保从类定义中删除 static 关键字。Make sure to remove the static keyword from the class definition.

    public class AnalyzeSentiment
    {
    
    }
    

创建数据模型Create data models

需要为输入数据和预测创建一些类。You need to create some classes for your input data and predictions. 向项目添加一个新类:Add a new class to your project:

  1. 在项目中创建“DataModels” 目录,用于保存数据模型:在“解决方案资源管理器”中,右键单击项目,并依次选择“添加”>“新文件夹” 。Create a directory named DataModels in your project to save your data models: In Solution Explorer, right-click on your project and select Add > New Folder. 键入“DataModels”,再按 Enter。Type "DataModels" and hit Enter.

  2. 在“解决方案资源管理器”中,右键单击“DataModels” 目录,再依次选择“添加”>“新项” 。In Solution Explorer, right-click the DataModels directory, and then select Add > New Item.

  3. 在“添加新项” 对话框中,选择“类” 并将“名称” 字段更改为“SentimentData.cs” 。In the Add New Item dialog box, select Class and change the Name field to SentimentData.cs. 然后,选择“添加” 按钮。Then, select the Add button.

    “SentimentData.cs” 文件随即在代码编辑器中打开。The SentimentData.cs file opens in the code editor. 将以下 using 语句添加到 SentimentData.cs 顶部:Add the following using statement to the top of SentimentData.cs:

    using Microsoft.ML.Data;
    

    删除现有类定义,并将以下代码添加到 SentimentData.cs 文件:Remove the existing class definition and add the following code to the SentimentData.cs file:

    public class SentimentData
    {
        [LoadColumn(0)]
        public string SentimentText;
    
        [LoadColumn(1)]
        [ColumnName("Label")]
        public bool Sentiment;
    }
    
  4. 在“解决方案资源管理器”中,右键单击“DataModels” 目录,再依次选择“添加”>“新项” 。In Solution Explorer, right-click the DataModels directory, and then select Add > New Item.

  5. 在“添加新项” 对话框中,选择“类” ,并将“名称” 字段更改为“SentimentPrediction.cs” 。In the Add New Item dialog box, select Class and change the Name field to SentimentPrediction.cs. 然后,选择“添加” 按钮。Then, select the Add button. 此时,SentimentPrediction.cs 文件在代码编辑器中打开。The SentimentPrediction.cs file opens in the code editor. 将以下 using 语句添加到 SentimentPrediction.cs 顶部:Add the following using statement to the top of SentimentPrediction.cs:

    using Microsoft.ML.Data;
    

    删除现有类定义,并将以下代码添加到 SentimentPrediction.cs 文件:Remove the existing class definition and add the following code to the SentimentPrediction.cs file:

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

    SentimentPrediction 继承自 SentimentData,后者提供对 SentimentText 属性中原始数据以及模型生成的输出的访问。SentimentPrediction inherits from SentimentData which provides access to the original data in the SentimentText property as well as the output generated by the model.

注册 PredictionEnginePool 服务Register PredictionEnginePool service

若要进行单一预测,必须创建 PredictionEngineTo make a single prediction, you have to create a PredictionEngine. PredictionEngine 不是线程安全型。PredictionEngine is not thread-safe. 此外,必须在应用程序中的每一处所需位置创建它的实例。Additionally, you have to create an instance of it everywhere it is needed within your application. 随着应用程序的增长,此过程可能会变得难以管理。As your application grows, this process can become unmanageable. 为了提高性能和线程安全,请结合使用依赖项注入和 PredictionEnginePool 服务,这将创建一个在整个应用程序中使用的 PredictionEngine 对象的 ObjectPoolFor improved performance and thread safety, use a combination of dependency injection and the PredictionEnginePool service, which creates an ObjectPool of PredictionEngine objects for use throughout your application.

若要详细了解依赖项注入,请单击下面的链接。The following link provides more information if you want to learn more about dependency injection.

  1. 在“解决方案资源管理器” 中,右键单击项目,然后选择“添加” > “新项” 。In Solution Explorer, right-click the project, and then select Add > New Item.

  2. 在“添加新项”对话框中,选择“类”并将“名称”字段更改为“Startup.cs” 。In the Add New Item dialog box, select Class and change the Name field to Startup.cs. 然后,选择“添加” 按钮。Then, select the Add button.

  3. 将以下 using 语句添加到 Startup.cs 的顶部: Add the following using statements to the top of Startup.cs:

    using System;
    using System.IO;
    using Microsoft.Azure.Functions.Extensions.DependencyInjection;
    using Microsoft.Extensions.ML;
    using SentimentAnalysisFunctionsApp;
    using SentimentAnalysisFunctionsApp.DataModels;
    
  4. 删除 using 语句下的现有代码,并添加以下代码:Remove the existing code below the using statements and add the following code:

    [assembly: FunctionsStartup(typeof(Startup))]
    namespace SentimentAnalysisFunctionsApp
    {
        public class Startup : FunctionsStartup
        {
    
        }
    }
    
  5. Startup 类中定义变量,以存储运行应用的环境以及模型所在的文件路径Define variables to store the environment the app is running in and the file path where the model is located inside the Startup class

    private readonly string _environment;
    private readonly string _modelPath;
    
  6. 在它下面创建一个构造函数,以设置 _environment_modelPath 变量的值。Below that, create a constructor to set the values of the _environment and _modelPath variables. 应用程序在本地运行时,默认环境为“开发”。 When the application is running locally, the default environment is Development.

    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 服务。Then, add a new method called Configure to register the PredictionEnginePool service below the constructor.

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

概括地讲,此代码在应用程序请求时自动初始化对象和服务供以后使用,无需手动执行初始化。At a high level, this code initializes the objects and services automatically for later use when requested by the application instead of having to manually do it.

机器学习模型不是静态的。Machine learning models are not static. 随着新的训练数据变得可用,模型将重新训练和重新部署。As new training data becomes available, the model is retrained and redeployed. 将最新版本的模型引入应用程序的一种方法是重新部署整个应用程序。One way to get the latest version of the model into your application is to redeploy the entire application. 但这会导致应用程序关闭。However, this introduces application downtime. PredictionEnginePool 服务提供了一种机制,用于在不使应用程序关闭的情况下重新加载已更新的模型。The PredictionEnginePool service provides a mechanism to reload an updated model without taking your application down.

watchForChanges 参数设置为 true,则 PredictionEnginePool 会启动 FileSystemWatcher,用于侦听文件系统更改通知并在文件发生更改时引发事件。Set the watchForChanges parameter to true, and the PredictionEnginePool starts a FileSystemWatcher that listens to the file system change notifications and raises events when there is a change to the file. 这会提示 PredictionEnginePool 自动重新加载模型。This prompts the PredictionEnginePool to automatically reload the model.

模型由 modelName 参数标识,因此更改时可以重新加载每个应用程序的多个模型。The model is identified by the modelName parameter so that more than one model per application can be reloaded upon change.

提示

或者,如果使用远程存储的模型,则可以使用 FromUri 方法。Alternatively, you can use the FromUri method when working with models stored remotely. FromUri 会轮询远程位置以获取更改,而不是监视文件更改事件。Rather than watching for file changed events, FromUri polls the remote location for changes. 轮询间隔默认为 5 分钟。The polling interval defaults to 5 minutes. 你可以根据应用程序的要求,增加或减少轮询间隔。You can increase or decrease the polling interval based on your application's requirements. 在下面的代码示例中,PredictionEnginePool 每分钟轮询存储在指定 URI 中的模型。In the code sample below, the PredictionEnginePool polls the model stored at the specified URI every minute.

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

将模型加载到函数中Load the model into the function

AnalyzeSentiment 类中插入以下代码:Insert the following code inside the AnalyzeSentiment class:

private readonly PredictionEnginePool<SentimentData, SentimentPrediction> _predictionEnginePool;

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

此代码通过将 PredictionEnginePool 传递给函数的构造函数(通过依赖项注入获得)来分配它。This code assigns the PredictionEnginePool by passing it to the function's constructor which you get via dependency injection.

使用模型进行预测Use the model to make predictions

将 AnalyzeSentiment 类中 Run 方法的现有实现替换为以下代码:Replace the existing implementation of Run method in AnalyzeSentiment class with the following code:

[FunctionName("AnalyzeSentiment")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "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 (ActionResult)new OkObjectResult(sentiment);
}

执行 Run 方法时,来自 HTTP 请求的传入数据被反序列化并用作 PredictionEnginePool 的输入。When the Run method executes, the incoming data from the HTTP request is deserialized and used as input for the PredictionEnginePool. 然后,调用 Predict 方法,使用在 Startup 类中注册的 SentimentAnalysisModel 进行预测,并在成功时将结果返回给用户。The Predict method is then called to make predictions using the SentimentAnalysisModel registered in the Startup class and returns the results back to the user if successful.

在本地测试Test locally

至此,已完成所有设置,是时候测试应用程序了:Now that everything is set up, it's time to test the application:

  1. 运行此应用程序Run the application

  2. 打开 PowerShell,并在提示符中输入代码(其中,PORT 是应用程序运行所在的端口)。Open PowerShell and enter the code into the prompt where PORT is the port your application is running on. 端口通常是 7071。Typically the port is 7071.

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

    如果成功,输出文本应如下所示:If successful, the output should look similar to the text below:

    Negative
    

祝贺你!Congratulations! 你已成功使用 Azure Functions 通过 Internet 提供模型进行预测。You have successfully served your model to make predictions over the internet using an Azure Function.

后续步骤Next Steps