← Blog

CI/CD para modelos de ML com GitHub Actions

2024-10-14MLOpsGitHub ActionsPython

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:

Pipeline CI/CD de ML

Desenvolvedor faz push para uma branch. O GitHub Actions detecta mudanças em src/, data/ ou pipeline/.

in → git pushout → workflow triggered

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ênciascache: pip no 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-hosted runners ou trigger manual
  • Secrets rotacionados — credenciais AWS e MLflow URI em secrets, nunca no código
  • Reprodutibilidade — fixe versões em requirements.txt; pip install sem 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.

Referencias

  1. GitHub Actions — Documentação
  2. GitHub Actions — Workflow syntax
  3. GitHub Actions — Encrypted secrets
  4. GitHub Actions — Storing workflow data as artifacts
  5. DVC — CI/CD integration
  6. MLflow — Model deployment patterns