Dale vida a tu IA
Ya tienes tu modelo, probado, funciona bastante bien y está listo para entrar en la acción. Entonces, ¿cómo lo desplegamos? Si es una solución que quieres ofrecer al público desde la nube, puedes implementar tu propio servicio online y ofrecer soluciones de Machine Learning!
Veamos cómo hacerlo!
Implementar modelos de Machine Learning
Muchas veces el modelo creado por el equipo de Machine Learning, será una “pieza más” de un sistema mayor, como por ejemplo una app, un chatbot, algún sistema de marketing, un sistema de monitoreo de seguridad. Y si bien el modelo puede correr por lo general en Python o R, es probable que interactúe con otro stack distinto de desarrollo. Por ejemplo, una app Android en Java o Kotlin, algún sistema PHP, en la nube ó hasta podría ser aplicaciones de escritorio o CRM . Entonces, nuestro modelo deberá ser capaz de interactuar y “servir” a los pedidos de esas otras herramientas.
Como opciones, podríamos reescribir nuestro código en otro lenguaje (javascript, java, c…) ó si nuestro proceso ejecutara batch, podría ser una “tarea cron” del sistema operativo que ejecute automáticamente cada X tiempo y deje por ejemplo un archivo de salida CSV (ó entradas en una base de datos).
Pero, si nuestro modelo tiene que servir a otros sistemas en tiempo real y no podemos reescribirlo (incluso por temas de mantenimiento futuro, actualización ó imposibilidad de recrear módulos completos de Python) podemos desplegar nuestro modelo en una API accesible desde un servidor (que podría ser público ó privado) y mediante una clave secreta -si hiciera falta-.
Servir mediante una API
Una API es la manera más popular que hay para ofrecer servicios online en la actualidad. Sin meterme en profundidad en el tema, podemos decir que lo que hacemos es publicar un punto de entrada desde donde los usuarios (clientes, apps, u otras máquinas) harán peticiones de consulta, inserción, actualización o borrado de los datos a los que tienen acceso. En nuestro caso, lo típico será ofrecer un servicio de Machine Learning de predicción ó clasificación (por nombrar alguno). Entonces nos llegarán en la petición GET ó POST las entradas que tendrá el modelo (nuestras features, ó lo que normalmente son las “columnas” de un csv que usamos para entrenar). Y nuestra salida podría ser según el caso, el resultado de la predicción, ó una probabilidad, ó un número (por ej. “cantidad de ventas pronosticadas para ese día”).
Para crear una API, podemos utilizar diversas infraestructuras ya existentes en el mercado que ofrecen Google, Amazon, Microsoft (u otros) ó podemos “levantar” nuestro propio servicio con Flask. Flask es un web framework en Python que simplifica la manera de publicar nuestra propia API (Hay otros como Django, Falcon y más).
Instalar Flask
Veamos rápidamente como instalar y dejar montado Flask.
- Instalar Anaconda en el servidor ó en nuestra máquina local para desarrollo. (Para servidores también puedes usar la versión de mini-conda)
- Prueba ejecutar el comando “conda” en el terminal para verificar que esté todo ok.
- Crear un nuevo environment en el que trabajaremos
conda create --name mi_ambiente python=3.6
- Activa el ambiente creado con
source activate mi_ambiente
- Instalar los paquetes Python que utilizaremos:
pip install flask gunicorn
Hagamos un “Hello world” con Flask. Crea un archivo de texto nuevo llamado “mi_server.py“
1 2 3 4 5 6 7 8 9 10 11 |
"""Creando un servidor Flask """ from flask import Flask app = Flask(__name__) @app.route('/users/<string:nombre>') def hello_world(nombre=None): return("Hola {}!".format(nombre)) |
Guarda el archivo y escribe en la terminal:
1 |
gunicorn --bind 0.0.0.0:8000 mi_server:app |
una vez iniciado, verás algo así:
Entonces abre tu navegador web favorito y entra en la ruta http://localhost:8000/users/juan
Con eso ya tenemos nuestro servidor ejecutando. En breve haremos cambios para poder servir nuestro modelo de Machine Learning desde Flask al mundo 🙂
Crear el modelo de ML
Hagamos un ejemplo de un modelo de ML basándonos en el ejercicio de Pronóstico de Series Temporales que hace un pronóstico de ventas con redes neuronales con Embeddings. Esta vez no usaremos una notebook de Jupyter, si no, archivos de “texto plano” Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler from utiles import * df = pd.read_csv('time_series.csv', parse_dates=[0], header=None,index_col=0, names=['fecha','unidades']) df['weekday']=[x.weekday() for x in df.index] df['month']=[x.month for x in df.index] print(df.head()) EPOCHS=40 PASOS=7 scaler = MinMaxScaler(feature_range=(-1, 1)) reframed = transformar(df, scaler) reordenado=reframed[ ['weekday','month','var1(t-7)','var1(t-6)','var1(t-5)','var1(t-4)','var1(t-3)','var1(t-2)','var1(t-1)','var1(t)'] ] reordenado.dropna(inplace=True) training_data = reordenado.drop('var1(t)', axis=1) target_data=reordenado['var1(t)'] cant = len(df.index) valid_data = training_data[cant-30:cant] valid_target=target_data[cant-30:cant] training_data = training_data[0:cant] target_data=target_data[0:cant] print(training_data.shape, target_data.shape, valid_data.shape, valid_target.shape) print(training_data.head()) model = crear_modeloEmbeddings() continuas = training_data[['var1(t-7)','var1(t-6)','var1(t-5)','var1(t-4)','var1(t-3)','var1(t-2)','var1(t-1)']] valid_continuas = valid_data[['var1(t-7)','var1(t-6)','var1(t-5)','var1(t-4)','var1(t-3)','var1(t-2)','var1(t-1)']] history = model.fit([training_data['weekday'],training_data['month'],continuas], target_data, epochs=EPOCHS, validation_data=([valid_data['weekday'],valid_data['month'],valid_continuas],valid_target)) results = model.predict([valid_data['weekday'],valid_data['month'],valid_continuas]) print( 'Resultados escalados',results ) inverted = scaler.inverse_transform(results) print( 'Resultados',inverted ) |
Ya logramos entrenar un nuevo modelo del que estamos conformes. Ahora veamos como guardarlo para poder reutilizarlo en la API!
Guardar el modelo; Serialización de objetos en Python
El proceso de serialización consiste en poder transformar nuestro modelo en ceros y unos que puedan ser almacenados en un archivo y que luego, al momento de la carga vuelva a regenerar ese mismo objeto, con sus características.
Aunque existen diversas maneras de guardar los modelos, comentemos rápidamente las que usaremos:
- Pickle de Python para almacenar objetos (en nuestro caso un Transformador que debemos mantener para “reconvertir los resultados escalados” al finalizar de entrenar)
- h5py para el modelo Keras (podemos guardar el modelo completo ó los pesos asociados a la red)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import pickle #definimos funciones de guardar y cargar def save_object(filename, object): with open(''+filename, 'wb') as file: pickle.dump(object, file) def load_object(filename): with open(''+filename ,'rb') as f: loaded = pickle.load(f) return loaded # guardamos los objetos que necesitaremos mas tarde save_object('scaler_time_series.pkl', scaler) model.save_weights("pesos.h5") # cargamos cuando haga falta loaded_scaler = load_object('scaler_time_series.pkl') loaded_model = crear_modeloEmbeddings() loaded_model.load_weights("pesos.h5") |
Podemos comprobar a ver las predicciones sobre el set de validación antes y después de guardar los objetos y veremos que da los mismos resultados.
Crear una API con Flask
Ahora veamos el código con el que crearemos la API y donde incorporaremos nuestro modelo.
Utilizaremos los siguientes archivos:
- server.py – El servidor Flask
- test_api.py – Ejemplo de request POST para probar la API
- utiles.py – las funciones comunes al proyecto
- api_train_model.py – entreno y creación del modelo, una red neuronal con Embeddings (del ejercicio de TimeSeries).
- time_series.csv – archivo con datos para el ejercicio
Vamos a la acción:
- Crearemos un método inicial que será invocado desde la url “predict”
- Cargaremos el modelo que entrenamos previamente
- Responderemos peticiones en formato JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
"""Filename: server.py """ import pandas as pd from sklearn.externals import joblib from flask import Flask, jsonify, request from utiles import * app = Flask(__name__) @app.route('/predict', methods=['POST']) def predict(): """API request """ try: req_json = request.get_json() input = pd.read_json(req_json, orient='records') except Exception as e: raise e if input.empty: return(bad_request()) else: #Load the saved model print("Cargar el modelo...") loaded_model = crear_modeloEmbeddings() loaded_model.load_weights("pesos.h5") print("Hacer Pronosticos") continuas = input[['var1(t-7)','var1(t-6)','var1(t-5)','var1(t-4)','var1(t-3)','var1(t-2)','var1(t-1)']] predictions = loaded_model.predict([input['weekday'], input['month'], continuas]) print("Transformando datos") loaded_scaler = load_object('scaler_time_series.pkl') inverted = loaded_scaler.inverse_transform(predictions) inverted = inverted.astype('int32') final_predictions = pd.DataFrame(inverted) final_predictions.columns = ['ventas'] print("Enviar respuesta") responses = jsonify(predictions=final_predictions.to_json(orient="records")) responses.status_code = 200 print("Fin de Peticion") return (responses) |
Muy bien, podemos ejecutar nuestra API desde la terminal para testear con:
1 |
gunicorn --bind 0.0.0.0:8000 server:app |
Y ahora hagamos una petición para probar nuestra API con un archivo Python y veamos la salida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import json import requests import pandas as pd import pickle from utiles import * """Setting the headers to send and accept json responses """ header = {'Content-Type': 'application/json', \ 'Accept': 'application/json'} # creamos un dataset de pruebas df = pd.DataFrame({"unidades": [289,288,260,240,290,255,270,300], "weekday": [5,0,1,2,3,4,5,0], "month": [4,4,4,4,4,4,4,4]}) loaded_scaler = load_object('scaler_time_series.pkl') reframed = transformar(df, loaded_scaler) reordenado=reframed[ ['weekday','month','var1(t-7)','var1(t-6)','var1(t-5)','var1(t-4)','var1(t-3)','var1(t-2)','var1(t-1)'] ] reordenado.dropna(inplace=True) """Converting Pandas Dataframe to json """ data = reordenado.to_json(orient='records') print('JSON para enviar en POST', data) """POST <url>/predict """ resp = requests.post("http://localhost:8000/predict", \ data = json.dumps(data),\ headers= header) print('status',resp.status_code) """The final response we get is as follows: """ print('Respuesta de Servidor') print(resp.json()) |
Este ejemplo nos devuelve de salida:
1 |
{'predictions': '[{"ventas":194}]'} |
Actualizar el modelo (según sea necesario!)
No olvidar que si nuestro modelo es dependiente del tiempo, ó de datos históricos, ó datos nuevos que vamos recopilando (nuevas muestras) deberemos reentrenar el modelo.
Eso se podría automatizar, re-ejecutando el archivo de entrenamiento y sobreescribiendo el archivo “h5py” que habíamos generado antes cada X días ó a raíz de otro evento que en nuestro negocio sea significativo y sea el detonador del re-entreno.
Conclusiones
En este artículo vimos que nuestro modelo de ML puede llegar a ser una pequeña pieza de un puzzle mayor y puede ofrecer soluciones a usuarios finales ó a otros subsistemas. Para poder ofrecer sus servicios podemos contar con diversas soluciones, siendo una de ellas el despliegue de una API. Podemos crear una API fácil y rápidamente con el web framework de Flask. Ya puedes ofrecer tus modelos al mundo!
NOTAS finales: Recuerda ejecutar los archivos en el siguiente orden:
- Copia el archivo timeseries.csv con los datos del ejercicio en tu server.
- Entrena el modelo y crea los archivos necesarios con
python api_train_model.py
- Inicia el server desde una terminal con gunicorn como vimos anteriormente.
- Ejecuta el archivo de pruebas desde otra terminal con
python test_api.py
Déjame tus comentarios y cuéntame qué proyectos Machine Learning te traes entre mano! Saludos.
Unete al Blog!
Recibe los próximos artículos sobre Machine Learning, estrategias, teoría y código Python en tu casilla de correo!
NOTA: algunos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente a tus contactos para evitar problemas. Gracias!
Recursos Adicionales
Descarga los archivos creados en este artículo
Otros artículos relacionados en Inglés
Hola Juan,
Como estamos? Estoy haciendo mis cosillas con python con anaconda desde windows y quería publicar mis proyectos como servicio web en un apache en ubuntu y he visto tu post.
Es correcto que con flask y unicorn puedes publicar un proyecto python a un puerto y una ruta de tu dominio?
Gracias por la ayuda!
David
Hola David, sí, claro, lo puedes desplegar en apache, te dejo un enlace: Flask configuring Apache y en esa misma web tienes la doc oficial de flask
Perfecto, miraré a ver que tal… otra pregunta… para hacer NLP es decir leer textos y aplicar deep learning… mi objetivo es crear un robot que hable y para eso necesito que aprenda de libros de textos en diferentes idiomas… y en base a lo aprendido que tenga la capacidad de hablar y razonar… mi otro proyecto, más realizable seguramente, es un reconocimiento de imagen que detecte lo que hay en una imagen y devuelva las etiquetas. Ambos proyectos son posibles con python y flask?
Hola David, si. Con independencia del modelo que utilices, lo podrás ofrecer mediante una API. De hecho la mayoría de apps que hacen reconocimiento de imágenes complejos (cómo faceapp) realizan los cálculos en server mediante apis
Hola Juan,
Y la API para qué sirve? Como se define una API?
Gracias,
David
Una api es una interface de comunicación entre tu servicio (en este caso de ejemplo, “pronóstico de ventas con ML”) y cualquier otro “cliente” interesado. Se utiliza como una manera estándar de brindar servicios. Mediante la API se realiza un “contrato” ente las partes para que puedan consultar los servicios que se ofrecen y participar.
Pero hay una representacion global visual de toda la api? O simplemente son un conjunto de servicios publicados?
Aqui he hecho mi primer proyecto python con flask http://davidmartinezros.com:8000/extract_files
http://davidmartinezros.com:8000/test
http://davidmartinezros.com:8000/training
Hola mil gracias por tu aporte muy buen articulo
Gracias por comentar! Saludos!
excelente artículo, felicidades
Gracias por visitar el blog! Saludos
Hola Juan: He estado trabajando en un clasificador de imágenes sísmicas basado y adaptado de la cnn de tu blog que clasifica deportes. Este un proyecto de titulación a nivel licenciatura. Me encantaría poder compartirlo contigo y referenciar el gran trabajo que haces en el tema de Machine Learning. Al igual que poder intercambiar retroalimentación del mismo. Excelente día, es un gran gusto seguir leyendo tu blog. ¡Saludos desde México!
Hola Ana, te he contestado al email. Gracias por escribir, tienes el permiso de usar el material de este blog y te agradecería que aparezca mi nombre y la url del blog. Te felicito por tu trabajo en geología! Saludos y seguimos en contacto
Estimado Juan, cuál es la diferencia entre “from” e “import”. “From” importa un modulo/paquete e “import” un sub-paquete/modulo del mismo? No logro encontrar una explicación clara en internet.
Desde ya, muchísimas gracias!
Saludos
Hola Mauro, si, es así como lo estás indicando. Con import te puedes traer toda una librería entera pero usando from seleccionar sólo algunas de sus funcionalidades.
Saludos!
hola algún ejemplo/tutorial/ link de crear una api con flask pero que clasifique imagen ? ya he entrenado el modelo y estoy probando llevarlo a producción
Hola Juan!. muy bueno el articulo. La verdad todas las entradas son excelentes. No se si me puedas referir un ejemplo en el que el modelo se ejecute batch y guarde un csv con el resultado de la predicción. Es decir, cómo puedo desplegar el modelo para que le ingresen los nuevos datos (csv) y lo exporte (csv) a una ubicación determinada..
Muchas gracias y te mando un saludo
Hola César, para escribir a csv puedes usar el mismo dataframe de pandas. Haces df.to_csv(“nombre.csv”) y listo.
Para reentrenar el modelo puedes tener una tarea por ejemplo 1 vez al mes que tome el archivo csv actualizado y hacer fit() con todo el dato y publicar ese nuevo modelo en el servicio (reemplazando el anterior).
Otra opción es que si tienes un modelo que acepte reentrenos -por ej. redes neuronales- ó modelos que tengan el método refit() ó partial_fit() sólo le deberías pasar los registros nuevos.
Saludos!
Hola Juan Ignacio. Una consulta, vengo ya hace algun tiempo trabajando en proyectos personales en machine learning y Tensorflow, de hecho tu blog ha sido de gran ayuda para avanzar en muchos temas, pero a proposito de una etrada que hiciste sobre las APIs, quisiera aprender a desarrollar aplicaciones para explotar modelos de machine learning y si puedo deployar en flask, pareciera ser lo mas razonable aprender entonces a crear aplicaciones en flask. Me refiero a que como no se programar en ningun framework ni en HTML ni nada, pareciera ser que flask es la opcion porque me mantendria en entorno python y flask provee la api para deployar, pero no lo se realmente.
Por otra parte, dictas cursos o clases particulares sobre el tema?, gracias….
Hola Rubén, me parece buena idea avanzar con Flask para proyectos pequeños. Para la parte html usaría algún framework que tenga el diseño ya resulto, como por ejemplo Bootstrap u otro similar.
Si fuera un proyecto más grande habría que ir a un servidor en la nube con mayor infraestructura, tienes de Aws, g.cloud ó Azure.
No estoy dando clases particulares, pero estoy pensando en dar algún curso.
Mientras tanto os ofrezco el libro 😉
Saludos,
Hola Juan, realmente tus explicaciones, y demás lecturas sobre ML, son realmente geniales, estoy diseñando un dataSet para poderlo analizar con Keras y TensorFlow, su estructura inicial es (Fecha Venta, Tienda, Producto, Categoría, Unidades, Temperatura, Presión, Radiación Solar) con este DataSet (sobre unos 1.200.000 registros), se podría programar la api para que responda la predicción sobre un elemento que no tenga?, es decir, una vez entrenado podría preguntar en fecha futura, si un producto o una categoría o un día determinado que venta podría esperar. No se si me he explicado con claridad.
Gracias
Hola Jordi, gracias por escribir! Si si claro que lo puedes hacer.
Por lo que dices, tienes muchos registros, lo cual es muy bueno, eso si, ten en cuenta el grado de granularidad con que entrenar al modelo. Si usas minutos (o lo que fuera) y luego tu predicción será en minutos, si usas días, será en días, etc.
Un Saludo!
Genial, muchas gracias, y lo tendré en cuenta, según los deseos de los usuarios y la información que quieran tener.
Estoy probando el servidor, y el test_api.py, y me ha surgido una pregunta, el DtaSet de pruebas que indica? que le pide al modelo? no me queda claro.
Gracias por todo
Estoy probando el servidor, y el test_api.py, y me ha surgido una pregunta, el DataSet de pruebas que indica? que le pide al modelo? no me queda claro.
Gracias por todo
Podría a ver algún inconveniente si lo implemento como un cloud function mi modelo de machine learning?