Azure Functions にモデルをデプロイする

Azure Functions のサーバーレス環境を介して、HTTP 経由での予測のために、事前トレーニング済みの ML.NET 機械学習モデルをデプロイする方法について説明します。

前提条件

Azure Functions サンプルの概要

このサンプルは、事前トレーニング済みの二項分類モデルを使用してテキストのセンチメントを正または負として分類する、C# HTTP トリガー Azure Functions アプリケーションです。 Azure Functions では、クラウド内の管理されたサーバーレス環境で小規模なコードを大規模に実行する簡単な方法を提供します。 このサンプルのコードについては、GitHub の dotnet/machinelearning-samples リポジトリで見つけることができます。

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. [変更のプレビュー] ダイアログで [OK] ボタンを選択します。
    7. 一覧表示されているパッケージのライセンス条項に同意する場合は、 [ライセンスへの同意] ダイアログで [同意する] ボタンを選択します。

    同じ手順に従って、Microsoft.Extensions.MLMicrosoft.Extensions.DependencyInjectionMicrosoft.Azure.Functions.Extensions の NuGet パッケージをインストールします。

事前トレーニング済みモデルをプロジェクトに追加する

  1. 事前構築済みモデルを保存するための MLModels という名前のディレクトリをプロジェクト内に作成します。ソリューション エクスプローラーで、そのプロジェクトを右クリックし、[追加] > [新しいフォルダー] の順に選択します。 「MLModels」と入力し、Enter キーを押します。
  2. 構築済みのモデルを MLModels フォルダーにコピーします。
  3. ソリューション エクスプローラーで、構築済みのモデルのファイルを右クリックし、 [プロパティ] を選択します。 [詳細設定] で、 [出力ディレクトリにコピー] の値を [新しい場合はコピーする] に変更します。

Azure 関数を作成してセンチメントを分析する

センチメントを予測するクラスを作成します。 プロジェクトに新しいクラスを追加します。

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加]>[新しい Azure 関数] の順に選択します。

  2. [新しい項目の追加] ダイアログ ボックスで、 [Azure 関数] を選択し、 [名前] フィールドを SentimentData.cs に変更します。 次に [追加] を選択します。

  3. [新しい Azure 関数] ダイアログ ボックスで、[Http トリガー] を選択し、[承認レベル] ドロップダウンから [匿名] を選択します。 次に、 [OK] ボタンを選択します。

    コード エディターに AnalyzeSentiment.cs ファイルが開きます。 AnalyzeSentiment.cs の先頭に次の using ディレクティブを追加します。

    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 ファイルが開きます。 SentimentData.cs の先頭に次の using ディレクティブを追加します。

    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 ファイルが開きます。 SentimentPrediction.cs の先頭に次の using ディレクティブを追加します。

    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; }
    }
    

    SentimentPredictionSentimentData を継承し、モデルによって生成された出力だけでなく SentimentText プロパティで元のデータへのアクセスを提供します。

PredictionEnginePool サービスを登録する

1 つの予測を作成するには、PredictionEngine を作成する必要があります。 PredictionEngine はスレッド セーフではありません。 さらに、アプリケーション内で必要なすべての場所にそのインスタンスを作成する必要があります。 アプリケーションの規模が拡大すると、このプロセスが管理不能になる可能性があります。 パフォーマンスとスレッド セーフを向上させるには、依存性の挿入と PredictionEnginePool サービスを組み合わせて使用します。これにより、アプリケーション全体で使用する PredictionEngine オブジェクトの ObjectPool が作成されます。

依存関係の注入の詳細については、こちらのリンクを参照してください。

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加]>[クラス] の順に選択します。

  2. [新しい項目の追加] ダイアログ ボックスで、 [クラス] を選択し、 [名前] フィールドを「Startup.cs」に変更します。 次に [追加] を選択します。

  3. Startup.cs の先頭に次の using ディレクティブを追加します。

    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 変数の値を設定します。 アプリケーションがローカルで実行されている場合、既定の環境は 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 サービスをコンストラクターの下に登録します。

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

大まかに言えば、オブジェクトとサービスがアプリケーションによって要求されたときに、このコードでは、後で使用できるように手動ではなく自動的に初期化が実行されます。

機械学習モデルは静的ではありません。 新しいトレーニング データが使用可能になると、モデルの再トレーニングと再展開が行われます。 ご自分のアプリケーションで、モデルの最新バージョンが使用されるようにする 1 つの方法に、アプリケーションの再起動または再展開があります。 ただし、これによってアプリケーションのダウンタイムが発生します。 PredictionEnginePool サービスには、アプリケーションを再起動したり、再展開せずに、更新されたモデルを再度読み込むメカニズムが用意されています。

watchForChanges パラメーターを true に設定すると、PredictionEnginePool では、ファイル システムの変更通知をリッスンし、ファイルに変更があったときにイベントを発生させる FileSystemWatcher が開始されます。 これにより、モデルを自動的に再度読み込む PredictionEnginePool のプロンプトが表示されます。

モデルは modelName パラメーターによって識別されるため、変更時にアプリケーションごとに複数のモデルを再度読み込むことができます。

ヒント

また、リモートに格納されているモデルを操作するときは FromUri メソッドを使用できます。 FromUri では、ファイルの変更イベントを監視するのではなく、リモートの場所の変更をポーリングします。 ポーリング間隔は既定で 5 分に設定されています。 アプリケーションの要件に基づいてポーリング間隔を増減することができます。 次のコード サンプルでは、PredictionEnginePool は、指定された URI に格納されているモデルを 1 分ごとにポーリングします。

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 関数を使用したインターネット経由での予測の実行に対して、モデルを正常に提供できました。

次の手順