Delen via


Zelfstudie: Personalizer gebruiken in Azure Notebook

Belangrijk

Vanaf 20 september 2023 kunt u geen nieuwe Personalizer-resources maken. De Personalizer-service wordt op 1 oktober 2026 buiten gebruik gesteld.

In deze zelfstudie wordt een Personalizer-lus in een Azure Notebook uitgevoerd, waarbij de end-to-end levenscyclus van een Personalizer-lus wordt gedemonstreerd.

De lus geeft aan welk type koffie een klant moet bestellen. De gebruikers en hun voorkeuren worden opgeslagen in een gebruikersgegevensset. Informatie over de koffie wordt opgeslagen in een koffie-gegevensset.

Gebruikers en koffie

Het notebook, dat gebruikersinteractie met een website simuleert, selecteert een willekeurige gebruiker, tijdstip van de dag en type weer door middel van de gegevensset. Een samenvatting van de gebruikersgegevens is:

Klant - contextfuncties Tijdstippen van de dag Typen weer
Alice
Bob
Cathy
Dave
Ochtend
Middag
Avond
Zonnig
Regenachtig
Sneeuw

Om Personalizer te helpen met leren, heeft het systeem ook informatie over de koffieselectie voor elke persoon.

Koffie - actiekenmerken Typen temperatuur Plaatsen van oorsprong Typen branding Biologisch
Cappuccino Heet Kenia Donker Biologisch
Cold brew Koud Brazilië Licht Biologisch
Iced mocha Koud Ethiopië Licht Niet biologisch
Latte Heet Brazilië Donker Niet biologisch

Het doel van de Personalizer-lus is om zo vaak mogelijk de beste overeenkomst te vinden tussen de gebruikers en de koffie.

De code voor deze zelfstudie is beschikbaar in de GitHub-opslagplaats met Personalizer-voorbeelden.

Zo werkt de simulatie

Aan het begin van het actieve systeem zijn de suggesties van Personalizer slechts 20% tot 30% van de tijd juist. Dit succes wordt aangegeven door de beloning die wordt teruggestuurd naar de Personalizer Reward-API, met een score van 1. Na een aantal Rank- en Reward-aanroepen wordt het systeem verbeterd.

Voer na de eerste aanvragen een offline evaluatie uit. Hierdoor kan Personalizer de gegevens beoordelen en een beter leerbeleid voorstellen. Pas het nieuwe leerbeleid toe en voer het notebook opnieuw uit met 20% van de eerdere aanvragen. De lus presteert beter met het nieuwe leerbeleid.

Rank- en Reward-aanroepen

Voor elk van de duizenden aanroepen naar de Personalizer-service stuurt Azure Notebook de Rank-aanvraag naar de REST API:

  • Een unieke id voor de Rank/Aanvraag-gebeurtenis
  • Contextfuncties: een willekeurige keuze van de gebruiker, het weer en het tijdstip van de dag. Hierop wordt een gebruiker op een website of mobiel apparaat gesimuleerd
  • Acties met functies - Alle koffiegegevens - waaruit Personalizer een suggestie doet

Het systeem ontvangt de aanvraag en vergelijkt deze voorspelling met de bekende keuze van de gebruiker voor hetzelfde tijdstip van de dag en het weer. Als de bekende keuze hetzelfde is als de voorspelde keuze, wordt de Reward van 1 teruggestuurd naar Personalizer. Anders is de terug te sturen reward 0.

Notitie

Dit is een simulatie, daardoor is het algoritme voor de reward eenvoudig. In een praktijkscenario moet het algoritme bedrijfslogica gebruiken, mogelijk met gewichten voor verschillende aspecten van de klantervaring, om de reward-score te bepalen.

Vereisten

Bestandsbeschrijvingen:

Azure Personalizer-resource configureren

Configureer in Azure Portal uw Personalizer-resource met de frequentie van het updatemodel ingesteld op 15 seconden en een beloningswachttijd van 10 minuten. Deze waarden zijn te vinden op de pagina configuratie.

Instelling Waarde
modelfrequentie bijwerken 15 seconden
wachttijd voor reward 10 minuten

Deze waarden hebben een zeer korte duur, zodat wijzigingen in deze zelfstudie kunnen worden weergegeven. Deze waarden mogen niet worden gebruikt in een productiescenario zonder te valideren dat ze uw doel bereiken met uw Personalizer-lus.

Azure Notebook instellen

  1. Wijzig de kernel in Python 3.6.
  2. Open het bestand Personalizer.ipynb.

Notebook-cellen uitvoeren

Voer elke uitvoerbare cel uit en wacht totdat deze is geretourneerd. U weet dat de bewerking is voltooid wanneer binnen de brackets naast de cel een getal wordt weergegeven in plaats van een *. In de volgende secties wordt uitgelegd wat elke cel programmatisch doet en wat u kunt verwachten voor de uitvoer.

De Python-modules opnemen

Neem de vereiste Python-modules op. De cel heeft geen uitvoer.

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

Resourcesleutel en naam van de Personalizer instellen

Zoek in Azure Portal uw sleutel en eindpunt op via de pagina Snelstart van uw Personalizer-resource. Wijzig de waarde van <your-resource-name> in de naam van uw Personalizer-resource. Wijzig de waarde van <your-resource-key> naar uw Personalizer-sleutel.

# 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>"

Gebruik deze functie om de begin- en eindtijd van de iteratieve functie en iteraties te noteren.

Deze cellen hebben geen uitvoer. De functie voert de huidige datum en tijd uit als deze wordt aangeroepen.

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

De laatste updatetijd van het model ophalen

Wanneer de functie, get_last_updated, wordt aangeroepen, wordt in de functie de datum en tijd afgedrukt waarop het model voor het laatst is bijgewerkt.

Deze cellen hebben geen uitvoer. De functie voert de laatste modeltrainingsdatum uit als deze wordt aangeroepen.

De functie maakt gebruik van een GET REST API om modeleigenschappen op te halen.

# 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}')

Beleids- en serviceconfiguratie ophalen

Valideer de status van de service met deze twee REST-aanroepen.

Deze cellen hebben geen uitvoer. De functie voert de servicewaarden uit als hij wordt aangeroepen.

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())

URL's maken en JSON-gegevensbestanden lezen

Met deze cel

  • bouwt u de URL's die worden gebruikt in REST-aanroepen
  • stelt u de beveiligingskoptekst in met uw Personalizer-resourcesleutel
  • stelt u de willekeurige seed in voor de gebeurtenis-id van de Rank
  • leest u bewerkingen in de JSON-gegevensbestanden
  • roept u de get_last_updated methode aan - leerbeleid is verwijderd in voorbeelduitvoer
  • roept u get_service_settings methode aan

De cel heeft uitvoer van de aanroep van get_last_updated- en get_service_settings-functies.

# 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)}')

Controleer of de uitvoer rewardWaitTime is ingesteld op 10 minuten en modelExportFrequency is ingesteld op 15 seconden.

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

Problemen met de eerste REST-aanroep oplossen

Deze vorige cel is de eerste cel die Personalizer aanroept. Zorg ervoor dat de REST-statuscode in de uitvoer <Response [200]> is. Als er een fout optreedt, zoals 404, maar u zeker weet dat de resourcesleutel en de naam juist zijn, laadt u het notebook opnieuw.

Zorg ervoor dat het aantal koffiebereiding en het aantal gebruikers beide 4 is. Als er een fout optreedt, controleert u of u alle 3 de JSON-bestanden hebt geüpload.

Een metrische grafiek instellen in Azure Portal

Verderop in deze zelfstudie wordt het langdurige proces van 10.000 aanvragen weergegeven in de browser met een tekstvak dat steeds wordt bijgewerkt. Het kan eenvoudiger zijn om het in een grafiek of als een totaalsom te bekijken wanneer het langdurige proces wordt beëindigd. Als u deze informatie wilt weergeven, gebruikt u de metrische gegevens die bij de resource zijn meegeleverd. Nu u een aanvraag voor de service hebt voltooid kunt u de grafiek maken. Vervolgens moet u de grafiek periodiek vernieuwen terwijl het langdurige proces wordt uitgevoerd.

  1. Selecteer uw Personalizer-resource in Azure Portal.

  2. Selecteer in de resourcenavigatie Metrische gegevens onder Monitoring.

  3. Selecteer in de grafiek Metrische gegevens toevoegen.

  4. De naamruimte voor de resource en metrische gegevens is al ingesteld. U hoeft alleen de metrische gegevens van geslaagde aanroepen en de aggregatie van sum te selecteren.

  5. Wijzig het tijdfilter in de afgelopen 4 uur.

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

    U ziet nu drie geslaagde aanroepen in de grafiek.

