O que é CI/CD para ML e por que importa
CI/CD (Continuous Integration / Continuous Deployment) automatiza o que acontece depois de um git push. Em software tradicional, isso significa rodar testes e deployar a aplicação. Em ML, o pipeline é mais rico: validar dados, treinar modelo, comparar métricas com produção e só então promover o artefato.
Sem automação, o fluxo típico é: cientista treina no notebook, salva um .pkl manualmente, alguém copia para o servidor. Não há gate de qualidade, não há comparação com o modelo atual, não há rollback fácil. Um AUC que caiu 5 pontos passa despercebido até o impacto no negócio.
GitHub Actions é uma forma acessível de implementar esse pipeline — integrado ao repositório, com secrets para credenciais e artifacts para modelos. Este post mostra um workflow completo para projetos Python com DVC e MLflow.
Estrutura do pipeline
Um pipeline de ML em CI segue etapas sequenciais com gates entre elas:
Desenvolvedor faz push para uma branch. O GitHub Actions detecta mudanças em src/, data/ ou pipeline/.
Cada etapa pode falhar independentemente. A regra de ouro: nenhum modelo chega em produção sem passar por avaliação automatizada.
Workflow básico: testes e lint
Comece com um workflow que roda em todo push e pull request:
# .github/workflows/ci.yml
name: ML CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Lint
run: ruff check src/ tests/
- name: Unit tests
run: pytest tests/ -v --tb=short
Testes unitários para ML devem cobrir: transformações de features, validação de schema de dados, e funções de métricas — não o modelo inteiro (isso é job de integração).
Job de treino com DVC
O treino roda em branch main ou manualmente via workflow_dispatch:
train:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Pull data with DVC
run: |
pip install dvc[s3]
dvc pull
- name: Train model
run: dvc repro train
- name: Upload model artifact
uses: actions/upload-artifact@v4
with:
name: model-${{ github.sha }}
path: models/model.pkl
retention-days: 30
Secrets no GitHub (Settings → Secrets) guardam credenciais AWS para dvc pull. Artifacts persistem o modelo por 30 dias — suficiente para deploy posterior.
Gate de qualidade: comparar métricas
Antes do deploy, compare métricas do modelo novo com o baseline em produção:
- name: Evaluate model gate
run: |
python scripts/evaluate_gate.py \
--current metrics.json \
--baseline s3://models/production/metrics.json \
--min-auc 0.82
O script evaluate_gate.py implementa a lógica de aprovação:
import argparse
import json
import sys
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--current", required=True)
parser.add_argument("--baseline", required=True)
parser.add_argument("--min-auc", type=float, default=0.80)
args = parser.parse_args()
with open(args.current) as f:
current = json.load(f)
# Em produção, baixar baseline do S3 antes de comparar
baseline_auc = 0.84 # substituir por leitura real
if current["auc_roc"] < args.min_auc:
print(f"FAIL: AUC {current['auc_roc']:.4f} below minimum {args.min_auc}")
sys.exit(1)
if current["auc_roc"] < baseline_auc - 0.02:
print(f"FAIL: AUC dropped more than 2pp vs production ({baseline_auc})")
sys.exit(1)
print(f"PASS: AUC {current['auc_roc']:.4f} approved for deploy")
if __name__ == "__main__":
main()
sys.exit(1) falha o workflow — o deploy não acontece.
Deploy e registro no MLflow
Com o gate aprovado, registre e promova o modelo:
deploy:
needs: train
runs-on: ubuntu-latest
if: success()
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: model-${{ github.sha }}
path: models/
- name: Register in MLflow
env:
MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_TRACKING_URI }}
run: |
python scripts/register_model.py \
--model-path models/model.pkl \
--model-name credit-score \
--stage Production
Para APIs, o deploy pode ser um push de container para ECR + update no ECS, ou um kubectl apply — o princípio é o mesmo: só após gate aprovado.
Além do básico: pontos de atenção
Boas práticas e armadilhas em CI/CD de ML:
- Cache de dependências —
cache: pipno setup-python acelera builds repetidos - Treino em PR vs main — treino completo só em
main; PRs rodam testes e validação de schema - Custos de CI — treino pesado pode usar
self-hostedrunners ou trigger manual - Secrets rotacionados — credenciais AWS e MLflow URI em secrets, nunca no código
- Reprodutibilidade — fixe versões em
requirements.txt;pip installsem pin quebra pipelines silenciosamente - Artifacts vs registry — artifacts são temporários; MLflow registry é a fonte de verdade para produção
Workflow completo recomendado:
push → test + lint → (main only) dvc pull → train → gate → register → deploy
Conclusão
CI/CD para ML transforma o ciclo de treino em processo automatizado com gates de qualidade. GitHub Actions integra testes, DVC, treino, avaliação e deploy no mesmo repositório — sem infraestrutura extra para começar.
O próximo passo após deploy é monitorar o modelo em produção — métricas, drift e alertas com Prometheus e Grafana.