Versionamento (Dados, Código, Modelos)
O versionamento em ML não é apenas “Git para código”. Um modelo implantado é o resultado de código + dados + configuração + ambiente (e, frequentemente, prompts, definições de features e diretrizes de rotulagem). “Coordenar versões entre código, dados e artefatos de modelo” significa que você consegue responder — de forma rápida e precisa:
- O que exatamente está rodando em produção agora?
- Qual conjunto de dados e qual pré-processamento produziram este modelo?
- Conseguimos reproduzir a execução de treinamento?
- Conseguimos fazer rollback com segurança?
- Se um bug for encontrado nos dados ou no código, quais modelos são afetados?
Este artigo explica os fundamentos de versionamento em sistemas de ML, padrões/ferramentas práticas e como costurar tudo em uma linhagem confiável.
Por que o versionamento coordenado importa
No software tradicional, uma release é em grande parte determinada por um commit de código e por um artefato de build. Em ML, o “comportamento” de um sistema pode mudar mesmo quando o código não muda, porque:
- Os dados de treinamento são atualizados (novas linhas, nova política de rotulagem, remoção de duplicatas)
- A engenharia de features muda (nova normalização, tokenização diferente)
- A configuração de treinamento muda (otimizador, seed, aumento de dados)
- Dependências mudam (CUDA, PyTorch, biblioteca de tokenizador)
- Critérios de avaliação e seleção evoluem
Sem versionamento coordenado, as equipes caem em modos de falha comuns:
- “Funcionava na semana passada”, mas você não consegue reproduzir o modelo exato
- Resultados de teste A/B não podem ser atribuídos a uma mudança específica
- Rollbacks revertem o código, mas não o modelo (ou revertem o modelo, mas não o pipeline de dados)
- Bugs de dados são descobertos, mas você não consegue identificar os modelos impactados
- Solicitações de conformidade/auditoria não podem ser atendidas (proveniência, origem do conjunto de treinamento)
O versionamento dá sustentação a vários outros pilares de MLOps, incluindo Rastreamento de Experimentos, Registro de Modelos, CI/CD para Modelos e Treinamento Reprodutível (Configs, Artefatos).
O que deve ser versionado?
Uma forma prática de pensar: um artefato de modelo é uma função de múltiplas entradas, todas as quais podem (e devem) ser versionadas.
Código (e mais do que apenas código de treinamento)
- Loop de treinamento, definição do modelo, funções de perda
- Código de pré-processamento de dados (ETL, engenharia de features, tokenização)
- Código de avaliação e definições de métricas
- Código de serving (parsing de requisições, pós-processamento, limiarização)
- Infraestrutura como código (infrastructure-as-code) que afeta a execução (builds de container, configurações de acelerador)
Dados (brutos, processados e rotulados)
- Conjuntos de dados brutos (snapshots de tabelas de origem ou armazenamento de objetos)
- Dados rotulados e política/versão de rotulagem
- Divisões de treino/validação/teste (incluindo lógica de amostragem e seeds aleatórias)
- Conjuntos de dados derivados (filtrados, deduplicados, aumentados, por janelas)
- Versão de esquema/contrato (colunas, tipos, restrições)
Portas de qualidade de dados se conectam de perto com Validação de Dados.
Artefatos de modelo (a “coisa que você implanta”)
- Pesos/checkpoints
- Configuração de arquitetura do modelo
- Tokenizador/vocabulário (crítico para PLN/LLMs)
- Parâmetros de pré-processamento/pós-processamento (estatísticas de normalização, limiares)
- Assinatura do modelo (schema de entrada/saída, shapes, dtypes)
- Artefatos de calibração (por exemplo, temperature scaling)
Fluxos de promoção/rollback de modelos normalmente são tratados via um Registro de Modelos.
Configuração, ambiente e contexto de execução
- Hiperparâmetros, seeds aleatórias
- Versões de frameworks (PyTorch/TensorFlow), versões de CUDA/cuDNN
- Digest da imagem de container
- Tipo de hardware (às vezes afeta numéricos/desempenho)
- Configurações de treinamento distribuído
Artefatos específicos de LLM (frequentemente negligenciados)
Para aplicações com LLM, “o modelo” pode incluir mais do que pesos:
- Templates de prompt, schemas de ferramentas, lógica de roteamento → veja PromptOps
- Versões do índice de recuperação (índice de embeddings, configurações do chunker)
- Prompts de sistema/políticas de guardrails
- Definições de function-calling/tooling
Observabilidade e depuração dessas versões se conectam a Observabilidade para Apps com LLM.
Fundamentos teóricos: versionamento como proveniência e imutabilidade
Em sua essência, o versionamento coordenado trata de proveniência (de onde algo veio) e imutabilidade (uma versão nunca muda).
Conceitos-chave:
Endereçamento por conteúdo (content-addressing) (identidade baseada em hash)
Em vez de nomear artefatos por “latest” ou por um caminho mutável, você os identifica por um hash do seu conteúdo (ou um digest criptográfico do blob do artefato). Isso torna as versões à prova de adulteração e reprodutíveis.
Exemplos:
- SHA do commit do Git para código
- ID de snapshot do conjunto de dados (ou um hash de manifesto)
- Digest de imagem de container (por exemplo,
sha256:...) - Checksum do artefato de modelo
Linhagem como um grafo (DAG) (grafo acíclico direcionado)
Artefatos de ML formam um grafo acíclico direcionado:
- Snapshot de conjunto de dados → código de featurização → execução de treinamento → artefato de modelo → implantação
- Um conjunto de dados pode alimentar muitos modelos; um modelo pode ser implantado em muitos ambientes
Armazenar a linhagem permite análise de impacto: “se o conjunto de dados X teve um problema, quais modelos o usaram?”
Determinismo vs reprodutibilidade
Mesmo com versionamento perfeito, o treinamento pode não ser deterministicamente idêntico em bits (não determinismo de GPU, efeitos distribuídos). O objetivo normalmente é:
- Reprodutível o suficiente para retreinar e obter desempenho estatisticamente equivalente
- Auditável para mostrar exatamente quais entradas produziram o modelo liberado
Versionamento de código: Git é necessário, mas não suficiente
A maioria das equipes usa Git (ou similar) para código. Boas práticas para ML incluem:
Fixe (pin) o código de treinamento em identificadores imutáveis
Armazene o SHA do commit do Git em toda execução de treinamento e nos metadados do modelo.
Padrões comuns:
- Exigir uma working tree limpa para treinamento de “candidato a release”
- Usar tags para releases relevantes para o modelo (por exemplo,
train-pipeline-v2.3.1) - Registrar SHAs de submódulos se estiver usando submódulos
Exemplo (registrar versão do código em um manifesto de execução):
{
"code": {
"repo": "git@github.com:org/ml-platform.git",
"commit": "3f2a9c1d9d7b8b0d2a1c4b...",
"dirty": false
}
}
Trate transformações de dados como código versionado
Se o pré-processamento mudar, isso deve ser rastreado como qualquer outra mudança de comportamento. Um pequeno ajuste de normalização pode alterar significativamente o comportamento do modelo.
Coordene com CI/CD
Use CI/CD para Modelos para:
- Executar testes unitários em pré-processamento e métricas
- Validar expectativas de schema
- Construir e assinar imagens de treinamento/serving
- Aplicar políticas (“sem treinamento a partir de código não commitado”)
Versionamento de dados: snapshots, time travel e manifestos de conjuntos de dados
O versionamento de dados é mais difícil do que o de código, porque conjuntos de dados podem ser enormes, atualizados continuamente e derivados via pipelines.
Princípio: versionar *o que o modelo viu*
Para reprodutibilidade de treinamento, você precisa identificar o conjunto exato de exemplos (e rótulos) usado.
Abordagens comuns:
1) Snapshotting (copy-on-write ou cópias físicas)
Você cria um snapshot imutável do conjunto de dados em um ponto no tempo.
Ferramentas/técnicas:
- Time travel em lakehouse (Delta Lake, Apache Iceberg, Hudi)
- Versionamento estilo Git para armazenamento de objetos (por exemplo, lakeFS)
- Exportações periódicas para armazenamento de objetos com um ID de snapshot
Prós: modelo mental simples (“treinar no snapshot S”)
Contras: overhead de armazenamento/gestão (mitigado por formatos copy-on-write)
2) Manifestos de conjuntos de dados (índice endereçável por conteúdo de arquivos/linhas)
Em vez de copiar todos os dados, armazene um manifesto descrevendo exatamente quais arquivos (e checksums) constituem a versão do conjunto de dados.
Exemplo de manifesto:
dataset:
name: transactions-train
version: 2026-01-01T00:00:00Z
files:
- path: s3://bucket/data/part-0001.parquet
etag: "9b2cf535f27731c974..."
- path: s3://bucket/data/part-0002.parquet
etag: "4c1a0e22d4b7c3d3a1..."
schema_version: 7
label_policy_version: "fraud-labels-v3"
Isso pode ser combinado com um hash de conjunto de dados derivado (hash do manifesto) que se torna o identificador de versão do conjunto de dados.
3) Versionamento baseado em consulta (SQL + tempo)
Você armazena a consulta e a referência de time travel (por exemplo, “as of timestamp T” ou “table version V”).
Exemplo:
SELECT * FROM lake.transactions VERSION AS OF 1842
WHERE country IN ('US','CA') AND ts >= '2025-10-01';
Isso é poderoso, mas só é seguro se:
- O armazenamento subjacente suporta versões históricas imutáveis
- O mecanismo de consulta e UDFs são versionados/fixados (pinned)
Não esqueça as divisões e a amostragem
Sua “versão do conjunto de dados” deve incluir:
- Lógica de atribuição de split (divisão baseada em hash é comum)
- Regras de amostragem
- Seeds aleatórias
Se você armazenar apenas “snapshot bruto”, ainda pode não reproduzir o conjunto de treinamento exatamente.
Portas de qualidade de dados fazem parte da história de versionamento
Se uma versão do conjunto de dados passou pela validação, você quer manter essa associação:
dataset_version -> validation_report_id
Isso dá suporte direto a Validação de Dados e ajuda a evitar que “drift silencioso de dados” entre no treinamento.
Versionamento de modelos: artefatos, metadados e registries
Uma “versão de modelo” deve ser mais do que um nome de arquivo como model_final.pkl.
O que uma versão adequada de modelo inclui
- Um identificador único e imutável (versão no registry, hash do artefato)
- O binário do modelo (pesos) e quaisquer arquivos auxiliares (tokenizador, estatísticas de normalização)
- Uma assinatura legível por máquina (entradas/saídas)
- Metadados que conectam de volta a código, dados e configs
Exemplo de metadados de modelo (simplificado):
{
"model": {
"name": "fraud-detector",
"version": "42",
"artifact_uri": "s3://models/fraud-detector/42/model.onnx",
"sha256": "a1b2c3..."
},
"trained_with": {
"dataset_hash": "d7e8f9...",
"code_commit": "3f2a9c1...",
"config_hash": "9c0d1e...",
"container_image": "ghcr.io/org/train:1.8.0@sha256:abcd..."
}
}
Registries de modelo como o sistema de registro (system of record)
Um Registro de Modelos normalmente oferece:
- Numeração de versões por nome de modelo
- Estágios/ambientes (por exemplo,
Staging,Production,Archived) - Fluxos de aprovação e governança
- Links para relatórios de avaliação e linhagem
- Rollback (“promover a versão 41 de volta para Production”)
Versionamento semântico vs versionamento de registry
Muitas equipes usam:
- Versões inteiras do registry para artefatos imutáveis (1, 2, 3…)
- Versões semânticas opcionais para comunicação humana (
v2.1.0)
Cuidado: o versionamento semântico implica que você entende “quebras de compatibilidade”. Em ML, “quebra” pode significar um schema de features alterado, um contrato de API alterado ou um comportamento materialmente alterado. Se o schema de saída do modelo mudar, isso é claramente uma quebra. Se a fronteira de decisão mudar, isso pode ou não ser “quebra”, dependendo das garantias downstream.
Coordenando versões: tornando a linhagem explícita
Versionamento coordenado significa que você consegue navegar em ambas as direções:
- De uma implantação → qual versão de modelo? qual código e dados a construíram?
- De uma versão de conjunto de dados → quais modelos treinados a consumiram?
- De um commit de código → quais modelos foram treinados com ele?
O padrão do “manifesto de execução” (run manifest)
Crie um manifesto único para cada execução de treinamento que capture todas as versões relevantes e seja armazenado junto do artefato de modelo e no seu rastreador de experimentos.
Campos típicos:
- Commit de código + URL do repositório
- Versão(ões) de conjunto(s) de dados + hashes
- Arquivo(s) de config + hashes
- Lockfiles de dependências (ou digest do container)
- Seeds aleatórias
- Comando de treinamento + entrypoint
- Métricas e referências a relatórios de avaliação
Isso complementa fortemente o Rastreamento de Experimentos.
Um exemplo prático de fluxo de trabalho (Git + DVC + MLflow)
Uma configuração comum:
- Git para código
- DVC (ou lakeFS/Delta/Iceberg) para versionamento de conjuntos de dados
- MLflow para rastreamento de experimentos + logging de modelos
- Registro de modelos (registry do MLflow ou sistema dedicado) para promoção
Comandos de exemplo (ilustrativos):
# Version code
git checkout -b feat/new-tokenizer
git commit -am "Update tokenizer + preprocessing"
# Version data with DVC (tracks pointers/hashes, stores data in remote)
dvc add data/train.parquet
git add data/train.parquet.dvc .gitignore
git commit -m "Add train dataset v2026-01-01"
# Tag the combined state for a training run
git tag -a fraud-train-2026-01-01 -m "Training run inputs snapshot"
git push --tags
O script de treinamento registra metadados coordenados:
import mlflow, subprocess, json, hashlib, pathlib
def git_sha():
return subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip()
def file_sha256(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(1<<20), b""):
h.update(chunk)
return h.hexdigest()
with mlflow.start_run():
mlflow.log_param("code_commit", git_sha())
mlflow.log_param("dataset_dvc_file", "data/train.parquet.dvc")
mlflow.log_param("config_sha256", file_sha256("configs/train.yaml"))
mlflow.log_artifact("configs/train.yaml", artifact_path="config")
mlflow.log_dict({"code_commit": git_sha()}, "lineage.json")
# train(...)
# mlflow.log_metric(...)
# mlflow.pytorch.log_model(...)
A parte importante não são as ferramentas específicas — é que o artefato de modelo contém (ou referencia) identificadores imutáveis para código, dados e config.
Coordenando treinamento e serving
Uma lacuna frequente: o treinamento é versionado, mas o serving não é fixado (pinned) nas mesmas suposições.
Boa prática:
- A imagem de serving deve ser construída a partir de um commit SHA e ter um digest de imagem
- A configuração de implantação deve referenciar uma versão do registry de modelos
- A versão do modelo deve referenciar o digest da imagem de treinamento (ou a especificação do ambiente)
Esse “triângulo” (código ↔ modelo ↔ implantação) é o que torna rollbacks seguros.
Sistemas de LLM e RAG: versionar mais do que pesos
Para apps com LLM, o comportamento pode mudar drasticamente devido a mudanças em prompt e recuperação, mesmo que o modelo base seja constante.
Coordene versões entre:
- Identificador do modelo base (por exemplo,
gpt-4.1-mini-2025-xx-xxou um ID interno de checkpoint) - Versão do template de prompt (veja PromptOps)
- Versão do schema de ferramentas (assinaturas de funções)
- Versão do índice de recuperação (versão do modelo de embeddings, parâmetros de chunking, snapshot do corpus)
- Versão de políticas de segurança/guardrails
Exemplo de “manifesto de release de RAG” (simplificado):
rag_system:
app_version: "2026.1.6"
base_model: "internal-llm-13b:v17"
prompt_version: "support-agent-prompt:v12"
embedding_model: "text-embedding-3-large:v5"
index_snapshot: "kb-index:2026-01-01T00:00Z"
chunker:
strategy: "semantic"
max_tokens: 350
reranker: "cross-encoder:v3"
Sem isso, você não consegue interpretar mudanças observadas em Monitoramento ou Avaliação em Produção.
Armadilhas comuns (e como evitá-las)
“Latest” em todo lugar
Se o treinamento puxa s3://bucket/data/latest/ ou usa uma tabela não fixada sem time travel, você não tem um conjunto de dados reprodutível.
Correção: use IDs de snapshot, manifestos ou versões de tabela com time travel.
Pré-processamento de dados não versionado
Uma mudança sutil no pré-processamento pode invalidar comparações entre versões de modelos.
Correção: trate o pré-processamento como código de primeira classe; registre o commit SHA e as versões de pacotes.
Splits não estáveis
Se os splits de treino/val/test são reamostrados a cada execução, comparações de desempenho ficam ruidosas e podem esconder regressões.
Correção: divisão determinística baseada em IDs estáveis (por exemplo, hash de user_id) e registre a versão da lógica de split.
O artefato do modelo não inclui tokenizadores ou estatísticas de normalização
Implantar pesos sem o tokenizador/vocab exato ou sem escalonadores de features pode quebrar a inferência silenciosamente.
Correção: empacote todos os artefatos auxiliares necessários e inclua verificações no carregamento do modelo.
Drift oculto de dependências
Mesmo código + mesmos dados podem produzir resultados diferentes se versões de bibliotecas mudarem.
Correção: fixe (pin) dependências (lockfiles) ou use imagens de container; registre digests.
Sem linhagem de ponta a ponta
Armazenar versões em sistemas separados sem conectá-las torna a resposta a incidentes lenta.
Correção: adote um manifesto de execução e garanta que cada entrada no registro de modelos aponte para identificadores de código+dados+config.
Checklist de boas práticas
Mínimo “obrigatório” para sistemas de ML sérios
- Código: commit SHA do Git registrado para cada execução
- Dados: versão imutável do conjunto de dados (snapshot/manifesto/referência de time travel)
- Config: configs versionadas com hashes
- Ambiente: digest do container ou dependências totalmente fixadas (pinned)
- Modelo: entrada no registry com checksum do artefato e assinatura
- Linhagem: modelo vincula a código+dados+config; implantação vincula à versão do modelo
Práticas operacionais
- Exigir captura de versões em pipelines de treinamento (tornar difícil pular)
- Exigir artefatos de reprodutibilidade para promoção a produção (relatório de avaliação, relatórios de validação)
- Automatizar rollback promovendo uma versão anterior do registro de modelos
- Usar CI/CD para Modelos para testar compatibilidade (checagens de schema, inferência de smoke)
- Integrar com Monitoramento e Avaliação em Produção para que o comportamento observado possa ser vinculado a um conjunto específico de versões
Resumo
O versionamento coordenado entre código, dados e modelos transforma ML de um processo artesanal em uma disciplina de engenharia. A ideia central é simples: todo modelo e toda implantação devem ser rastreáveis a identificadores imutáveis do código, conjunto de dados, configuração e ambiente exatos que os produziram. Na prática, isso significa combinar controle de versão de software (Git), snapshotting/manifestos de dados, registros de modelos e manifestos de execução que unem tudo.
Quando bem feito, o versionamento habilita reprodutibilidade, iteração segura, rollbacks confiáveis, auditorias/conformidade e depuração mais rápida — especialmente à medida que sistemas de ML evoluem para pipelines com múltiplos componentes (features, recuperação, prompts, ferramentas) em vez de um único modelo isolado.