Automatizando el ciclo de vida de analytics con Opensource y MLOps

El concepto de MLOps está tomando cada vez más tracción en el ámbito empresarial. Esta práctica intenta agilizar la creación de modelos analíticos, facilitar el iterar su desarrollo y gobernar el número creciente de estos.

Este post intenta ser un tutorial de cómo montar una pequeña plataforma de desarrollo de modelos analíticos, en la que se busca automatizar la generación de los modelos para poder tener una trazabilidad entre el modelo, el código y los datos que se han usado para entrenarlo, utilizando componentes existentes en las arquitecturas tradicionales de
desarrollo de software.

La plataforma tendrá un entorno de desarrollo y un entorno de integración/entrega continua.

Entorno de desarrollo

Lo primero que tenemos que hacer es construir el entorno de desarrollo de los modelos con un flujo de trabajo básico con los siguientes requisitos:

  1. El Data Scientist desarrollará los modelos y explorará los datos sobre una herramienta del tipo notebook.
  2. Cada ejecución de entrenamiento se registrará el modelo y los resultados para poder comparar entre entrenamientos y registrar los modelos de prueba.
  3. El código generado para el entrenamiento y exploración de los modelos se irá versionando en una herramienta de gestión de versiones de código.

Con este flujo, necesitaremos las siguientes herramientas:

  • Jupyter para explorar los datos y entrenar los modelos analíticos.
  • Mlflow será la herramienta que utilizaremos para tener trazabilidad automática del entrenamiento de modelos.
  • Para el versionado del código, utilizaremos un servidor Git. en este caso se utilizará Github.

Para montar el entorno, seguiremos los pasos descritos a continuación. En este caso se pueden ejecutar sobre una distribución de Linux o sobre Windows con WSL:

Instalar dependencias:

Lo primero será instalar una distribución Python. En este caso concreto utilizaremos conda, ya que es portable a los distintos sistemas operativos:

https://conda.io/docs/user-guide/install/index.html#

Una vez tengamos el entorno python, instalamos MLflow:

pip install mlflow

Proyecto de prueba:

Para poder probar el entorno que vamos a montar, utilizaremos un proyecto de prueba:

git clone https://github.com/anllogui/python_mlops.git
  • nb: notebooks de entrenamiento
  • flaskr: aplicación donde ejecutaremos el modelo (no cubierta en este tutorial)
  • data: datos de entrenamiento
  • models: donde se guardarán los modelos de manera temporal
  • tests: directorio para guardar los tests de la aplicación

A tener en cuenta de este proyecto de ejemplo:

  • La buena práctica es tener separados los proyectos de entrenamiento de modelos y de ejecución de modelos. En este caso los tengo juntos para facilitar demos.
  • Existen estructuras predefinidas para organizar los proyectos de entrenamiento. Aquí tenéis un ejemplo.

Configuración del entorno

Una vez descargado el proyecto, toca configurar el entorno. Para ello, ejecutamos los siguientes comandos:

conda env create -f environment.yml
conda activate pythonCI

El primer comando creará un entorno de python con las librerías necesarias para ejecutar el notebook (como si fuese una nueva instalación de python). El segundo activará el entorno para que cualquier comando que ejecutemos a partir de aquí utilice este entorno.

Entrenamiento del modelo

Ahora vamos a entrenar el modelo en local. para ello ejecutaremos Jupyter (entorno de desarrollo para python) y ejecutaremos el notebook de entrenamiento desde éste:

  • Ejecutamos: jupyter notebook.
  • Vamos a “nb/Simple_Regression.ipynb”.
  • Ejecutamos el notebook.

Como hemos visto antes, para registrar el entrenamiento, vamos a utilizar Mlflow. Para ello simplemente hay que instalar la librería necesaria y añadir en el código de nuestro proyecto las líneas para almacenar los datos que queramos (variables, resultados, modelo, etc.). Aquí tienes un pequeño ejemplo para empezar a utilizarlo en tu código.

