Cómo poner en producción un modelo de machine learning de R

Share on linkedin
Share on twitter
Share on email

Crear un algoritmo de machine learning está muy bien, pero… ¿qué pasa después? ¿Cómo se puede poner un modelo (en este caso de R) en producción?

En este blog ya hemos visto cómo automatizar scripts en Google Cloud o cómo automaitzar scripts en Windows o Mac. Si bien algunas veces una automatización puede ser suficiente para poner algo en producción, otras veces necesitamos que otros servicios usen ese algoritmo.

Para ello se suelen utilizar las APIs. Como ya expliqué en este webinar en la Universidad de Deusto, una API es una forma de comunicar entre dos servicios informáticos.

Imagina, por ejemplo, que tenemos un algoritmo de clasificación de flores en base a distinas variables. Los usuarios tienen un formulario en la web que, introduciendo unas variables, te dice el tipo de planta que es. La idea es que, cuando un usuario en la web envíe un formulario, los datos del formulario entren como input a nuestro modelo y este devuelva la predicción correspondiente. Esa predicción, se recogerá y mostrará en la página.

Suena interesante, ¿verdad? Pues, ¡veámos cómo podemos poner un modelo de R en producción!

Cómo crear una API en R con Plumber

Entendiendo conceptos básicos de una API

Antes de entrar y ver cómo crear una API, es importante primero entender algunos conceptos fundamentales de una API. Si viste el webinar de cómo trabajar con APIs en R o, ya sabes sobre APIs, puedes saltarte este apartado.

Como hemos dicho antes, una API permite que dos servicios informáticos se comuniquen. Un servicio manda una orden al otro. Si la orden se ha mandado correctamente, este segundo servicio ejecuta una acción y devuelve un resultado.

Ahora bien, ¿qué tipos de acciones o métodos se pueden hacer? Pues principalmente existen las siguientes:

  • POST: permite crear o mandar datos.
  • GET: permite leer o recibir datos.
  • PUT: permite actualizar datos existentes.
  • DELETE: permite eliminar datos.

En nuestro caso, al tendremos que mandar datos a nuestro algoritmo para que este prediga, por lo que usaremos el método POST.

Por otro lado, una API puede tener varios endpoints, es decir, distintos puntos de contacto con un servicio. Por ejemplo, esta API de Football (link) te puede ofrecer desde las temporada (link) pero también información de los equipos (link).

Cada uno de esos endpoints tienen URLs diferentes y puede (o no) que requieran de parámetros diferentes.

En nuestro caso, crearemos una API con un único endpoint, ya que aprendiendo a crear un endpoint, sabrás crear todo.

Por último, lo más probable es que una API tenga algunos parámetros obligatorios y otros optativos. Como su nombre indica, los parámetros obligatorios son eso, obligatorios: si no pasamos los parámetros obligatorios, la API nos devolverá un error.

En nuestro caso, los parámetros obligatorios serán los datos del modelo: el alto y ancho del pétalo y el alto y ancho del sépalo.

Los parámetros optativos o filtros, en cambio, suelen servir para hacer nuestra petición más sencilla. Por ejemplo, en el endpoint de ligas de la API de fútbol, los parámetros optativos nos permiten filtrar los datos por un equipo, un país o incluso un año para obtener los datos de las ligas que nos interesen.

Parametros de la API de Ligas de Football
Parametros de la API de Ligas de Football

Ahora que sabes qué es una API, veámos cómo crear la API para poner nuestro modelo de machine learning en producción en R.

Poner un modelo de R en producción.

Ejemplo de modelo de R a subir a producción

Antes de poner un modelo en producción y para que el ejemplo se asemeje lo máximo posible a la realidad, vamos a entrenar un modelo. Será un modelo de clasificación simple, basado en el clásico dataset iris.

Para ello, creo y entreno un modelo de clasificación con RandomForest.

model = randomForest(Species ~ ., data = iris)
model

Call:
 randomForest(formula = Species ~ ., data = iris) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 2

        OOB estimate of  error rate: 4.67%
Confusion matrix:
           setosa versicolor virginica class.error
setosa         50          0         0        0.00
versicolor      0         47         3        0.06
virginica       0          4        46        0.08

Por último, tenemos que guardar el modelo, de tal forma que cuando vayamos a predecir simplemente tengamos que cargar el archivo y no haya que entrenar nada. Esto hará que el proceso sea mucho más rápido y homogéneo.

save(model, file = "API/model.RData")

Ya tenemos nuestro modelo entrenado y guardado. Pero, ¿cómo podemos poner este modelo de R en producción? ¡Veámoslo!

Cómo crear una API con Plumber

