Curso
Considere a seguinte situação:
Você criou um modelo de machine learning super bacana que prevê se uma determinada transação é fraudulenta ou não. Agora, um amigo seu está desenvolvendo um app Android para atividades bancárias em geral e quer integrar seu modelo ao aplicativo para esse objetivo específico.
Mas seu amigo descobriu que você codificou o modelo em Python, enquanto ele está construindo o app em Java. E aí? Não dá para integrar seu modelo ao aplicativo do seu amigo?
Felizmente, você tem o poder das APIs. E o cenário acima é apenas um entre muitos em que transformar seus modelos de machine learning em APIs é fundamental. Muitas empresas buscam hoje cientistas de dados que saibam fazer isso. Encapar um modelo em uma API não é bicho de sete cabeças — e é exatamente isso que você vai fazer neste tutorial: transformar seu modelo de machine learning em uma API.
Opções para implementar modelos de machine learning
Na maioria das vezes, o valor real do seu modelo está no coração de um produto inteligente — pode ser um componente de um sistema de recomendação ou um chatbot. É nessas horas que as barreiras parecem mais difíceis de vencer.
Por exemplo, a maior parte dos praticantes de ML usa R/Python para seus experimentos. Mas os consumidores desses modelos costumam ser engenheiros de software que trabalham com um stack totalmente diferente. Existem duas formas de resolver esse problema:
- Reescrever todo o código na linguagem usada pelo time de engenharia. Parece uma boa ideia, mas o tempo e a energia para replicar modelos complexos seriam desperdiçados. A maioria das linguagens, como JavaScript, não tem bibliotecas excelentes para ML. Seria mais sensato evitar esse caminho.
- Abordagem API-first — as Web APIs facilitaram a integração entre aplicações escritas em linguagens diferentes. Se um desenvolvedor frontend precisa usar seu modelo para criar um app web com ML, basta ter a URL do endpoint onde a API está sendo servida.
Antes de avançar, vamos entender o que é uma API.
O que são APIs?
"Em palavras simples, uma API é um contrato (hipotético) entre dois softwares dizendo que, se o software cliente fornecer a entrada em um formato predefinido, o outro estenderá sua funcionalidade e devolverá o resultado ao software cliente." - Analytics Vidhya
Leia os artigos abaixo para entender por que APIs são tão populares entre desenvolvedores:
Basicamente, APIs são muito parecidas com aplicativos web, mas, em vez de devolver uma página HTML estilizada, retornam dados em um formato padrão de troca, como JSON, XML etc. Com a saída em mãos, o desenvolvedor estiliza como quiser. Existem muitas APIs de ML conhecidas, por exemplo, a API de ML do IBM Watson, que é capaz de:
- Tradução automática — ajuda a traduzir textos em diferentes pares de idiomas.
- Message Resonance — identifica a popularidade de uma frase ou palavra em um público pré-definido.
- Perguntas e respostas — fornece respostas diretas para consultas baseadas em fontes documentais primárias.
- Modelagem de usuário — prevê características sociais de alguém a partir de um texto.
A Google Vision API também é um excelente exemplo, com serviços dedicados a tarefas de visão computacional. Clique aqui para ter uma ideia do que é possível fazer com a Google Vision API.
Em resumo, a maioria dos provedores de nuvem e empresas focadas em machine learning oferecem APIs prontas para uso. Elas atendem desenvolvedores/negócios sem expertise em ML, mas que querem implementar ML em processos ou produtos.
Exemplos populares de APIs de machine learning voltadas para desenvolvimento web são DialogFlow, Microsoft's Cognitive Toolkit, TensorFlow.js, entre outras.
Agora que você já tem uma boa noção do que são APIs, vamos ver como empacotar um modelo de machine learning (desenvolvido em Python) em uma API usando Python.
Domine suas habilidades em dados com o DataCamp
Mais de 10 milhões de pessoas aprendem Python, R, SQL e outras habilidades tecnológicas usando nossos cursos práticos elaborados por especialistas do setor.

Flask — um framework de serviços web em Python:
Talvez você esteja pensando: o que é um web service? Um web service é um tipo de API hospedada em um servidor e consumível. Web API, Web Service — esses termos costumam ser usados como sinônimos.
Falando do Flask: é um framework em Python para desenvolvimento de serviços web. Não é o único na linguagem — há também Django, Falcon, Hug, etc. Mas neste tutorial você usará Flask. Para aprender mais sobre Flask, confira estes tutoriais.
Se você instalou a distribuição Anaconda, o Flask já vem instalado. Caso contrário, instale com:
pip install flask
O Flask é bem minimalista — por isso é queridinho dos desenvolvedores Python. Ele vem com um servidor web leve embutido, que exige configuração mínima e pode ser controlado a partir do seu código Python. Esse é um dos motivos da sua popularidade.
O código a seguir demonstra bem essa simplicidade. Ele cria uma Web API básica que, ao receber uma URL específica, produz uma saída 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)
Depois de executar, acesse no navegador o endereço exibido no terminal e veja o resultado.

Alguns pontos:
-
Jupyter Notebooks são ótimos para markdown, R e Python. Mas, para subir um servidor web, podem ter comportamentos inconsistentes. Então, é uma boa ideia escrever o código Flask em um editor de texto como o Sublime e rodar pelo terminal/Prompt de Comando.
-
Não dê o nome flask.py ao arquivo.
-
O Flask roda por padrão na porta 5000. Às vezes o servidor inicia nessa porta, mas, ao acessar a URL (informada no terminal) no navegador ou em um cliente de API como o Postman, nada acontece. Considere o seguinte:

-
Segundo o Flask, o servidor foi iniciado na porta 5000, mas ao abrir a URL no navegador nada voltou. Isso pode ser conflito de porta. Nesse caso, mude a porta padrão 5000 para outra de sua preferência, assim:
app.run(debug=True,port=12345) -
O servidor Flask ficará semelhante a isto:

Agora, vamos passar passo a passo pelo código que você escreveu:
-
Você criou uma instância da classe
Flaske passou a variável "name" (preenchida pelo próprio Python). Essa variável será "main" se o arquivo estiver sendo executado diretamente como script. Se o arquivo for importado, o valor de "name" será o nome do arquivo importado. Por exemplo, se você temtest.pyerun.pye importa test.py em run.py, o valor de "name" em test.py será test (app = Flask(test)). -
Acima da definição do método
hello()há@app.route("").route()é um decorator que diz ao Flask qual URL deve acionar a funçãohello(). -
O método
hello()produz uma saída (Welcome to machine learning model APIs!) sempre que sua API é chamada corretamente (consumida). Neste caso, acessarlocalhost:5000/no navegador produzirá a saída (desde que o servidor Flask esteja na porta 5000).
Agora você vai ver alguns pontos importantes ao transformar modelos (treinados com scikit-learn) em uma API Flask.
Modelos scikit-learn com Flask
Criar modelos de machine learning — de simples a complexos — nunca foi tão fácil em Python com o scikit-learn. Mas há alguns pontos a lembrar:
- Scikit-learn é uma biblioteca Python com ferramentas simples e eficientes para mineração e análise de dados. Seus principais módulos incluem:
- Clustering
- Regressão
- Classificação
- Redução de dimensionalidade
- Seleção de modelos
- Pré-processamento
(Não deixe de conferir o curso da DataCamp Supervised Learning with scikit-learn, ministrado por um dos desenvolvedores core do scikit-learn, Andreas Müller)
- O scikit-learn permite serializar e desserializar os modelos treinados. Isso evita ter que retreinar. Com uma cópia serializada do seu modelo, você pode escrever uma API em Flask.
- Modelos do scikit-learn exigem dados numéricos. Se o dataset tiver variáveis categóricas não numéricas, é essencial convertê-las. Para isso, o scikit-learn oferece utilitários como
LabelEncoder,OneHotEncoder, etc., no módulosklearn.preprocessing. - Modelos do scikit-learn não lidam com valores ausentes de forma implícita. Você precisa tratá-los antes de alimentar o modelo. Para isso, há diversas utilidades em
sklearn.preprocessing.
Codificação de rótulos e tratamento de valores ausentes são etapas essenciais de pré-processamento para construir bons modelos. Para saber mais, confira o curso da DataCamp:
Neste tutorial, você usará o dataset Titanic, um dos mais populares por vários motivos — contém diferentes tipos de variáveis e valores ausentes, entre outros. Este tutorial da DataCamp traz uma ótima análise, e você pode baixar o dataset aqui.
Esse dataset trata de um problema de classificação: prever se um passageiro sobreviveu ou não, dados alguns atributos.
Observação: variáveis e features são termos usados de forma intercambiável em vários momentos neste tutorial.
Para simplificar, você usará apenas quatro variáveis: age, sex, embarked e survived, sendo survived o rótulo.
# 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]
"Sex" e "Embarked" são categóricas com valores não numéricos e, por isso, exigem transformação. A variável “Age” tem valores ausentes. Eles podem ser imputados com uma estatística-resumo, como mediana ou média. Valores ausentes podem ser bastante informativos, então vale investigar seu significado no mundo real.
O scikit-learn trata células vazias como NaNs. Aqui, você simplesmente substituirá NaNs por 0 e escreverá uma função auxiliar para isso.
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
O código acima faz o seguinte:
- Itera por todas as colunas do dataframe
df, adicionando as colunas com valores não numéricos a uma listacategorical. -
Se a coluna não tiver valores não numéricos (apenas
Ageneste caso), verifica se há valores ausentes e os preenche com 0.Preencher NaNs com um único valor pode ter efeitos indesejados, especialmente se o valor escolhido estiver dentro da faixa observada para a variável. Como zero não é uma idade válida observada, você não introduz viés — o que aconteceria se usasse, por exemplo, 36! - Fonte
Agora que você tratou os ausentes e separou as colunas não numéricas, pode convertê-las para numéricas usando One-Hot Encoding. O pandas oferece o método get_dummies() para criar variáveis OHE em um dataframe.
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
Ao usar OHE, uma nova coluna é criada para cada combinação coluna=valor, no formato coluna_valor. Por exemplo, para “Embarked”, teremos “Embarked_C”, “Embarked_Q”, “Embarked_S” e “Embarked_nan”.
Com o pré-processamento concluído, você está pronto para treinar o modelo. Usaremos um classificador de regressão 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)
Modelo treinado! Agora vamos salvá-lo. Tecnicamente, vamos serializá-lo. Em Python, isso é chamado de pickling.
Salvando o modelo: serialização e desserialização
Você usará o joblib do sklearn.
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
['model.pkl']
O modelo de regressão logística foi persistido. Você pode carregá-lo com uma única linha. Carregar o modelo de volta ao workspace é a desserialização.
lr = joblib.load('model.pkl')
Agora você está pronto para servir o modelo com Flask. Você já viu como o Flask é minimalista para começar.
Criando uma API a partir de um modelo de machine learning usando Flask
Para servir seu modelo com Flask, você fará duas coisas:
- Carregar o modelo persistido na memória quando a aplicação iniciar;
- Criar um endpoint que receba variáveis de entrada, as transforme no formato adequado e retorne as previsões.
Mais especificamente, sua entrada de exemplo para a API será assim:
[
{"Age": 85, "Sex": "male", "Embarked": "S"},
{"Age": 24, "Sex": '"female"', "Embarked": "C"},
{"Age": 3, "Sex": "male", "Embarked": "C"},
{"Age": 21, "Sex": "male", "Embarked": "S"}
]
(uma lista JSON de entradas)
E a saída da sua API será algo como:
{"prediction": [0, 1, 1, 0]}
As previsões indicam o status de sobrevivência: 0 para Não e 1 para Sim.
JSON significa JavaScript Object Notation e é um dos formatos de intercâmbio de dados mais usados. Se precisar de uma introdução rápida, veja estes tutoriais.
Vamos escrever uma função predict() que fará o seguinte:
- Carregar o modelo persistido na memória quando a aplicação iniciar;
- Criar um endpoint que receba variáveis de entrada, as transforme no formato adequado e retorne previsões.
Você já viu como carregar um modelo persistido. Agora, vamos focar em usá-lo para prever a sobrevivência ao receber 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)})
Perfeito! Mas há um pequeno problema.
Essa função só funcionará se a requisição de entrada trouxer todos os valores possíveis para as variáveis categóricas, o que pode não acontecer no mundo real. Caso a entrada não inclua todos os valores possíveis, o get_dummies() gerará um dataframe com menos colunas do que o classificador espera, causando erro em tempo de execução.
Para resolver, você também vai persistir a lista de colunas durante o treinamento. Qualquer objeto Python pode ser serializado em um .pkl. Vamos usar joblib da mesma forma.
(Lembre-se: como dito antes, é melhor fazer todo o código de servidor em um editor de texto e rodar via terminal)
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
['model_columns.pkl']
Como você já persistiu a lista de colunas, basta tratar os ausentes na hora da previsão. Carregue as colunas do modelo quando a aplicação iniciar.
@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')
Você incluiu tudo o que precisa no endpoint "/predict" e agora só falta escrever a classe 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)
Sua API está pronta para ser hospedada. Antes de seguir, vamos recapitular:
Juntando tudo:
- Você carregou o dataset Titanic e selecionou quatro features.
- Fez o pré-processamento necessário.
- Treinou um classificador de regressão logística e o serializou.
- Também serializou as colunas de treinamento para garantir a compatibilidade na predição.
- Escreveu uma API simples em Flask que prevê se uma pessoa sobreviveria ao naufrágio dada sua idade, sexo e porto de embarque.
Vamos reunir todo o código em um só lugar para você não perder nada. Também é uma boa prática separar o código do modelo de regressão logística e o da API Flask em arquivos .py distintos.
Seu model.py deve ficar assim:
# 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!")
Seu api.py deve ficar assim:
# 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)
Show! Agora vamos testar essa API em um cliente chamado Postman. Só garanta que model.py e api.py estejam no mesmo diretório e que você compilou ambos antes de testar. Veja o snapshot do terminal após a compilação bem-sucedida dos dois arquivos .py.

