Indicium Logo
Blog Indicium

Machine learning: como construir modelos como produtos usando MLOps

Parte 4 | Deploy de modelos em APIs com kedro-fast-api

Machine learning é uma inteligência artificial (IA) que o mercado descobriu, mas ainda não sabe bem como usar nem para que serve. A Indicium sabe e vai ensinar em quatro partes.

Caso você não tenha visto as primeiras partes desta série, confira:

Nesta parte 4, nossos modelos de machine learning já estão treinados, o código é reproduzível e fácil de manter, e nós já temos o controle das métricas.

Mas ainda não temos um produto utilizável. Ou melhor, não tínhamos até agora.

Para isso, vamos utilizar uma das formas mais comuns para a integração de serviços de tecnologia, que é uma API. Essa sigla vem do nome em inglês application programming interface, ou interface de programação de aplicação, em tradução livre.

A principal vantagem de uma API é que, com ela, é possível interagir com uma aplicação sem que se saiba como ela foi implementada. Basicamente, é como chegar a um restaurante, fazer um pedido e recebê-lo sem saber como foi preparado dentro da cozinha.

Atenção: neste post, vamos utilizar o conceito de criação de classes em Python; caso ele não seja familiar para você, sugerimos estudar Python orientado a objetos. Há muitos materiais disponíveis gratuitamente na internet, inclusive vídeos no YouTube em português.

Criar uma API é criar um ponto de acesso para que outras aplicações recebam os resultados do seu modelo (o prato pronto) a partir de uma solicitação contendo os inputs do modelo (o pedido feito ao garçom).

Uma API pode estar pública em uma URL na internet (onde normalmente se controla a segurança utilizando uma chave de acesso) ou em uma porta local de uma rede interna.

Pela versatilidade e segurança, a API é uma ótima ferramenta para o deploy dos seus modelos. Para tal, vamos utilizar o Kedro aliado a uma nova ferramenta: o Kedro Fast API, uma extensão criada pela Indicium!

kedro-fast-api

Isso mesmo! Desenvolvemos um plugin para a criação de API utilizando Fast API para possibilitar a criação com poucos arquivos adicionais de configuração, e aproveitar todas as vantagens que já vimos do Kedro para estruturar esse processo todo de modo a mantê-lo organizado, sustentável e escalável.

A Indicium fez uma extensão de Kedro para criação de API e nós vamos apresentar aqui essa nova abordagem e como criar sua primeira API pronta para entrar em produção. O repositório do projeto você pode encontrar neste link:

https://bitbucket.org/indiciumtech/kedro_fast_api

Lá, você pode ler a documentação oficial, que está em inglês.

A seguir, vamos mostrar para você essa estrutura com mais detalhes.

Instalação

A instalação do pacote pode ser feita a partir do seguinte comando no terminal, na mesma pasta do projeto Kedro:

$ pip install kedro-fast-api

Essa instalação será confirmada pelo comando kedro info, e esse comando retornará, incluindo o kedro-fast-api:

Inicialização

Após a instalação, é necessário iniciar o plugin através do comando:

$ kedro fast-api init

Essa inicialização vai criar os arquivos padrão do plugin, que são:

  • conf/api.yml
  • conf/base/api_pipeline/catalog.yml
  • src/api_pipeline/nodes.py
  • src/api_pipeline/catalog.py

Os arquivos nodes.py e pipeline.py serão criados em um novo pipeline de Kedro chamado api_pipeline, que será responsável pelo salvamento do predictor (vamos falar sobre isso mais adiante).

O centro do processo é o pipeline predictor, sendo ele o objeto que de fato faz as previsões do modelo.

Entenda melhor como isso acontece.

Predictor

Resumidamente, o predictor é um encapsulamento do modelo para que ele possa ser armazenado em memória (utilizando-se o catalog e salvando em formato pickle) para ser usado com quaisquer dados de entrada, em qualquer ambiente. Isso prepara o modelo para ser enviado a qualquer lugar na galáxia.

Para isso, o arquivo de nodes cria uma classe chamada predictor, que possui o método predict.

O objeto criado a partir dessa classe é salvo instanciado utilizando o seu modelo de machine learning. Como ele é salvo como um objeto instanciado, os inputs necessários para fazer uma predição podem ser passados como argumentos de método, já que o modelo foi incluído no objeto durante seu salvamento.

Portanto, o predictor (aqui chamado de MLPredictor) propicia que a predição possa ser feita utilizando o seguinte comando em Python:

MLPredictor.predict(inputs)

Aqui, inputs são as entradas para o modelo de machine learning, e os outputs são os resultados das previsões, que serão devolvidos como um dicionário Python, ou seja, como basicamente é a resposta de uma API em JSON.

Conforme mencionamos anteriormente, o nó de Kedro vai executar apenas um comando para instanciar a classe (criar o objeto com o modelo dentro dele) e salvar o predictor em memória, fazendo com que toda a estrutura funcione perfeitamente.

Para ficar mais claro, perceba a estrutura do arquivo nodes.py:

import pandas as pd
class MLPredictor:
def __init__(self, model):
self.model = model
def predict(self, args_API: pd.DataFrame):
df_args = args_API
prediction = self.model.predict(df_args)
return {"prediction": prediction}
def save_predictor(model):
predictor = MLPredictor(model)
return predictor

O nó de Kedro é a função save_predictor, que vai receber apenas o modelo já treinado. Você também pode criar predictors mais complexos, que incluam a criação de features ou utilização de múltiplos modelos, mas a estrutura será a mesma.

Assim, ao salvar, o modelo estará dentro do predictor através do objeto self.model. E o arquivo pipeline.py será:

from kedro.pipeline import Pipeline, node

from .nodes import save_predictor

def create_pipeline(**kwargs):

return Pipeline(

[

node(

func=save_predictor,

name="save_predictor",

inputs="model",  ### Replace with model's name

outputs="MLPredictor",

)

]

)

Nesse caso, a entrada é o modelo “model”, cujo nome deve ser atualizado com o nome do próprio modelo usado no projeto. Pode-se perceber também que as entradas do nó de kedro não são as entradas do modelo em si. As entradas do nó de kedro são os arquivos necessários para criar um arquivo capaz de fazer previsões (parâmetros de classe), e as entradas do modelo são as entradas do método predict (parâmetros de método)

O predictor padrão não faz qualquer operação nos dados. O Pandas DataFrame recebido pela API (via dicionário de dados) é passado diretamente para o método predict. Porém, não há restrição para a inclusão de etapas intermediárias para feature engineering, por exemplo. O método predict pode alterar o DataFrame para deixá-lo da maneira mais apropriada para a predição correta.

Além disso, é possível incluir mais de um modelo treinado, e incluir respostas adicionais no dicionário de output do método predict. Essa flexibilidade é uma das principais vantagens desse método. Também é possível replicar os arquivos e criar diferentes predictors que sejam criados no mesmo pipeline, possibilitando mais de uma API ser criada com apenas um arquivo de configuração (veremos mais adiante como criar rotas para os diferentes predictors).

Arquivos de configuração

O primeiro arquivo de configuração de que vamos tratar é um velho conhecido: o catalog.yml. Esse catalog vai ser relacionado ao salvamento do predictor, tendo a seguinte estrutura:

MLPredictor:

type: pickle.PickleDataSet

filepath: data/09_predictor/MLPredictor.pickle

versioned: true

Como já vimos anteriormente em outros posts desta série, podemos salvar um objeto Python como um Pickle dataset usando Kedro.

Além dele, temos o  arquivo api.yml, que é o arquivo com as informações necessárias para a criação da API de fato. O seu arquivo padrão tem a seguinte estrutura:

routes:

model_API:

# Catalog reference to .pickle model

predictor: MLPredictor

# Inputs passed as a python dictionary

# allowed types int, float, str, bool and enum

parameters:

input_1:

# enum type requires options (numbers as options won't work)

type: enum

options:

- some

- options

- here

input_2:

type: int

input_3:

type: float

input_4:

type: bool

input_5:

type: str

Vamos analisar passo a passo. A estrutura é a do conhecido YAML. O arquivo começa com a informação routes: cada rota é uma API diferente, e o nome da rota é o nome que aparece na URL, logo após o endereço (por exemplo: localhost:8000/model_API).

Nesse exemplo, a rota configurada é a model_API. Ela especifica as seguintes propriedades:

  • predictor: o nome do predictor salvo no catalog
  • parameters: os parâmetros são as entradas da API, e é necessário especificar o seu nome e o tipo de dado, mas também é possível pré-determinar os valores aceitos.

