Självstudie: Använda Personanpassning i Azure Notebook

Viktigt!

Från och med den 20 september 2023 kommer du inte att kunna skapa nya personaliseringsresurser. Personanpassningstjänsten dras tillbaka den 1 oktober 2026. Vi rekommenderar att du migrerar till microsoft/learning-loop med öppen källkod.

I den här självstudien körs en Personalizer-loop i en Azure Notebook, som demonstrerar hela livscykeln för en Personalizer-loop.

Loopen föreslår vilken typ av kaffe en kund ska beställa. Användarna och deras inställningar lagras i en användardatauppsättning. Information om kaffet lagras i en kaffedatauppsättning.

Användare och kaffe

Anteckningsboken, som simulerar användarinteraktion med en webbplats, väljer en slumpmässig användare, en tidpunkt på dagen och en vädertyp från datauppsättningen. En sammanfattning av användarinformationen är:

Kunder – kontextfunktioner Tider på dagen Vädertyper
Alice
Bob
Cathy
Dave
Morgon
Eftermiddag
Kväll
Solig
Regnig
Snöig

För att hjälpa Personalizer att lära sig över tid, känner systemet också till detaljer om kaffevalet för varje person.

Kaffe – aktiva funktioner Temperaturtyper Ursprungsplatser Typer av stek Organisk
Cappacino Populär Kenya Mörk Organisk
Kallbryggning Kall Brasilien Ljus Organisk
Iskall mocha-kaffe Kall Etiopien Ljus Inte organiskt
Latte Populär Brasilien Mörk Inte organiskt

Syftet med personanpassningsloopen är att hitta den bästa matchningen mellan användarna och kaffet så mycket som möjligt.

Koden för den här handledningen finns i Personalizer Samples GitHub-repo.

Så här fungerar simuleringen

I början av det körande systemet är Personlizers förslag endast framgångsrika mellan 20 % och 30 %. Denna framgång indikeras av belöningen som skickas tillbaka till Personanpassningsbelönings-API:et med poängen 1. Efter några Rank- och Reward-samtal förbättras systemet.

Efter de första begärandena kör du en offlineutvärdering. Detta gör att Personalizer kan granska data och föreslå en bättre inlärningsprincip. Tillämpa den nya inlärningsprincipen och kör notebook-filen igen med 20 % av det tidigare antalet förfrågningar. Loopen presterar bättre med den nya inlärningsprincipen.

Ranknings- och belöningssamtal

För vart och ett av de få tusen anropen till personanpassningstjänsten skickar Azure Notebook Rank begäran till REST-API:et:

  • Ett unikt ID för händelsen Rank/Request
  • Kontextfunktioner – Ett slumpmässigt val av användare, väder och tid på dagen – simulera en användare på en webbplats eller mobil enhet
  • Åtgärder med funktioner – Alla kaffedata – från vilka Personanpassning ger ett förslag

Systemet tar emot begäran och jämför sedan förutsägelsen med användarens kända val för samma tid på dagen och vädret. Om det kända valet är samma som det förutsagda valet skickas belöningen på 1 tillbaka till Personalizer. Annars är belöningen som skickas tillbaka 0.

Kommentar

Det här är en simulering så att algoritmen för belöningen är enkel. I ett verkligt scenario bör algoritmen använda affärslogik, eventuellt med vikter för olika aspekter av kundens upplevelse, för att fastställa belöningspoängen.

Förutsättningar

Filbeskrivningar:

Konfigurera Personalizer-resurs

I Azure-portalen konfigurerar du din Personalizer-resurs med modellfrekvensen uppdate inställd på 15 sekunder och en reward-väntetid på 10 minuter. Dessa värden finns på sidan Konfiguration .

Inställning Värde
uppdatera modellfrekvens 15 sekunder
belöningsväntetid 10 minuter

Dessa värden har en mycket kort varaktighet för att visa ändringar i den här handledningen. Dessa värden bör inte användas i ett produktionsscenario utan att verifiera att de uppnår ditt mål med din Personalizer-loop.

Konfigurera Azure Notebook

  1. Ändra kerneln till Python 3.6.
  2. Öppna Personalizer.ipynb-filen.

Kör notebook-celler

Kör varje körbar cell och vänta på att den ska returnera. Du vet att det görs när hakparenteserna bredvid cellen visar ett tal i stället för en *. I följande avsnitt förklaras vad varje cell gör programmatiskt och vad du kan förvänta dig för utdata.

Ta med Python modulerna

Ta med de nödvändiga Python modulerna. Cellen har inga utdata.

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

Ange personaliserarens resursnyckel och namn

Från Azure-portalen hittar du din nyckel och slutpunkt på sidan Quickstart i personaliserarresursen. Ändra värdet <your-resource-name> till namnet på din Personalizer-resurs. Ändra <your-resource-key> till din personanpassningsnyckel.

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

Använd den här funktionen för att notera start- och sluttiderna för den iterativa funktionen iterationer.

