Chapitre 1 : Git pour les projets ML
Introduction
Git est la base du contrôle de version dans le développement logiciel, mais les projets ML introduisent des défis uniques qui nécessitent des pratiques et des configurations spécialisées.
Défis spécifiques à Git pour le ML
La nature unique du code ML
Contrairement aux logiciels traditionnels, les projets ML impliquent :
| Défi | Description |
|---|---|
| Fichiers volumineux | Jeux de données, poids des modèles (GB à TB) |
| Artéfacts binaires | Modèles entraînés, préprocesseurs |
| Prolifération d'expériences | Centaines de branches expérimentales |
| Non-déterminisme | Le même code peut produire des résultats différents |
| Chaos des notebooks | Notebooks Jupyter avec sorties intégrées |
Structure du dépôt pour les projets ML
Structure recommandée
ml-project/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ ├── training.yml
│ └── deployment.yml
├── data/
│ ├── raw/ # Données originales (suivies par DVC)
│ ├── processed/ # Données transformées (suivies par DVC)
│ └── features/ # Exports du magasin de caractéristiques
├── models/
│ ├── artifacts/ # Modèles entraînés (suivis par DVC)
│ └── configs/ # Configurations des modèles
├── notebooks/
│ ├── exploration/ # Notebooks d'EDA
│ └── experiments/ # Notebooks d'expérimentation
├── 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 complet pour le ML
# .gitignore pour les projets 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
# ===== Environnements virtuels =====
venv/
ENV/
env/
.venv/
.conda/
# ===== Notebooks Jupyter =====
.ipynb_checkpoints/
*/.ipynb_checkpoints/
# ===== Fichiers de données =====
# Fichiers de données volumineux (suivis par DVC à la place)
*.csv
*.parquet
*.feather
*.h5
*.hdf5
*.pkl
*.pickle
!data/.gitkeep
data/raw/*
data/processed/*
data/features/*
# ===== Artéfacts de modèles =====
# Modèles entraînés (suivis par DVC à la place)
*.pt
*.pth
*.onnx
*.pb
*.h5
*.keras
*.joblib
*.model
models/artifacts/*
!models/artifacts/.gitkeep
# ===== Artéfacts MLOps =====
mlruns/
outputs/
wandb/
.neptune/
tensorboard_logs/
# ===== IDE =====
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# ===== Variables d'environnement =====
.env
.env.local
*.env
# ===== Secrets =====
secrets/
credentials/
*.pem
*.key
# ===== Logs =====
logs/
*.log
# ===== Cache =====
.cache/
.pytest_cache/
.mypy_cache/
.ruff_cache/
# ===== Couverture =====
htmlcov/
.coverage
.coverage.*
coverage.xml
Git LFS pour les fichiers volumineux
Quand utiliser Git LFS
Git LFS (Large File Storage) est utile pour les fichiers qui :
- Sont essentiels au dépôt
- Sont mis à jour peu fréquemment
- Sont plus petits que 2GB
- Ne peuvent pas être suivis par DVC
Configuration de Git LFS
# Installer Git LFS
git lfs install
# Suivre des types de fichiers spécifiques
git lfs track "*.bin"
git lfs track "*.weights"
git lfs track "*.ckpt"
# Suivre des répertoires spécifiques
git lfs track "models/pretrained/*"
# Vérifier le suivi
cat .gitattributes
Exemple de .gitattributes
# Suivi Git LFS
*.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
# Assurer des fins de ligne cohérentes
*.py text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.json text eol=lf
*.md text eol=lf
# Notebooks en tant que binaire (prévenir les conflits de fusion)
*.ipynb binary
Stratégies de branchement pour le ML
GitFlow adapté pour le ML
Conventions de nommage des branches
# Branches de fonctionnalités (infrastructure, pipelines)
feature/data-pipeline
feature/monitoring-setup
feature/api-endpoint
# Branches d'expérimentation (développement de modèles)
experiment/baseline-rf
experiment/deep-learning-v1
experiment/transformer-tuning
# Branches de correctifs
hotfix/data-drift-fix
hotfix/prediction-bug
# Branches de version
release/v1.0.0
release/v1.1.0
Règles de branchement spécifiques au 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 # Les expériences peuvent être fusionnées librement
allow_force_push: true # Autoriser le rebasage des expériences
Conventions de message de commit
Messages de commit structurés
# Format : <type>(<scope>): <description>
# Types pour les projets ML :
# - data : Changements liés aux données
# - feat : Nouvelle fonctionnalité
# - fix : Correction de bug
# - model : Changements d'architecture de modèle
# - train : Changements liés à l'entraînement
# - eval : Changements d'évaluation/métriques
# - docs : Documentation
# - refactor : Refactorisation de code
# - test : Tests
# - ci : Changements CI/CD
# - config : Changements de configuration
# Exemples :
git commit -m "data: add new customer segmentation dataset v2.1"
git commit -m "model: implement attention mechanism in encoder"
git commit -m "train: add learning rate scheduling"
git commit -m "eval: add precision-recall analysis"
git commit -m "config: update training hyperparameters"
Modèle de message de commit
# .gitmessage
# <type>(<scope>): <subject>
# |<---- Using a Maximum Of 50 Characters ---->|
# Explain why this change is being made
# |<---- Try To Limit Each Line to a Maximum Of 72 Characters ---->|
# Provide links or keys to any relevant tickets, articles or other resources
# Example: Experiment: exp-2024-01-15-baseline
# MLflow Run: abc123
# --- COMMIT END ---
# Type can be:
# data (data-related changes)
# feat (new feature)
# fix (bug fix)
# model (model architecture)
# train (training changes)
# eval (evaluation/metrics)
# docs (documentation)
# refactor (refactoring code)
# test (adding tests)
# ci (CI/CD changes)
# config (configuration changes)
Définissez-le comme modèle par défaut :
git config --global commit.template ~/.gitmessage
Hooks de pré-commit pour le ML
Configuration de pré-commit
# .pre-commit-config.yaml
repos:
# Python général
- 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'] # Prévenir les fichiers volumineux
- id: detect-private-key
- id: check-merge-conflict
# Formatage Python
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# Vérification de type
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-all]
# Sécurité
- repo: https://github.com/PyCQA/bandit
rev: 1.7.6
hooks:
- id: bandit
args: ['-c', 'pyproject.toml']
# Notebooks Jupyter
- repo: https://github.com/kynan/nbstripout
rev: 0.6.1
hooks:
- id: nbstripout # Supprimer les sorties des notebooks avant le commit
# DVC
- repo: https://github.com/iterative/dvc
rev: 3.38.1
hooks:
- id: dvc-pre-commit
- id: dvc-pre-push
Installation de pré-commit
# Installer pré-commit
pip install pre-commit
# Installer les hooks
pre-commit install
# Exécuter sur tous les fichiers
pre-commit run --all-files
Gestion des notebooks Jupyter
Le problème des notebooks
Les notebooks Jupyter posent problème pour le contrôle de version :
- Contiennent des cellules de sortie (images, dataframes)
- Incluent des comptes d'exécution
- Diffs volumineux dus aux changements de métadonnées
Solution 1 : Supprimer les sorties
# Utilisation de nbstripout
pip install nbstripout
# Installer comme filtre git
nbstripout --install
# Supprimer manuellement
nbstripout notebook.ipynb
Solution 2 : Jupytext pour les notebooks
# Installer jupytext
pip install jupytext
# Convertir le notebook en script Python
jupytext --to py:percent notebooks/experiment.ipynb
# Synchroniser le notebook et le script
jupytext --sync notebooks/experiment.ipynb
# pyproject.toml - Configuration Jupytext
[tool.jupytext]
formats = "notebooks///ipynb,scripts///py:percent"
Solution 3 : ReviewNB pour les revues PR
Utilisez ReviewNB pour les revues PR GitHub des notebooks :
- Diffs visuels pour les notebooks
- Commentaires sur des cellules spécifiques
- Suivi efficace des changements
Intégration CI/CD
GitHub Actions pour le 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 # Historique complet pour 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
Points clés à retenir
Résumé
- Structurez votre dépôt avec une séparation claire du code, des données et des modèles
- Utilisez un .gitignore complet pour exclure les fichiers volumineux et les données sensibles
- Envisagez Git LFS pour les fichiers volumineux essentiels qui ne peuvent pas utiliser DVC
- Adoptez un branchement spécifique au ML avec des branches d'expérimentation
- Imposez des conventions de commit pour un meilleur suivi de l'historique
- Utilisez des hooks de pré-commit pour maintenir la qualité du code
- Gérez les notebooks avec soin avec nbstripout ou jupytext
Exercice pratique
- Créez un nouveau dépôt ML avec la structure recommandée
- Configurez .gitignore et .gitattributes
- Configurez les hooks de pré-commit
- Créez et fusionnez une branche d'expérimentation
Suivant : Chapitre 2 : Contrôle de version des données (DVC)