Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Importante
A partir del 20 de septiembre de 2023, no podrá crear nuevos recursos de Personalizer. El servicio Personalizer se va a retirar el 1 de octubre de 2026. Se recomienda migrar al proyecto de código abierto microsoft/learning-loop.
En este tutorial se ejecuta un bucle personalizer en un cuaderno de Azure, que muestra el ciclo de vida completo de un bucle Personalizer.
El bucle sugiere qué tipo de café debe solicitar un cliente. Los usuarios y sus preferencias están almacenados en un conjunto de datos de usuario. La información sobre el café está almacenada en un conjunto de datos de café.
Usuarios y café
El cuaderno, que simula una interacción del usuario con un sitio web, selecciona una combinación aleatoria de usuario, hora del día e información meteorológica del conjunto de datos. A continuación se muestra un resumen de la información de usuario:
| Clientes: características de contexto | Horas del día | Tipos de condición meteorológica |
|---|---|---|
| Alice Bob Cathy Dave |
Mañana Tarde Noche |
Soleado Lluvioso Nevado |
Para ayudar a que Personalizer aprenda, con el tiempo, el sistema también conoce detalles sobre la selección de café de cada persona.
| Café: características de acción | Tipos de temperatura | Lugares de origen | Tipos de tueste | Orgánico |
|---|---|---|---|---|
| Capuchino | Acceso frecuente | Kenia | Oscuro | Orgánico |
| Infusión fría | Frío | Brasil | Ligero | Orgánico |
| Moca con hielo | Frío | Etiopía | Ligero | No orgánico |
| Latte | Acceso frecuente | Brasil | Oscuro | No orgánico |
La finalidad del bucle de Personalizer es encontrar la mejor correspondencia entre los usuarios y el café tantas veces como sea posible.
El código de este tutorial está disponible en el repositorio GitHub Personalizer Samples GitHub.
Funcionamiento de la simulación
Cuando el sistema comienza a funcionar, las sugerencias de Personalizer solo aciertan entre el 20 y el 30 % de las veces. Este éxito se indica mediante la recompensa que se envía de vuelta a la API de recompensas de Personalizer, con una puntuación de 1. Después de algunas llamadas a Rank and Reward, el sistema mejora.
Después de las solicitudes iniciales, ejecute una evaluación sin conexión. Esto permite que Personalizer revise los datos y sugiera una directiva de aprendizaje mejor. Aplique la nueva directiva de aprendizaje y vuelva a ejecutar el cuaderno con un 20 % del total de solicitudes anteriores. El bucle funcionará mejor con la nueva directiva de aprendizaje.
Llamadas de clasificación y recompensa
Para cada una de las pocas miles de llamadas al servicio Personalizer, el cuaderno de Azure envía la solicitud Rank a la API REST:
- Un identificador único para el evento Rank/Request
- Características de contexto: una combinación aleatoria de usuario, condición meteorológica y hora del día que simula a un usuario en un sitio web o un dispositivo móvil
- Acciones con características: todos los datos de café a partir de los cuales Personalizer realiza una sugerencia
El sistema recibe la solicitud y compara esa predicción con la opción conocida del usuario para la misma hora del día y condición meteorológica. Si la opción conocida es la misma que la opción de la predicción, se devuelve a Personalizer un valor de Reward de 1. En caso contrario, la recompensa que se envía de vuelta es 0.
Nota
Se trata de una simulación, por lo que el algoritmo de la recompensa es sencillo. En un escenario real, para determinar la puntuación de la recompensa el algoritmo deberá utilizar lógica de negocios, posiblemente con pesos para los diversos aspectos de la experiencia del cliente.
Prerrequisitos
- Una cuenta Azure Notebook.
- Un recurso Personalizador de Azure AI.
- Si ya ha usado el recurso Personalizer, asegúrese de clear los datos en el portal de Azure para el recurso.
- Cargue todos los archivos de este ejemplo en un proyecto Azure Notebook.
Descripciones de archivo:
- Personalizer.ipynb es el notebook de Jupyter para este tutorial.
- User dataset se almacena en un objeto JSON.
- el conjunto de datos Coffee se almacena en un objeto JSON.
- Example Request JSON es el formato esperado para una solicitud POST a rank API.
Configuración del recurso Personalizer
En el portal de Azure, configure el recurso
| Configuración | Importancia |
|---|---|
| Frecuencia de actualización del modelo | 15 segundos |
| Tiempo de espera de recompensa | 10 minutos |
Estos valores tienen una duración muy corta para mostrar los cambios en este tutorial. Estos valores no deben usarse en un escenario de producción sin validar que logran tu objetivo con el bucle de Personalizer.
Configuración del cuaderno de Azure
- Cambie el kernel a
Python 3.6. - Abra el archivo
Personalizer.ipynb.
Ejecución de celdas del cuaderno
Ejecute cada celda ejecutable y espere su retorno. Sabrá que ha terminado cuando los corchetes que aparecen junto a la celda muestren un número en lugar de *. En las secciones siguientes se explica qué hace cada celda de programación y qué esperar como salida.
Incluir los módulos de Python
Incluya los módulos de Python necesarios. La celda no tiene ninguna salida.
import json
import matplotlib.pyplot as plt
import random
import requests
import time
import uuid
Establecimiento de la clave y el nombre del recurso Personalizer
En el portal de Azure, busque la clave y el punto de conexión en la página Quickstart del recurso de Personalizer. Cambie el valor de <your-resource-name> al nombre del recurso Personalizer. Cambie el valor de <your-resource-key> a la clave de 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>"
Impresión de la fecha y la hora actuales
Use esta función para anotar las horas de inicio y finalización de la función iterativa, iterations.
Estas celdas no tienen ninguna salida. La función sí genera la fecha y hora actuales cuando se llama.
# Print out current datetime
def currentDateTime():
currentDT = datetime.datetime.now()
print (str(currentDT))
Obtención de la última hora de actualización del modelo
Cuando se llama a la función, get_last_updated, esta imprime la fecha y la hora de la última actualización del modelo.
Estas celdas no tienen ninguna salida. Cuando se llama a la función, esta muestra la última fecha de entrenamiento del modelo.
La función usa una API REST GET para obtener las propiedades del modelo.
# 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}')
Obtener la configuración de políticas y servicios.
Compruebe el estado del servicio con estas dos llamadas a REST.
Estas celdas no tienen ninguna salida. Cuando se llama a la función, la salida indica los valores del servicio.
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())
Construcción de las direcciones URL y lectura de los archivos de datos JSON
Esta celda realiza lo siguiente:
- compila las direcciones URL usadas en las llamadas a REST;
- establece el encabezado de seguridad mediante la clave del recurso Personalizer;
- establece la semilla aleatoria para el identificador de evento Rank
- lee los archivos de datos JSON;
- llama al método
get_last_updated(se ha quitado la directiva de aprendizaje de la salida del ejemplo); - llama al método
get_service_settings.
La celda tiene la salida de la llamada a las funciones get_last_updated y 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)}')
Compruebe que el valor de rewardWaitTime de la salida está establecido en 10 minutos y que modelExportFrequency está establecido en 15 segundos.
-----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
Solución de problemas de la primera llamada a REST
Esta celda anterior es la primera celda que llama a Personalizer. Asegúrese de que el código de estado REST de la salida es <Response [200]>. Si recibe un error, como 404, pero está seguro de que la clave y el nombre del recurso son correctos, vuelva a cargar el cuaderno.
Asegúrese de que el número de cafés y de usuarios es 4 en ambos casos. Si recibe un error, compruebe que ha cargado los tres archivos JSON.
Configuración del gráfico de métricas en Azure portal
Más adelante en este tutorial se puede ver el proceso de larga duración de las 10 000 solicitudes desde el explorador, con un cuadro de texto de actualización. Cuando finalice ese proceso, puede que resulte más fácil verlo en un gráfico o como suma total. Para ver esta información, use las métricas proporcionadas con el recurso. Puede crear el gráfico ahora que ha completado una solicitud al servicio y, después, actualizar el gráfico periódicamente mientras continúa el proceso de larga duración.
En el portal de Azure, seleccione el recurso de Personalizer.
En la navegación del recurso, seleccione Métricas bajo Supervisión.
En el gráfico, seleccione Agregar métrica.
El espacio de nombres del recurso y la métrica ya están establecidos. Solo tiene que seleccionar la métrica de llamadas exitosas y la agregación de suma.
Cambie el filtro de tiempo a las últimas 4 horas.
Deberías ver tres llamadas exitosas en el gráfico.
Generación de un identificador de evento único
Esta función genera un identificador único para cada llamada a rango. El identificador se usa para identificar la información de la llamada a Rank y Reward. Este valor puede proceder de un proceso de negocio, como un identificador de vista web o un identificador de transacción.
La celda no tiene ninguna salida. Cuando se llama a la función, esta devuelve el identificador único.
def add_event_id(rankjsonobj):
eventid = uuid.uuid4().hex
rankjsonobj["eventId"] = eventid
return eventid
Obtén usuario aleatorio, condición meteorológica aleatoria y hora del día aleatoria
Esta función selecciona una combinación única de usuario, condición meteorológica y hora del día, y agrega esos elementos al objeto JSON que se va a enviar a la solicitud Rank.
La celda no tiene ninguna salida. Cuando se llama a la función, devuelve los valores aleatorios de nombre del usuario, condición meteorológica y hora del día.
Se muestra a continuación la lista de los cuatro usuarios y sus preferencias; por brevedad, solo se muestran algunas preferencias:
{
"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]
Incorporación de todos los datos de café
Esta función agrega la lista completa de café al objeto JSON que se va a enviar a la solicitud Rank.
La celda no tiene ninguna salida. Cuando se llama a la función, cambia el elemento rankjsonobj.
A continuación se muestra el ejemplo de las características de un solo café:
{
"id": "Cappucino",
"features": [
{
"type": "hot",
"origin": "kenya",
"organic": "yes",
"roast": "dark"
}
}
def add_action_features(rankjsonobj):
rankjsonobj["actions"] = actionfeaturesobj
Comparación de la predicción con la preferencia de usuario conocida
Se invoca a esta función después de llamar a la API Rank en cada iteración.
Esta función compara las preferencia de café del usuario, según la condición meteorológica y la hora del día, con la sugerencia de Personalizer para el usuario con esos filtros. Si la sugerencia coincide, se devuelve una puntuación de 1; de lo contrario, la puntuación es 0. La celda no tiene ninguna salida. La función sí devuelve la puntuación cuando es llamada.
def get_reward_from_simulated_data(name, weather, timeofday, prediction):
if(userpref[name][weather][timeofday] == str(prediction)):
return 1
return 0
Bucle mediante llamadas a Rank y Reward
La siguiente celda es el principal trabajo del Notebook: obtener un usuario aleatorio, obtener la lista de cafés y enviar ambos a la Rank API. Después, comparar la predicción con las preferencias conocidas del usuario y enviar la recompensa de vuelta al servicio Personalizer.
El bucle se ejecuta num_requests veces. Personalizer necesita unos pocos miles de llamadas a Rank y Reward para crear un modelo.
A continuación se muestra un ejemplo de los datos JSON que se envían a la API Rank. Por brevedad, la lista de cafés no está completa. Puede ver el JSON completo del café en coffee.json.
JSON enviado a la API Rank:
{
'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
}
Respuesta JSON de la API Rank:
{
'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'
}
Por último, cada bucle muestra la selección aleatoria de usuario, condición meteorológica y hora del día, y determina la recompensa. La recompensa de 1 indica que el recurso Personalizer seleccionó el tipo de café correcto para el usuario, la condición meteorológica y la hora del día indicados.
1 Alice Rainy Morning Latte 1
La función usa:
- Rank: una API REST POST para obtener la clasificación.
- Reward: una API REST POST para comunicar la recompensa.
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]
Ejecutar 10 000 iteraciones
Ejecute el bucle de Personalizer durante 10 000 iteraciones. Se trata de un evento de larga duración. No cierre el explorador que ejecuta el cuaderno. Actualice el gráfico de métricas del portal de Azure periódicamente para ver el total de llamadas al servicio. Cuando tenga en torno a 20 000 llamadas y una llamada a Rank y Reward para cada iteración del bucle, las iteraciones habrán terminado.
# 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)
Resultados en gráficas para observar la mejora
Cree un gráfico a partir de los valores de count y rewards.
def createChart(x, y):
plt.plot(x, y)
plt.xlabel("Batch of rank events")
plt.ylabel("Correct recommendations per batch")
plt.show()
Diagrama de ejecución para 10.000 solicitudes de rango
Ejecute la función createChart.
createChart(count,rewards)
Lectura del gráfico
Este gráfico muestra el éxito del modelo para la directiva de aprendizaje predeterminada actual.
El objetivo ideal es que, al final de la prueba, el bucle ofrezca una tasa de éxito media próxima al 100 % menos la exploración. El valor predeterminado de exploración es 20 %.
100-20=80
Este valor de exploración se encuentra en el portal de Azure, para el recurso Personalizer, en la página Configuration.
Si quiere encontrar una directiva de aprendizaje mejor, en función de los datos de la API Rank, ejecute una evaluación sin conexión en el portal para el bucle de Personalizer.
Ejecución de una evaluación sin conexión
En el portal de Azure, abra la página Evaluations del recurso Personalizer.
Seleccione Crear evaluación.
Ingrese los datos requeridos del nombre de la evaluación y el rango de fechas para la evaluación en bucle. El intervalo de fechas debe incluir solo los días en los que se centra para la evaluación.
La finalidad de la ejecución de esta evaluación sin conexión es determinar si hay una directiva de aprendizaje mejor para las características y las acciones que se usan en este bucle. Para encontrar esa directiva de aprendizaje mejor, asegúrese de que la opción Detección de optimización está activada.
Seleccione Aceptar para iniciar la evaluación.
En esta página Evaluaciones se muestra la nueva evaluación y su estado actual. En función de la cantidad de datos que tenga, puede tardar algún tiempo. Puede volver a esta página después de unos minutos para ver los resultados.
Una vez finalizada la evaluación, selecciónela y, a continuación, seleccione Comparison de directivas de aprendizaje distintas. Se muestran las directivas de aprendizaje disponibles y cómo se comportarían con los datos.
Seleccione la directiva de aprendizaje de nivel superior de la tabla y seleccione Aplicar. Esto aplica la mejor política de aprendizaje a tu modelo y lo reentrena.
Cambio de la frecuencia de actualización del modelo a 5 minutos
- En el portal de Azure, todavía en el recurso Personalizer, seleccione la página Configuration.
- Cambie los valores de Frecuencia de actualización del modelo y Tiempo de espera de recompensa a 5 minutos y seleccione Guardar.
Más información sobre Tiempo de espera de recompensa y Frecuencia de actualización del modelo.
#Verify new learning policy and times
get_service_settings()
Compruebe que los valores de rewardWaitTime y modelExportFrequency de la salida están establecidos en 5 minutos.
-----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
Comprobación de la nueva directiva de aprendizaje
Vuelva al archivo de cuadernos de Azure y continúe ejecutando el mismo bucle, pero solo para 2000 iteraciones. Actualice el gráfico de métricas del portal de Azure periódicamente para ver el total de llamadas al servicio. Cuando tenga aproximadamente 4000 llamadas y una llamada a clasificación y recompensa para cada iteración del bucle, se habrán completado las iteraciones.
# 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)
Ejecución del gráfico de 2000 solicitudes Rank
Ejecute la función createChart.
createChart(count2,rewards2)
Revisión del segundo gráfico
El segundo gráfico debe mostrar un aumento visible en las predicciones de Rank, en correspondencia con las preferencias del usuario.
Limpieza de recursos
Si no desea continuar con la serie de tutoriales, limpie los siguientes recursos:
- Elimine el proyecto Azure Notebook.
- Elimine el recurso Personalizer.
Pasos siguientes
El cuaderno Jupyter Notebook y los archivos de datos usados en este ejemplo están disponibles en el repositorio de GitHub para Personalizer.