Tutoriel : Utiliser Personalizer dans Azure Notebook

Important

À compter du 20 septembre 2023, vous ne pourrez pas créer de ressources Personalizer. Le service Personalizer est mis hors service le 1er octobre 2026.

Ce tutoriel exécute une boucle Personalizer dans un notebook Azure, illustrant le cycle de vie de bout en bout d’une boucle Personalizer.

La boucle suggère le type de café qu’un client doit commander. Les utilisateurs et leurs préférences sont stockés dans un jeu de données d’utilisateurs. Des informations sur le café sont stockées dans un jeu de données de cafés.

Utilisateurs et café

Le notebook, qui simule l’interaction utilisateur avec un site web, sélectionne un utilisateur aléatoire, l’heure de la journée et le type de météo dans le jeu de données. Voici un résumé des informations sur les utilisateurs :

Clients - caractéristiques de contexte Heures de la journée Types de météo
Alice
Bob
Cathy
Dave
Matin
Après-midi
Soir
Ensoleillé
Pluvieux
Neige

Pour aider Personalizer à apprendre, au fil du temps, le système connaît également les détails concernant la sélection de café de chaque personne.

Café - caractéristiques d’action Types d’entités temperature Lieu d’origine Type de torréfaction Bio
Cappuccino À chaud Kenya Foncé Bio
Infusion à froid Froid Brésil Léger Bio
Moka frappée Froid Éthiopie Léger Non bio
Latte À chaud Brésil Foncé Non bio

L’objectif de la boucle Personalizer est de trouver le plus souvent possible la meilleure correspondance entre les utilisateurs et le café.

Le code de ce tutoriel est disponible dans le dépôt GitHub d’exemples Personalizer.

Fonctionnement de la simulation

Au début, seules 20 à 30 % des suggestions de Personalizer sont correctes. Cette réussite est indiquée par la récompense renvoyée à l’API Reward de Personalizer, avec un score de 1. Après un certain nombre d’appels de classement et de récompense, le système s’améliore.

Après les requêtes initiales, exécutez une évaluation hors connexion. Cela permet à Personalizer d’examiner les données et de suggérer une meilleure stratégie d’apprentissage. Appliquez la nouvelle stratégie d’apprentissage et réexécutez le notebook avec 20 % du nombre de requêtes précédentes. La boucle sera plus performante avec la nouvelle stratégie d’apprentissage.

Appels de classement et de récompense

Pour chacun des quelques milliers d’appels au service Personalizer, le notebook Azure envoie la requête de classement à l’API REST :

  • Un ID unique de l’événement de classement/requête
  • Caractéristiques de contexte : un choix aléatoire d’utilisateur, de météo et d’heure de la journée, simulant un utilisateur sur un site web ou un appareil mobile
  • Actions avec caractéristiques : toutes les données sur le café, à partir desquelles Personalizer fait une suggestion

Le système reçoit la demande, puis compare cette prédiction avec le choix connu de l’utilisateur pour la même heure de la journée et la même météo. Si le choix connu est le même que le choix prédit, la récompense de 1 est renvoyée à Personalizer. Sinon, la récompense renvoyée est 0.

Notes

S’agissant d’une simulation, l’algorithme de récompense est simple. Dans un scénario réel, l’algorithme doit utiliser une logique métier, éventuellement avec des pondérations pour différents aspects de l’expérience du client, afin de déterminer le score de récompense.

Prérequis

Descriptions des fichiers :

Configurer la ressource Personalizer

Dans le portail Azure, configurez votre ressource Personalizer avec la fréquence de mise à jour du modèle définie sur 15 secondes et une durée d’attente de la récompense de 10 minutes. Ces valeurs figurent dans la page Configuration .

Paramètre Valeur
Fréquence de mise à jour du modèle 15 secondes
Durée d’attente de la récompense 10 minutes

Ces valeurs sont très brèves afin de pouvoir observer les changements dans ce tutoriel. Vous ne devez pas les utiliser dans un scénario de production sans avoir vérifié qu’elles vous permettent d’atteindre votre objectif avec votre boucle Personalizer.

