2025-11-14 12:26:34 +01:00

1078 lines
33 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🧠 Guide Complet : Workflows Reproductibles pour l'IA
> **Un tutoriel complet pour apprendre à créer des workflows d'IA reproductibles**
---
## 📋 Table des Matières
1. [Démarrage Rapide](#-démarrage-rapide)
2. [Introduction aux Concepts](#-introduction-aux-concepts)
3. [Guide Étape par Étape](#-guide-étape-par-étape)
4. [Documentation Technique Détaillée](#-documentation-technique-détaillée)
5. [Points Clés à Retenir](#-points-clés-à-retenir)
6. [Résolution des Problèmes](#-résolution-des-problèmes)
7. [Pour Aller Plus Loin](#-pour-aller-plus-loin)
---
## 🚀 Démarrage Rapide
### Prérequis
```bash
# Installer les dépendances nécessaires
pip install numpy tensorflow scikit-learn joblib jupyter
```
### Lancer le Lab
```bash
# Ouvrir le notebook
jupyter notebook lab.ipynb
```
### Exécuter les Cellules
1. Exécutez les cellules **une par une** avec `Shift + Enter`
2. Observez les résultats après chaque étape
3. Vérifiez que la précision finale est identique après rechargement
**Temps estimé** : 15-20 minutes
---
## 🎓 Introduction aux Concepts
### Qu'est-ce que la Reproductibilité ?
La **reproductibilité** signifie obtenir exactement les **mêmes résultats** chaque fois que vous exécutez votre code. C'est essentiel en IA pour :
| Avantage | Pourquoi c'est important |
|----------|-------------------------|
| 🐛 **Débogage** | Identifier et corriger les erreurs facilement |
| 🤝 **Collaboration** | D'autres peuvent reproduire vos résultats |
| 📊 **Validation** | Prouver la fiabilité de vos modèles |
| 🔬 **Recherche** | Publier des résultats vérifiables |
| 🏭 **Production** | Garantir la cohérence en production |
### Pourquoi les Résultats Peuvent Varier ?
Sans reproductibilité, vous pouvez obtenir des résultats différents à cause de :
1. **Initialisation aléatoire** des poids du réseau de neurones
2. **Division aléatoire** des données train/test
3. **Ordre aléatoire** des données pendant l'entraînement
4. **Dropout aléatoire** dans les réseaux de neurones
### La Solution : Les Graines Aléatoires (Random Seeds)
Une **graine aléatoire** est un nombre qui initialise un générateur de nombres pseudo-aléatoires. Avec la même graine, vous obtenez toujours la même séquence de nombres "aléatoires".
**Analogie** : C'est comme donner le même point de départ à une personne qui mélange un jeu de cartes. Si elle mélange toujours de la même façon depuis le même point, elle obtiendra toujours le même ordre de cartes.
---
## 📖 Guide Étape par Étape
### Étape 1⃣ : Configuration de l'Environnement
#### Code
```python
import numpy as np
import tensorflow as tf
from tensorflow import keras
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import random
import joblib
```
#### Explication
- **numpy** : Calculs mathématiques et manipulation de tableaux
- **tensorflow/keras** : Framework pour créer des réseaux de neurones
- **sklearn** : Outils pour le machine learning (données, prétraitement, validation)
- **random** : Générateur de nombres aléatoires Python
- **joblib** : Sauvegarde et chargement d'objets Python
---
### Étape 2⃣ : Définir les Graines Aléatoires
#### Code
```python
random.seed(42)
np.random.seed(42)
tf.random.set_seed(42)
```
#### Explication Détaillée
| Fonction | Bibliothèque | Ce qu'elle contrôle |
|----------|--------------|---------------------|
| `random.seed(42)` | Python standard | Fonctions aléatoires de base |
| `np.random.seed(42)` | NumPy | Génération de tableaux aléatoires |
| `tf.random.set_seed(42)` | TensorFlow | Initialisation des poids, dropout, etc. |
**Pourquoi 42 ?** C'est une convention (référence au *Guide du voyageur galactique*). Vous pouvez utiliser n'importe quel nombre, l'important est d'utiliser toujours le **même**.
#### À Retenir
-**Toujours** définir les graines en **premier**
- ✅ Utiliser la **même valeur** (ici 42) partout
- ✅ Définir les graines pour **toutes** les bibliothèques utilisées
---
### Étape 3⃣ : Générer des Données Synthétiques
#### Code
```python
X, y = make_classification(
n_samples=1000, # 1000 exemples
n_features=20, # 20 caractéristiques
n_informative=15, # 15 caractéristiques utiles
n_redundant=5, # 5 caractéristiques redondantes
n_classes=2, # Classification binaire (2 classes)
random_state=42 # Graine aléatoire
)
```
#### Explication des Paramètres
| Paramètre | Valeur | Signification |
|-----------|--------|---------------|
| `n_samples` | 1000 | Nombre d'exemples générés |
| `n_features` | 20 | Nombre total de caractéristiques (colonnes) |
| `n_informative` | 15 | Caractéristiques réellement utiles pour la prédiction |
| `n_redundant` | 5 | Caractéristiques corrélées aux informatives (bruit) |
| `n_classes` | 2 | Nombre de classes à prédire (0 ou 1) |
| `random_state` | 42 | **CRUCIAL** pour la reproductibilité |
#### Structure des Données
```
X : Matrice de forme (1000, 20)
- 1000 lignes (exemples)
- 20 colonnes (caractéristiques)
y : Vecteur de forme (1000,)
- Contient 0 ou 1 pour chaque exemple
```
#### À Retenir
-`X` contient les **caractéristiques** (données d'entrée)
-`y` contient les **labels** (ce qu'on veut prédire)
-`random_state=42` garantit les **mêmes données** à chaque exécution
---
### Étape 4⃣ : Diviser les Données (Train/Test Split)
#### Code
```python
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42
)
```
#### Explication du Concept
**Pourquoi diviser les données ?**
- **Données d'entraînement (80%)** : Le modèle apprend sur ces données
- **Données de test (20%)** : On évalue la performance sur des données **non vues**
**Analogie** : C'est comme étudier avec des exercices (entraînement) puis passer un examen avec de nouvelles questions (test).
#### Résultat de la Division
```
X_train : 800 exemples pour l'entraînement
y_train : 800 labels correspondants
X_test : 200 exemples pour le test
y_test : 200 labels correspondants
```
#### À Retenir
-**80/20** est une proportion standard (peut être 70/30 ou 90/10)
-**random_state=42** garantit la **même division** à chaque fois
-**Ne jamais** entraîner sur les données de test (c'est de la triche !)
---
### Étape 5⃣ : Normaliser les Données
#### Code
```python
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
```
#### Explication du StandardScaler
**Qu'est-ce que la normalisation ?**
Le `StandardScaler` transforme les données pour avoir :
- **Moyenne = 0**
- **Écart-type = 1**
**Formule mathématique** :
```
valeur_normalisée = (valeur - moyenne) / écart-type
```
#### Exemple Concret
```
Avant normalisation :
caractéristique_1 : [1, 2, 3, 1000]
caractéristique_2 : [0.1, 0.2, 0.3, 0.4]
Après normalisation :
caractéristique_1 : [-0.5, -0.4, -0.3, 2.1]
caractéristique_2 : [-0.8, -0.2, 0.4, 1.0]
```
#### Pourquoi C'est Important ?
| Sans normalisation | Avec normalisation |
|-------------------|-------------------|
| ❌ Les grandes valeurs dominent | ✅ Toutes les caractéristiques ont la même échelle |
| ❌ Apprentissage lent | ✅ Apprentissage plus rapide |
| ❌ Résultats instables | ✅ Résultats stables |
#### Différence entre fit_transform et transform
```python
scaler.fit_transform(X_train) # Apprend ET transforme
scaler.transform(X_test) # Transforme seulement
```
**IMPORTANT** : On apprend les statistiques (moyenne, écart-type) **uniquement** sur les données d'entraînement, puis on applique la **même transformation** aux données de test.
#### À Retenir
- ✅ Toujours **fit** sur train, **transform** sur test
- ✅ Évite les **fuites de données** (data leakage)
- ✅ Améliore les **performances** du modèle
---
### Étape 6⃣ : Sauvegarder les Données
#### Code
```python
joblib.dump((X_train, y_train), 'train_data.pkl')
joblib.dump((X_test, y_test), 'test_data.pkl')
```
#### Explication
**Pourquoi sauvegarder les données ?**
1. **Reproductibilité** : Garantir l'utilisation des mêmes données
2. **Efficacité** : Ne pas recalculer à chaque fois
3. **Traçabilité** : Savoir exactement quelles données ont été utilisées
**Format .pkl** : Format de sérialisation Python (pickle)
#### À Retenir
- ✅ Sauvegarde **après** la division train/test
- ✅ Permet de recharger les **mêmes données** plus tard
- ✅ Essentiel pour comparer différents modèles sur les **mêmes données**
---
### Étape 7⃣ : Construire le Réseau de Neurones
#### Code
```python
model = keras.Sequential([
keras.layers.Dense(32, activation='relu', input_shape=(X_train.shape[1],)),
keras.layers.Dense(16, activation='relu'),
keras.layers.Dense(1, activation='sigmoid')
])
```
> **⚠️ Note sur la Dépréciation (Deprecated)**
>
> L'utilisation de `input_shape` directement dans la première couche `Dense` est **dépréciée** dans les versions récentes de Keras/TensorFlow. Vous verrez cet avertissement :
> ```
> UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer.
> ```
>
> **Méthode Moderne Recommandée** :
> ```python
> model = keras.Sequential([
> keras.layers.Input(shape=(X_train.shape[1],)), # Couche Input séparée
> keras.layers.Dense(32, activation='relu'),
> keras.layers.Dense(16, activation='relu'),
> keras.layers.Dense(1, activation='sigmoid')
> ])
> ```
>
> **Pourquoi ce changement ?**
> - Plus claire : sépare l'entrée de la première couche
> - Plus flexible : facilite les architectures complexes
> - Meilleure pratique : suit les standards modernes de Keras
>
> **Dans ce lab** : Nous utilisons l'ancienne méthode pour la simplicité pédagogique, mais dans vos projets futurs, **utilisez la méthode moderne avec `Input()`**.
#### Architecture du Modèle
```
Entrée (20 caractéristiques)
Couche 1 : 32 neurones + ReLU
Couche 2 : 16 neurones + ReLU
Couche 3 : 1 neurone + Sigmoid
Sortie (probabilité 0-1)
```
#### Explication Détaillée de Chaque Couche
| Couche | Neurones | Activation | Rôle |
|--------|----------|------------|------|
| Couche 1 | 32 | ReLU | Extraction de caractéristiques complexes |
| Couche 2 | 16 | ReLU | Combinaison des caractéristiques |
| Couche 3 | 1 | Sigmoid | Probabilité de la classe 1 (0 à 1) |
#### Fonctions d'Activation
**ReLU (Rectified Linear Unit)**
```
ReLU(x) = max(0, x)
Exemple :
ReLU(-5) = 0
ReLU(0) = 0
ReLU(3) = 3
```
- **Avantage** : Rapide, évite le gradient qui disparaît
- **Usage** : Couches cachées
**Sigmoid**
```
Sigmoid(x) = 1 / (1 + e^(-x))
Exemple :
Sigmoid(-∞) = 0
Sigmoid(0) = 0.5
Sigmoid(+∞) = 1
```
- **Avantage** : Sortie entre 0 et 1 (probabilité)
- **Usage** : Couche de sortie pour classification binaire
#### À Retenir
-**Sequential** = couches empilées les unes après les autres
-**Dense** = couche entièrement connectée
-**ReLU** pour les couches cachées, **Sigmoid** pour la sortie binaire
---
### Étape 8⃣ : Compiler le Modèle
#### Code
```python
model.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy']
)
```
#### Explication des Paramètres
**Optimizer : Adam**
- **Rôle** : Algorithme qui ajuste les poids du réseau
- **Pourquoi Adam ?**
- Adapte automatiquement le taux d'apprentissage
- Fonctionne bien dans la plupart des cas
- Combine les avantages de RMSprop et Momentum
**Loss : Binary Crossentropy**
- **Rôle** : Mesure l'erreur du modèle
- **Formule** : `-[y*log(p) + (1-y)*log(1-p)]`
- **Pourquoi ?** Adapté à la classification binaire (0 ou 1)
**Metrics : Accuracy**
- **Rôle** : Métrique de performance
- **Formule** : `(Nombre de prédictions correctes) / (Nombre total)`
- **Exemple** : 95% d'accuracy = 95 prédictions correctes sur 100
#### À Retenir
-**Adam** est un excellent optimiseur par défaut
-**binary_crossentropy** pour la classification binaire
-**Compiler** avant d'entraîner le modèle
---
### Étape 9⃣ : Entraîner le Modèle
#### Code
```python
model.fit(X_train, y_train, epochs=20, batch_size=32, validation_split=0.1)
```
#### Explication des Paramètres
| Paramètre | Valeur | Signification |
|-----------|--------|---------------|
| `epochs` | 20 | Nombre de passages complets sur les données |
| `batch_size` | 32 | Nombre d'exemples traités avant mise à jour des poids |
| `validation_split` | 0.1 | 10% des données d'entraînement pour la validation |
#### Comprendre l'Entraînement
**Qu'est-ce qu'une Epoch ?**
- 1 epoch = le modèle voit **tous** les exemples d'entraînement une fois
- 20 epochs = le modèle voit 20 fois chaque exemple
**Qu'est-ce qu'un Batch ?**
```
800 exemples d'entraînement / batch_size 32 = 25 batches par epoch
Epoch 1:
Batch 1 : exemples 1-32 → mise à jour des poids
Batch 2 : exemples 33-64 → mise à jour des poids
...
Batch 25 : exemples 769-800 → mise à jour des poids
```
**Validation Split**
- Prend 10% de X_train pour validation
- **Entraînement** : 720 exemples (90% de 800)
- **Validation** : 80 exemples (10% de 800)
- Permet de surveiller le **surapprentissage** (overfitting)
#### Qu'Observer Pendant l'Entraînement ?
```
Epoch 1/20
25/25 [======] - loss: 0.6932 - accuracy: 0.5000 - val_loss: 0.6931 - val_accuracy: 0.5000
Epoch 20/20
25/25 [======] - loss: 0.1234 - accuracy: 0.9500 - val_loss: 0.1456 - val_accuracy: 0.9300
```
| Métrique | Signification |
|----------|---------------|
| `loss` | Erreur sur données d'entraînement (plus bas = mieux) |
| `accuracy` | Précision sur données d'entraînement |
| `val_loss` | Erreur sur données de validation |
| `val_accuracy` | Précision sur données de validation |
#### À Retenir
-**Loss diminue** = le modèle apprend
-**val_loss >> loss** = surapprentissage (overfitting)
- ✅ Surveiller les deux métriques pour détecter les problèmes
---
### Étape 🔟 : Évaluer le Modèle
#### Code
```python
loss, accuracy = model.evaluate(X_test, y_test)
print(f"Test accuracy: {accuracy:.2f}")
```
#### Explication
**Pourquoi évaluer sur les données de test ?**
| Données | Utilisées pour | Ce qu'elles mesurent |
|---------|----------------|----------------------|
| Entraînement | Apprendre | Performance sur données connues |
| Validation | Ajuster | Performance pendant l'entraînement |
| **Test** | **Évaluer** | **Performance sur données inconnues** |
**L'accuracy de test est la métrique finale** qui compte !
#### Interpréter les Résultats
```python
Test accuracy: 0.95 # 95% de précision
```
Cela signifie :
- Sur 200 exemples de test
- Le modèle prédit correctement 190 exemples
- Et se trompe sur 10 exemples
#### À Retenir
**Jamais** entraîner sur les données de test
✅ L'accuracy de test mesure la **vraie performance**
✅ C'est le résultat qu'on doit **reproduire**
---
### Étape 1⃣1⃣ : Sauvegarder le Modèle
#### Code
```python
model.save('my_model.keras')
```
#### Explication
**Que sauvegarde-t-on ?**
1. **Architecture** du modèle (nombre de couches, neurones)
2. **Poids** entraînés de tous les neurones
3. **Configuration** de compilation (optimizer, loss)
4. **État** de l'optimizer
**Format .keras** : Format moderne de Keras (recommandé vs .h5)
#### À Retenir
✅ Sauvegarde **après** l'entraînement
✅ Contient **tout** pour réutiliser le modèle
✅ Permet de faire des prédictions sans ré-entraîner
---
### Étape 1⃣2⃣ : Recharger et Vérifier la Reproductibilité
#### Code
```python
from tensorflow.keras.models import load_model
modelReloaded = load_model('my_model.keras')
X_train_reloaded, y_train_reloaded = joblib.load('train_data.pkl')
X_test_reloaded, y_test_reloaded = joblib.load('test_data.pkl')
loss_reloaded, accuracy_reloaded = modelReloaded.evaluate(X_test_reloaded, y_test_reloaded)
print(f"Test accuracy: {accuracy_reloaded:.2f}")
```
#### Vérification de la Reproductibilité
**Test de Reproductibilité Réussi ✅**
```
Modèle original : Test accuracy: 0.95
Modèle rechargé : Test accuracy: 0.95
```
**Si les valeurs sont différentes ❌**
- Vérifier que toutes les graines aléatoires sont définies
- Vérifier que les mêmes données sont utilisées
- Vérifier les versions des bibliothèques
#### À Retenir
✅ Les deux accuracies doivent être **identiques**
✅ C'est la preuve que le workflow est **reproductible**
✅ Vous pouvez partager votre travail en toute confiance
---
## 🔧 Documentation Technique Détaillée
### Architecture Complète du Workflow
```
┌─────────────────────────────────────────────────────┐
│ 1. INITIALISATION │
│ • random.seed(42) │
│ • np.random.seed(42) │
│ • tf.random.set_seed(42) │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 2. GÉNÉRATION DES DONNÉES │
│ • make_classification(random_state=42) │
│ • X: (1000, 20), y: (1000,) │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 3. DIVISION TRAIN/TEST │
│ • train_test_split(random_state=42) │
│ • Train: 800, Test: 200 │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 4. NORMALISATION │
│ • StandardScaler.fit_transform(X_train) │
│ • StandardScaler.transform(X_test) │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 5. SAUVEGARDE DES DONNÉES │
│ • joblib.dump(train_data.pkl) │
│ • joblib.dump(test_data.pkl) │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 6. CONSTRUCTION DU MODÈLE │
│ • Input(20) → Dense(32) → Dense(16) → Dense(1) │
│ • ReLU, ReLU, Sigmoid │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 7. COMPILATION │
│ • optimizer='adam' │
│ • loss='binary_crossentropy' │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 8. ENTRAÎNEMENT │
│ • model.fit(epochs=20, batch_size=32) │
│ • validation_split=0.1 │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 9. ÉVALUATION │
│ • model.evaluate(X_test, y_test) │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 10. SAUVEGARDE DU MODÈLE │
│ • model.save('my_model.keras') │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ 11. RECHARGEMENT ET VÉRIFICATION │
│ • load_model(), joblib.load() │
│ • Vérification: accuracy identique ✓ │
└─────────────────────────────────────────────────────┘
```
### Paramètres Importants
#### make_classification
```python
X, y = make_classification(
n_samples=1000, # Nombre d'exemples
n_features=20, # Nombre de caractéristiques
n_informative=15, # Caractéristiques informatives
n_redundant=5, # Caractéristiques redondantes
n_classes=2, # Nombre de classes
random_state=42, # CRUCIAL: graine aléatoire
flip_y=0.0, # Pourcentage de labels bruités (défaut: 0.01)
class_sep=1.0, # Séparation entre les classes (défaut: 1.0)
shuffle=True # Mélanger les exemples (défaut: True)
)
```
#### train_test_split
```python
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2, # 20% pour le test
train_size=None, # Automatique (1 - test_size)
random_state=42, # CRUCIAL: graine aléatoire
shuffle=True, # Mélanger avant division (défaut: True)
stratify=None # Préserver la distribution des classes (None par défaut)
)
```
#### StandardScaler
```python
scaler = StandardScaler(
copy=True, # Copier les données (défaut: True)
with_mean=True, # Centrer autour de 0 (défaut: True)
with_std=True # Mettre à l'échelle à 1 (défaut: True)
)
```
#### model.fit
```python
history = model.fit(
X_train, y_train,
epochs=20, # Nombre d'epochs
batch_size=32, # Taille des batchs
validation_split=0.1, # % pour validation
validation_data=None, # Ou tuple (X_val, y_val)
verbose=1, # Affichage (0: rien, 1: barre, 2: epoch)
shuffle=True, # Mélanger à chaque epoch (défaut: True)
callbacks=None # Callbacks (early stopping, etc.)
)
```
### Versions Recommandées
```
numpy==1.24.3
tensorflow==2.15.0
scikit-learn==1.3.2
joblib==1.3.2
```
Installer avec :
```bash
pip install numpy==1.24.3 tensorflow==2.15.0 scikit-learn==1.3.2 joblib==1.3.2
```
---
## 💡 Points Clés à Retenir
### Les 10 Commandements de la Reproductibilité
1.**Tu définiras les graines aléatoires** au début de ton code
2.**Tu utiliseras random_state** dans toutes les fonctions qui l'acceptent
3.**Tu sauvegarderas tes données** après division train/test
4.**Tu ne fit que sur train** et transform sur test
5.**Tu ne regarderas jamais** les données de test avant l'évaluation finale
6.**Tu sauvegarderas ton modèle** après entraînement
7.**Tu documenteras les versions** de tes bibliothèques
8.**Tu vérifieras la reproductibilité** en rechargeant tout
9.**Tu utiliseras le même environnement** (virtualenv, conda)
10.**Tu partageras ton code** avec les instructions complètes
### Checklist de Reproductibilité
Avant de partager votre travail, vérifiez :
- [ ] Toutes les graines aléatoires sont définies
- [ ] `random_state=42` est utilisé partout
- [ ] Les données train/test sont sauvegardées
- [ ] Le modèle est sauvegardé
- [ ] Les versions des bibliothèques sont documentées
- [ ] Le code est testé sur une machine différente
- [ ] Les résultats sont identiques après rechargement
- [ ] Un README explique comment exécuter le code
### Erreurs Courantes à Éviter
| Erreur | Conséquence | Solution |
|--------|-------------|----------|
| ❌ Oublier `random_state` | Résultats différents à chaque fois | Toujours définir `random_state=42` |
| ❌ Fit sur test | Data leakage | Fit sur train uniquement |
| ❌ Ne pas sauvegarder les données | Impossible de reproduire exactement | Sauvegarder après split |
| ❌ Mélanger train et test | Évaluation biaisée | Garder séparés |
| ❌ Différentes versions de bibliothèques | Résultats différents | Documenter les versions |
---
## 🐛 Résolution des Problèmes
### Problème 1 : Résultats Différents à Chaque Exécution
**Symptôme** : L'accuracy change à chaque fois
**Solutions** :
```python
# 1. Vérifier que TOUTES les graines sont définies
random.seed(42)
np.random.seed(42)
tf.random.set_seed(42)
# 2. Vérifier random_state dans train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
# 3. Vérifier random_state dans make_classification
X, y = make_classification(..., random_state=42)
```
### Problème 2 : Erreur de Shape
**Symptôme** : `ValueError: Input shape mismatch`
**Solutions** :
```python
# Vérifier les shapes
print(f"X_train shape: {X_train.shape}")
print(f"y_train shape: {y_train.shape}")
# S'assurer que input_shape correspond
input_shape=(X_train.shape[1],) # Pas X_train.shape !
```
### Problème 3 : Loss ne Diminue Pas
**Symptôme** : La loss reste élevée ou augmente
**Solutions** :
```python
# 1. Vérifier que les données sont normalisées
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
# 2. Essayer un taux d'apprentissage plus petit
from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.001)
model.compile(optimizer=optimizer, ...)
# 3. Augmenter le nombre d'epochs
model.fit(..., epochs=50)
```
### Problème 4 : Overfitting
**Symptôme** : `train_accuracy >> val_accuracy`
**Solutions** :
```python
# 1. Ajouter du Dropout
model = keras.Sequential([
keras.layers.Dense(32, activation='relu'),
keras.layers.Dropout(0.3), # Dropout 30%
keras.layers.Dense(16, activation='relu'),
keras.layers.Dropout(0.3),
keras.layers.Dense(1, activation='sigmoid')
])
# 2. Réduire la complexité du modèle
model = keras.Sequential([
keras.layers.Dense(16, activation='relu'), # Moins de neurones
keras.layers.Dense(1, activation='sigmoid')
])
# 3. Ajouter de la régularisation L2
from tensorflow.keras.regularizers import l2
keras.layers.Dense(32, activation='relu', kernel_regularizer=l2(0.01))
```
### Problème 5 : Erreur lors du Rechargement
**Symptôme** : `FileNotFoundError` ou modèle ne charge pas
**Solutions** :
```python
# 1. Vérifier que le fichier existe
import os
print(os.path.exists('my_model.keras')) # Doit être True
# 2. Utiliser le chemin absolu
import os
model_path = os.path.abspath('my_model.keras')
model = load_model(model_path)
# 3. Vérifier la version de TensorFlow
import tensorflow as tf
print(tf.__version__) # Doit être >= 2.12 pour .keras
```
---
## 📚 Pour Aller Plus Loin
### Améliorations Possibles
#### 1. Ajouter des Callbacks
```python
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# Arrêt anticipé si pas d'amélioration
early_stop = EarlyStopping(
monitor='val_loss',
patience=5, # Attendre 5 epochs
restore_best_weights=True
)
# Sauvegarder le meilleur modèle
checkpoint = ModelCheckpoint(
'best_model.keras',
monitor='val_accuracy',
save_best_only=True
)
model.fit(
X_train, y_train,
epochs=100,
callbacks=[early_stop, checkpoint]
)
```
#### 2. Hyperparamètres Tuning
```python
# Tester différentes architectures
architectures = [
[32, 16],
[64, 32, 16],
[128, 64, 32]
]
for arch in architectures:
model = build_model(arch)
model.fit(...)
# Comparer les résultats
```
#### 3. Cross-Validation
```python
from sklearn.model_selection import KFold
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
for fold, (train_idx, val_idx) in enumerate(kfold.split(X)):
X_train_fold = X[train_idx]
y_train_fold = y[train_idx]
# Entraîner et évaluer
```
#### 4. Logging et Tracking
```python
import mlflow
mlflow.start_run()
mlflow.log_params({
"epochs": 20,
"batch_size": 32,
"architecture": "32-16-1"
})
history = model.fit(...)
mlflow.log_metrics({
"accuracy": accuracy,
"loss": loss
})
mlflow.end_run()
```
### Concepts Avancés
#### Stratified Split
Pour les datasets déséquilibrés :
```python
X_train, X_test, y_train, y_test = train_test_split(
X, y,
stratify=y, # Préserve la distribution des classes
random_state=42
)
```
#### K-Fold Cross-Validation
Pour une évaluation plus robuste :
```python
from sklearn.model_selection import cross_val_score
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(random_state=42)
scores = cross_val_score(clf, X, y, cv=5)
print(f"Accuracy: {scores.mean():.2f} (+/- {scores.std():.2f})")
```
#### Data Augmentation
Pour augmenter la taille du dataset :
```python
from sklearn.utils import resample
# Bootstrap sampling
X_resampled, y_resampled = resample(
X_train, y_train,
n_samples=len(X_train) * 2,
random_state=42
)
```
### Ressources Supplémentaires
#### Documentation Officielle
- [TensorFlow/Keras](https://www.tensorflow.org/guide/keras)
- [Scikit-learn](https://scikit-learn.org/stable/documentation.html)
- [NumPy](https://numpy.org/doc/)
#### Tutoriels Recommandés
- [Machine Learning Mastery](https://machinelearningmastery.com/)
- [Towards Data Science](https://towardsdatascience.com/)
- [Fast.ai](https://www.fast.ai/)
#### Livres
- "Hands-On Machine Learning" - Aurélien Géron
- "Deep Learning with Python" - François Chollet
- "Python Machine Learning" - Sebastian Raschka
---
## 📝 Résumé en 5 Minutes
### Ce que Vous Avez Appris
1. **La reproductibilité** est essentielle en IA
2. **Les graines aléatoires** permettent d'obtenir les mêmes résultats
3. **Diviser train/test** correctement est crucial
4. **Normaliser les données** améliore l'apprentissage
5. **Sauvegarder modèle et données** permet de reproduire
### Code Minimal Reproductible
```python
import numpy as np
import tensorflow as tf
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import random
# 1. Graines aléatoires
random.seed(42)
np.random.seed(42)
tf.random.set_seed(42)
# 2. Données
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
# 3. Division et normalisation
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 4. Modèle
model = tf.keras.Sequential([
tf.keras.layers.Dense(32, activation='relu', input_shape=(20,)),
tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 5. Entraînement
model.fit(X_train, y_train, epochs=20, batch_size=32)
# 6. Évaluation
loss, accuracy = model.evaluate(X_test, y_test)
print(f"Accuracy: {accuracy:.2f}")
# 7. Sauvegarde
model.save('model.keras')
```
---
## 🎯 Conclusion
Félicitations ! Vous avez maintenant une compréhension complète des workflows reproductibles en IA.
**N'oubliez pas** :
- ✅ Toujours définir les graines aléatoires
- ✅ Utiliser `random_state` partout
- ✅ Sauvegarder modèles et données
- ✅ Vérifier la reproductibilité
**Prochaines étapes** :
1. Pratiquez avec vos propres datasets
2. Expérimentez avec différentes architectures
3. Explorez les techniques avancées
4. Partagez votre travail reproductible !
---
## 📞 Support
Si vous rencontrez des problèmes :
1. Relisez la section **Résolution des Problèmes**
2. Vérifiez les versions de vos bibliothèques
3. Consultez la documentation officielle
4. Recherchez sur Stack Overflow
Bon apprentissage ! 🚀