E pronto! Depois de configurar essas propriedades, a API está pronta para ser criada.

Criando a API

O comando para criação da API é:

$ kedro fast-api run

Sim! Apenas isso.

Se tudo estiver correto, a API será criada a partir do arquivo de configurações na pasta conf/api.yml.

Caso seja necessário especificar outro arquivo, utilize o comando:

$ kedro fast-api run -p path_to_file/API_file_name.yml

Agora, a API vai estar disponível para o acesso e a criação das primeiras predições. O acesso pode ser feito na seguinte URL:

localhost:8000/model_API/

Utilização da API


O kedro-fast-api gera uma interface para fazer requisições manualmente, onde cada um dos campos detalhados no api.yml aparece com seus respectivos formatos e valores aceitos. Assim, é possível preencher os campos manualmente na interface e gerar uma requisição.

Também é possível criar descrição e adicionar mais detalhes à sua API. A página encontrada será parecida com esta:

Clicando em “GET” abaixo de “users”, e depois em “Try it out”, é possível preencher os campos e obter uma resposta do modelo. Se tudo der certo, os seus resultados aparecerão logo abaixo, além de um link para que você faça isso de maneira automatizada (por exemplo, usando os requests de Python). Assim, usando o link padrão, é possível automatizar a escrita da URL e o processo em outros ambientes que tenham acesso ao endereço em que a API está localizada.

Um exemplo de resultado usando o projeto do Pokémon (citado em outros posts desta série), é a previsão para o tipo de Pokémon a partir das suas características.

Agora, com a API pronta, é possível passar para o time de desenvolvimento da sua equipe para que possa ser feito o deploy.

Boas práticas

  • Documente seu código: a pessoa que vai usar seu modelo não participou da sua construção e, portanto, não conhece os inputs, outputs, nem a metodologia utilizada; facilite a leitura e a interpretação do código; faça alterações e correções necessárias e o que mais precisar para facilitar o dia a dia da sua equipe.
  • Escreva um código legível:
  • Preste atenção ao nome das variáveis; inclua um indicativo do que cada variável é ao invés de utilizar nomes como “x” ou o clássico “df”, que dificultam a leitura do código, utilize “clientes” ou “pedidos”;
  • Seja linear sempre que possível, evite ordens aleatórias de funções no corpo do código;
  • Use funções separadas para executar etapas complexas do seu código para ajudar na legibilidade. A dica é: caso uma função tenha mais de um nível lógico, quebre em funções menores;
  • Nunca repita código: Se você precisou copiar e colar um trecho de código, ele deveria ser uma função chamada duas vezes;
  • Use nomes de função que já deem uma ideia do que está acontecendo nela, por exemplo: limpar_data_frame(data_frame)
  • Tente prever os erros: se você sabe que o seu modelo vai quebrar caso uma coluna seja enviada com um valor diferente daqueles usados no treino do modelo, inclua um teste em seu predictor que retorne dicionários específicos de erro. Por exemplo:

if erro:

return {'erro': 'descrição do erro'}

Assim, o retorno da API será um erro, e a pessoa conseguirá saber o que aconteceu e como alterar seus inputs para evitar que ocorra novamente. Se o seu modelo quebrar internamente, não haverá informação sobre isso.

O mesmo aconteceria se um cozinheiro queimasse seu bife, não avisasse o que houve, e você recebesse um prato vazio sem explicação. Certamente, não queremos que isso aconteça.

Seguindo essas boas práticas, seu código será mais fácil de manter tanto para você no futuro quanto para os utilizadores da sua API. Dito isso, chegamos ao fim da última etapa desta série Machine learning: como construir modelos como produtos usando MLOps.

Nosso modelo ficou pronto e, durante os artigos da série, nós aprendemos como deixá-lo estruturado, com acompanhamento de métricas, e deployado em API.

Quer saber mais sobre machine learning, data science, analytics e muito mais?

Inscreva-se em nossa newsletter para receber todos os nossos conteúdos em primeira mão.

  • Ricardo Raspini Motta
    Por Ricardo Raspini Motta Data Scientist at Indicium Tech
  • Bianca Santos
    Por Bianca Santos Analista de conteúdo

Assine nossa newsletter