TD2 - Réseau de neurones FCNN avec Keras Tensorflow

(à rendre sur moodle avant le 11 Novembre 2025 à 23h59)

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 :

  1. collecter les données,
  2. survoler les données,
  3. préparation de données,
  4. séparer les données en base d’entraînement et base de test,
  5. choisir et entrainer le ou les modèles,
  6. évaluer le ou les modèles,
  7. fine tuning,
  8. évaluer sur la base de test,
  9. production : faire les prédictions,
  10. 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 Adam

Vé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')
df
Question
  1. Combien de données étiquettées (labeled data) sont maintenant chargé dans la DataFrame df ?

  2. 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()
Question
  1. Combien de dimensions ont les données d’entrée ?

  2. 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_train

Cet 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()
Question
  1. 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 tanh pour les couches cachées, et sigmoid pour 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()
Question
  1. 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 Keras est conforme au modèle par défaut du Playground ? Sinon, corrigez.

  2. 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)
Question
  1. Analysez la sortie de votre cellule. Quelle est la perte des données d’entraînement (loss) ?

  2. Quelle est la perte des données de validation (val_loss) ?

  3. 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()
Question
  1. A votre avis, en regardant les courbes, est-ce que le modèle a assez bien appris ?

  2. 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)
Question
  1. 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)
Question
  1. Combien parmi ces 3 données sont prédites pour appartenir à la classe \(0\) ?
  2. 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()
Question
  1. 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 :

coordinate-axes.svg

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) :