Se tudo compilou certo, a estrutura de diretórios deve ficar assim: 
Observação: o arquivo IPYNB é opcional.
Testando sua API no Postman
Para testar sua API, você vai precisar de um cliente de API. O Postman é, sem dúvida, um dos melhores. Baixe pelo link acima.
A interface do Postman (na versão mais recente) é assim: 
Com o servidor Flask iniciado, informe no Postman a URL correta com a porta certa. Deve ficar parecido com isto:

Parabéns! Você acabou de construir sua primeira API de machine learning.
Sua API prevê se um passageiro sobreviveu ao naufrágio do Titanic a partir de age, sex e embarked. Agora seu amigo pode chamá-la no frontend e transformar a resposta em algo incrível.
Levando adiante:
Neste tutorial, você viu uma das habilidades mais demandadas no mercado para um Data Scientist full-stack: construir uma API a partir de um modelo de machine learning. Embora simples, é sempre melhor começar pelo básico para dominar o passo a passo.
Você pode ir além e melhorar bastante. Algumas opções:
- Escrever uma API "/train" que treine um classificador de regressão logística com os dados.
- Codar uma rede neural com
kerase criar uma API para ela. - Hospedar sua API na nuvem para consumo externo.
- Para níveis mais avançados, veja este post no Machine Learning Mastery com abordagens maduras de mercado.
As possibilidades são enormes. Basta escolher com cuidado o que faz mais sentido para você.
Se quiser aprender mais sobre Machine Learning em Python, faça o curso da DataCamp Preprocessing for Machine Learning in Python e confira o tutorial Machine Learning Basics - The Norms.
Referências:
As seguintes referências foram usadas na escrita deste blog:


