1 Introduction : réseaux de neurones FCNN à propagation avant avec Keras.
Tout comme dans le dernier TD, dans ce TD, nous allons travailler sur des réseaux de neurones classiques, qui sont
- fully connected (FCNN - fully connected neural networks),
- non bouclés (feed forward net),
- avec apprentissage supervisé (supervised learning).
Tous les exercices de ce TD vont faire une classification, l’objectif sera de reconstruire une fonction \(f: R^2 \rightarrow [0,1]\) à partir des échantillons :
\[ f(X) = y \]
Les données d’entrée \(X\) vivent donc dans l’espace 2D, et la sortie \(y\) est binaire (donc dans l’espace 1D).
Le premier objectif de ce TD est de reproduire l’exemple par défaut de l’excellente application web interactive Tensorflow Playground (Tensorflow Playground) dans un notebook Jupyterlab python, en utilisant divers bibliothèques :
- Tensorflow keras
- Matplotlib
- Numpy et Pandas
Le deuxième objectif de ce TD est de se familiariser avec le cycle de vie de l’apprentissage automatique, avec cet exemple de deep learning, qui consiste de divers étapes :
- collecter les données,
- survoler les données,
- préparation de données,
- séparer les données en base d’entraînement et base de test,
- choisir et entrainer le ou les modèles,
- évaluer le ou les modèles,
- fine tuning,
- évaluer sur la base de test,
- production : faire les prédictions,
- maintenir le modèle
Veuillez noter que pas toujours toutes les étapes sont absolument nécessaires, mais que les deux étapes les plus importantes sont
- le choix et l’entraînement du modèle (étape 5), et
- ensuite faire les prédictions (étape 8, aussi appelé inférence)
Exercice 2.0 : Préparation de l’environnement JupyterHub
Nous allons travailler sur le JupyterHub. Avec votre navigateur préféré, ouvrez l’URL suivant et authentifiez-vous :
https://jupyterhub.emi.u-bordeaux.fr/
Sur votre espace, créez un nouveau dossier s’appelant deeplearning. Dans ce dossier, créez un sous-dossier td2. Dans ce sous-dossier, créez un nouveau notebook sous le nom td2-lifecycle.ipynb.
Nous allons importer maintenant toutes les bibliothèques dont on aura besoin pour ce TD. Pour cela, créez une nouvelle cellule Code dans votre notebook, mettez le code suivant et exécutez.
# calcul numérique et analyse de données
import numpy as np
import pandas as pd
# plotting
import matplotlib.pyplot as plt
# tensorflow keras
import tensorflow as tf
from tensorflow import keras
from keras import layers
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import AdamVérifiez qu’il n’y ait pas d’erreurs, sinon faites signe au plus vite.
Exercice 2.1 : Etape 1 : Collecter les données
Dans ce TD, la collection de données à été fait pour vous et elles sont disponibles sous le format .csv, et vous n’avez qu’à à lire un fichier de ce format en utilisant la bibliothèque Pandas.
Donc, télécharger le fichier playground-circle.csv ici :
https://www.labri.fr/perso/preuter/dl2025/files/playground-circle.csv
Téléversez (upload) ce fichier dans votre dossier td2 sur JupyterHub playground-circle.csv.
Ensuite, créez une nouvelle cellule Markdown dans votre notebook, et ajouter le titre suivant, et exécutez la cellule :
# 1. Collecter de données
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant qui utilise la bibliotheque Pandas, et exécutez la cellule :
df = pd.read_csv('playground-circle.csv')
dfCombien de données étiquettées (labeled data) sont maintenant chargé dans la DataFrame
df?Est-ce que les classes de classification sont bien balancées ?
Exercice 2.2 : Etape 2 : Survoler les données
Dans cet exercice, nous allons utiliser la bibliothèque Matplotlib pour afficher les données afin de pouvoir les survoler et examiner.
Pour la lisibilité de votre notebook, créez une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# 2. Survoler les données
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant, exécutez la cellule :
plt.scatter(df.x1, df.x2, c=df.y, cmap="bwr")
plt.colorbar()Combien de dimensions ont les données d’entrée ?
Les données en sortie sont en une seule dimension. Quelles seules deux valeurs ont ces données de sorties ?
Exercice 2.3 : Etape 3 : Préparer les données
Normalement, il faudrait nettoyer les données et éventuellement les transformer (p.ex. normaliser les axes \(x_1\) et \(x_2\) entre \(-1\) et \(1\)). Pour coller au maximum avec l’exemple de Tensorflow playground, on va juste laisser les données telles qu’elles sont préparées.
Créez donc juste une nouvelle cellule Markdown avec le titre et texte suivant.
# 3. Préparer les données
Les données sont déjà prêtes à l'emploi dans cet exemple.
Exercice 2.4 : Etape 4 : Séparer les données
Dans cet exercice, nous allons utiliser la bibliothèque Pandas pour séparer les données (df) en une base d’entrâinement (df_train) et une base de test (df_test). Nous n’allons ensuite plus utiliser la base de test jusqu’à l’étape 8 !!
Créez une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# 4. Séparer les données
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant, exécutez la cellule :
PERCENTAGE_TRAINING_DATA = 0.8
df['train_flag'] = (np.random.rand(len(df))) < PERCENTAGE_TRAINING_DATA
df_train = df[df['train_flag']]
df_test = df[~df['train_flag']]
df_trainCet exemple affiche les données d’entraînement. Pour afficher les données de test, changez la dernière ligne en df_testsimplement.
Vous pouvez également visualiser la séparation de données avec Matplotlib. Pour cela, créez une cellule Code, ajouter le code suivant, et exécutez la cellule :
# Visualiser la séparation des données
plt.scatter(df_train.x1, df_train.x2, c=df_train.y, marker="o", cmap="bwr")
plt.scatter(df_test.x1, df_test.x2, c=df_test.y, marker="x", cmap="bwr")
plt.colorbar()- Dans cet exemple, combien de pourcent des données représente la base de test qui ne sera pas utilisé pendant l’entrainement ?
Exercice 2.5 : Etape 5 : Choisir et entrainer un modèles
Dans cet exercice, nous allons choisir le modèle de deeplearning : le modèle le plus classique, à savoir un modèle séquentiel FCNN (FCNN - fully connected neural network), non bouclé (feed forward net), avec apprentissage supervisé (supervised learning).
Voici une illustration du modèle que nous allons utiliser, qui correspond exactement au réseau de neurones utilisé par défaut par Tensorflow playground :
Quant à l’architecture de ce modèle réseaux de neurones, donc la structure globale du réseau (le nombre de couches, le nombre de neurones par couche), et la fonction d’activation, nous avons :
- une couche d’entrée en \(R^2\)
- deux couches cachées (une avec 4 neurones, et une avec 2 neurones)
- une couche de sortie en \(R^1\)
- des fonctions d’activation de
tanhpour les couches cachées, etsigmoidpour la couche de sortie.
Créez une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# 5. Choisir et entrainer un modèle
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant sans pour autant exécuter la cellule :
# Créer un modèle sequentiel
model = Sequential()
# Première couche cachée avec 4 neurones et fonction d'activation 'tanh'
model.add(Dense(units=4, activation='tanh', input_shape=(2,),name="layer1"))
# Deuxième couche cachée avec 3 neurones et fonction d'activation 'tanh'
model.add(Dense(units=3, activation='tanh',name="layer2"))
# Couche de sortie avec 1 neurone et fonction d'activation 'sigmoid'
# pour une classification binaire
model.add(Dense(units=1, activation='sigmoid',name="layer3"))
# Optimisation Adam avec un taux d'apprentissage spécifique
custom_optimizer = Adam(learning_rate=0.02)
# Compiler le réseau avec une fonction de perte d'entropie croisée binaire et taux de prédictions correctes
model.compile(optimizer=custom_optimizer, loss='binary_crossentropy', metrics=['accuracy'],)
# Montrer un résumé du modèle
model.summary()Toujours sans exécuter, lisez attentivement le code et essayez de trouver la spécification de dimensions d’entrée et sortie, ainsi que le nombre de neurones par couche cachée. Est-ce que l’architecture spécifié dans le code
Kerasest conforme au modèle par défaut duPlayground? Sinon, corrigez.Exécutez maintenant la cellule. L’instruction
model.summary()permet de voir le nombre de couches et paramètres du modèle. Quel est le nombre de paramètres au total ? Vérifiez si cela correspond à ce que vous avez calculé dans votre dernier TD !
Nous allons maintenant lancer l’entrainement avec la méthode model.fit(). En sus du taux d’apprentissage déjà spécifié (\(0.02\)), nous allons aussi spécifier le nombre d’époques, et la séparation entre données d’entrainement et validation (souvenez-vous, sur Tensorflow Playground, validation s’appelait test…). Créez donc une cellule Code, ajouter le code suivant, et exécutez la cellule :
X_train = np.column_stack((np.array(df_train.x1),np.array(df_train.x2)))
Y_train = np.array(df_train.y)
history = model.fit(X_train,Y_train, epochs=20, validation_split=0.375, batch_size=10)Analysez la sortie de votre cellule. Quelle est la perte des données d’entraînement (loss) ?
Quelle est la perte des données de validation (val_loss) ?
Combien de données sont dans les données de validation avec le validation_split utilisé, ce qui correspond au taux utilisé pour la validation de la base d’entraînement ?
Exercice 2.6 : Etape 6 : Evaluer le modèle
Il y a plusieurs façons d’évaluer le modèle. Généralement, on étudie l’évolution de la perte de données d’entraînement et de validation, pour savoir si le modèle a bien “appris” (donc trouvé les bons paramètres de poids et biais), ou s’il s’agit d’un sous-apprentissage (underfitting) ou sur-apprentissage (overfitting), par exemple. Nous allons faire cela en étudiant les courbes d’apprentissage (learning curves) avec la bibliothèque Matplotlib.
Créez donc une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# 6. Evaluer le modèle
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant, exécutez la cellule :
#list all data in history
print(history.history.keys())
plt.plot(history.history['loss'], label="Training loss")
plt.plot(history.history['val_loss'], label="Validation loss")
plt.title("Données d'entrâinement")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend()A votre avis, en regardant les courbes, est-ce que le modèle a assez bien appris ?
Changez le taux d’apprentissage à \(0.005\), et exécutez les cellules nécessaires pour étudier de nouveaux les courbes d’apprentissage. Est-ce que l’apprentissage est meilleur maintenant ?
Exercice 2.7 : Etape 7 : Finetuning
Une fois on est content de l’architecture du modèle, on peut maintenant continuer à trouver les meilleurs hyperparamètres pour optimiser l’apprentissage. Nous allons omettre cette étape afin de se concentrer aux autres étapes.
Créez donc juste une nouvelle cellule Markdown avec le titre et texte suivant.
# 7. Finetuning.
Omis dans cet exemple.
Exercice 2.8 : Etape 8 : Evaluer sur la base de test
Nous allons maintenant évaluer sur la base de test.
Créez donc une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# 8. Evaluer sur la base de test
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant, et exécutez la cellule :
X_test = np.column_stack((np.array(df_test.x1),np.array(df_test.x2)))
Y_test = np.array(df_test.y)
print("Evaluer sur la base de test")
results = model.evaluate(X_test, Y_test)
print("test loss, test accuracy :", results)- A votre avis, en regardant les résultats, est-ce que le modèle a assez bien appris aussi sur la base de test ?
Exercice 2.9 : Etape 9 : Production : faire les prédictions
Nous allons maintenant passer à la phase production : le modèle est entrainé, et nous allons utiliser le modèle entraîné afin de faire des prédictions pour de nouvelles données avec la méthode model.predict(...), c’est aussi appelé inférence.
Créez donc une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# 9. Production : faire les prédictions
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant, et exécutez la cellule :
nouvelles_donnees = np.array([[0.3, 0.4], [-4, -4], [4, 3]]) # format : liste des points [[x1, x2], [x1, x2], [x1, x2], ...]
predictions_nouvelles_donnees = model.predict(nouvelles_donnees)
print("Prédictions : ", predictions_nouvelles_donnees)
predictions_nouvelles_donnees_classes = (predictions_nouvelles_donnees > 0.5).astype(int)
print("Prédictions de classes : ", predictions_nouvelles_donnees_classes)- Combien parmi ces 3 données sont prédites pour appartenir à la classe \(0\) ?
- Est-ce que cela est conforme à ce que vous attendiez ?
Vous pouvez aussi visualiser le résultat dans une nouvelle cellule Code :
plt.scatter([item[0] for item in nouvelles_donnees], [item[1] for item in nouvelles_donnees], c=predictions_nouvelles_donnees, cmap="bwr")
plt.colorbar()Si vous souhaitez avoir un balayage de l’espace d’entrée plus complet, vous pouvez constuire des nouvelles données sur toute une grille de points de l’espace 2D, et faire des prédictions, toujours avec model.predict(...), comme suit :
vecteur_x1 = np.arange(-5, 5.1, 0.25)
vecteur_x2 = np.arange(-5, 5.1, 0.25)
matrice_x1,matrice_x2 = np.meshgrid(vecteur_x1,vecteur_x2)
grille_X1X2=np.array([matrice_x1.flatten(),matrice_x2.flatten()]).T
print(vecteur_x1)
print(vecteur_x2)
print(grille_X1X2)
predicted_labels_grille = model.predict(grille_X1X2)
plt.scatter([item[0] for item in grille_X1X2], [item[1] for item in grille_X1X2], c=predicted_labels_grille, cmap="bwr")
plt.colorbar()- Combien de données sont classifiées maintenant ?
Vous pouvez aussi modifier le pas de grille, par exemple de \(0.25\) à \(0.1\) afin d’avoir une granularité plus fine.
Exercice 2.10 : Etape 10 : Maintenir le modèle
Pour beaucoup d’exemples en apprentissage automatique, les données évoluent au fur et à mesure du temps. Il faut donc veiller à re-entrainer le modèle avec des données actualisées, voire changer le modèle.
Nous allons omettre cette étape dans cet exercice comme il s’agit de données synthétisées.
Créez donc juste une nouvelle cellule Markdown avec le titre et texte suivant.
# 10. Maintenir le modèle.
Omis dans cet exemple.
2 Pour aller plus loin
Exercice 2.11 : Etape 1 : Collecter les données
Dans cet exercice, vous allez vous pratiquer à créer votre propre notebook, et entrainez un modèle avec des données de classification que vous pouvez créer vous même.
Créez un nouveau notebook dans votre dossier td2 que vous appelez td2-mesdonnees.ipynb.
Importez toutes les bibliothèques nécessaires comme dans votre autre projet td2 d’aujourd’hui.
Créez une répartition de classes de votre choix en modifiant les points rouge et verts dans l’URL suivant, en faisant du glisser-déposer (drag and drop) :
(https://www.labri.fr/perso/preuter/dl2025/tools/td2-creer-donnees-etiquettees.html)
Une fois vous êtes content de votre jeu de données, sauvegardez le en format .csv, sous le nom mesdonnees.csv.
Maintenant, comme vous l’avez appris dans ce TD, charger ces données dans votre nouveau notebook td2-mesdonnees.ipynb, définissez un modèle, entraînez le modèle, évaluez le modèle, etc.
Exercice optionnel : Etape 9 : Production : faire les prédictions
Très souvent, une fois le modèle entraîné, il est utilisé en production dans un environnement différent que celui d’entrainement. Donc, bien qu’on ait entrainé un modèle ici dans un envrionnement JupyterHub notebook avec Keras Tensorflow, nous pouvons l’utiliser dans un autre environnement, par exemple dans un objet connecté, une application mobile, ou simplement dans une page web, ce qu’on va faire maintenant.
Nous allons reprendre votre notebook JupyterLab de ce TD td2-lifecycle.ipynb (donc celui qu’on a utilisé tout au long du TD).
Créez donc une nouvelle cellule Markdown et ajoutez le titre suivant, et exécutez la cellule :
# Exemple pour 9. Production : faire les prédictions dans un autre environnement
Puis, créez une nouvelle cellule Code dans votre notebook, ajouter le code suivant, et exécutez la cellule :
# convertir un modèle TF en TFLite pour déployer dans une plateforme à faible ressources :
converter = tf.lite.TFLiteConverter.from_keras_model(model=model)
tfmodel = converter.convert()
# Sauvegarder le modèle TFLite dans un fichier
open("td2-deeplearning.tflite","wb").write(tfmodel)Ce code sauvegarde le modèle entraîné dans un fichier .tflite, que vous pouvez ensuite utiliser dans un autre environnement.
Vous pouvez trouver un exemple de page web utilisant le fichier td2-deeplearning.tflite ici :
td2-web-prediction-inference.html
Ce fichier utilise une image vectorielle, juste pour le système de coordonnées :
Souvenez-vous, pour mettre vos pages web sur internet, il faut les mettre dans le dossier espaces/www/ au CREMI. Vous pouvez donc créer un dossier deeplearning dans ce dossier, et y mettre les 3 fichiers (td2-web-prediction-inference.html, coordinate-axes.svg, et bien sûr votre modèle entraîné td2-deeplearning.tflite).
Testez. L’URL à ouvrir sera quelque chose de type
https://prenom-nom.emi.u-bordeaux.fr/deeplearning/td2-web-prediction-inference.html
3 TD à rendre
A rendre sur moodle avant le 11 Novembre 2025 à 23h59. Pour cela, il y a deux liens sur la page du cours (https://moodle.u-bordeaux.fr/course/view.php?id=22721) :