Dessa celler har inga utdata. Funktionen matar ut aktuellt datum och tid när den anropas.

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

Hämta den senaste uppdateringstiden för modellen

När funktionen, get_last_updated, anropas, skriver funktionen ut det senast ändrade datum och den tid då modellen uppdaterades.

Dessa celler har inga utdata. Funktionen visar det senaste modellträningsdatumet när den anropas.

Funktionen använder ett GET REST API för att hämta modellegenskaper.

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

Hämta princip- och tjänstkonfiguration

Verifiera tjänstens tillstånd med dessa två REST-anrop.

Dessa celler har inga utdata. Funktionen matar ut tjänstvärdena när den anropas.

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

Skapa URL:er och läs JSON-datafiler

Den här cellen

  • skapar url:er som används i REST-anrop
  • ställer in säkerhetsrubriken med hjälp av din Personalizer-resursnyckel
  • anger det slumpmässiga fröet för händelse-ID för rankning
  • läser i JSON-datafilerna
  • anropar get_last_updated-metod - inlärningsstrategin har tagits bort i exempel på utdata
  • anropsmetod get_service_settings

Cellen har utdata från anropet till get_last_updated och get_service_settings funktionerna.

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

Kontrollera att utdata rewardWaitTime är inställt på 10 minuter och modelExportFrequency är inställt på 15 sekunder.

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

Felsöka det första REST-anropet

Föregående cell är den första cellen som anropar Personalizer. Kontrollera att REST-statuskoden i utdata är <Response [200]>. Om du får ett fel, till exempel 404, men du är säker på att resursnyckeln och namnet är korrekta läser du in anteckningsboken igen.

Kontrollera att antalet kaffe och användare är båda 4. Om du får ett fel kontrollerar du att du har laddat upp alla 3 JSON-filer.

Konfigurera måttdiagram i Azure portalen

Senare i den här självstudien visas den tidskrävande processen med 10 000 begäranden från webbläsaren med ett textfält som uppdateras. Det kan vara lättare att se i ett diagram eller som en total summa när den tidskrävande processen slutar. Om du vill visa den här informationen använder du måtten som tillhandahålls med resursen. Du kan skapa diagrammet nu när du har slutfört en begäran till tjänsten och sedan uppdatera diagrammet regelbundet medan den tidskrävande processen pågår.

  1. I Azure-portalen väljer du din Personalizer-resurs.

  2. I resursnavigering väljer du Mått under Övervakning.

  3. I diagrammet väljer du Lägg till mått.

  4. Resurs- och måttnamnområdet har redan angetts. Du behöver bara välja måttet lyckade anrop och summeringen summa.

  5. Ändra tidsfiltret till de senaste 4 timmarna.

    Ange metrikdiagram i Azure-portalen, lägg till metrik för lyckade anrop de senaste 4 timmarna.

    Du bör se tre lyckade anrop i diagrammet.

Generera ett unikt händelse-ID

Den här funktionen genererar ett unikt ID för varje rankningsanrop. ID:t används för att identifiera ranknings- och belöningssamtalsinformationen. Det här värdet kan komma från en affärsprocess, till exempel ett webbvy-ID eller transaktions-ID.

Cellen har inga utdata. Funktionen matar ut det unika ID:t när det anropas.

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

Hämta slumpmässig användare, väder och tid på dagen

Den här funktionen väljer en unik användare, väder och tid på dagen och lägger sedan till objekten i JSON-objektet som ska skickas till Rank-begäran.

Cellen har inga utdata. När funktionen anropas returneras den slumpmässiga användarens namn, slumpmässiga väder och slumpmässig tid på dagen.

Listan med 4 användare och deras inställningar – endast vissa inställningar visas för korthet:

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

Lägga till alla kaffedata

Den här funktionen lägger till hela listan med kaffe till JSON-objektet som ska skickas till Rank-begäran.

Cellen har inga utdata. Funktionen ändrar rankjsonobj när den anropas.

Exemplet på en enda kaffefunktion är:

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

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

Jämför förutsägelse med kända användarinställningar

Den här funktionen anropas efter att ranknings-API:et anropats för varje iteration.

Den här funktionen jämför användarens inställningar för kaffe, baserat på väder och tid på dagen, med personanpassningens förslag för användaren för dessa filter. Om förslaget matchar returneras poängen 1, annars är poängen 0. Cellen har inga utdata. Funktionen matar ut poängen när den anropas.

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

Loopa igenom anrop till Rank and Reward

Nästa cell är det huvudsakliga arbetet i notebook-filen: att hämta en slumpmässig användare, hämta kaffelistan och skicka båda till Rank API. Jämföra förutsägelsen med användarens kända inställningar och skicka sedan belöningen tillbaka till personanpassningstjänsten.

Loopen körs num_requests gånger. Personanpassning behöver några tusen anrop till Rank and Reward för att skapa en modell.

