Vertex AI: Usa rutinas de predicción personalizadas con Sklearn a fin de realizar el procesamiento previo y posterior de los datos para las predicciones.

1. Descripción general

En este lab, aprenderás a usar rutinas de predicción personalizadas en Vertex AI para escribir lógica personalizada de procesamiento previo y posterior. Si bien en esta muestra se usa Scikit-learn, las rutinas de predicción personalizadas pueden funcionar con otros frameworks de AA de Python, como XGBoost, PyTorch y TensorFlow.

Qué aprenderá

Aprenderás a hacer lo siguiente:

  • Cómo escribir una lógica de predicción personalizada con rutinas de predicción personalizadas
  • Prueba el contenedor y el modelo de entrega personalizados de forma local
  • Prueba el contenedor de entrega personalizado en Vertex AI Predictions

El costo total de la ejecución de este lab en Google Cloud es de aproximadamente USD 1.

2. Introducción a Vertex AI

En este lab, se utiliza la oferta de productos de IA más reciente de Google Cloud. Vertex AI integra las ofertas de AA de Google Cloud en una experiencia de desarrollo fluida. Anteriormente, se podía acceder a los modelos personalizados y a los entrenados con AutoML mediante servicios independientes. La nueva oferta combina ambos en una sola API, junto con otros productos nuevos. También puedes migrar proyectos existentes a Vertex AI.

Vertex AI incluye muchos productos distintos para respaldar flujos de trabajo de AA de extremo a extremo. Este lab se enfocará en Predictions y Workbench.

Descripción general del producto Vertex

3. Descripción general del caso de uso

Caso de uso

En este lab, compilarás un modelo de regresión de bosque aleatorio para predecir el precio de un diamante según atributos como el corte, la claridad y el tamaño.

Escribirás una lógica de procesamiento previo personalizada para verificar que los datos en el momento de la publicación estén en el formato que espera el modelo. También escribirás una lógica de procesamiento posterior personalizada para redondear las predicciones y convertirlas en cadenas. Para escribir esta lógica, usarás rutinas de predicción personalizadas.

Introducción a las rutinas de predicción personalizadas

Los contenedores precompilados de Vertex AI controlan las solicitudes de predicción mediante la operación de predicción del framework de aprendizaje automático. Antes de las rutinas de predicción personalizadas, si querías procesar previamente la entrada antes de que se realizara la predicción o procesar de manera posterior la predicción del modelo antes de mostrar el resultado, debías compilar un contenedor personalizado.

La compilación de un contenedor de entrega personalizado requiere escribir un servidor HTTP que una el modelo entrenado, traduzca las solicitudes HTTP en entradas del modelo y traduzca las salidas del modelo en respuestas.

Con las rutinas de predicción personalizadas, Vertex AI te proporciona los componentes relacionados con la publicación para que puedas enfocarte en tu modelo y en las transformaciones de datos.

4. Configura el entorno

Para ejecutar este codelab, necesitarás un proyecto de Google Cloud Platform que tenga habilitada la facturación. Para crear un proyecto, sigue estas instrucciones.

Paso 1: Habilita la API de Compute Engine

Ve a Compute Engine y selecciona Habilitar (si aún no está habilitada). La necesitarás para crear la instancia de notebook.

Paso 2: Habilita la API de Artifact Registry

Ve a Artifact Registry y selecciona Habilitar si aún no está habilitada. La usarás para crear un contenedor de entrega personalizado.

Paso 3: Habilita la API de Vertex AI

Navega hasta la sección Vertex AI en Cloud Console y haz clic en Habilitar API de Vertex AI (Enable Vertex AI API).

Panel de Vertex AI

Paso 4: Crea una instancia de Vertex AI Workbench

En la sección Vertex AI de Cloud Console, haz clic en Workbench:

Menú Vertex AI

Habilita la API de Notebooks si aún no está habilitada.

Notebook_api

Una vez habilitada, haz clic en INSTANCIAS y, luego, selecciona CREAR NUEVA.

Acepta las opciones predeterminadas y haz clic en Crear.

Cuando la instancia esté lista, haz clic en ABRIR JUPYTERLAB para abrirla.

5. Escribe el código de entrenamiento

Paso 1: Crea un bucket de Cloud Storage

Almacenarás el modelo y los artefactos de procesamiento previo en un bucket de Cloud Storage. Si ya tienes un bucket en tu proyecto que deseas usar, puedes omitir este paso.

Desde el selector, abre una nueva sesión de terminal.

Open_terminal

En la terminal, ejecuta el siguiente comando para definir una variable de entorno para tu proyecto y asegúrate de reemplazar your-cloud-project por el ID de tu proyecto:

PROJECT_ID='your-cloud-project'

Luego, ejecuta el siguiente comando en tu terminal para crear un bucket nuevo en el proyecto.

BUCKET="gs://${PROJECT_ID}-cpr-bucket"
gsutil mb -l us-central1 $BUCKET

Paso 2: Entrena el modelo

En la terminal, crea un directorio nuevo llamado cpr-codelab y ábrelo con el comando cd.

mkdir cpr-codelab
cd cpr-codelab

En el navegador de archivos, navega al nuevo directorio cpr-codelab y, luego, usa el selector para crear un nuevo notebook de Python 3 llamado task.ipynb.

file_browser

Tu directorio cpr-codelab debería verse de la siguiente manera:

+ cpr-codelab/
    + task.ipynb

En el notebook, pega el siguiente código.

Primero, escribe un archivo requirements.txt.

%%writefile requirements.txt
fastapi
uvicorn==0.17.6
joblib~=1.0
numpy~=1.20
scikit-learn>=1.2.2
pandas
google-cloud-storage>=1.26.0,<2.0.0dev
google-cloud-aiplatform[prediction]>=1.16.0

El modelo que implementes tendrá un conjunto diferente de dependencias preinstaladas que el entorno de tu notebook. Por este motivo, te recomendamos que enumeres todas las dependencias del modelo en requirements.txt y, luego, uses pip para instalar las mismas dependencias en el notebook. Más adelante, probarás el modelo de forma local antes de implementarlo en Vertex AI para verificar que los entornos coincidan.

pip instale las dependencias en el notebook.

!pip install -U --user -r requirements.txt

Ten en cuenta que deberás reiniciar el kernel después de que se complete la instalación de pip.

restart_kernel

A continuación, crea los directorios en los que almacenarás el modelo y los artefactos de procesamiento previo.

USER_SRC_DIR = "src_dir"
!mkdir $USER_SRC_DIR
!mkdir model_artifacts

# copy the requirements to the source dir
!cp requirements.txt $USER_SRC_DIR/requirements.txt

Tu directorio cpr-codelab debería verse de la siguiente manera:

+ cpr-codelab/
    + model_artifacts/
    + scr_dir/
        + requirements.txt
    + task.ipynb
    + requirements.txt

Ahora que se configuró la estructura de directorios, es hora de entrenar un modelo.

Primero, importa las bibliotecas.

import seaborn as sns
import numpy as np
import pandas as pd

from sklearn import preprocessing
from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import make_pipeline
from sklearn.compose import make_column_transformer

import joblib
import logging

# set logging to see the docker container logs
logging.basicConfig(level=logging.INFO)

Luego, define las siguientes variables. Asegúrate de reemplazar PROJECT_ID por el ID de tu proyecto y BUCKET_NAME por el bucket que creaste en el paso anterior.

REGION = "us-central1"
MODEL_ARTIFACT_DIR = "sklearn-model-artifacts"
REPOSITORY = "diamonds"
IMAGE = "sklearn-image"
MODEL_DISPLAY_NAME = "diamonds-cpr"

# Replace with your project
PROJECT_ID = "{PROJECT_ID}"