Configurer le notebook Azure

  1. Remplacez le noyau par Python 3.6.
  2. Ouvrez le fichier Personalizer.ipynb .

Exécuter les cellules du notebook

Exécutez chaque cellule exécutable et attendez qu’elle retourne. Vous savez qu’elle est terminée quand les crochets à côté de la cellule affichent un nombre au lieu de *. Les sections suivantes expliquent ce que fait chaque cellule programmatiquement et ce qui est attendu pour la sortie.

Inclure les modules Python

Incluez les modules Python requis. La cellule n’a pas de sortie.

import json
import matplotlib.pyplot as plt
import random
import requests
import time
import uuid

Définir le nom et la clé de ressource Personalizer

À partir du portail Azure, recherchez votre clé et votre point de terminaison dans la page Démarrage rapide de votre ressource Personalizer. Remplacez la valeur de <your-resource-name> par le nom de votre ressource Personalizer. Remplacez la valeur de <your-resource-key> par votre clé Personalizer.

# Replace 'personalization_base_url' and 'resource_key' with your valid endpoint values.
personalization_base_url = "https://<your-resource-name>.cognitiveservices.azure.com/"
resource_key = "<your-resource-key>"

Utilisez cette fonction pour noter les heures de début et de fin de la fonction itérative.

Ces cellules n’ont pas de sortie. La fonction génère la date et l’heure actuelles quand elle est appelée.

# Print out current datetime
def currentDateTime():
    currentDT = datetime.datetime.now()
    print (str(currentDT))

Obtenir l’heure de la dernière mise à jour du modèle

Quand la fonction get_last_updated est appelée, elle imprime la date et l’heure de la dernière modification du modèle.

Ces cellules n’ont pas de sortie. La fonction génère la date du dernier entraînement du modèle quand elle est appelée.

La fonction utilise une API REST pour obtenir les propriétés du modèle.

# ititialize variable for model's last modified date
modelLastModified = ""
def get_last_updated(currentModifiedDate):

    print('-----checking model')

    # get model properties
    response = requests.get(personalization_model_properties_url, headers = headers, params = None)

    print(response)
    print(response.json())

    # get lastModifiedTime
    lastModifiedTime = json.dumps(response.json()["lastModifiedTime"])

    if (currentModifiedDate != lastModifiedTime):
        currentModifiedDate = lastModifiedTime
        print(f'-----model updated: {lastModifiedTime}')

Obtenir la configuration du service et de la stratégie

Validez l’état du service à l’aide de ces deux appels REST.

Ces cellules n’ont pas de sortie. La fonction génère les valeurs du service quand elle est appelée.

def get_service_settings():

    print('-----checking service settings')

    # get learning policy
    response = requests.get(personalization_model_policy_url, headers = headers, params = None)

    print(response)
    print(response.json())

    # get service settings
    response = requests.get(personalization_service_configuration_url, headers = headers, params = None)

    print(response)
    print(response.json())

Construire des URL et lire des fichiers de données JSON

Cette cellule

  • Génère les URL utilisées dans les appels REST.
  • Définit l’en-tête de sécurité à l’aide de votre clé de ressource Personalizer.
  • Définit la valeur de départ aléatoire pour l’ID d’événement de classement.
  • Lit les fichiers de données JSON.
  • Appelle la méthode get_last_updated (la stratégie d’apprentissage a été supprimée dans l’exemple de sortie).
  • Appelle la méthode get_service_settings.

La cellule contient la sortie de l’appel aux fonctions get_last_updated et get_service_settings.

# build URLs
personalization_rank_url = personalization_base_url + "personalizer/v1.0/rank"
personalization_reward_url = personalization_base_url + "personalizer/v1.0/events/" #add "{eventId}/reward"
personalization_model_properties_url = personalization_base_url + "personalizer/v1.0/model/properties"
personalization_model_policy_url = personalization_base_url + "personalizer/v1.0/configurations/policy"
personalization_service_configuration_url = personalization_base_url + "personalizer/v1.0/configurations/service"

headers = {'Ocp-Apim-Subscription-Key' : resource_key, 'Content-Type': 'application/json'}

# context
users = "users.json"

# action features
coffee = "coffee.json"

# empty JSON for Rank request
requestpath = "example-rankrequest.json"

# initialize random
random.seed(time.time())

userpref = None
rankactionsjsonobj = None
actionfeaturesobj = None

with open(users) as handle:
    userpref = json.loads(handle.read())

with open(coffee) as handle:
    actionfeaturesobj = json.loads(handle.read())

with open(requestpath) as handle:
    rankactionsjsonobj = json.loads(handle.read())

get_last_updated(modelLastModified)
get_service_settings()

print(f'User count {len(userpref)}')
print(f'Coffee count {len(actionfeaturesobj)}')

Vérifiez que la valeur rewardWaitTime de la sortie est définie sur 10 minutes et que la valeur modelExportFrequency est définie sur 15 secondes.

-----checking model
<Response [200]>
{'creationTime': '0001-01-01T00:00:00+00:00', 'lastModifiedTime': '0001-01-01T00:00:00+00:00'}
-----model updated: "0001-01-01T00:00:00+00:00"
-----checking service settings
<Response [200]>
{...learning policy...}
<Response [200]>
{'rewardWaitTime': '00:10:00', 'defaultReward': 0.0, 'rewardAggregation': 'earliest', 'explorationPercentage': 0.2, 'modelExportFrequency': '00:00:15', 'logRetentionDays': -1}
User count 4
Coffee count 4

Résolution des problèmes liés au premier appel REST

Cette cellule précédente est la première cellule qui appelle Personalizer. Vérifiez que le code d’état REST dans la sortie est <Response [200]>. Si vous recevez une erreur, telle que 404, mais que vous êtes sûr que la clé de ressource et le nom sont corrects, rechargez le notebook.

Vérifiez qu’il y a bien quatre cafés et quatre utilisateurs. Si vous recevez une erreur, vérifiez que vous avez chargé les trois fichiers JSON.

Configurer le graphique de métriques dans le portail Azure

Plus loin dans ce tutoriel, le processus longue durée des 10 000 requêtes est visible à partir du navigateur avec une zone de texte mise à jour. Il peut être plus simple d’afficher un graphique ou une somme totale pour savoir quand le processus longue durée se termine. Pour voir ces informations, utilisez les métriques fournies avec la ressource. Vous pouvez créer le graphique maintenant que vous avez terminé une requête adressée au service, puis actualiser le graphique régulièrement pendant le processus longue durée.

  1. Dans le portail Azure, sélectionnez votre ressource Personalizer.

  2. Dans la barre de navigation des ressources, sélectionnez Métriques sous Supervision.

  3. Dans le graphique, sélectionnez Ajouter une métrique.

  4. Les espaces de noms de ressources et de métriques sont déjà définis. Il vous suffit de sélectionner la métrique des appels ayant abouti et l’agrégation de somme.

  5. Modifiez le filtre de temps sur les quatre dernières heures.

    Set up metric chart in Azure portal, adding metric for successful calls for the last 4 hours.

    Vous devez voir trois appels ayant abouti dans le graphique.

Générer un ID d’événement unique

Cette fonction génère un ID unique pour chaque appel de classement. L’ID est utilisé pour identifier les informations d’appel de récompense et de classement. Cette valeur peut provenir d’un processus métier, tel qu’un ID de vue web ou un ID de transaction.

La cellule n’a pas de sortie. La fonction génère l’ID unique quand elle est appelée.

def add_event_id(rankjsonobj):
    eventid = uuid.uuid4().hex
    rankjsonobj["eventId"] = eventid
    return eventid

Obtenir un utilisateur, une météo et une heure de la journée aléatoires

Cette fonction sélectionne un utilisateur, une météo et une heure de la journée, puis ajoute ces éléments à l’objet JSON à envoyer à la requête de classement.

La cellule n’a pas de sortie. Quand la fonction est appelée, elle retourne le nom de l’utilisateur aléatoire, la météo aléatoire et l’heure aléatoire de la journée.

La liste de quatre utilisateurs et leurs préférences (seules certaines préférences sont affichées par souci de concision) :

{
  "Alice": {
    "Sunny": {
      "Morning": "Cold brew",
      "Afternoon": "Iced mocha",
      "Evening": "Cold brew"
    }...
  },
  "Bob": {
    "Sunny": {
      "Morning": "Cappucino",
      "Afternoon": "Iced mocha",
      "Evening": "Cold brew"
    }...
  },
  "Cathy": {
    "Sunny": {
      "Morning": "Latte",
      "Afternoon": "Cold brew",
      "Evening": "Cappucino"
    }...
  },
  "Dave": {
    "Sunny": {
      "Morning": "Iced mocha",
      "Afternoon": "Iced mocha",
      "Evening": "Iced mocha"
    }...
  }
}
def add_random_user_and_contextfeatures(namesoption, weatheropt, timeofdayopt, rankjsonobj):
    name = namesoption[random.randint(0,3)]
    weather = weatheropt[random.randint(0,2)]
    timeofday = timeofdayopt[random.randint(0,2)]
    rankjsonobj['contextFeatures'] = [{'timeofday': timeofday, 'weather': weather, 'name': name}]
    return [name, weather, timeofday]

Ajouter toutes les données sur les cafés

Cette fonction ajoute la liste entière de cafés à l’objet JSON à envoyer à la requête de classement.

La cellule n’a pas de sortie. La fonction modifie rankjsonobj quand elle est appelée.

Voici un exemple des caractéristiques d’un café :

