Azure AI Personalizer のローカル推論 SDK の概要

重要

2023 年 9 月 20 日以降は、新しい Personalizer リソースを作成できなくなります。 Personalizer サービスは、2026 年 10 月 1 日に廃止されます。

Personalizer のローカル推論 SDK (プレビュー) は Personalizer モデルをローカルにダウンロードするため、ネットワーク呼び出しが除去されて Rank 呼び出しの待機時間が大幅に短縮されます。 毎分、クライアントはバックグラウンドで最新のモデルをダウンロードし、推論に使用します。

このガイドでは、Personalizer のローカル推論 SDK を使用する方法について説明します。

次のことを行うには、.NET 用の Personalizer クライアント ライブラリをインストールする必要があります。

  • Azure の Personalizer リソースで、クイックスタートの例のクライアントを認証します。
  • コンテキストとアクションの特徴を Reward API に送信します。Personalizer モデルから最適なアクションが返されます
  • Rank API に報酬スコアを送信し、Personalizer モデルをトレーニングします。

リファレンス ドキュメント | ライブラリのソース コード | パッケージ (NuGet)

前提条件

  • Azure サブスクリプション - 無料アカウントを作成します
  • 最新バージョンの .NET Core
  • Azure サブスクリプションを用意できたら、Azure portal で Personalizer リソースを作成し、自分のキーとエンドポイントを取得します。 デプロイされたら、 [リソースに移動] を選択します。
    • アプリケーションを Personalizer API に接続するには、作成したリソースのキーとエンドポイントが必要です。 このクイックスタートで後に示すコードに、自分のキーとエンドポイントを貼り付けます。
    • Free 価格レベル (F0) を使用してサービスを試用し、後から運用環境用の有料レベルにアップグレードすることができます。

設定

モデルの更新頻度を変更する

Azure portal で、Personalizer リソースの [構成] ページに進み、[モデルの更新頻度] を 30 秒に変更します。 このような短い時間を設定することでモデルは迅速にトレーニングされ、繰り返しのたびに推奨されるアクションが変化する様子を確認できます。

Change model update frequency

報酬の待機時間を変更する

Azure portal の Personalizer リソースの [構成] ページに進み、[報酬の待機時間] を 10 分に変更します。 これにより、レコメンデーションの送信後、そのモデルがそのレコメンデーションからの報酬のフィードバックを受け取る待機時間が決まります。 トレーニングは、報酬の待機時間が経過するまで行われません。

Change reward wait time

新しい C# アプリケーションを作成する

好みのエディターまたは IDE で、新しい .NET Core アプリケーションを作成します。

コンソール ウィンドウ (cmd、PowerShell、Bash など) で、dotnet new コマンドを使用し、personalizer-quickstart という名前で新しいコンソール アプリを作成します。 このコマンドにより、1 つのソース ファイル (Program.cs) を使用する単純な "Hello World" C# プロジェクトが作成されます。

dotnet new console -n personalizer-quickstart

新しく作成されたアプリ フォルダーにディレクトリを変更します。 次を使用してアプリケーションをビルドできます。

dotnet build

ビルドの出力に警告やエラーが含まれないようにする必要があります。

...
Build succeeded.
 0 Warning(s)
 0 Error(s)
...

クライアント ライブラリをインストールする

次のコマンドを使用して、アプリケーション ディレクトリ内に .NET 用 Personalizer クライアント ライブラリをインストールします。

dotnet add package Azure.AI.Personalizer --version 2.0.0-beta.2

ヒント

Visual Studio IDE を使用している場合、クライアント ライブラリは、ダウンロード可能な NuGet パッケージとして入手できます。

プロジェクト ディレクトリから、好みのエディターまたは IDE で Program.cs ファイルを開きます。 ディレクティブを使用して以下を追加します。

using Azure;
using Azure.AI.Personalizer;
using System;
using System.Collections.Generic;
using System.Linq;

オブジェクト モデル

Personalizer クライアントは、自分のキーが含まれている Azure.AzureKeyCredential を使用して Azure に対する認証を行う PersonalizerClient オブジェクトです。

ユーザーを表示するための最適な項目を 1 つ要求するには、PersonalizerRankOptions を作成し、それを PersonalizerClient.Rank メソッドに渡します。 Rank メソッドから PersonalizerRankResult が返されます。

報酬スコアを Personalizer に送信するには、イベント ID と報酬スコアを PersonalizerClient.Reward メソッドに渡します。

このクイックスタートでは、報酬スコアの決定は重要ではありません。 実稼働システムでは、何がどの程度まで報酬スコアに影響を及ぼすかを特定するのは複雑なプロセスとなる場合があり、そのプロセスはやがて変更することになる場合もあります。 実際の Personalizer アーキテクチャでは、この設計上の意思決定を主要な意思決定に含めるようにしてください。

コード例

以下のコード スニペットは、.NET 用 Personalizer クライアント ライブラリを使用して次のタスクを実行する方法を示します。

クライアントを認証する

このセクションでは、次の 2 つのことを行います。

  • キーとエンドポイントを指定する
  • Personalizer クライアントを作成する

まず、Program クラスに以下の行を追加します。 必ず Personalizer リソースから取得したキーとエンドポイントを追加してください。

重要

Azure Portal にアクセスします。 「前提条件」セクションで作成した Personalizer リソースが正常にデプロイされた場合、 [次の手順] の下にある [リソースに移動] ボタンをクリックします。 キーとエンドポイントは、リソースの [key and endpoint](キーとエンドポイント) ページの [リソース管理] にあります。

終わったらコードからキーを削除し、公開しないよう注意してください。 運用環境では、資格情報を安全に格納して利用するための方法を用いることを検討してください。 たとえば、Azure Key Vault が考えられます。

private const string ServiceEndpoint  = "https://REPLACE-WITH-YOUR-PERSONALIZER-RESOURCE-NAME.cognitiveservices.azure.com";
private const string ResourceKey = "<REPLACE-WITH-YOUR-PERSONALIZER-KEY>";

次に、ランクと報酬の URL を作成します。 ローカル推論を有効にするには、useLocalInference: truePersonalizerClientOptions のパラメーターとして設定することが必要であることに注意してください。

static PersonalizerClient InitializePersonalizerClient(Uri url)
{
    // Set the local inference flag to true when initializing the client.
    return new PersonalizerClient(url, new AzureKeyCredential(ResourceKey), new PersonalizerClientOptions(useLocalInference: true));
}

アクションとして表されるコンテンツの選択肢を取得する

アクションはコンテンツの選択肢を表します。Personalizer を使用して、この中から最適なコンテンツ項目を選択します。 一連のアクションとそのフィーチャーを表す次のメソッドを Program クラスに追加します。

static IList<PersonalizerRankableAction> GetActions()
{
    IList<PersonalizerRankableAction> actions = new List<PersonalizerRankableAction>
    {
        new PersonalizerRankableAction(
            id: "pasta",
            features: new List<object>() { new { taste = "salty", spiceLevel = "medium" }, new { nutritionLevel = 5, cuisine = "italian" } }
        ),

        new PersonalizerRankableAction(
            id: "ice cream",
            features: new List<object>() { new { taste = "sweet", spiceLevel = "none" }, new { nutritionalLevel = 2 } }
        ),

        new PersonalizerRankableAction(
            id: "juice",
            features: new List<object>() { new { taste = "sweet", spiceLevel = "none" }, new { nutritionLevel = 5 }, new { drink = true } }
        ),

        new PersonalizerRankableAction(
            id: "salad",
            features: new List<object>() { new { taste = "salty", spiceLevel = "low" }, new { nutritionLevel = 8 } }
        )
    };

    return actions;
}

コンテキストに対するユーザーの好みを取得する

Program クラスに次のメソッドを追加して、時間帯とユーザーの味の好みに関するユーザーの入力をコマンド ラインから取得します。 これらはコンテキストのフィーチャーとして使用されます。

static string GetTimeOfDayForContext()
{
    string[] timeOfDayFeatures = new string[] { "morning", "afternoon", "evening", "night" };

    Console.WriteLine("\nWhat time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night");
    if (!int.TryParse(GetKey(), out int timeIndex) || timeIndex < 1 || timeIndex > timeOfDayFeatures.Length)
    {
        Console.WriteLine("\nEntered value is invalid. Setting feature value to " + timeOfDayFeatures[0] + ".");
        timeIndex = 1;
    }

    return timeOfDayFeatures[timeIndex - 1];
}
static string GetUsersTastePreference()
{
    string[] tasteFeatures = new string[] { "salty", "sweet" };
    var random = new Random();
    var taste = random.Next(1, 2);

    Console.WriteLine("\nWhat type of food would you prefer (enter number)? 1. salty 2. sweet");
    if (!int.TryParse(GetKey(), out int tasteIndex) || tasteIndex < 1 || tasteIndex > tasteFeatures.Length)
    {
        Console.WriteLine("\nEntered value is invalid. Setting feature value to " + tasteFeatures[0] + ".");
        tasteIndex = 1;
    }

    return tasteFeatures[taste - 1];
}

どちらのメソッドも、GetKey メソッドを使用して、ユーザーの選択内容をコマンド ラインから読み取ります。

private static string GetKey()
{
    return Console.ReadKey().Key.ToString().Last().ToString().ToUpper();
}
private static IList<object> GetContext(string time, string taste)
{
    return new List<object>()
    {
        new { time = time },
        new { taste = taste }
    };
}

学習ループを作成する

Personalizer の学習ループとは、Rank 呼び出しと Reward 呼び出しのサイクルです。 このクイックスタートでは、コンテンツをパーソナライズするための各 Rank 呼び出しの後に Reward 呼び出しを行って、サービスがどの程度適切に実行されたかを Personalizer に伝えます。

次のコードでは、コマンド ラインでユーザーに好みをたずね、その情報を Personalizer に送信して各スロットに最適なアクションを選択し、その選択をリストから選択できるようユーザーに提示した後、その選択にサービスがどの程度寄与したかを示す報酬スコアを Personalizer に送る形でサイクルをループで処理しています。

static void Main(string[] args)
{
    Console.WriteLine($"Welcome to this Personalizer Quickstart!\n" +
    $"This code will help you understand how to use the Personalizer APIs (rank and reward).\n" +
    $"Each iteration represents a user interaction and will demonstrate how context, actions, and rewards work.\n" +
    $"Note: Personalizer AI models learn from a large number of user interactions:\n" +
    $"You won't be able to tell the difference in what Personalizer returns by simulating a few events by hand.\n" +
    $"If you want a sample that focuses on seeing how Personalizer learns, see the Python Notebook sample.");

    int iteration = 1;
    bool runLoop = true;

    IList<PersonalizerRankableAction> actions = GetActions();
    PersonalizerClient client = InitializePersonalizerClient(new Uri(ServiceEndpoint));

    do
    {
        Console.WriteLine("\nIteration: " + iteration++);

        string timeOfDayFeature = GetTimeOfDayForContext();
        string deviceFeature = GetUsersTastePreference();

        IList<object> currentContext = GetContext(timeOfDayFeature, deviceFeature);

        string eventId = Guid.NewGuid().ToString();

        var rankOptions = new PersonalizerRankOptions(actions: actions, contextFeatures: currentContext, eventId: eventId);
        PersonalizerRankResult rankResult = client.Rank(rankOptions);

        Console.WriteLine("\nPersonalizer service thinks you would like to have: " + rankResult.RewardActionId + ". Is this correct? (y/n)");

        float reward = 0.0f;
        string answer = GetKey();

        if (answer == "Y")
        {
            reward = 1.0f;
            Console.WriteLine("\nGreat! Enjoy your food.");
        }
        else if (answer == "N")
        {
            reward = 0.0f;
            Console.WriteLine("\nYou didn't like the recommended food choice.");
        }
        else
        {
            Console.WriteLine("\nEntered choice is invalid. Service assumes that you didn't like the recommended food choice.");
        }

        client.Reward(rankResult.EventId, reward);

        Console.WriteLine("\nPress q to break, any other key to continue:");
        runLoop = !(GetKey() == "Q");

    } while (runLoop);
}

プログラムの実行

アプリケーション ディレクトリから、dotnet run コマンドを使用してアプリケーションを実行します。

dotnet run

The quickstart program asks a couple of questions to gather user preferences, known as features, then provides the top action.