# Replace with your bucket
BUCKET_NAME = "gs://{BUCKET_NAME}"

Carga los datos desde la biblioteca de Seaborn y, luego, crea dos marcos de datos, uno con las funciones y el otro con la etiqueta.

data = sns.load_dataset('diamonds', cache=True, data_home=None)

label = 'price'

y_train = data['price']
x_train = data.drop(columns=['price'])

Analicemos los datos de entrenamiento. Puedes ver que cada fila representa un diamante.

x_train.head()

Y las etiquetas, que son los precios correspondientes.

y_train.head()

Ahora, define una transformación de columnas de sklearn para que codifique en caliente los atributos categóricos y escale los atributos numéricos.

column_transform = make_column_transformer(
    (preprocessing.OneHotEncoder(), [1,2,3]),
    (preprocessing.StandardScaler(), [0,4,5,6,7,8]))

Define el modelo de bosque aleatorio

regr = RandomForestRegressor(max_depth=10, random_state=0)

A continuación, crea una canalización de sklearn. Esto significa que los datos que se alimentan a esta canalización primero se codificarán o escalarán y, luego, se pasarán al modelo.

my_pipeline = make_pipeline(column_transform, regr)

Ajusta la canalización a los datos de entrenamiento

my_pipeline.fit(x_train, y_train)

Probemos el modelo para asegurarnos de que funciona según lo esperado. Llama al método predict en el modelo y pasa una muestra de prueba.

my_pipeline.predict([[0.23, 'Ideal', 'E', 'SI2', 61.5, 55.0, 3.95, 3.98, 2.43]])

Ahora podemos guardar la canalización en el directorio model_artifacts y copiarla en el bucket de Cloud Storage.

joblib.dump(my_pipeline, 'model_artifacts/model.joblib')

!gsutil cp model_artifacts/model.joblib {BUCKET_NAME}/{MODEL_ARTIFACT_DIR}/

Paso 3: Guarda un artefacto de procesamiento previo

A continuación, crearás un artefacto de procesamiento previo. Este artefacto se cargará en el contenedor personalizado cuando se inicie el servidor del modelo. Tu artefacto de procesamiento previo puede tener casi cualquier forma (como un archivo pickle), pero en este caso, escribirás un diccionario en un archivo JSON.

clarity_dict={"Flawless": "FL",
              "Internally Flawless": "IF",
              "Very Very Slightly Included": "VVS1",
              "Very Slightly Included": "VS2",
              "Slightly Included": "S12",
              "Included": "I3"}

El atributo clarity de nuestros datos de entrenamiento siempre estuvo en el formato Abbreviated (es decir, "FL" en lugar de "Flawless"). En el momento de la publicación, queremos verificar que los datos de este atributo también se abrevienten. Esto se debe a que nuestro modelo sabe cómo hacer codificación one-hot "FL" pero no "impecable". Escribirás esta lógica personalizada de procesamiento previo más adelante. Por ahora, solo guarda esta tabla de consulta en un archivo JSON y, luego, escríbela en el bucket de Cloud Storage.

import json
with open("model_artifacts/preprocessor.json", "w") as f:
    json.dump(clarity_dict, f)

!gsutil cp model_artifacts/preprocessor.json {BUCKET_NAME}/{MODEL_ARTIFACT_DIR}/

Tu directorio cpr-codelab local ahora debería verse de la siguiente manera:

+ cpr-codelab/
    + model_artifacts/
        + model.joblib
        + preprocessor.json
    + scr_dir/
        + requirements.txt
    + task.ipynb
    + requirements.txt

6. Crea un contenedor de entrega personalizado con el servidor de modelo de CPR

Ahora que se entrenó el modelo y se guardó el artefacto de procesamiento previo, es hora de compilar el contenedor de entrega personalizado. Por lo general, compilar un contenedor de entrega requiere escribir código del servidor de modelos. Sin embargo, con las rutinas de predicción personalizadas, Vertex AI Predictions genera un servidor de modelos y compila una imagen de contenedor personalizada por ti.

Un contenedor de entrega personalizado contiene los siguientes 3 fragmentos de código:

  1. Servidor del modelo (el SDK lo generará automáticamente y se almacenará en scr_dir/)
    • Servidor HTTP que aloja el modelo
    • Es responsable de configurar rutas, puertos, etcétera.
  2. Controlador de solicitudes
    • Es responsable de los aspectos del servidor web relacionados con el manejo de una solicitud, como la deserialización del cuerpo de la solicitud, la serialización de la respuesta, la configuración de encabezados de respuesta, etcétera.
    • En este ejemplo, usarás el controlador predeterminado, google.cloud.aiplatform.prediction.handler.PredictionHandler, que se proporciona en el SDK.
  3. Predictor
    • Es responsable de la lógica de AA para procesar una solicitud de predicción.

Cada uno de estos componentes se puede personalizar según los requisitos de tu caso de uso. En este ejemplo, solo implementarás el predictor.

El predictor es responsable de la lógica del AA para procesar una solicitud de predicción, como el procesamiento previo y posterior personalizados. Para escribir lógica de predicción personalizada, crearás una subclase de la interfaz de Predictor de Vertex AI.

Esta versión de rutinas de predicción personalizadas incluye predictores reutilizables de XGBoost y Sklearn, pero si necesitas usar un framework diferente, puedes crear el tuyo propio creando una subclase del predictor base.

A continuación, puedes ver un ejemplo del predictor de Sklearn. Este es todo el código que deberías escribir para compilar este servidor de modelos personalizados.

sklearn_predictor

En tu notebook, pega el siguiente código para crear una subclase de SklearnPredictor y escríbelo en un archivo de Python en src_dir/. Ten en cuenta que, en este ejemplo, solo personalizaremos los métodos load, preprocess y postprocess, y no el método predict.

%%writefile $USER_SRC_DIR/predictor.py

import joblib
import numpy as np
import json

from google.cloud import storage
from google.cloud.aiplatform.prediction.sklearn.predictor import SklearnPredictor


class CprPredictor(SklearnPredictor):

    def __init__(self):
        return

    def load(self, artifacts_uri: str) -> None:
        """Loads the sklearn pipeline and preprocessing artifact."""

        super().load(artifacts_uri)

        # open preprocessing artifact
        with open("preprocessor.json", "rb") as f:
            self._preprocessor = json.load(f)


    def preprocess(self, prediction_input: np.ndarray) -> np.ndarray:
        """Performs preprocessing by checking if clarity feature is in abbreviated form."""

        inputs = super().preprocess(prediction_input)

        for sample in inputs:
            if sample[3] not in self._preprocessor.values():
                sample[3] = self._preprocessor[sample[3]]
        return inputs

    def postprocess(self, prediction_results: np.ndarray) -> dict:
        """Performs postprocessing by rounding predictions and converting to str."""

        return {"predictions": [f"${value}" for value in np.round(prediction_results)]}

Analicemos con más detalle cada uno de estos métodos.

  • El método load se carga en el artefacto de procesamiento previo, que en este caso es un diccionario que asigna los valores de claridad del diamante a sus abreviaturas.
  • El método preprocess usa ese artefacto para garantizar que, en el momento de la publicación, la función de claridad esté en su formato abreviado. De lo contrario, convierte la cadena completa en su abreviatura.
  • El método postprocess muestra el valor previsto como una cadena con un signo $ y lo redondea.

A continuación, usa el SDK de Python de Vertex AI para compilar la imagen. Con rutinas de predicción personalizadas, se generará el Dockerfile y se compilará la imagen por ti.

from google.cloud import aiplatform

aiplatform.init(project=PROJECT_ID, location=REGION)

import os

from google.cloud.aiplatform.prediction import LocalModel

from src_dir.predictor import CprPredictor  # Should be path of variable $USER_SRC_DIR

local_model = LocalModel.build_cpr_model(
    USER_SRC_DIR,
    f"{REGION}-docker.pkg.dev/{PROJECT_ID}/{REPOSITORY}/{IMAGE}",
    predictor=CprPredictor,
    requirements_path=os.path.join(USER_SRC_DIR, "requirements.txt"),
)

Escribe un archivo de prueba con dos muestras para la predicción. Una de las instancias tiene el nombre abreviado de claridad, pero la otra necesita convertirse primero.

import json

sample = {"instances": [
  [0.23, 'Ideal', 'E', 'VS2', 61.5, 55.0, 3.95, 3.98, 2.43],
  [0.29, 'Premium', 'J', 'Internally Flawless', 52.5, 49.0, 4.00, 2.13, 3.11]]}

with open('instances.json', 'w') as fp:
    json.dump(sample, fp)

Implementa un modelo local para probar el contenedor de manera local.

with local_model.deploy_to_local_endpoint(
    artifact_uri = 'model_artifacts/', # local path to artifacts
) as local_endpoint:
    predict_response = local_endpoint.predict(
        request_file='instances.json',
        headers={"Content-Type": "application/json"},
    )

    health_check_response = local_endpoint.run_health_check()

Puedes ver los resultados de la predicción con lo siguiente:

predict_response.content

7. Implementar el modelo en Vertex AI

Ahora que probaste el contenedor de manera local, debes enviar la imagen a Artifact Registry y subir el modelo a Vertex AI Model Registry.

Primero, configura Docker para acceder a Artifact Registry.

!gcloud artifacts repositories create {REPOSITORY} --repository-format=docker \
--location=us-central1 --description="Docker repository"


!gcloud auth configure-docker {REGION}-docker.pkg.dev --quiet

Luego, envía la imagen.

local_model.push_image()

Y sube el modelo.

model = aiplatform.Model.upload(local_model = local_model,
                                display_name=MODEL_DISPLAY_NAME,
                                artifact_uri=f"{BUCKET_NAME}/{MODEL_ARTIFACT_DIR}",)

Cuando se suba el modelo, deberías verlo en la consola:

model_registry

A continuación, implementa el modelo para que puedas usarlo en las predicciones en línea. Las rutinas de predicción personalizadas también funcionan con la predicción por lotes, por lo que si tu caso de uso no requiere predicciones en línea, no necesitas implementar el modelo.

endpoint = model.deploy(machine_type="n1-standard-2")

Por último, prueba el modelo implementado a través de la obtención de una predicción.

endpoint.predict(instances=[[0.23, 'Ideal', 'E', 'VS2', 61.5, 55.0, 3.95, 3.98, 2.43]])

🎉 ¡Felicitaciones! 🎉

Aprendiste a usar Vertex AI para hacer lo siguiente:

  • Escribe lógica de procesamiento previo y posterior personalizada con rutinas de predicción personalizadas

Si quieres obtener más información sobre los diferentes componentes de Vertex AI, consulta la documentación.

8. Limpieza

Si quieres continuar usando el notebook que creaste en este lab, te recomendamos que lo desactives cuando no lo utilices. En la IU de Workbench de la consola de Google Cloud, selecciona el notebook y, luego, Detener.

Si quieres borrar el notebook por completo, haz clic en el botón Borrar en la parte superior derecha.

Stop_nb

Para borrar el extremo que implementaste, navega a la sección Extremos de la consola, haz clic en el extremo que creaste y, luego, selecciona Anular la implementación del modelo en el extremo:

delete_endpoint

Para borrar la imagen del contenedor, navega a Artifact Registry, selecciona el repositorio que creaste y elige Borrar.

delete_image

Para borrar el bucket de almacenamiento, en el menú de navegación de la consola de Cloud, navega a Almacenamiento, selecciona tu bucket y haz clic en Borrar:

Borrar almacenamiento