Para revisar la ejecución hemos de ir a la interfaz de Mlflow. Para ello ejecutaremos el siguiente comando en el mismo directorio donde hemos ejecutado Jupyter:

mlflow ui

Esto nos lanzará la aplicación y podremos acceder vía navegador con la url http://127.0.0.1:5000

Entorno de integración / entrega contínua

A la hora de generar el modelo candidato de ser promocionado a producción, montaremos un sistema para que no haya ninguna acción manual. De esa manera aseguraremos la trazabilidad entre el código utilizado, los datos utilizados para entrenarlo y el artefacto generado (en este caso el modelo). Para ello, implementaremos el siguiente flujo:

  1. El código (en este caso un ejemplo sencillo de regresión lineal para predecir salarios según experiencia) se descargará del repositorio de versiones y se ejecutará el notebook de manera desatendida.
  2. Se registrará la ejecución del entrenamiento en un repositorio distinto al de desarrollo, de tal manera que solo se registrará los entrenamientos de modelos que son candidatos a ser productivizados. De esta manera el registro será más limpio y controlado.
  3. 1. Una vez generado el modelo, se almacenará en un repositorio de artefactos para que posteriormente sea descargado para ser embebido en una aplicación y desplegado en producción.

Para poder implementar este flujo, necesitaremos las siguientes herramientas:

  • Papermill para poder lanzar desde Jenkins los notebook de manera desatendida y parametrizada.
  • MLflow para registrar los entrenamientos.
  • Nexus como almacén de artefactos donde guardaremos la versión del modelo.
  • Jenkins como herramienta orquestadora del flujo de trabajo.

Papermill

Como herramienta de integración contínua, utilizaremos Jenkins para orquestar los pasos a seguir y Papermill para lanzar de manera parametrizada el notebook. En la documentación del proyecto se explica fácilmente cómo hacerlo. En nuestro caso parametrizaremos la versión de los datos a utilizar y la versión del modelo generado:

Papermill

Para probar que todo está configurado correctamente, basta con lanzar la siguiente línea de comandos:

papermill Simple_Regression.ipynb output.ipynb -p data_ver 1 -p model_ver 1

Este comando utilizará el fichero de datos SalaryData1.csv y generará el modelo linear_regression_model_v1.pkl.

Para que estos parámetros no vayan embebidos en el código, en este caso utilizaremos el fichero build.properties. De esta manera, tenemos más flexibilidad a la hora de reutilizar el mismo código para entrar distintas versiones de modelos con distintos sets de datos.

MlFlow

Para registrar los entrenamientos en un servidor centralizado, arrancaremos Mlflow en modo servidor:

mlflow server

Esto arrancará mlflow en modo servidor y

Nexus

Para poder guardar de manera versionada los modelos que generemos, utilizaremos Nexus, una herramienta de versionado de artefactos ampliamente utilizada en el mundo de desarrollo de software.

Jenkins

Una vez instalado Jenkins y arrancado (aquí tenéis cómo hacerlo en Ubuntu) configuramos una tarea para lanzar la ejecución desatendida. Las acciones que realizaremos serán las siguientes:

  • Bajar el código del repositorio git.
  • Configurar las variables de entorno, entre ellas la url de nuestro servidor MLFlow de producción.
  • Obtener los parámetros de entrenamiento (versión de datos y de modelo) que habremos guardado en el fichero build.properties.
  • Entrenar el modelo:

    - Crearemos el entorno de python desde el fichero environment.yml y lo Activaremos.

    - Entrenaremos el modelo ejecutando el notebook parametrizado.

    - Guardaremos el modelo generado y versionado en Nexus.

Para crear esta tarea realizaremos los siguientes pasos:

  • Entraremos en Jenkins y en el menú lateral seleccionaremos “Nueva tarea”
  • Crearemos “proyecto estilo libre” y lo llamaremos python_mlops
  • En la ventana de creación y configuración de la tarea:

- Marcaremos que es un proyecto Github y meteremos la url

- Configuraremos el origen del código fuente con los datos del repositorio de código

- En “Ejecutar”, añadiremos un paso del tipo “Ejecutar línea de comandos (shell)” y añadiremos el siguiente código, sustituyendo “” por la ruta donde hayamos instalado la distribución de anaconda:

#!/bin/bash
echo "---- SETING ENVS ---- "



export PATH=$PATH:<PATH_INSTALACION_CONDA>/anaconda3/bin

PYENV_HOME=$WORKSPACE/venv/
export LC_ALL=es_ES.utf-8

export LANG=es_ES.utf-8

export MLFLOW_TRACKING_URI="http://127.0.0.1:5000"

echo "---- GETING PROPERTIES ----"

file="./build.properties"

if [ -f "$file" ]
then
  
echo "$file found."

while IFS='=' read -r key value
  
do
    key=$(echo $key | tr '.' '_')
    
eval ${key}=\${value}
  
done < "$file"

echo "Model Version = " ${model_version}
  
echo "Data Version  = " ${data_version}

else
  
echo "$file not found."

fi


echo "---- CLEANING ENVIRONMENT ----"

if [ -d $PYENV_HOME ]; then

    echo "- Project exists: cleanning.."
    
    rm -Rf $PYENV_HOME 

fi

source <PATH_INSTALACION_CONDA>/anaconda3/etc/profile.d/conda.sh

echo "*** creating env ***"

echo $PYENV_HOME
conda env create -f environment.yml --prefix $PYENV_HOME

conda activate pythonCI

cd nb
 
papermill Simple_Regression.ipynb output.ipynb -p data_ver ${data_version} 
-p model_ver ${model_version}


ls -la ../models

curl -v -u admin:admin -X POST 
'http://localhost:8081/service/rest/v1/components?repository=maven-releases' -F "maven2.groupId=models" 
-F "maven2.artifactId=simple_regresion" -F "maven2.version=${data_version}.${model_version}" 
-F "maven2.asset1=../models/linear_regression_model_v${model_version}.pkl" 
-F "maven2.asset1.extension=pkl"

 

Esto nos entrenará el modelo registrando los parámetros que hemos definido:

 

Y también nos versionará el modelo en el repositorio de artefactos:

En este caso hemos utilizado la siguiente nomenclatura para almacenar los modelos:

nombre_de_modelo.version_dato.version_modelo

A partir de aquí el modelo quedará disponible para poder ser embebido en una aplicación.

Conclusiones

Hemos montado una arquitectura básica que nos da las siguientes características:

  • Automatizamos el registro de los experimentos y sus resultados en una herramienta que nos permitirá realizar búsquedas entre estos para, por ejemplo, encontrar la ejecución más óptima.
  • Tenemos versionado el código de nuestros notebooks, de tal manera que cualquier persona podrá replicar los experimentos antiguos en caso necesario.
  • El entrenamiento de los modelos candidatos de ser desplegados en producción se realiza sin intervención humana, lo que asegura una correcta trazabilidad entre el dato, el código de entrenamiento y el modelo generado, facilitando las auditorías y la resolución de errores de estos.

Siguiendo estos mismos patrones,este tipo de plataforma se puede implementar con otras tecnologías o en cloud, facilitando su adopción en arquitecturas ya existentes empresariales y permitiendo el escalado de su uso.

En este ejemplo cubrimos una parte del ciclo de vida del desarrollo de modelos analíticos, en concreto el entrenamiento, pero existen otras fases como la exploración y ejecución de los modelos que no están cubiertas. En everis hemos desarrollado un marco de trabajo y una arquitectura de referencia empresarial para cubrir el ciclo de vida completo en grandes compañías. Si te gusta este ámbito, no dudes en contactar con nosotros!.