Lo primero de todo es convertir nuestro modelo en una API. Para ello usaremos el paquete Plumber. ¿Cómo se crea una API? Pues, simplemente tienes que añadir comentarios embellecidos, como los siguientes, a un script:

#* @apiTitle Titulo de la API
#* @param paramtero_prueba 
#* @get /nombre_del_endpoint

Lo primero de todo, el @apiTitle es el nombre de nuestra API. Por otro lado, con la línea @param podemos pasar parámetros a nuestra API. En este caso solo hay un único parámetro, llamado parámetro prueba, pero podríamos añadir más.

Por último, tenemos el método de nuestra API y el nombre del endpoint. Como ves @get indica que usaremos el método GET, mientras que /nombre_del_endpoint se refiere al nombre del endpoint.

Aunque con estos parámetros bastarían, podríamos configurar aun más nuestra API. Por ejemplo, con la siguiente línea podríamos incluir un filtro opcional:

#* @filter filter_example

Una vez tenemos esto, simplemente tenemos que crear una función que use esos parámetros de entrada como argumentos de la función y los use para devolver un objeto.

Veámos cómo podemos crear la API en el caso de nuestro modelo de clasificación:

library(plumber)
library(randomForest)
#* @apiTitle API de clasificacion de flores
#* @param petal_length Longitud del Petalo 
#* @param petal_width Ancho del Petalo 
#* @param sepal_length Longitud del Sepalo 
#* @param sepal_width Ancho del Sepalo 
#* @post /clasificador

function(petal_length, petal_width, sepal_length, sepal_width){

  load("model.RData")

  test = c(sepal_length, sepal_width, petal_length, petal_width)
  test = sapply(test, as.numeric)
  test = data.frame(matrix(test, ncol = 4))

  colnames(test) = colnames(iris[,1:4])
  predict(model, test)

}

Cómo comprobar que la API funciona

Si ejecutamos este código en un archivo a parte, R nos abrirá una venta con una página que nos ayudará a realizar nuestra petición a la API que hemos creado, como podemos ver en esta imagen:

Ayuda en la petición a la API que hemos creado
Ayuda en la petición a la API que hemos creado

Ahora, si hacemos clic en ‘Try it out’, cambiará la descripción por unos cajones donde deberemos meter los valores correspondientes. Una vez rellenados esos campos, podemos hacer clic en ‘Execute’. Así, se hará una petición con los parámetros que hemos incluido a nuestra API.

Si la solicitud es correcta, tendremos una respuesta 200 y nos devolverá el tipo de flor que se trate:

Respuesta de la API creada con Plumber para poner en producción un modelo de machine learning de R
Respuesta de la API creada con Plumber

¡Ya tenemos nuestra API funcionando! Ahora solo falta poder subir esta API a Cloud y… ¡ya habremos aprendido a poner cualquier modelo de R en producción!

Hostear una API de R en Google Cloud

Configuraciones en Google Cloud

Antes de subir nuestra API a Google Cloud, AWS o cualquier otro sitio, debemos crear una imagen docker que contenga esta API. Aunque ya lo expliqué en este post más en detalle, una imagen docker permite poder ejecutar el código que tenga dentro en cualquier servidor con docker.

Esto es algo muy interesante, ya que así no hace falta que ese servidor tenga R y los paquetes que usamos instalados, ya que simplemente se instalará al ejecutar el propio docker.

La subida de la API a Google Cloud lo haremos desde R. Para ello, previamente deberemos:

  1. Haber creado una cuenta de servicio.
  2. Darle los permisos de: desarrollador de Cloud Functions, Administrador de Compute, Editor del proyecto, Usuario de cuenta de servicio, Administrador de Cloud Run y Administrador de objetos de Storage.
  3. Descargar el account key en formato JSON.
  4. Crear un bucket de Google Cloud. Puedes hacerlo desde esta url

Si no sabes cómo seguir estos pasos, puedes echarle un vistazo a este post, donde lo expliqué detenidamente.

Una vez los hayamos hecho, tendremos que crear las variables de entorno en nuestra sesión de R. Es importante que las variables las definamos antes de cargar la librería, sino, no funcionará.

project = "abcde-abcde-123456"
region = "europe-west1"
account_key = "clave_prueba.json"
bucket = "afexamplebucket"

Sys.setenv(
  GCE_AUTH_FILE = account_key,
  GCE_DEFAULT_PROJECT_ID = project,
  CR_REGION = region,
  GCS_DEFAULT_BUCKET = bucket,
  PORT = 8080
)


library(googleCloudRunner)

De esta forma, la librería googleCloudRunner ya tiene acceso a nuestra cuentra de servicio. Ahora, debemos crear el dockerfile para subirlo a nuestra cuenta de Google.

Ahora, vamos a crear nuestra imagen docker!

Montar nuestra API en Docker

