Skip to main content

Command Palette

Search for a command to run...

A lib que o seu time de dados precisa (e que você pode construir)

Como um wrapper simples em torno dos seus modelos pode resolver o caos silencioso de produção — sem pedir que o cientista de dados vire engenheiro de ML.

Published
8 min read
A lib que o seu time de dados precisa (e que você pode construir)

Era uma reunião de rotina com o cliente. Daquelas de alinhamento mensal, sem pauta específica. Até que, quase de passagem, ele comentou: "Ah, e aquele modelo de previsão de demanda — a gente parou de usar faz uns dois meses. Parou de funcionar e a gente foi ajustando na mão mesmo."

Dois meses.

O modelo havia quebrado dois meses atrás e ninguém no time tinha descoberto antes daquele café virtual. Nenhum alerta, nenhuma métrica de observabilidade, nenhuma notificação. O cliente simplesmente tinha absorvido o problema e seguido em frente.

Esse episódio foi o gatilho. Éramos um time com cerca de uma dezena de cientistas de dados, nenhum engenheiro de ML dedicado, e estávamos em pleno processo de profissionalizar a operação — sair do modo "entrega modelo e abandona" e começar a ter posse real do que colocávamos em produção. Depois daquela conversa, ficou claro que precisávamos de infraestrutura. E que essa infraestrutura precisava ser construída por nós.

O que veio depois foi uma lib interna. Este artigo é a história de como ela nasceu, o que ela faz, e — mais importante — uma proposta aberta para você fazer o mesmo no seu ambiente.


O custo do caos silencioso

Antes da lib, o cenário era esse:

Cada cientista tinha o seu jeito de salvar modelo. Um usava pickle, outro joblib, um terceiro tinha inventado um esquema com JSON + arquivo de configuração separado. Ninguém sabia ao certo o que estava em produção, quais hiperparâmetros tinham sido usados, qual versão do dado havia treinado aquele modelo.

Quando um modelo dava problema, o único caminho era chamar o cientista original. Que tinha mais três projetos rodando em paralelo. Que precisava parar tudo, reabrir o notebook, tentar lembrar o que havia feito seis meses atrás.

Os melhores cientistas do time — os mais produtivos, os que tocavam mais projetos — eram exatamente os que mais travavam a operação quando algo quebrava. Porque tudo dependia da memória deles.

Não era falta de competência técnica. Era falta de padronização. E padronização não acontece por boa vontade — acontece quando você remove a fricção de fazer a coisa certa e aumenta a fricção de fazer a coisa errada.


Por que pedir para todo mundo aprender MLOps não resolve

A solução óbvia seria: "vamos treinar o time em MLOps, ensinar a usar MLflow direito, definir um padrão de salvamento de modelo e criar uma documentação."

Isso não funciona. Ou pelo menos não funciona sozinho.

Primeiro, porque documentação não é enforcement. As pessoas vão seguir o padrão nos primeiros dois sprints e depois vão cair no velho jeito quando o prazo apertar. Segundo, porque você está pedindo para o cientista de dados — cuja especialidade é modelagem — se tornar fluente em ferramentas de engenharia de ML que não são o core do trabalho dele. Isso é custo cognitivo desnecessário.

O cientista precisa estar focado no problema de negócio, na feature engineering, na escolha do algoritmo. Não em qual método usar para serializar o modelo ou como registrar um experimento no MLflow.

A solução que faz sentido é inversa: você constrói a infraestrutura de MLOps de forma que ela seja invisível para o cientista. Ele continua fazendo o que sempre fez. A lib faz o resto.


A abordagem: um wrapper que carrega o MLOps junto

A ideia central é simples: você cria um wrapper em torno dos modelos do scikit-learn (e do ecossistema compatível — XGBoost, LightGBM, CatBoost) que intercepta os momentos-chave do ciclo de vida do modelo e executa automaticamente as práticas que o time definiu.

Na prática, o código do cientista fica assim:

from sua_lib import Model

# Instancia o modelo — por dentro, já abre um experimento no MLflow
model = Model(XGBClassifier(n_estimators=100, max_depth=4))

# Treina — por dentro, loga hiperparâmetros, métricas e salva o modelo versionado
model.fit(X_train, y_train)

# Predict — igual a qualquer modelo sklearn
predictions = model.predict(X_test)

O que acontece por baixo em cada etapa:

Na instanciação: um experimento é aberto no MLflow com nome padronizado (definido pelas regras da empresa), vinculado ao projeto corrente. Nada vai se perder.

No fit: os hiperparâmetros são logados automaticamente. As métricas de treino são calculadas e registradas. O modelo é serializado no formato definido pelo time — incluindo metadados como features utilizadas, data de treino, versão do dado, ambiente. Uma versão é criada no Model Registry.

No predict em produção: cada chamada é rastreada. Os inputs e outputs ficam salvos numa tabela de log — no Databricks, no S3, onde a infra da empresa estiver.

O cientista não precisa saber que nada disso está acontecendo. Ele usa model.fit() e model.predict() como sempre fez.


O módulo de deploy

Quando um modelo é aprovado para produção, existe um módulo específico na lib que cuida do processo:

from sua_lib import deploy

# Promove a versão para produção e cria o endpoint
deploy.promote(model_name="churn_predictor", version=12)

Por baixo, isso:

  • Adiciona a tag production à versão no MLflow Registry

  • Disponibiliza um endpoint de inferência (via Databricks Model Serving, SageMaker, ou container, dependendo da infra)

  • Ativa o rastreamento de produção para aquela versão

A partir daí, cada predição feita em produção fica registrada com timestamp, os X de entrada, o output do modelo, e a versão que foi usada. Isso não é dado de treino — é dado de operação. É o que permite, depois, construir dashboards de observabilidade e detectar drift antes que o cliente note.

Se você está no Databricks, esse rastreamento pode alimentar diretamente uma tabela Delta. Se está na AWS, é questão de apontar para um bucket S3 com um schema fixo e construir a query em cima com boto3. A lógica é a mesma — o que muda é o conector.


O que mudou na prática

Depois que a lib entrou em operação, algumas coisas mudaram de forma concreta:

A primeira é que o onboarding de novos cientistas ficou trivial em relação a MLOps. Você instala a lib, configura as variáveis de ambiente uma vez, e pronto. A pessoa não precisa aprender MLflow, não precisa saber onde os modelos ficam salvos, não precisa se preocupar com versionamento. Ela aprende a usar Model() e está operacional.

A segunda é que, pela primeira vez, tínhamos uma visão centralizada de todos os modelos em produção. Qual versão estava rodando, quando havia sido treinada, qual performance havia apresentado no treino, quando foi a última predição. Não era perfeito — mas era infinitamente melhor do que antes, quando a resposta para "o que está em produção?" dependia de perguntar para cada cientista individualmente.

A terceira — e mais importante do ponto de vista de gestão — é que os engenheiros de ML pararam de ser bombeiros. Em vez de ficarem sendo acionados toda vez que um modelo quebrava ou precisava de um redeploy, passaram a focar na evolução da própria lib. Cada melhoria na lib multiplica por todos os projetos do time.


O que essa lib ainda não faz (e o que vem depois)

Importante ser honesto: essa abordagem, como descrevi, cobre modelo supervisionado. Classificação, regressão — o ciclo clássico de fit e predict.

Mas o mundo não é só isso. Modelos de linguagem, sistemas de recomendação não supervisionados, pipelines semi-supervisionados — cada um desses tem um ciclo de vida diferente, métricas diferentes, e padrões de observabilidade diferentes. Há muito espaço para expandir.

Além disso, o que descrevi é a camada de rastreamento e versionamento. Tem uma camada de qualidade de dado que pode vir antes — validação de schema, detecção de drift nos inputs antes de o modelo nem ser chamado. Tem uma camada de retraining automático que pode vir depois. A lib que construímos foi um ponto de partida, não um destino.


A carta aberta

Se você está liderando um time de dados com mais de dois ou três cientistas, já passou — ou vai passar — pelo problema que descrevi. O modelo que ninguém sabe quem treinou. O script que só o autor entende. O deploy que é um arquivo copiado à mão para um servidor.

A lib que o seu time precisa não precisa ser um projeto de seis meses. Pode começar pequena: um wrapper que força um formato de salvamento padronizado. Uma função que abre um experimento no MLflow antes de qualquer treino. Um script que loga as predições em produção numa tabela.

O importante é começar. Porque a alternativa — confiar que cada pessoa vai fazer a coisa certa de forma independente, indefinidamente — não escala. E o custo dessa aposta aparece sempre da pior forma possível: numa reunião de rotina, quando o cliente menciona de passagem que o modelo parou de funcionar dois meses atrás.

Você já tem esse problema no seu time? Tem feito algo parecido? Curioso pra saber como outras empresas estão resolvendo isso.