Introducción
Partiendo de datos de Diabetes vamos a generar el modelo correspondiente que prediga una medida cuantitativa de progresión de la enfermedad un año después de la línea base. Vamos a utilizar
File Repository sobre MinIO para guardar el conjunto de datos original. Cargaremos el archivo utilizando el módulo Create Entity in Historical Database
Notebooks para tener un proceso paramétrico para obtener los datos de MinIO, entrenar y generar el modelo y registrar todo en MLFlow
Gestor de Modelos (MLFlow) para registrar todos los experimentos del cuaderno y guardar el modelo y otros archivos para el entrenamiento.
Módulo Serverless para crear una función python escalable que usando el modelo pueda predecir la progresión de la enfermedad.
Dataset
La información del conjunto de datos sobre diabetes es la siguiente.
Se obtuvieron diez variables de base, edad, sexo, índice de masa corporal, presión y seis mediciones de suero sanguíneo para cada uno de los 442 pacientes diabéticos, así como la respuesta de interés, una medida cuantitativa de la progresión de la enfermedad un año después de la línea de base.
Características del dataset:
Número de instancias: 44
Número de atributos: Las 10 primeras columnas son valores predictivos numéricos
Target: La columna 11 es una medida cuantitativa de la progresión de la enfermedad un año después de la línea de base
Información sobre atributos:
age age in years
sex
bmi body mass index
bp average blood pressure
s1 tc, total serum cholesterol
s2 ldl, low-density lipoproteins
s3 hdl, high-density lipoproteins
s4 tch, total cholesterol / HDL
s5 ltg, possibly log of serum triglycerides level
s6 glu, blood sugar level
Nota: Cada una de estas 10 variables de características se ha centrado en la media y se ha escalado por la desviación estándar multiplicada por n_muestras
(es decir, la suma de cuadrados de cada columna suma 1).
Source URL:
https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html
For more information see:
Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499.
(https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf)'
Paso 1: Cargar datos en la plataforma MinIO
Desde el enlace vamos a obtener el archivo de esta fuente https://www4.stat.ncsu.edu/~boos/var.select/diabetes.tab.txt
Vamos a crear una "Entidad en Base de Datos Histórica" a partir de este archivo así que iremos a esta opción:
Rellenaremos la información principal
Y pulsar en continuar. A continuación, tenemos que rellenar todas las columnas del archivo con el formato de cadena (esto es porque el archivo CSV necesita ser cargado con este tipo de columna)
Finalmente pulsamos el botón crear y se creará nuestra nueva entidad:
También podemos consultar esta entidad en SQL a través de Presto con el Query Tool
Paso 2: Crear libreta para obtener datos, entrenar y registrar el experimento
En primer lugar, vamos a crear un nuevo notebook, nos dirigimos a la opción Analytics Tools y pulsamos en el botón nuevo notebook (+) le escribimos un nombre
También podemos importar este archivo que contiene el cuaderno completo para este ejemplo (sólo tenemos que establecer el parámetro token)
Tenemos algunos párrafos explicativos para el conjunto de datos, pero vamos a la sección de código.
El primer párrafo que vamos a enfocar es el de importación
Cargamos muchas librerías y establecemos la url base para el repositorio MinIO. El siguiente párrafo va a ser el párrafo de parámetros con el fin de establecer variables que pueden venir de fuera
Para obtener la ruta del archivo podemos ir a la sección My Files:
Luego a MinIO
Y en la siguiente página podemos obtener la ruta del archivo
El token será algún token X-OP-APIKey que pueda acceder al fichero.
A continuación en 3 apartados cargaremos el propio fichero csv y el filepath del apartado anterior, lo leemos como csv con la columna del dataset (necesitamos incluir las columnas en la función read_csv) y mostramos el contenido cargado:
Ahora que tenemos nuestro archivo como pandas dataframe podemos dividir los datos en conjuntos de entrenamiento y prueba:
Divida también estos conjuntos de datos en conjuntos de datos X e Y para los parámetros de entrada y los resultados esperados:
Y ejecute el entrenamiento de ElasticNet con estos datos y obtenga en lr el modelo de salida
Por último, evaluamos alguna métrica para el resultado de la predicción:
Paso 3: Registrar los datos de entrenamiento y del modelo en MLFlow
El Notebook Engineestá integrado con el servicio de seguimiento de MLFlow, por lo que lo único que tenemos que hacer en el cuaderno es importar la librería "MLFlow" necesaria y utilizar las funciones de seguimiento de MLFlow. Eso se hará en la sección de librerías de importación
Los parámetros de conexión y las variables de entorno ya están hechos, así que ahora podemos registrar los parámetros en MLFlow directamente de esta manera
%python with mlflow.start_run(): mlflow.set_tag("mlflow.runName", "DiabetesModelGenerator") mlflow.log_param("alpha", alpha) mlflow.log_param("l1_ratio", l1_ratio) mlflow.log_metric("rmse", rmse) mlflow.log_metric("r2", r2) mlflow.log_metric("mae", mae) mlflow.sklearn.log_model(lr, "model") mlflow.end_run()
Este es el código estándar para el seguimiento de un experimento en MLFlow. Incluimos todo dentro de “with mlflow.start_run()“ para iniciar un nuevo experimento.
Las otras funciones son:
mlflow.set_tag("mlflow.runName", ...)
→(opcional) para establecer un nombre de ejecución del experimento. Si no usamos esto sólo tendremos un id autogenerado, el ID del experimentomlflow.log_param(...)
→ registrar una parámetro de entrada para el experimentomlflow.log_metric(...)
→ registrar una métrica de salida para el experimentomlflow.sklearn.log_model(lr, "model")
→ registra y guarda el modelo entrenado con todos los archivos de metadatos necesarios
Si ejecutamos este párrafo vamos a tener una salida como esta. El proceso de registro ha terminado bien.
Si vamos a la interfaz de usuario del Gestor de Modelos en el Control Panel:
Podemos ver la ejecución del experimento con todos los parámetros de registro, las métricas y los archivos:
Al hacer clic en el experimento, se abre la página de detalles:
Y, al final de la página, podemos revisar todos los archivos de este experimento y el propio modelo
El run id de la derecha (runs:/859953c3a0dd4596bd15d864e91081ab/model) es importante porque lo vamos a utilizar para publicar el modelo en el siguiente paso. Esta es la referencia que necesitamos para recoger el modelo en el MLFlow y hacer algunas evaluaciones con él.
También podemos registrar el con el fin de etiquetarlo, versionarlo y tenerlo fuera del experimento, podemos hacerlo con el código o podemos utilizar el botón de registro en el lado derecho:
Y si vamos a la pestaña modelo podemos verlo y trabajar con él
Paso 4: Crear una función Serverless en Python que evalúe los datos contra el modelo MLFlow
Con el modelo generado anteriormente vamos a crear una función Python que, con una entrada simple o múltiple, pueda obtener una predicción usando el modelo.
El primer paso es ir al menú Serverless Applications
A continuación vamos a crear (con el botón +) una nueva aplicación y vamos a rellenar todas las entradas necesarias
Podemos crear un nuevo repositorio o utilizar uno existente. En cualquier caso, vamos a tener una nueva aplicación como esta:
Luego podemos ir al botón "Ver" y luego a la pestaña de funciones
El siguiente paso es crear o utilizar una función serverless existente, hacemos clic en "Create Function" y vamos a crear tres archivos.
En primer lugar, seleccionamos la rama principal en el lado derecho:
Luego vamos a crear (aquí o en el repositorio git con un editor externo) los 3 archivos:
requirements.txt → librerías que necesita nuestro modelo para ejecutarse. En este caso vamos a tener estos:
fdk
protobuf==3.20.*
numpy==1.23.4
mlflow==1.19.0
mlflow-onesaitplatform-plugin==0.2.11
scikit-learn
func.yaml → los metadatos del proyecto necesarios para la función sin servidor. El contenido será:
schema_version: 20180708 name: diabetes-predictor version: 0.1.1 runtime: python build_image: fnproject/python:3.9-dev run_image: fnproject/python:3.9 entrypoint: /python/bin/fdk /function/func.py handler memory: 256 triggers: - name: endpoint type: http source: /diabetes-predictor
Es importante el triggers.source config para tener el endpoint para esta función, el nombre y el tiempo de ejecución
func.py →el contenido de la función de evaluación en sí. Tenemos que cargar las bibliotecas para evaluar el modelo, MLFlow y fdk para el punto final.
También utilizamos una variable de entorno para la entrada paramétrica del host, experimento y token
import io import json import logging import os os.environ["HOME"] = "/tmp" import random import mlflow from fdk import response host = os.environ['HOST'] token = os.environ['TOKEN'] experimentid = os.environ['EXPERIMENTID'] tracking_uri = "https://" + host + "/controlpanel/modelsmanager" model_uri = "onesait-platform://" + token + "@" + host + "/0/" + experimentid + "/artifacts/model" global pyfunc_predictor mlflow.set_tracking_uri(tracking_uri) pyfunc_predictor = mlflow.pyfunc.load_model(model_uri=model_uri) logging.getLogger().info("Diabetes Progression Predictor ready") def handler(ctx, data: io.BytesIO = None): try: logging.getLogger().info("Try") answer = [] json_obj = json.loads(data.getvalue()) logging.getLogger().info("json_obj") logging.getLogger().info(str(json_obj)) if isinstance(json_obj, list): logging.getLogger().info("isinstance") answer = [] values = [] inputvector = [] for input in json_obj: logging.getLogger().info("for") logging.getLogger().info("input: " + str(input)) inputvector = [ input['age'], input['sex'], input['bmi'], input['bp'], input['s1'], input['s2'], input['s3'], input['s4'], input['s5'], input['s6']] values.append(inputvector) predict = pyfunc_predictor.predict(values) answer = predict.tolist() logging.getLogger().info("prediction") else: answer = "input object is not an array of objects:" + str(json_obj) logging.getLogger().error('error isinstance(json_obj, list):' + isinstance(json_obj, list)) raise Exception(answer) except (Exception, ValueError) as ex: logging.getLogger().error('error parsing json payload: ' + str(ex)) logging.getLogger().info("Inside Python ML function") return response.Response( ctx, response_data=json.dumps(answer), headers={"Content-Type": "application/json"} )
Podemos guardar todo y desplegar nuestra función con el botón Rocket:
El último paso es poner las variables de entorno para el modelo con el botón:
Paso 5: Evaluación del modelo
Ahora podemos probar el modelo con la API Rest con postman por ejemplo enviando un array de json con la entrada:
O podemos crear un evaluador de modelo en el Dashboard Engine que utilice este punto final con alguna entrada proporcionada
O podemos evaluar este modelo en un flujo de datos en batch o en streaming en el DataFlow con el componente evaluador correspondiente