Para crear una imagen docker, podemos usar el paquete containerit. Sin embargo, aunque nos basemos en containerit, necesitaremos hacer unas modificaciones al dockerfile que genere.

Por eso, en mi caso, he creado el dockerfile yo mismo. Veréis que es muy sencillo. Al fin y al cabo, no son más que secuencias que debe ir ejecutando.

FROM trestletech/plumber

# Install random forest
RUN R -e 'install.packages(c("randomForest"))'

# Copy model and script
RUN mkdir /data
COPY model.RData /data
COPY api.R /data
WORKDIR /data

# Plumb & run server
EXPOSE 8080
ENTRYPOINT ["R", "-e", \
    "pr <- plumber::plumb('/data/api.R'); pr$run(host='0.0.0.0', port=8080)"]

Por último, vamos a usar tanto el archivo de R como el dockerfile para publicar nuestra API. Para ello, primero vamos a cambiar el directorio a la carpeta que hemos creado. De esta forma nos aseguramos que solo los archivos api.R y Dockerfile se suban al bucket.

setwd(paste0(getwd(),"/API"))
cr <- cr_deploy_plumber(getwd())

Básicamente, este código sube los archivos al bucket que hayamos elegido. De ahí, monta la imagen docker en Container Registry. Por último, despliega la imagen en Cloud Run.

Puede ser que el paso anterior os dé algún error al montar la imagen docker. Si ese es el caso, deberéis ir a Container Registry y publicar la imagen desde allí, como en la imagen.

Cómo poner un modelo de R en producción.  Cómo desplegar una imagen Docker desde Container Registry
Cómo desplegar una imagen Docker desde Container Registry

Una vez hecho os redigirá a Cloud Run. En caso de que todo haya sido correcto, os aparecerá un punto verde y podréis ver la URL de vuestra API, como a continuación.

Cómo poner un modelo de R en producción. Comprobación de despliege correcto de la API en Cloud Run
Comprobación de despliege correcto de la API en Cloud Run

¡Con esto, ya tendríamos nuestra API publicada! En mi caso, puedo acceder a la API en esta URL: https://api-rk6gh2l6da-uc.a.run.app. Así, ya tenemos nuestro modelo de machine learning hecho en R “expuesto” para que cualquier otro servicio que lo necesita obtenga una predicción mediante una llamada a la API que hemos creado.

Comprobar que el modelo de R se ha puesto en producción correctamente

Por último, vamos a comprobar que nuestra API funciona correctamente y nos devuelve la predicción de nuestro algoritmo. Para ello, vamos a hacer una petición POST a la API, a ver qué nos devuelve:

library(httr)
library(jsonlite)

url = "https://api-rk6gh2l6da-ew.a.run.app/clasificador?sepal_width=2&sepal_length=2&petal_width=1&petal_length=3"

response = POST(url)

fromJSON(content(response, type = "text", encoding = "utf-8"))
[1] "versicolor"

¡Funciona! Hemos subido nuestro modelo de Random Forest hecho en R a Google Cloud y vemos como si le pasamos los parámetros adecuados, funciona correctamente!

Ahora, solo quedaría ver en funcionamiento en un caso real… ¡Veámoslo!

Ejemplo de cómo funcina un modelo de machine learning en R en producción

Para demostrar un ejemplo de cómo funcionaría nuestro modelo de R en producción, he creado un pequeño formulario en HTML. Este formulario tiene 4 campos, donde se debe indicar el ancho y largo tanto del pétalo como del sépalo. Así pues, Una vez se envía el formulario, se comprueba que los campos se han rellenado correctamente y, si es así, se hace una petición POST a la API que hemos creado.

Además, mostramos la respuesta que nos devuelve en una API mediante un alert, indicando así el tipo de flor que corresponde a esos tamaños que hemos indicado.

Ancho del Pétalo:
Largo del Pétalo:
Ancho del Sépalo:
Largo del Sépalo:

¡Y eso que esto es solo un modelo muy sencillo con un ejemplo muy sencillo! Pero, ¿te imaginas el potencial de poder poner tu modelo de machine learning en producción? Y es que, si no ponemos nuestros modelos (en este caso de R), en producción… ¿para qué nos sirven más allá de obtener un análisis en nuestro ordenador?

En definitiva, espero que te haya resultado interesante aprender a poner en producción un modelo de R. Para cualquier sugerencia, no dudes en escribirme por Linkedin o respondiendo el formulario de la página. De hecho, si he escrito este post es porque me lo pidió un lector! Así que ya sabes, cualquier cos, no dudes en contactar. ¡Nos vemos en la próxima!

¡No te pierdas ningún post!

Si te gusta lo que lees... suscríbete para estar al día de los contenidos que subo.
¡Serás el primero en enterarte!