¿Cómo crear un Clasificador de texto Python como Cloud Function?

Lectura previa: https://onesaitplatform.atlassian.net/wiki/spaces/DOC/pages/2283668527

 

En este post vamos a ver un ejemplo de cómo migrar una aplicación Flask a una Cloud Function.

Se trata de un clasificador con una serie de modelos pre-entrenados que expone un endpoint REST para predecir una clasificación en base a un texto.

Estructura del código

La estructura del código es la siguiente:

El main está contenido en el fichero application.py, donde se levanta la aplicación flask y se declara el endpoint “/predict”.

#!/usr/bin/env python # coding: utf-8 # # Imports from flask import Flask, request, Response import numpy as np import pandas as pd import pickle import json import base64 # # Carga de modelos from model import load_model, predict primario_model, primario_vectorizer, primario_encoder = load_model() # Instanciamos una aplicación flask app = Flask(__name__) # Definimos las rutas donde vamos a trabajar APP_PATH = app.root_path @app.route('/predict', methods=['POST']) def predict_post(): if request.method == 'POST': result = {} request_data = request.data.decode('latin1') request_data = json.loads(request_data, strict=False) content_str = request_data.get('text', '') base64_content = request_data.get('base64', False) if base64_content: content_str = base64.b64decode(content_str) if content_str: result = predict(content_str, primario_model, primario_vectorizer, primario_encoder) return Response( json.dumps(result), mimetype='application/json') # Función principal que arranca el servicio if __name__ == '__main__': app.run(host='localhost', port=8000, debug = False)

Este será el código que tendremos que adaptar para poder desplegar la aplicación Flask como una función. El resto de pickles y componentes de la aplicación irán empaquetados en la función, por lo que serán accesibles por el main sin necesidad de modificar nada.

Adaptación del main

Siguiendo la documentación oficial del repositorio adaptaremos la clase principal para poder desplegarla como función.

Tendremos que crear una nueva clase “func.py” (el nombre no importa, pero más adelante lo tendremos que anotar en el Dockerfile).

Esta clase debería ser idéntica a “application.py” con la salvedad de que no usaremos Flask, y por tanto esas dependencias las podremos quitar. En su lugar, leeremos los datos de entrada a través de un método “handler” que expone la API FDK de python.

def handler(ctx, data: io.BytesIO = None):

En las últimas versiones de la API FDK, data pasa a ser un array de bytes en vez de un string, por lo que aumenta la flexibilidad para decodificar contenido de entrada (p.e. codificaciones no UTF-8).

En este método irá el contenido del método predict_post() original, con las adaptaciones para leer los datos de la variable “data”, y devolviendo una respuesta con la API del FDK en vez de la de Flask.

# # Imports import io import json import logging import numpy as np import pandas as pd import pickle import base64 from fdk import response # # Carga de modelos from model import load_model, predict primario_model, primario_vectorizer, primario_encoder = load_model() def handler(ctx, data: io.BytesIO = None): result = {} byte_str = data.read() request_data = byte_str.decode('latin1') request_data = json.loads(request_data, strict=False) content_str = request_data.get('text', '') base64_content = request_data.get('base64', False) if base64_content: content_str = base64.b64decode(content_str) if content_str: result = predict(content_str, primario_model, primario_vectorizer, primario_encoder) return response.Response(ctx, response_data=json.dumps(result),headers={"Content-Type": "application/json"})

Yaml de la function

El siguiente paso será crear un descriptor Yaml para que lo procese Fn a la hora de generar y desplegar la función.

En este yaml además deberemos definir el endpoint que queramos para poder invocar la función.

En función de la memoria requerida (estimada) podemos querer cambiar el límite (“limit”) de “memory”.

Dockerfile de la function

Por último necesitaremos definir un Dockerfile para desplegar a la “function”. Este Dockerfile lo da de forma genérica la documentación de Fn.

Podremos modificar el Dockerfile genérico para elegir la versión de Python que queramos además de poder instalar dependencias o librerías custom.

En este caso hemos utilizado la versión 3.7, que era compatible con las versiones declaradas de las dependencias.

Como podemos ver, la instalaciones de dependencias Python, las coge del fichero requirements.txt, por lo que es importante que este fichero tenga la dependencia del fdk.

También se puede ver que la única línea extra añadida al Dockerfile genérico es la que está comentada:

Esto es debido a que se necesita instalar esta librería en primer lugar antes que el resto.

Crear la function en plataforma

Repositorio Git

Para poder crear la función y desplegarla en plataforma, necesitaremos subir el código a un repositorio Git.

Crear la aplicación

Con el código Git subido, crearemos la aplicación serverless en plataforma (revisar la guía anterior para estos pasos).

Crear la function

Por último, crearemos la función indicando el nombre y el path al “func.yaml”, teniendo en cuenta que la estructura del repositorio es la siguiente:

Desplegar la function

Una vez definida la función dentro de la aplicación, la desplegaremos.

Testear la function

Por último, probaremos la función desplegada a través del endpoint definido:

La primera vez que se invoque la función, tardará bastante como en la captura anterior (4,52 segundos), pero las subsecuentes llamadas serán muy rápidas: