¿Cómo crear un Clasificador de texto Python como Cloud Function?
Lectura previa: ¿Cómo utilizar el módulo Serverless Manager?
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.
schema_version: 20180708
name: clasificador
version: 0.0.1
runtime: python
memory: 512
triggers:
- name: endpoint
type: http
source: /predict
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.
FROM fnproject/python:3.7-dev as build-stage
WORKDIR /function
ADD requirements.txt /function/
#START LINEA AÑADIDA
RUN pip3 install --force-reinstall numpy==1.19.4
#END LINEA AÑADIDA
RUN pip3 install --target /python/ --no-cache --no-cache-dir -r requirements.txt &&\
rm -fr ~/.cache/pip /tmp* requirements.txt func.yaml Dockerfile .venv &&\
chmod -R o+r /python
ADD . /function/
RUN rm -fr /function/.pip_cache
FROM fnproject/python:3.7
WORKDIR /function
COPY --from=build-stage /python /python
COPY --from=build-stage /function /function
RUN chmod -R o+r /function
ENV PYTHONPATH=/function:/python
ENTRYPOINT ["/python/bin/fdk", "/function/func.py", "handler"]
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.
fdk>=0.1.47También se puede ver que la única línea extra añadida al Dockerfile genérico es la que está comentada:
RUN pip3 install --force-reinstall numpy==1.19.4Esto 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:
curl --silent --location --request POST 'https://development.onesaitplatform.com/fn/t/plenitude-classifier/predict' \
--header 'Content-Type: application/json' \
--data-raw '{ "text": "TASACIÓN DE VIVIENDA EN EDIFICIO Firma electrónica RESUMEN DE TASACIÓN Solicitante: NIF/CIF: Tipo de inmueble: Nº expediente: VIVIENDA EN EDIFICIO ALICANTE ESPAÑA País: Provincia: Municipio: Población: CP: Vía Pública: Nº: Bloque: Escalera: Planta: Puerta: 2 Ent. Mandataria: Finalidad Legal: Garantía hipotecaria de créditos o préstamos que formen o vayan a formar parte de la cartera de cobertura de títulos hipotecarios emitidos por las entidades, promotores y constructores a que se refiere el artículo segundo del Real Decreto 685/1982 de 17 de Marzo, por el que se desarrollan determinados aspectos de la Ley 2/1981, de 25 de marzo, de Regulación del Mercado Hipotecario. Provincia: País: ESPAÑA ALICANTE Domicilio del bien tasado: SUPERFICIES Sup. Const. con Z.C. Sup. const. Sup. Útil Sup. registrada Sup. comprobada Sup. valorada Vivienda m² m² m² m² m² m² m² m² m² Sup. catastral 90,00 m² 90,00 90,00 m² m² 106,55 106,55 106,55 119,00 116,10 116,10 Vivienda TABIMED es miembro de la Asociación Profesional de Sociedades de Valoración de España (A.T.A.S.A.) (*) El alcance de la Certificación AENOR se refiere a la Gestión de Expedientes de Tasación para todo tipo de bienes Página 1 de 3 Valor de tasación (Hipotecario): Valor mínimo a asegurar contra incendios y otros daños del continente CONDICIONANTES Y ADVERTENCIAS ADVERTENCIAS Consideración ligada al elemento Vivienda: A-42 Sobre el bien objeto de este expediente existen afecciones de cargas consistentes en servidumbres de luces y vistas, predio dominate la finca , predio sirviente la finca . Sevidumbre de paso recíproca predio dominante y sirviente las fincas 31.366 y 29.685, que no han sido tenidas en cuenta para la obtención del valor de tasación. Temporalidad: Riesgo: T-3 R-3 Consideración ligada al inmueble completo: A-34A Los linderos observados en la visita al bien no coinciden con los descritos en la Nota Simple aportada, sin embargo no existen dudas sobre la identificación del bien. Por lo que, a los efectos de finalidad hipotecaria deberá realizarse la adecuación de la descripción que figura en la Nota Simple con la realidad existente reflejada en este informe. Temporalidad: Riesgo: T-2 R-2 TABIMED, como asociado de A.T.A.S.A. es miembro de The European Group of Valuers Association (TEGOVA) TASACIÓN DE VIVIENDA EN EDIFICIO Consideración ligada al elemento Vivienda: A04A Se advierte que el bien objeto de estudio presenta una superficie edificada distinta de la que consta como reflejada en la documentación catastral. La diferencia radica en que: 1.- En la Nota Simple se indica una superficie construida de 106,55 m2 que se corresponde con la superficie construida sin comunes. 2.- En la ficha catastral se indica una superficie construida de 119,00 m2 3.- La superficie construida con comunes computable es de 116,10 m2, siendo coincidente la construida sin comunes (106,55 m2) con lo especificado en la Nota Simple. La diferencia se debe, posiblemente, a un error en la asignación catastral de la superficie. Este aspecto (en ausencia de condicionantes relacionados) no influye previsiblemente sobre los valores calculados; no obstante, esta discrepancia debería corregirse o regularse, si ello es posible, con arreglo a la realidad física comprobada en visita. Temporalidad: Riesgo: T-3 R-2 Consideración ligada al inmueble completo: A01A Se advierte que existen discrepancias entre la realidad física del inmueble y sus descripciones registral y catastral en cuanto a la localización, que no inducen a dudar sobre su identificación o características; no obstante, este aspecto debería ser corregido. Las discrepancias se deben a que: 1.- En la Nota Simple que el bien es de tipo B... y se ubica en la calle .... Temporalidad: Riesgo: T-3 R-3 OBSERVACIONES Ver en informe apartado Condicionantes, advertencias y observaciones. Situación de ocupación: Usuario actual (según visita): La propiedad Edad edificación: 10 DESGLOSE DE ELEMENTOS -DESGLOSE DEL VALOR DE TASACIÓN POR ELEMENTOS Elementos Valor de Tasación Seguro de incendios y otros daños del continente V.P.O. Vivienda (Vivienda en edificio vertical) VPO y se aporta documento acreditativo del VML TABIMED está inscrita en el registro especial de Sociedades de Tasación Hipotec. del Ministerio de E. y H. (B. de E. Nº 4320) desde 27-05-86 Página 3 de FINALIDAD LEGAL PARA LA QUE SE SOLICITA EL INFORME Garantía hipotecaria de créditos o préstamos que formen o vayan a formar parte de la cartera de cobertura de títulos hipotecarios emitidos por las entidades, promotores y constructores a que se refiere el artículo segundo del Real Decreto 685/1982 de 17 de Marzo, por el que se desarrollan determinados aspectos de la Ley 2/1981, de 25 de marzo, de Regulación del Mercado Hipotecario. " }'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: