Capítulo 1: Git para Projetos de ML
Introdução
O Git é a base do controle de versão no desenvolvimento de software, mas projetos de ML apresentam desafios únicos que exigem práticas e configurações especializadas.
Desafios Específicos de Git em ML
A Natureza Única do Código de ML
Diferente do software tradicional, projetos de ML envolvem:
| Desafio | Descrição |
|---|---|
| Arquivos grandes | Conjuntos de dados, pesos de modelos (GB a TB) |
| Artefatos binários | Modelos treinados, pré-processadores |
| Proliferação de experimentos | Centenas de branches experimentais |
| Não-determinismo | O mesmo código pode produzir resultados diferentes |
| Caos de notebooks | Notebooks Jupyter com outputs embutidos |
Estrutura de Repositório para Projetos de ML
Estrutura Recomendada
ml-project/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ ├── training.yml
│ └── deployment.yml
├── data/
│ ├── raw/ # Dados originais (controlados pelo DVC)
│ ├── processed/ # Dados transformados (controlados pelo DVC)
│ └── features/ # Exportações do feature store
├── models/
│ ├── artifacts/ # Modelos treinados (controlados pelo DVC)
│ └── configs/ # Configurações de modelos
├── notebooks/
│ ├── exploration/ # Notebooks de EDA
│ └── experiments/ # Notebooks de experimentos
├── src/
│ ├── __init__.py
│ ├── data/
│ │ ├── __init__.py
│ │ ├── ingestion.py
│ │ └── preprocessing.py
│ ├── features/
│ │ ├── __init__.py
│ │ └── engineering.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── train.py
│ │ └── predict.py
│ └── utils/
│ ├── __init__.py
│ └── helpers.py
├── tests/
│ ├── unit/
│ ├── integration/
│ └── data/
├── configs/
│ ├── training_config.yaml
│ └── serving_config.yaml
├── .dvc/
├── .gitignore
├── .pre-commit-config.yaml
├── dvc.yaml
├── dvc.lock
├── pyproject.toml
├── requirements.txt
└── README.md
.gitignore Abrangente para ML
# .gitignore para Projetos de ML
# ===== Python =====
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# ===== Ambientes Virtuais =====
venv/
ENV/
env/
.venv/
.conda/
# ===== Jupyter Notebooks =====
.ipynb_checkpoints/
*/.ipynb_checkpoints/
# ===== Arquivos de Dados =====
# Arquivos de dados grandes (controlados pelo DVC)
*.csv
*.parquet
*.feather
*.h5
*.hdf5
*.pkl
*.pickle
!data/.gitkeep
data/raw/*
data/processed/*
data/features/*
# ===== Artefatos de Modelos =====
# Modelos treinados (controlados pelo DVC)
*.pt
*.pth
*.onnx
*.pb
*.h5
*.keras
*.joblib
*.model
models/artifacts/*
!models/artifacts/.gitkeep
# ===== Artefatos de MLOps =====
mlruns/
outputs/
wandb/
.neptune/
tensorboard_logs/
# ===== IDE =====
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# ===== Variáveis de Ambiente =====
.env
.env.local
*.env
# ===== Segredos =====
secrets/
credentials/
*.pem
*.key
# ===== Logs =====
logs/
*.log
# ===== Cache =====
.cache/
.pytest_cache/
.mypy_cache/
.ruff_cache/
# ===== Cobertura =====
htmlcov/
.coverage
.coverage.*
coverage.xml
Git LFS para Arquivos Grandes
Quando Usar Git LFS
O Git LFS (Large File Storage) é útil para arquivos que:
- São essenciais para o repositório
- São atualizados com pouca frequência
- Têm menos de 2GB
- Não podem ser controlados pelo DVC
Configurando o Git LFS
# Instalar o Git LFS
git lfs install
# Rastrear tipos de arquivos específicos
git lfs track "*.bin"
git lfs track "*.weights"
git lfs track "*.ckpt"
# Rastrear diretórios específicos
git lfs track "models/pretrained/*"
# Verificar rastreamento
cat .gitattributes
Exemplo de .gitattributes
# Git LFS tracking
*.bin filter=lfs diff=lfs merge=lfs -text
*.weights filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
models/pretrained/* filter=lfs diff=lfs merge=lfs -text
# Garantir finais de linha consistentes
*.py text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.json text eol=lf
*.md text eol=lf
# Notebooks como binário (evitar conflitos de merge)
*.ipynb binary
Estratégias de Branch para ML
GitFlow Adaptado para ML
Convenções de Nomes de Branch
# Branches de feature (infraestrutura, pipelines)
feature/data-pipeline
feature/monitoring-setup
feature/api-endpoint
# Branches de experimento (desenvolvimento de modelo)
experiment/baseline-rf
experiment/deep-learning-v1
experiment/transformer-tuning
# Branches de hotfix
hotfix/data-drift-fix
hotfix/prediction-bug
# Branches de release
release/v1.0.0
release/v1.1.0
Regras de Branch Específicas para ML
# .github/branch-protection.yml
branches:
main:
protection:
required_reviews: 2
required_status_checks:
- unit-tests
- integration-tests
- model-validation
enforce_admins: true
develop:
protection:
required_reviews: 1
required_status_checks:
- unit-tests
- linting
experiment/*:
protection:
required_reviews: 0 # Experimentos podem ser mesclados livremente
allow_force_push: true # Permitir rebase em experimentos
Convenções de Mensagens de Commit
Mensagens de Commit Estruturadas
# Formato: <type>(<scope>): <description>
# Tipos para projetos de ML:
# - data: Mudanças relacionadas a dados
# - feat: Nova funcionalidade
# - fix: Correção de bug
# - model: Mudanças na arquitetura do modelo
# - train: Mudanças relacionadas ao treinamento
# - eval: Mudanças de avaliação/métricas
# - docs: Documentação
# - refactor: Refatoração de código
# - test: Testes
# - ci: Mudanças em CI/CD
# - config: Mudanças de configuração
# Exemplos:
git commit -m "data: adicionar novo dataset de segmentação de clientes v2.1"
git commit -m "model: implementar mecanismo de atenção no encoder"
git commit -m "train: adicionar agendamento de learning rate"
git commit -m "eval: adicionar análise de precisão-recall"
git commit -m "config: atualizar hiperparâmetros de treinamento"
Template de Mensagem de Commit
# .gitmessage
# <type>(<scope>): <subject>
# |<---- Usar no Máximo 50 Caracteres ---->|
# Explique por que esta mudança está sendo feita
# |<---- Tente Limitar Cada Linha a no Máximo 72 Caracteres ---->|
# Forneça links ou chaves para tickets, artigos ou outros recursos relevantes
# Exemplo: Experiment: exp-2024-01-15-baseline
# MLflow Run: abc123
# --- COMMIT END ---
# Type pode ser:
# data (mudanças relacionadas a dados)
# feat (nova funcionalidade)
# fix (correção de bug)
# model (arquitetura do modelo)
# train (mudanças de treinamento)
# eval (avaliação/métricas)
# docs (documentação)
# refactor (refatoração de código)
# test (adição de testes)
# ci (mudanças em CI/CD)
# config (mudanças de configuração)
Defina como padrão:
git config --global commit.template ~/.gitmessage
Hooks de Pre-commit para ML
Configuração do Pre-commit
# .pre-commit-config.yaml
repos:
# Python geral
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-added-large-files
args: ['--maxkb=1000'] # Prevenir arquivos grandes
- id: detect-private-key
- id: check-merge-conflict
# Formatação Python
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# Type checking
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-all]
# Segurança
- repo: https://github.com/PyCQA/bandit
rev: 1.7.6
hooks:
- id: bandit
args: ['-c', 'pyproject.toml']
# Jupyter notebooks
- repo: https://github.com/kynan/nbstripout
rev: 0.6.1
hooks:
- id: nbstripout # Remove outputs dos notebooks antes do commit
# DVC
- repo: https://github.com/iterative/dvc
rev: 3.38.1
hooks:
- id: dvc-pre-commit
- id: dvc-pre-push
Instalando o Pre-commit
# Instalar pre-commit
pip install pre-commit
# Instalar hooks
pre-commit install
# Rodar em todos os arquivos
pre-commit run --all-files
Lidando com Jupyter Notebooks
O Problema dos Notebooks
Notebooks Jupyter são problemáticos para controle de versão:
- Contêm células de output (imagens, dataframes)
- Incluem contadores de execução
- Diffs grandes devido a mudanças de metadata
Solução 1: Remover Outputs
# Usando nbstripout
pip install nbstripout
# Instalar como filtro do git
nbstripout --install
# Remover manualmente
nbstripout notebook.ipynb
Solução 2: Jupytext para Notebooks
# Instalar jupytext
pip install jupytext
# Converter notebook para script Python
jupytext --to py:percent notebooks/experiment.ipynb
# Sincronizar notebook e script
jupytext --sync notebooks/experiment.ipynb
# pyproject.toml - Configuração do Jupytext
[tool.jupytext]
formats = "notebooks///ipynb,scripts///py:percent"
Solução 3: ReviewNB para Revisão de PR
Use o ReviewNB para revisões de PR de notebooks no GitHub:
- Diffs visuais para notebooks
- Comentários em células específicas
- Acompanhe mudanças de forma eficaz
Integração CI/CD
GitHub Actions para ML
# .github/workflows/ml-ci.yml
name: ML CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Histórico completo para DVC
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run linting
run: ruff check .
- name: Run type checking
run: mypy src/
- name: Run unit tests
run: pytest tests/unit/ -v --cov=src/
- name: Upload coverage
uses: codecov/codecov-action@v3
data-validation:
runs-on: ubuntu-latest
needs: lint-and-test
steps:
- uses: actions/checkout@v4
- name: Setup DVC
uses: iterative/setup-dvc@v1
- name: Pull data
run: dvc pull data/processed/
- name: Validate data
run: python src/data/validate.py
Principais Aprendizados
Summary
- Estruture seu repositório com separação clara de código, dados e modelos
- Use um .gitignore abrangente para excluir arquivos grandes e dados sensíveis
- Considere Git LFS para arquivos grandes essenciais que não podem usar DVC
- Adote branching específico para ML com branches de experimento
- Enforce convenções de commit para melhor rastreabilidade do histórico
- Use hooks de pre-commit para manter a qualidade do código
- Gerencie notebooks com cuidado usando nbstripout ou jupytext
Exercício Prático
- Crie um novo repositório de ML com a estrutura recomendada
- Configure o .gitignore e o .gitattributes
- Configure hooks de pre-commit
- Crie e faça merge de um branch de experimento