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.

Updated
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.

Como Construir uma Lib de MLOps Times de Data Science