Modelos de Machine Learning, IA e APIs
Você construiu um modelo de machine learning ou uma inteligência artificial (IA) super legal. Agora, um amigo seu está desenvolvendo um aplicativo Flutter e quer integrar seu modelo. Mas seu amigo descobriu que seu modelo é em Python. E agora? Será possível integrar o seu modelo de machine learning na aplicação do seu amigo?
Essa situação não é um caso isolado, mas um evento comum para quem desenvolve modelos de machine learning e IA. Na maioria das vezes, modelos de IA estão no centro de produtos inteligentes. Contudo, eles precisam interagir com outras linguagens de programação que lidam com a interface e a experiência dos usuários. Python pode ser usado para isso, mas nem sempre ele é a melhor escolha. Felizmente, para essas e muitas outras situações, podemos contar com as APIs.
O que é API?
Essencialmente, as APIs (Application Programming Interface) funcionam como aplicações web. Porém, o foco delas está na troca de dados em vez de apresentar informações em um formato HTML amigável. Portanto, em vez de fornecer uma página HTML bem estilizada, as APIs costumam retornar dados em um formato padrão de troca de dados, como JSON, XML, etc.
As APIs da web revolucionaram a maneira como os aplicativos realizam interações entre linguagens. Desenvolvedores front-end, por exemplo, podem acessar um modelo de machine learning por meio de um ponto de extremidade de URL simples. Isso permite que eles integrem recursos de machine learning e IA em web apps sem esforço.
Etapas Gerais para Transformar um Modelo de Machine Learning em API
Para criar uma API a partir de um modelo de IA ou machine learning, as etapas gerais envolvidas podem ser:
- A obtenção de um conjunto de dados.
- A definição de um modelo.
- O treinamento de um modelo.
- A validação do modelo treinado.
- A serialização do modelo.
Escrita de uma API simples.
O modelo que usaremos foi apresentado aqui. Ele recebe quatro atributos para classificar flores Iris em uma de três espécies possíveis. O modelo foi desenvolvido anteriormente para ser usado na forma serializada em um web app com Streamlit. Portanto, utilizaremos o mesmo modelo serializado. Nosso foco neste post será em como transformar esse modelo desenvolvido anteriormente numa API.
Neste post, utilizaremos duas bibliotecas Python: Flask e NumPy.
Flask
Flask é um framework de desenvolvimento de web service em Python. Ele é muito popular por possuir um design minimalista. Entre seus recursos atraentes está um servidor web leve integrado que exige configuração mínima e pode ser gerenciado sem esforço com um código Python. Essa simplicidade inerente é um fator significativo que contribui para a popularidade generalizada de Flask.
Para criar um servidor web com Flask, o codigo é bastante simples (clique aqui para saber mais detalhes). Mostramos um exemplo abaixo.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def predict_api():
return "API com Flask"
if __name__ == "__main__":
app.run(debug=True)
Esse código é executado como um código Python regular. Após ele ser executado, você pode ir até o navegador, colar o endereço http://127.0.0.1:5000 e verificar o resultado.
Por padrão, o Flask é executado na porta número 5000. Porém, mudaremos a porta padrão 5000 para facilitar a inspeção posterior da API em um cliente de API. Portanto, modificaremos a linha 10 anterior da seguinte maneira (linhas 4-5 abaixo):
...
if __name__ == "__main__":
port = 12345
app.run(port=port, debug=True)
Carregamento do Modelo
Flask é facilmente usado para criar uma API com modelos desenvolvidos em Python. Para ilustrar esse processo, mudaremos o codigo apresentado acima. Primeiro, faremos várias importações.
from flask import Flask, jsonify, request
import numpy as np
import pickle
import json
Partindo do modelo desenvolvido anteriormente, criaremos uma função (trecho abaixo) para carregá-lo no código Flask mostrado acima.
# Carrega o modelo
def carrega_modelo():
''' função que carrega o modelo treinado'''
with open('knn_model.pkl', 'rb') as f:
model = pickle.load(f)
return model
app = Flask(__name__)
@app.route("/")
def predict_api():
return "API com Flask"
if __name__ == "__main__":
model = carrega_modelo()
port = 12345
app.run(port=port, debug=True)
O modelo em formato serializado (pickle) precisa estar na mesma pasta do código. A função que carrega o modelo tem que ser chamada assim que o aplicativo for iniciado (linha 15 acima).
Função de Predição
Também precisamos definir uma função para realizar as predições com o modelo. Como o modelo é um classificador, ao receber os recursos de entrada (sepal length, sepal width, petal length, petal width), ele será utilizado para retornar a classe prevista (leia mais sobre a classificação do dataset Iris aqui). A função que realiza isso é bem simples. Ela foi retirada de maneira quase inalterada daqui.
# Cria uma função para realizar predição usando o modelo treinado
def model_predict(sepal_length, sepal_width, petal_length, petal_width):
''' função que realiza predição usando o modelo treinado'''
features = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
prediction = model.predict(features)
# Para converter predição de número para nome
if prediction[0] == 0:
iris_nome = "Iris setosa"
if prediction[0] == 1:
iris_nome = "Iris versicolor"
if prediction[0] == 2:
iris_nome = "Iris virginica"
return iris_nome
Ponto de Extremidade de API
O passo seguinte é criar um ponto de extremidade de API que recebe as variáveis de entrada, as transforma no formato apropriado e retorna previsões. A função é uma modificação da função predict_api mostrada no início do post. Nessa versão, seu método é especificado como POST e ela processa e envia arquivos JSON.
@app.route('/', methods=['POST'])
def predict_api():
"""Ponto de Extremidade da API """
json_file = request.json
prediction = model_predict(json_file["sepal_length"], json_file["sepal_width"], json_file["petal_length"], json_file["petal_width"])
return jsonify({'prediction': prediction})
Especificamente, a entrada dessa função para a API terá a seguinte aparência (os valores numéricos são exemplos ilustrativos):
{
"sepal_length": 2.3,
"sepal_width": 7.0,
"petal_length": 4.1,
"petal_width": 3.2
}
Esses dados estão num formato JSON. JSON significa JavaScript Object Notation, e é um dos formatos de trocas de dados mais utilizados.
A saída da API deve ser nesse formato:
{
"prediction": "Iris versicolor"
}
A saída da API nesse exemplo é uma predição da classe de Iris gerada a partir dos dados ilustrativos mostrados acima.
Esse código contém os elementos mínimos necessários para transformar um modelo de machine learning em API. Evidentemente, o código pode ser expandido com verificações do tipo try/except, condicionais e funções adicionais.
Como testar a API?
Para testar sua API, você precisará de algum tipo de cliente de API. O Postman é, sem dúvida, um dos melhores que existem. Você pode facilmente baixar o Postman a partir desse link. A interface do Postman se parece com a imagem mostrada abaixo:
Quando você iniciar o Postman no seu computador, será preciso indicar o endereço da API (http://127.0.0.1:12345) e preencher os dados para fazer uma solicitação, como mostrado na imagem acima. O método de envio precisa ser POST. Copie a configuração mostrada na imagem e clique Send. Se tudo estiver certo com seu código, você deve receber de volta a classificação correta no próprio Postman. Veja a classificação recebida na imagem acima.
Código
O código completo usado no post é mostrado abaixo.
from flask import Flask, jsonify, request
import numpy as np
import pickle
import json
# Carrega o modelo
def carrega_modelo():
''' função que carrega o modelo treinado'''
with open('knn_model.pkl', 'rb') as f:
model = pickle.load(f)
return model
# Cria uma função para realizar predição usando o modelo treinado
def model_predict(sepal_length, sepal_width, petal_length, petal_width):
''' função que realiza predição usando o modelo treinado'''
features = np.array([[sepal_length, sepal_width, petal_length, petal_width]])
prediction = model.predict(features)
# Para converter predição de número para nome
if prediction[0] == 0:
iris_nome = "Iris setosa"
if prediction[0] == 1:
iris_nome = "Iris versicolor"
if prediction[0] == 2:
iris_nome = "Iris virginica"
return iris_nome
app = Flask(__name__)
# API
@app.route('/', methods=['POST'])
def predict_api():
"""Ponto de Extremidade da API"""
json_file = request.json
prediction = model_predict(json_file["sepal_length"], json_file["sepal_width"], json_file["petal_length"], json_file["petal_width"])
return jsonify({'prediction': prediction})
if __name__ == '__main__':
port = 12345
model = carrega_modelo()
app.run(port=port, debug=True)