Ett exempel på JSON som skickas till ranknings-API:et följer. Listan över kaffe är inte fullständig, för korthet. Du kan se hela JSON för kaffe i coffee.json.

JSON skickas till ranknings-API:et:

{
   '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-svar från ranknings-API:et:

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

Slutligen visar varje loop det slumpmässiga valet av användare, väder, tid på dagen och fast belöning. Belöningen på 1 anger att personaliserarresursen har valt rätt kaffetyp för den angivna användaren, vädret och tiden på dagen.

1 Alice Rainy Morning Latte 1

Funktionen använder:

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]

Kör 10 000 iterationer

Kör personanpassningsloopen för 10 000 iterationer. Det här är en tidskrävande händelse. Stäng inte webbläsaren som kör notebook. Uppdatera måttdiagrammet i Azure portalen regelbundet för att se totalt antal anrop till tjänsten. När du har cirka 20 000 anrop, och ett ranknings- och belöningsanrop sker för varje iteration i loopen, är iterationerna klara.

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

Diagramresultat för att se förbättringar

Skapa ett diagram från count och rewards.

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

Kör diagram för 10 000 rankningsbegäranden

createChart Kör funktionen.

createChart(count,rewards)

Läsa diagrammet

Det här diagrammet visar modellens framgång för den aktuella standardinlärningsprincipen.

Det här diagrammet visar den aktuella inlärningsprincipens framgång under hela testet.

Det idealiska målet att loopen i slutet av testet i genomsnitt är en lyckad frekvens som är nära 100 procent minus utforskningen. Standardvärdet för utforskning är 20 %.

100-20=80

Det här utforskningsvärdet finns i Azure-portalen för personanpassningsresursen på sidan Configuration.

För att hitta en bättre inlärningsstrategi, baserat på dina data till Rank API, kör du en offline utvärdering i portalen för din Personaliseringsloop.

Kör en offlineutvärdering

  1. Öppna Personalizer-resursens Evaluations sida i Azure-portalen.

  2. Välj Skapa utvärdering.

  3. Ange nödvändiga data för utvärderingsnamnet och datumintervallet för looputvärderingen. Datumintervallet bör bara innehålla de dagar som du fokuserar på för utvärderingen. I Azure-portalen öppnar du sidan Utvärderingar för personanpassningsresursen. Välj Skapa utvärdering. Ange utvärderingsnamnet och datumintervallet.

    Syftet med att köra den här offlineutvärderingen är att avgöra om det finns en bättre inlärningsprincip för de funktioner och åtgärder som används i den här loopen. Kontrollera att Optimeringsupptäckning är aktiverat för att hitta den bättre inlärningsprincipen.

  4. Välj OK för att påbörja utvärderingen.

  5. sidan Utvärderingar visas den nya utvärderingen och dess aktuella status. Beroende på hur mycket data du har kan den här utvärderingen ta lite tid. Du kan komma tillbaka till den här sidan efter några minuter för att se resultatet.

  6. När utvärderingen är klar väljer du utvärderingen och sedan Jämförelse av olika inlärningsprinciper. Detta visar tillgängliga inlärningsprinciper och hur de skulle bete sig med data.

  7. Välj den mest populära utbildningsprincipen i tabellen och välj Använd. Detta tillämpar den bästa inlärningsprincipen på din modell och omtränar den.

Ändra uppdateringsmodellfrekvens till 5 minuter

  1. Fortfarande på Personalizer-resursen i Azure-portalen, välj sidan Configuration.
  2. Ändra modellens uppdateringsfrekvens och belöningsväntetid till 5 minuter och välj Spara.

Läs mer om väntetiden för belöningen och uppdateringsfrekvensen för modellen.

#Verify new learning policy and times
get_service_settings()

Kontrollera att utdataelementen rewardWaitTime och modelExportFrequency båda är inställda på 5 minuter.

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

Validera ny inlärningsprincip

Gå tillbaka till filen Azure Notebooks och fortsätt genom att köra samma loop, men endast för 2 000 iterationer. Uppdatera måttdiagrammet i Azure portalen regelbundet för att se totalt antal anrop till tjänsten. När du har cirka 4 000 samtal och ett ranknings- och belöningsanrop för varje iteration av loopen, är iterationerna slutförda.

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

Körningsdiagram för 2 000 rankningsbegäranden

createChart Kör funktionen.

createChart(count2,rewards2)

Granska det andra diagrammet

Det andra diagrammet bör visa en synlig ökning av rankningsförutsägelser som överensstämmer med användarinställningar.

Det andra diagrammet bör visa en synlig ökning av rankningsförutsägelser som överensstämmer med användarinställningar.

Rensa resurser

Om du inte tänker fortsätta självstudieserien rensar du upp följande resurser:

  • Ta bort ditt Azure Notebook-projekt.
  • Ta bort Personalizer-resursen.

Nästa steg

Den Jupyter notebook och datafiler som används i det här exemplet finns tillgängliga i GitHub-repot för Personalizer.