Een unieke gebeurtenis-id genereren

Met deze functie genereert u een unieke id voor elke Rank-aanroep. De id wordt gebruikt om de Rank- en Reward-informatie te identificeren. Deze waarde kan afkomstig zijn van een bedrijfsproces, zoals een webweergave-id of een transactie-id.

De cel heeft geen uitvoer. Met de functie wordt de unieke id uitgevoerd als deze wordt aangeroepen.

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

Willekeurige gebruiker, weergave en tijdstip van de dag ophalen

Met deze functie worden een unieke gebruiker, het weer en het tijdstip van de dag geselecteerd. Vervolgens worden deze items aan het JSON-object toegevoegd om te verzenden naar de Rank-aanvraag.

De cel heeft geen uitvoer. Wanneer de functie wordt aangeroepen, wordt de naam van de willekeurige gebruiker, het willekeurige weer en de willekeurige tijd van de dag geretourneerd.

De lijst met 4 gebruikers en hun voorkeuren: slechts een paar voorkeuren worden weergegeven voor de overzichtelijkheid:

{
  "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]

Alle koffiegegevens toevoegen

Met deze functie voegt u de volledige lijst met koffie toe aan het JSON-object dat naar de Rank-aanvraag wordt verzonden.

De cel heeft geen uitvoer. Met de functie wijzigt u de rankjsonobj als deze wordt aangeroepen.

Het voorbeeld van de functies van één koffie is:

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

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

Voorspellingen vergelijken met bekende gebruikersvoorkeuren

Deze functie wordt aangeroepen nadat de Rank-API is aangeroepen, voor elke iteratie.

Met deze functie vergelijkt u de voorkeuren van de gebruiker voor koffie, op basis van weer en tijdstip, met de suggestie van de Personalizer voor de gebruiker voor deze filters. Als het voorstel overeenkomt, wordt er een score van 1 geretourneerd, anders is de score 0. De cel heeft geen uitvoer. Met de functie voert u de score uit als deze wordt aangeroepen.

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

Doorloop aanroepen naar Rank en Reward

De volgende cel is het belangrijkste werk van het Notebook, het ophalen van een willekeurige gebruiker, de lijst met koffie en het verzenden van beide naar de Rank-API. Vergelijking van de voorspelling met de bekende gebruikersvoorkeuren en deze vervolgens terugsturen naar de Personalizer-service.

De lus wordt num_requests keer uitgevoerd. Voor Personalizer zijn een paar duizend aanroepen naar Rank en Reward vereist om een model te kunnen creëren.

Hier volgt een voorbeeld van de JSON die wordt verzonden naar de Rank-API. De lijst met koffie is niet volledig met het oog op beknoptheid. U kunt de volledige JSON voor koffie bekijken in coffee.json.

JSON verzonden naar de Rank-API:

{
   '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
}

JSON-antwoord van de Rank-API:

{
    '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'
}

Ten slotte toont elke lus de willekeurige selectie van de gebruiker, het weer, het tijdstip en de vastgestelde reward. De reward 1 geeft aan dat de Personalizer-resource de juiste koffiesoort heeft geselecteerd voor de geselecteerde gebruiker, het weer en het tijdstip van de dag.

1 Alice Rainy Morning Latte 1

De functie gebruikt:

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]

10.000 iteraties uitvoeren

Voer voor de Personalizer-lus 10.000 iteraties uit. Dit is een langdurige gebeurtenis. Sluit de browser waarin het Notebook wordt uitgevoerd niet. Vernieuw de grafiek met metrische gegevens in Azure Portal regelmatig om het totale aantal aanroepen naar de service te bekijken. Wanneer u circa 20.000 aanroepen hebt, wordt er voor elke iteratie van de lus een Rank- en Reward-aanroep gedaan.

# 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)

Bekijk de resultaten in een grafiek om de verbeteringen te zien

Een grafiek maken op basis van de count en rewards.

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

Grafiek uitvoeren voor 10.000 Rank-aanvragen

Voer de functie createChart uit.

createChart(count,rewards)

De grafiek lezen

Dit diagram toont het succes van het model voor het huidige standaard leerbeleid.

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

Het ideale doel dat aan het einde van de test wordt bereikt, is een gemiddeld succespercentage dat dicht bij 100 procent ligt, met aftrek van de exploratie. De standaard waarde voor exploratie is 20%.

100-20=80

Deze waarde voor exploratie vindt u in Azure Portal voor de Personalizer-resource op de Configuratiepagina.

Als u een beter leerbeleid wilt vinden op basis van uw gegevens naar de Rank-API, voert u een offline evaluatie uit in de portal voor uw Personalizer-lus.

Een offline-evaluatie uitvoeren

  1. Open in Azure Portal de pagina Evaluaties van de Personalizer-resource.

  2. Selecteer Evaluatie maken.

  3. Voer de vereiste gegevens in, namelijk de naam van de evaluatie en het datumbereik voor de lus-evaluatie. Het datumbereik moet alleen de dagen bevatten die u wilt opnemen in de evaluatie. In the Azure portal, open the Personalizer resource's Evaluations page. Select Create Evaluation. Enter the evaluation name and date range.

    Het doel van deze offline-evaluatie is om te bepalen of er een beter leerbeleid is voor de functies en acties die in deze lus worden gebruikt. Zorg ervoor dat Optimalisatie detecteren is ingeschakeld om het betere leerbeleid te vinden.

  4. Selecteer OK om de evaluatie te starten.

  5. Deze Evaluatiepagina bevat de nieuwe evaluatie en de huidige status. Afhankelijk van hoeveel gegevens u hebt, kan deze evaluatie enige tijd in beslag nemen. U kunt na een paar minuten terugkeren naar deze pagina om de resultaten te bekijken.

  6. Wanneer de evaluatie is voltooid, selecteert u de evaluatie en selecteert u Vergelijken met ander leerbeleid. Hier ziet u de beschikbare leerbeleidsregels en hoe die zich zouden gedragen met de gegevens.

  7. Selecteer het bovenste trainingsbeleid in de tabel en selecteer Toepassen. Hiermee wordt het beste leerbeleid toegepast op uw model en wordt het opnieuw getraind.

De frequentie van het updatemodel wijzigen in 5 minuten

  1. Selecteer de pagina Configuratie in Azure Portal, terwijl u nog steeds op de Personalizer-resource bent.
  2. Wijzig de updatefrequentie van het model en de reward-wachttijd in 5 minuten en selecteer Opslaan.

Meer informatie over de reward-wachttijd en updatefrequentie van het model.

#Verify new learning policy and times
get_service_settings()

Controleer of de rewardWaitTime en modelExportFrequency van de uitvoer beide zijn ingesteld op 5 minuten.

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

Nieuw leerbeleid valideren

Ga terug naar het Azure Notebooks-bestand en ga door met het uitvoeren van dezelfde lus, maar slechts voor 2.000 iteraties. Vernieuw de grafiek met metrische gegevens in Azure Portal regelmatig om het totale aantal aanroepen naar de service te bekijken. Wanneer u circa 4.000 aanroepen hebt, wordt er voor elke iteratie van de lus een Rank- en Reward-aanroep gedaan.

# 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)

Grafiek uitvoeren voor 2.000 Rank-aanvragen

Voer de functie createChart uit.

createChart(count2,rewards2)

De tweede grafiek bekijken

In de tweede grafiek ziet u een zichtbare toename van de Rank-voorspellingen die met gebruikersvoorkeuren worden uitgelijnd.

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

Resources opschonen

Als u niet van plan bent om door te gaan met de reeks zelfstudies, moet u de volgende resources opschonen:

  • Verwijder uw Azure Notebook-project.
  • Verwijder uw Personalizer-resource.

Volgende stappen

De Jupyter notebook- en gegevensbestanden die in dit voorbeeld worden gebruikt, zijn beschikbaar in de GitHub-opslagplaats voor Personalizer.