Generación y publicación de modelos ML

Introducción

Partiendo de datos de Diabetes, vas 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. Vas a utilizar:

  • File Repository sobre MinIO para guardar el conjunto de datos original. Cargarás 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 (en inglés):

  • 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).

URL de la fuente:

https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html

Para más información, véase:

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, vas a obtener el archivo de esta fuente: https://www4.stat.ncsu.edu/~boos/var.select/diabetes.tab.txt

Vas a crear una "Entidad en Base de Datos Histórica" a partir de este archivo así que debes ir a esta opción:

Rellena la información principal:

Y pulsa “Continuar”. A continuación, tendrás 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, pulsa el botón “Crear” y se creará tu nueva entidad:

También puedes 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, vas a crear un nuevo notebook. Dirígete a la opción Analytics Tools y pulsa en el botón nuevo notebook (+) y después escríbele un nombre.

También podemos importar este archivo que contiene el cuaderno completo para este ejemplo (sólo tienes que establecer el parámetro token).

Tienes algunos párrafos explicativos para el conjunto de datos, pero ve a la sección de código.

El primer párrafo que vas a enfocar es el de importación.

Carga muchas librerías y establece 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 puedes ir a la sección My Files:

Luego a MinIO:

 

Y en la siguiente página puedes obtener la ruta del archivo:

El token será algún token X-OP-APIKey que pueda acceder al fichero.

A continuación, en tres apartados, carga el propio fichero csv y el filepath del apartado anterior, léelo como csv con la columna del dataset (necesitamos incluir las columnas en la función read_csv) y muestra el contenido cargado:

Ahora que tienes tu archivo como pandas dataframe, puedes dividir los datos en conjuntos de entrenamiento y prueba:

Divide también estos conjuntos de datos en conjuntos de datos X e Y para los parámetros de entrada y los resultados esperados:

Y ejecuta el entrenamiento de ElasticNet con estos datos y obtenga en lr el modelo de salida:

Por último, evalúa alguna métrica para el resultado de la predicción.

Paso 3: Registrar los datos de entrenamiento y del modelo en MLFlow

El Notebook Engine está integrado con el servicio de seguimiento de MLFlow, por lo que lo único que tienes 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 puedes 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. Incluye 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 usas esto. sólo tendrás un id autogenerado, el ID del experimento.

  • mlflow.log_param(...) → registra un parámetro de entrada para el experimento.

  • mlflow.log_metric(...) → registra una métrica de salida para el experimento.

  • mlflow.sklearn.log_model(lr, "model") → registra y guarda el modelo entrenado con todos los archivos de metadatos necesarios.

Si ejecutas este párrafo, tendrás una salida como esta. El proceso de registro ha terminado bien.

Si vas a la interfaz de usuario del Gestor de Modelos en el Control Panel:

Podrás 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 vas a utilizar para publicar el modelo en el siguiente paso. Ésta es la referencia que necesitas para recoger el modelo en el MLFlow y hacer algunas evaluaciones con él.

También puedes registrarlo con el fin de etiquetarlo, versionarlo y tenerlo fuera del experimento. Puedes hacerlo con el código o puedes utilizar el botón de registro en el lado derecho:

Y si vas a la pestaña modelo, puedes 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, vas 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 vas a crear (con el botón +) una nueva aplicación y vas a rellenar todas las entradas necesarias:

Puedes crear un nuevo repositorio o utilizar uno existente. En cualquier caso, vas a tener una nueva aplicación como esta:

Luego puedes 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. Haz clic en "Create Function" y vas a crear tres archivos.

En primer lugar, selecciona la rama principal en el lado derecho:

Luego vas a crear (aquí o en el repositorio Git con un editor externo) los tres archivos:

requirements.txt → librerías que necesita tu modelo para ejecutarse. En este caso, vas 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í. Tienes que cargar las bibliotecas para evaluar el modelo, MLFlow y fdk para el punto final.

También utilizarás 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"} )

Puedes guardar todo y desplegar tu 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 puedes probar el modelo con la API REST con Postman por ejemplo enviando un array de JSON con la entrada:

O puedes crear un evaluador de modelo en el Dashboard Engine que utilice este punto final con alguna entrada proporcionada:

O puedes evaluar este modelo en un flujo de datos en batch o en streaming en el DataFlow con el componente evaluador correspondiente: