# 🧠 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') ]) ``` #### 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 ! 🚀