{
    "id": "Cappucino",
    "features": [
    {
        "type": "hot",
        "origin": "kenya",
        "organic": "yes",
        "roast": "dark"

    }
}
def add_action_features(rankjsonobj):
    rankjsonobj["actions"] = actionfeaturesobj

Comparer la prédiction avec la préférence connue de l’utilisateur

Cette fonction est appelée après l’appel de l’API de classement, pour chaque itération.

Elle compare la préférence de l’utilisateur en terme de café, en fonction de la météo et de l’heure de la journée, avec la suggestion de Personalizer pour l’utilisateur et pour ces filtres. Si la suggestion correspond, un score de 1 est retourné ; sinon, le score est 0. La cellule n’a pas de sortie. La fonction génère le score quand elle est appelée.

def get_reward_from_simulated_data(name, weather, timeofday, prediction):
    if(userpref[name][weather][timeofday] == str(prediction)):
        return 1
    return 0

Parcourir les appels de classement et de récompense en boucle

La cellule suivante correspond au travail principal du notebook : obtenir un utilisateur aléatoire, obtenir la liste des cafés, envoyer ces deux informations à l’API de classement, comparer la prédiction avec les préférences connues de l’utilisateur, puis renvoyer la récompense au service Personalizer.

La boucle s’exécute num_requests fois. Personalizer a besoin de quelques milliers d’appels de classement et de récompense pour créer un modèle.

Voici un exemple de code JSON envoyé à l’API de classement. La liste des cafés n’est pas complète, par souci de concision. Vous pouvez voir la totalité du JSON pour le café dans coffee.json.

JSON envoyé à l’API de classement :

{
   'contextFeatures':[
      {
         'timeofday':'Evening',
         'weather':'Snowy',
         'name':'Alice'
      }
   ],
   'actions':[
      {
         'id':'Cappucino',
         'features':[
            {
               'type':'hot',
               'origin':'kenya',
               'organic':'yes',
               'roast':'dark'
            }
         ]
      }
        ...rest of coffee list
   ],
   'excludedActions':[

   ],
   'eventId':'b5c4ef3e8c434f358382b04be8963f62',
   'deferActivation':False
}

Réponse JSON de l’API de classement :

{
    'ranking': [
        {'id': 'Latte', 'probability': 0.85 },
        {'id': 'Iced mocha', 'probability': 0.05 },
        {'id': 'Cappucino', 'probability': 0.05 },
        {'id': 'Cold brew', 'probability': 0.05 }
    ],
    'eventId': '5001bcfe3bb542a1a238e6d18d57f2d2',
    'rewardActionId': 'Latte'
}

Pour finir, chaque boucle affiche la sélection aléatoire d’utilisateur, de météo et d’heure de la journée, ainsi que la récompense déterminée. La récompense 1 indique que la ressource Personalizer a sélectionné le bon type de café pour l’utilisateur, la météo et l’heure de la journée donnés.

1 Alice Rainy Morning Latte 1

La fonction utilise :

def iterations(n, modelCheck, jsonFormat):

    i = 1

    # default reward value - assumes failed prediction
    reward = 0

    # Print out dateTime
    currentDateTime()

    # collect results to aggregate in graph
    total = 0
    rewards = []
    count = []

    # default list of user, weather, time of day
    namesopt = ['Alice', 'Bob', 'Cathy', 'Dave']
    weatheropt = ['Sunny', 'Rainy', 'Snowy']
    timeofdayopt = ['Morning', 'Afternoon', 'Evening']


    while(i <= n):

        # create unique id to associate with an event
        eventid = add_event_id(jsonFormat)

        # generate a random sample
        [name, weather, timeofday] = add_random_user_and_contextfeatures(namesopt, weatheropt, timeofdayopt, jsonFormat)

        # add action features to rank
        add_action_features(jsonFormat)

        # show JSON to send to Rank
        print('To: ', jsonFormat)

        # choose an action - get prediction from Personalizer
        response = requests.post(personalization_rank_url, headers = headers, params = None, json = jsonFormat)

        # show Rank prediction
        print ('From: ',response.json())

        # compare personalization service recommendation with the simulated data to generate a reward value
        prediction = json.dumps(response.json()["rewardActionId"]).replace('"','')
        reward = get_reward_from_simulated_data(name, weather, timeofday, prediction)

        # show result for iteration
        print(f'   {i} {currentDateTime()} {name} {weather} {timeofday} {prediction} {reward}')

        # send the reward to the service
        response = requests.post(personalization_reward_url + eventid + "/reward", headers = headers, params= None, json = { "value" : reward })

        # for every N rank requests, compute total correct  total
         total =  total + reward

        # every N iteration, get last updated model date and time
        if(i % modelCheck == 0):

            print("**** 10% of loop found")

            get_last_updated(modelLastModified)

        # aggregate so chart is easier to read
        if(i % 10 == 0):
            rewards.append( total)
            count.append(i)
             total = 0

        i = i + 1

    # Print out dateTime
    currentDateTime()

    return [count, rewards]

Exécuter pour 10 000 itérations

Exécutez la boucle Personalizer pour 10 000 itérations. Il s’agit d’un événement de longue durée. Ne fermez pas le navigateur qui exécute le notebook. Actualisez régulièrement le graphique des métriques dans le portail Azure pour voir le nombre total d’appels au service. Quand vous avez environ 20 000 appels, un appel de classement et de récompense pour chaque itération de la boucle, les itérations sont terminées.

# max iterations
num_requests = 200

# check last mod date N% of time - currently 10%
lastModCheck = int(num_requests * .10)

jsonTemplate = rankactionsjsonobj

# main iterations
[count, rewards] = iterations(num_requests, lastModCheck, jsonTemplate)

Afficher un graphique des résultats pour voir l’amélioration

Créez un graphique à partir de count et rewards.

def createChart(x, y):
    plt.plot(x, y)
    plt.xlabel("Batch of rank events")
    plt.ylabel("Correct recommendations per batch")
    plt.show()

Exécuter le graphique pour 10 000 requêtes de classement

Exécutez la fonction createChart.

createChart(count,rewards)

Lecture du graphique

Ce graphique montre la réussite du modèle pour la stratégie d’apprentissage par défaut actuelle.

This chart shows the success of the current learning policy for the duration of the test.

La cible idéale est qu’à la fin du test, la boucle obtienne un taux de réussite moyen proche de 100 % moins l’exploration. La valeur d’exploration par défaut est 20 %.

100-20=80

Cette valeur d’exploration figure dans le portail Azure pour la ressource Personalizer, dans la page Configuration.

Pour trouver une meilleure stratégie d’apprentissage, basée sur vos données envoyées à l’API de classement, exécutez une évaluation hors connexion dans le portail pour votre boucle Personalizer.

Exécuter une évaluation hors connexion

  1. Dans le portail Azure, ouvrez la page Évaluations de la ressource Personalizer.

  2. Sélectionnez Créer une évaluation.

  3. Entrez les données requises (nom de l’évaluation et plage de dates pour l’évaluation de la boucle). La plage de dates doit inclure uniquement les jours sur lesquels vous vous concentrez pour votre évaluation. In the Azure portal, open the Personalizer resource's Evaluations page. Select Create Evaluation. Enter the evaluation name and date range.

    L’objectif de cette évaluation hors connexion est de déterminer s’il existe une meilleure stratégie d’apprentissage pour les caractéristiques et actions utilisées dans cette boucle. Pour trouver cette meilleure stratégie d’apprentissage, n’oubliez pas d’activer l’option Découverte d’optimisation.

  4. Sélectionnez OK pour commencer l’évaluation.

  5. Cette page Évaluations montre la nouvelle évaluation et son état actuel. En fonction de la quantité de données dont vous disposez, cette évaluation peut prendre un certain temps. Vous pouvez revenir à cette page après quelques minutes pour voir les résultats.

  6. Une fois l’évaluation terminée, sélectionnez-la, puis sélectionnez Comparaison des différentes stratégies d’apprentissage. Cela montre les stratégies d’apprentissage disponibles et comment elles se comporteraient avec les données.

  7. Sélectionnez la stratégie d’apprentissage qui figure en haut du tableau, puis sélectionnez Appliquer. La meilleure stratégie d’apprentissage est alors appliquée à votre modèle, et un nouvel entraînement est effectué.

Régler la fréquence de mise à jour du modèle sur cinq minutes

  1. Dans le portail Azure, toujours sur la ressource Personalizer, sélectionnez la page Configuration.
  2. Réglez la Fréquence de mise à jour du modèle et la Durée d’attente de la récompense sur cinq minutes et sélectionnez Enregistrer.

Apprenez-en davantage sur la durée d’attente de la récompense et sur la fréquence de mise à jour du modèle.

#Verify new learning policy and times
get_service_settings()

Vérifiez que les valeurs rewardWaitTime et modelExportFrequency de la sortie sont toutes deux définies sur cinq minutes.

-----checking model
<Response [200]>
{'creationTime': '0001-01-01T00:00:00+00:00', 'lastModifiedTime': '0001-01-01T00:00:00+00:00'}
-----model updated: "0001-01-01T00:00:00+00:00"
-----checking service settings
<Response [200]>
{...learning policy...}
<Response [200]>
{'rewardWaitTime': '00:05:00', 'defaultReward': 0.0, 'rewardAggregation': 'earliest', 'explorationPercentage': 0.2, 'modelExportFrequency': '00:05:00', 'logRetentionDays': -1}
User count 4
Coffee count 4

Valider la nouvelle stratégie d’apprentissage

Revenez au fichier Azure Notebooks et continuez en exécutant la même boucle, mais seulement pendant 2000 itérations. Actualisez régulièrement le graphique des métriques dans le portail Azure pour voir le nombre total d’appels au service. Quand vous avez environ 4 000 appels, un appel de classement et de récompense pour chaque itération de la boucle, les itérations sont terminées.

# max iterations
num_requests = 2000

# check last mod date N% of time - currently 10%
lastModCheck2 = int(num_requests * .10)

jsonTemplate2 = rankactionsjsonobj

# main iterations
[count2, rewards2] = iterations(num_requests, lastModCheck2, jsonTemplate)

Exécuter le graphique pour 2 000 requêtes de classement

Exécutez la fonction createChart.

createChart(count2,rewards2)

Examiner le deuxième graphique

Le deuxième graphique doit montrer une augmentation visible de la correspondance entre les prédictions de classement et les préférences de l’utilisateur.

The second chart should show a visible increase in Rank predictions aligning with user preferences.

Nettoyer les ressources

Si vous n’envisagez pas de continuer la série de tutoriels, nettoyez les ressources suivantes :

  • Supprimez votre projet Azure Notebook.
  • Supprimez votre ressource Personalizer.

Étapes suivantes

Le notebook Jupyter et les fichiers de données utilisés dans cet exemple sont disponibles dans le dépôt GitHub pour Personalizer.