Course
Convertir modelos de aprendizaje automático en APIs en Python
Considera la siguiente situación:
Has construido un modelo de aprendizaje automático superguay que puede predecir si una transacción concreta es fraudulenta o no. Ahora, un amigo tuyo está desarrollando una aplicación android para actividades bancarias en general y quiere integrar tu modelo de aprendizaje automático en su aplicación para su superobjetivo.
Pero tu amigo descubrió que, tú has codificado tu modelo en Python mientras que tu amigo está construyendo su aplicación en Java. ¿Y? ¿No será posible integrar tu modelo de aprendizaje automático en la aplicación de tu amigo?
Afortunadamente, tienes el poder de las API. Y la situación anterior es una de las muchas en las que la necesidad de convertir tus modelos de aprendizaje automático en APIs es extremadamente importante. Muchas de las industrias buscan ahora Científicos de Datos que puedan hacer esto. Ahora bien, envolver un modelo de aprendizaje automático en una API no es muy difícil, y eso es precisamente lo que harás en este tutorial: convertir tu modelo de aprendizaje automático en una API.
Opciones para implementar modelos de Aprendizaje Automático
La mayoría de las veces, el uso real de tu modelo de aprendizaje automático se encuentra en el corazón de un producto inteligente, que puede ser un pequeño componente de un sistema de recomendación o un chat-bot inteligente. Estos son los momentos en que las barreras parecen muy difíciles de superar.
Por ejemplo, la mayoría de los profesionales del ML utilizan R/Python para sus experimentos. Pero los consumidores de esos modelos ML serían ingenieros de software que utilizan una pila tecnológica completamente distinta. Hay dos formas de resolver este problema:
- Reescribiendo todo el código en el lenguaje en el que trabajan los ingenieros de software. Lo anterior parece una buena idea, pero el tiempo y la energía necesarios para conseguir replicar esos intrincados modelos sería un auténtico despilfarro. La mayoría de lenguajes, como JavaScript, no disponen de grandes bibliotecas para realizar ML. Sería prudente mantenerse alejado de ella.
- Enfoque "primero la API" - Las API de la Web han facilitado el buen funcionamiento de las aplicaciones multilingües. Si un desarrollador frontend necesita utilizar tu Modelo ML para crear una aplicación web potenciada por ML, sólo tendría que obtener el Punto Final de la URL desde donde se sirve la API.
Ahora, antes de seguir adelante, estudiemos qué es realmente una API.
¿Qué son las API?
"En palabras sencillas, una API es un contrato (hipotético) entre dos programas informáticos que dice que si el programa informático del usuario proporciona datos en un formato predefinido, el segundo ampliará su funcionalidad y proporcionará el resultado al programa informático del usuario" - Analytics Vidhya
Puedes leer los siguientes artículos para entender por qué las API son una opción popular entre los desarrolladores:
En esencia, las API son muy parecidas a las aplicaciones web, pero en lugar de ofrecerte una página HTML con un bonito estilo, las API suelen devolver datos en un formato estándar de intercambio de datos, como JSON, XML, etc. Una vez que un desarrollador tiene el resultado deseado, puede darle el estilo que quiera. También hay muchas API de ML populares, por ejemplo: la API de ML de IBM Watson, que es capaz de lo siguiente:
- Traducción automática - Ayuda a traducir textos en diferentes pares de idiomas.
- Resonancia del mensaje - Para averiguar la popularidad de una frase o palabra entre un público predeterminado.
- Preguntas y respuestas - Este servicio proporciona respuestas directas a las consultas que se plantean a partir de fuentes documentales primarias.
- Modelización de usuarios - Para hacer predicciones sobre las características sociales de alguien a partir de un texto dado.
La API de Visión de Google también es un ejemplo excelente que proporciona servicios dedicados a tareas de Visión por Computador. Haz clic aquí para hacerte una idea de lo que se puede hacer utilizando Google Vision API.
Básicamente, lo que ocurre es que la mayoría de los proveedores de la nube y las empresas más pequeñas centradas en el aprendizaje automático proporcionan API listas para usar. Atienden las necesidades de los desarrolladores/empresas que no tienen experiencia en ML, que quieren implementar ML en sus procesos o conjuntos de productos.
Ejemplos populares de API de aprendizaje automático adaptadas explícitamente para el desarrollo web son DialogFlow, Cognitive Toolkit de Microsoft, TensorFlow.js, etc.
Ahora que ya tienes una idea de lo que son las API, veamos cómo puedes envolver un modelo de aprendizaje automático (desarrollado en Python) en una API en Python.
Domina tus habilidades de datos con DataCamp
Más de 10 millones de personas aprenden Python, R, SQL y otras habilidades tecnológicas con nuestros cursos prácticos elaborados por expertos del sector.
Flask - Un Framework de Servicios Web en Python:
Ahora, puede que pienses ¿qué es un servicio web? El servicio web es una forma de API únicamente que supone que una API está alojada en un servidor y puede ser consumida. API Web, Servicio Web: estos términos suelen utilizarse indistintamente.
En cuanto a Flask, es un marco de desarrollo de servicios web en Python. No es el único en Python, también hay otros como Django, Falcon, Hug, etc. Pero para este tutorial utilizarás Flask. Para aprender sobre Flask, puedes consultar estos tutoriales.
Si has descargado la distribución de Anaconda, ya tienes Flask instalado. Si no, tendrás que instalarlo tú mismo con:
pip install flask
Flask es muy mínimo. Flask es el favorito de los desarrolladores de Python por muchas razones. El framework Flask viene con un servidor web ligero incorporado que necesita una configuración mínima, y se puede controlar desde tu código Python. Ésta es una de las razones por las que es tan popular.
El código siguiente demuestra la minimalidad de Flask de una forma agradable. El código se utiliza para crear una sencilla Web-API que al recibir una determinada URL produce una salida específica.
from flask import Flask
app = Flask(__name__)
@app.route("")
def hello():
return "Welcome to machine learning model APIs!"
if __name__ == '__main__':
app.run(debug=True)
Una vez ejecutado, puedes navegar hasta la dirección web (introduce la dirección en un navegador web), que se muestra en el terminal, y observar el resultado.
Algunos puntos:
-
Los Cuadernos Jupyter son geniales para cualquier cosa relacionada con las marcas, R y Python. Pero cuando se trata de construir un servidor web, puede mostrar un comportamiento incoherente. Por tanto, es una buena idea escribir los códigos de Flask en un editor de texto como Sublime y ejecutar el código desde el terminal/símbolo de comandos.
-
Asegúrate de no nombrar el archivo como flask.py.
-
Flask se ejecuta por defecto en el puerto número 5000. A veces, el servidor Flask se inicia en este número de puerto con éxito, pero cuando pulsas la URL (que los servidores devuelven en el terminal) en un navegador web o en cualquier API-cliente como Postman, puede que no obtengas la salida. Considera la siguiente situación:
-
Según Flask, su servidor se ha iniciado correctamente en el puerto 5000, pero cuando se disparó la URL en el navegador, no devolvió nada. Por tanto, éste puede ser un posible caso de conflicto de número de puerto. En este caso, cambiar el puerto por defecto 5000 por el número de puerto que desees sería una buena opción. Puedes hacerlo simplemente haciendo lo siguiente:
app.run(debug=True,port=12345)
-
En ese caso, el servidor Flask tendría un aspecto similar al siguiente:
Ahora, vamos a ver paso a paso el código que has escrito:
-
Has creado una instancia de la clase
Flask
y le has pasado la variable "nombre" (que rellena el propio Python). Esta variable será "main", si este archivo se está ejecutando directamente a través de Python como un script. Si en cambio importaste el archivo, el valor de "nombre" sería el nombre del archivo que importaste. Por ejemplo, si tuvierastest.py
yrun.py
, e importaras test.py en ejecutar.py el valor de "nombre" de test.py será test (app = Flask(test)
). -
Sobre la definición del método
hello()
, aparece @app.route("").route()
es un decorador que indica a Flask qué URL debe activar la función definida comohello()
. -
El método
hello()
se encarga de producir una salida (¡Bienvenido a las API de modelos de aprendizaje automático!) cada vez que tu API es golpeada (o consumida) correctamente. En este caso, si se accede a un navegador web conlocalhost:5000/
, se obtendrá el resultado deseado (siempre que el servidor flask se ejecute en el puerto 5000).
Ahora estudiarás algunos de los factores que deberás tener en cuenta si vas a convertir tus modelos de aprendizaje automático (construidos con scikit-learn) en una API de Flask.
Modelos Scikit-learn con Flask
Crear modelos de aprendizaje automático de muy sencillos a muy complejos nunca ha sido tan fácil en Python con scikit-learn
. Pero hay algunos puntos que tendrás que recordar sobre scikit-learn:
- Scikit-learn es una biblioteca de Python que proporciona herramientas sencillas y eficaces para la minería y el análisis de datos. Scikit-learn tiene los siguientes módulos principales:
- Agrupación
- Regresión
- Clasificación
- Reducción de la dimensionalidad
- Selección del modelo
- Preprocesamiento
(No dejes de consultar el curso de Aprendizaje Supervisado con scikit-learn de DataCamp, impartido por el desarrollador principal de scikit-learn, Andreas Müller).
- Scikit-learn proporciona el soporte de serialización y de-serialización de los modelos que entrenas utilizando scikit-learn. Esto te ahorra el tiempo de volver a entrenar un modelo. Con una copia serializada de tu modelo hecha con scikit-learn puedes escribir una API de Flask.
- Los modelos Scikit-learn requieren que los datos estén en formato numérico. Por eso, si el conjunto de datos contiene características categóricas que no son numéricas, es importante convertirlas en numéricas. Para esta transformación, scikit-learn proporciona utilidades como
LabelEncoder
,OneHotEncoder
, etc. Puedes encontrarlos en el módulosklearn.preprocessing
. - Los modelos Scikit-learn no pueden manejar implícitamente los valores perdidos. Tienes que manejar tú mismo los valores perdidos de tu conjunto de datos, y luego puedes introducirlos en tu modelo. Para tratar los valores perdidos, scikit-learn proporciona una amplia gama de utilidades que puedes encontrar en el módulo
sklearn.preprocessing
.
La codificación de etiquetas y los valores perdidos son pasos importantes del preprocesamiento de datos, muy esenciales para construir un buen modelo de aprendizaje automático. Si quieres aprender más sobre esto, no dejes de consultar el siguiente curso ofrecido por DataCamp:
Para este tutorial, utilizarás el conjunto de datos Titanic, que es uno de los conjuntos de datos más populares por muchas razones, como por ejemplo: el conjunto de datos contiene una gran variedad de tipos de variables, y el conjunto de datos contiene valores perdidos, etc. Este tutorial de DataCamp cubre un excelente análisis del conjunto de datos, y el conjunto de datos puede descargarse desde aquí.
Este conjunto de datos aborda un problema de clasificación consistente en predecir si un pasajero sobrevivirá o no, dada cierta información sobre él.
Nota: Variables y Funciones estos términos se utilizan indistintamente en muchas ocasiones en este tutorial.
Para simplificar aún más las cosas, sólo utilizarás cuatro variables: age
, sex
, embarked
, y survived
donde survived
es la etiqueta de clase.
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
"Sexo" y "Embarcado" son características categóricas con valores no numéricos y por eso requieren algunas transformaciones numéricas. La característica "Edad" tiene valores perdidos. Estos valores pueden imputarse con una estadística de resumen, como la mediana o la media. Los valores perdidos pueden ser bastante significativos, y merece la pena investigar qué representan en las aplicaciones del mundo real.
Scikit-learn trata los valores de las celdas que no contienen nada como NaNs. Aquí te limitarás a sustituir los NaN por 0, y para ello escribirás una función de ayuda.
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
Las líneas de código anteriores hacen lo siguiente
- Recorre todas las columnas del marco de datos
df
y añade las columnas (con valores no numéricos) a una listacategorical
. -
Si las columnas no tienen valores no numéricos (que en este caso es sólo
Age
), comprueba si tiene valores omitidos o no y rellénalos con 0.Rellenar los NaN con un único valor puede tener consecuencias no deseadas, sobre todo si la cantidad con la que sustituyes los NaN está dentro del intervalo observado para la variable numérica. Como el cero no es un valor de edad observado y legítimo, no estás introduciendo un sesgo, ¡lo habrías hecho si hubieras utilizado, por ejemplo, 36! - Fuente
Ahora que has tratado los valores que faltaban y has separado las columnas no numéricas, estás preparado para convertirlas en numéricas. Lo harás utilizando la Codificación en caliente. Pandas proporciona un método sencillo get_dummies()
para crear variables OHE para un marco de datos dado.
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
Cuando utilizas OHE, se crea una nueva columna para cada combinación columna/valor, en formato columna_valor. Por ejemplo: para la variable "Embarcado", OHE producirá "Embarcado_C", "Embarcado_Q", "Embarcado_S" y "Embarcado_nan".
Ahora que has preprocesado con éxito tu conjunto de datos, estás listo para entrenar el modelo de aprendizaje automático. Para ello utilizarás un clasificador de Regresión Logística.
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
Has construido tu modelo de aprendizaje automático. Ahora guardarás este modelo. Técnicamente hablando, serializarás este modelo. En Python, a esto se le llama Pickling.
Guardar el modelo: Serialización y deserialización
Para ello utilizarás la página joblib
de sklearn.
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
['model.pkl']
Ahora se mantiene el modelo de Regresión Logística. Puedes cargar este modelo en memoria con una sola línea de código. Cargar el modelo de nuevo en tu espacio de trabajo se conoce como Deserialización.
lr = joblib.load('model.pkl')
Ya estás listo para utilizar Flask para servir tu modelo persistente. Ya has visto lo minimalista que es Flask para empezar.
Crear una API a partir de un modelo de aprendizaje automático con Flask
Para servir tu modelo con Flask, harás las dos cosas siguientes:
- Carga en memoria el modelo ya persistido cuando se inicia la aplicación,
- Crea un punto final de la API que tome variables de entrada, las transforme al formato adecuado y devuelva predicciones.
Más concretamente, tu ejemplo de entrada a la API tendrá el siguiente aspecto:
[
{"Age": 85, "Sex": "male", "Embarked": "S"},
{"Age": 24, "Sex": '"female"', "Embarked": "C"},
{"Age": 3, "Sex": "male", "Embarked": "C"},
{"Age": 21, "Sex": "male", "Embarked": "S"}
]
(que es un JSON list
de entradas)
y tu API dará un resultado como el siguiente
{"prediction": [0, 1, 1, 0]}
Las predicciones denotan los estados de supervivencia, donde 0 representa No y 1 representa Sí.
JSON significa JavaScript Object Notation, y es uno de los formatos de intercambio de datos más utilizados. Si necesitas una introducción rápida, sigue estos tutoriales.
Escribamos una función predict()
que lo haga:
- Carga el modelo persistente en memoria cuando se inicia la aplicación,
- Crea un punto final de la API que tome variables de entrada, las transforme al formato adecuado y devuelva predicciones.
Ya has visto cómo cargar un modelo persistente. Ahora, te centrarás en cómo puedes utilizarlo para predecir el estado de supervivencia al recibir entradas.
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
json_ = request.json
query_df = pd.DataFrame(json_)
query = pd.get_dummies(query_df)
prediction = lr.predict(query)
return jsonify({'prediction': list(prediction)})
¡Fantástico! Pero aquí tienes un pequeño problema.
La función que has escrito sólo funcionaría en condiciones en las que la solicitud entrante contuviera todos los valores posibles para las variables categóricas, lo que puede ocurrir o no en tiempo real. Si la solicitud entrante no incluye todos los valores posibles de las variables categóricas, entonces, según la definición actual del método predict()
, get_dummies()
generaría un marco de datos que tiene menos columnas de las que el clasificador exceptúa, lo que provocaría un error de ejecución.
Para resolver este problema, persistirás en la lista de columnas también durante el entrenamiento del modelo. Puedes serializar cualquier objeto Python en un archivo .pkl. Utilizarás joblib
del mismo modo que antes.
(Ten en cuenta que, como se ha dicho antes, siempre es mejor hacer toda la codificación a nivel de servidor en un editor de texto y luego ejecutarla desde un terminal)
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
['model_columns.pkl']
Como ya has persistido en la lista de columnas, sólo tienes que tratar los valores perdidos en el momento de la predicción. Tendrás que cargar columnas modelo cuando se inicie la aplicación.
@app.route('/predict', methods=['POST']) # Your API endpoint URL would consist /predict
def predict():
if lr:
try:
json_ = request.json
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': prediction})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
Has incluido todos los elementos necesarios en la API "/predict", y ahora sólo tienes que escribir la clase principal.
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line argument
except:
port = 12345 # If you don't provide any port then the port will be set to 12345
lr = joblib.load(model_file_name) # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load(model_columns_file_name) # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
Tu API ya está lista para ser alojada. Pero antes de seguir adelante, recapitulemos todo lo que has hecho hasta ahora:
Ponerlo todo junto:
- Has cargado el conjunto de datos Titanic y has seleccionado las cuatro características.
- Hiciste el preprocesamiento de datos necesario.
- Has construido un clasificador de Regresión Logística y lo has serializado.
- También serializaste todas las columnas del entrenamiento, ya que una solución al número menor de columnas de lo esperado es persistir en la lista de columnas del entrenamiento.
- A continuación, escribiste una API sencilla utilizando Flask que predeciría si una persona había sobrevivido en el naufragio dada su edad, sexo e información de embarque.
Pongamos todo el código en un solo lugar para que no te pierdas nada. Además, es una buena práctica de programación si separas el código de tu modelo de Regresión Logística y el código de tu API de Flask en archivos .py
distintos.
Así que tu model.py
debería tener el siguiente aspecto:
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
# Data Preprocessing
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
# Logistic Regression classifier
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
# Save your model
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
print("Model dumped!")
# Load the model that you just saved
lr = joblib.load('model.pkl')
# Saving the data columns from training
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
print("Models columns dumped!")
Tu api.py
debe tener el siguiente aspecto:
# Dependencies
from flask import Flask, request, jsonify
from sklearn.externals import joblib
import traceback
import pandas as pd
import numpy as np
# Your API definition
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
if lr:
try:
json_ = request.json
print(json_)
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': str(prediction)})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line input
except:
port = 12345 # If you don't provide any port the port will be set to 12345
lr = joblib.load("model.pkl") # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load("model_columns.pkl") # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
¡Muy bonito! Ahora probarás esta API en un cliente de API llamado Postman. Sólo asegúrate de que model.py
y api.py
están en el mismo directorio y asegúrate también de que has compilado ambos antes de probarlos. Consulta la siguiente instantánea del terminal, tomada después de que ambos archivos .py
se hayan compilado correctamente.
Si todos tus archivos se compilaron correctamente, la siguiente debería ser la estructura de directorios:
Nota: Sin embargo, el archivo IPYNB es opcional.
Probar tu API en Postman
Para probar tu API, necesitarás algún tipo de cliente API. El cartero es sin duda uno de los mejores que existen. Puedes descargar fácilmente Postman desde el enlace anterior.
La interfaz de Postman tiene el siguiente aspecto si has descargado la última versión:
Una vez que hayas iniciado correctamente el servidor Flask, tendrás que introducir la URL correcta con el número de puerto correcto en Postman. Debe tener un aspecto similar al siguiente:
¡Enhorabuena! Acabas de construir tu primera API de aprendizaje automático.
Tu API puede predecir si un pasajero sobrevivió al naufragio del Titanic dada su información en age
, sex
y embarked
. Ahora, tu amigo puede llamarla desde su código front-end y procesar la salida de la API en algo fascinante.
Llevarlo más lejos:
En este tutorial, has cubierto una de las habilidades más demandadas por la industria de un Científico de Datos full-stack, es decir, la construcción de una API a partir de un modelo de aprendizaje automático. Aunque la API es sencilla, siempre es mejor empezar por lo más simple para conocer los detalles.
Puedes hacer mucho más para mejorar esto. Posibles opciones que puedes considerar:
- Escribe una API "/entrenar" que entrene un clasificador de Regresión Logística con los datos.
- Codifica un modelo de Red Neuronal utilizando
keras
y construye una API a partir de él. - Aloja tu API en la Nube para que pueda ser consumida.
- Para llevar las cosas a niveles más avanzados, puedes consultar este blog Machine Learning Mastery, en el que se analizan varios enfoques clasificados por sectores.
Las posibilidades y oportunidades son enormes. Sólo tienes que seleccionar cuidadosamente los más adecuados para ti.
Si quieres aprender más sobre el Aprendizaje Automático en Python, sigue el curso de DataCamp Preprocesamiento para el Aprendizaje Automático en Python y consulta nuestro tutorial Fundamentos del Aprendizaje Automático - Las Normas.
Referencias:
Las siguientes son algunas referencias que se tomaron al escribir este blog:
Más información sobre Python y el Aprendizaje Automático
Course
Feature Engineering for Machine Learning in Python
Course
Predicting CTR with Machine Learning in Python
blog
8 modelos de machine learning explicados en 20 minutos
tutorial
Aprendizaje automático de datos categóricos con el tutorial de Python
tutorial
Construir un transformador con PyTorch
tutorial
Tutorial de FastAPI: Introducción al uso de FastAPI
tutorial
Ajuste fino de GPT-3 mediante la API OpenAI y Python
tutorial