Get started with the local inference SDK for Azure Personalizer

The Personalizer local inference SDK (Preview) downloads the Personalizer model locally, and thus significantly reduces the latency of Rank calls by eliminating network calls. Every minute the client will download the most recent model in the background and use it for inference.

In this guide, you'll learn how to use the Personalizer local inference SDK.

You will need to install the Personalizer client library for .NET to:

  • Authenticate the quickstart example client with a Personalizer resource in Azure.
  • Send context and action features to the Reward API, which will return the best action from the Personalizer model
  • Send a reward score to the Rank API and train the Personalizer model.

Reference documentation | Library source code | Package (NuGet)

Prerequisites

  • Azure subscription - Create one for free
  • The current version of .NET Core.
  • Once you have your Azure subscription, create a Personalizer resource in the Azure portal to get your key and endpoint. After it deploys, click Go to resource.
    • You will need the key and endpoint from the resource you create to connect your application to the Personalizer API. You'll paste your key and endpoint into the code below later in the quickstart.
    • You can use the free pricing tier (F0) to try the service, and upgrade later to a paid tier for production.

Setting up

Change the model update frequency

In the Azure portal, in the Personalizer resource on the Configuration page, change the Model update frequency to 30 seconds. This short duration will train the service rapidly, allowing you to see how the top action changes for each iteration.

Change model update frequency

When a Personalizer loop is first instantiated, there is no model since there has been no Reward API calls to train from. Rank calls will return equal probabilities for each item. Your application should still always rank content using the output of RewardActionId.

Change the reward wait time

In the Azure portal, in the Personalizer resource on the Configuration page, change the Reward wait time to 10 minutes. This short duration will train the service rapidly, allowing you to see how the top action changes for each iteration.

Change reward wait time

Create a new C# application

Create a new .NET Core application in your preferred editor or IDE.

In a console window (such as cmd, PowerShell, or Bash), use the dotnet new command to create a new console app with the name personalizer-quickstart. This command creates a simple "Hello World" C# project with a single source file: Program.cs.

dotnet new console -n personalizer-quickstart

Change your directory to the newly created app folder. You can build the application with:

dotnet build

The build output should contain no warnings or errors.

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

Install the client library

Within the application directory, install the Personalizer client library for .NET with the following command:

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

Tip

If you're using the Visual Studio IDE, the client library is available as a downloadable NuGet package.

From the project directory, open the Program.cs file in your preferred editor or IDE. Add the following using directives:

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

Object model

The Personalizer client is a PersonalizerClient object that authenticates to Azure using Azure.AzureKeyCredential, which contains your key.

To ask for the single best item to show the user, create a PersonalizerRankOptions, then pass it to PersonalizerClient.Rank method. The Rank method returns a PersonalizerRankResult.

To send a reward score to Personalizer, pass the event ID and the reward score to the PersonalizerClient.Reward method.

Determining the reward score, in this quickstart is trivial. In a production system, the determination of what impacts the reward score and by how much can be a complex process, that you may decide to change over time. This design decision should be one of the primary decisions in your Personalizer architecture.

Code examples

These code snippets show you how to do the following tasks with the Personalizer client library for .NET:

Authenticate the client

In this section you'll do two things:

  • Specify your key and endpoint
  • Create a Personalizer client

Start by adding the following lines to your Program class. Make sure to add your key and endpoint from your Personalizer resource.

Important

Go to the Azure portal. If the Personalizer resource you created in the Prerequisites section deployed successfully, click the Go to Resource button under Next Steps. You can find your key and endpoint in the resource's key and endpoint page, under resource management.

Remember to remove the key from your code when you're done, and never post it publicly. For production, consider using a secure way of storing and accessing your credentials. For example, 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>";

Next, construct the Rank and Reward URLs. Note that setting useLocalInference: true as a parameter for PersonalizerClientOptions is required to enable local inference.

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

Get content choices represented as actions

Actions represent the content choices from which you want Personalizer to select the best content item. Add the following methods to the Program class to represent the set of actions and their features.

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

Get user preferences for context

Add the following methods to the Program class to get a user's input from the command line for the time of day and user taste preference. These will be used as context features.

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

Both methods use the GetKey method to read the user's selection from the command line.

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

Create the learning loop

The Personalizer learning loop is a cycle of Rank and Reward calls. In this quickstart, each Rank call, to personalize the content, is followed by a Reward call to tell Personalizer how well the service performed.

The following code loops through a cycle of asking the user their preferences through the command line, sending that information to Personalizer to select the best action for each slot, presenting the selection to the customer to choose from among the list, then sending a reward score to Personalizer signaling how well the service did in its selection.

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

Run the program

Run the application with the dotnet run command from your application directory.

dotnet run

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