Modèles Monocouche
Pourquoi Monocouche ?
Avant de passer aux modèles multicouches, revenons sur les modèles à une seule couche (comme la régression logistique) et voyons ce que l'on peut accomplir en augmentant le nombre de neurones dans cette unique couche.
Rappel : Régression Logistique (Un Neurone)
Dans une régression logistique standard (classification binaire), nous avons un seul neurone dans la couche de sortie. Ce neurone :
- Calcule une combinaison linéaire des entrées :
- Applique une fonction d'activation (sigmoïde) pour obtenir une probabilité :
- Prédit la classe en fonction de cette probabilité (par exemple, classe 1 si ).
Géométriquement, cela revient à tracer une seule droite (ou hyperplan) de séparation dans l'espace des entrées.
Classification Multiclasse (Plusieurs Neurones)
Si nous voulons faire de la classification multiclasse (plus de deux classes), nous avons besoin de plusieurs neurones dans la couche de sortie, un pour chaque classe.
Exemple : Classification en 3 classes
Supposons que nous ayons 3 classes possibles (par exemple, "chat", "chien", "oiseau"). Notre couche de sortie aura 3 neurones :
- Neurone 1 : Calcule un score pour la classe "chat".
- Neurone 2 : Calcule un score pour la classe "chien".
- Neurone 3 : Calcule un score pour la classe "oiseau".
Chaque neurone aura ses propres poids et son propre biais. Par exemple, le neurone 1 aura des poids et un biais .
Fonction d'activation Softmax
Pour transformer ces scores en probabilités, on utilise généralement la fonction d'activation softmax sur la couche de sortie :
où :
- est la probabilité que l'entrée appartienne à la classe .
- est le score calculé par le neurone .
- est le nombre total de classes (le nombre de neurones dans la couche de sortie).
La fonction softmax garantit que les sorties sont des probabilités (entre 0 et 1) et que leur somme vaut 1.
Prédiction
La classe prédite est celle qui a la probabilité la plus élevée.
Frontières de Décision Multiples
Avec plusieurs neurones dans la couche de sortie, le modèle peut apprendre plusieurs frontières de décision. Chaque neurone définit implicitement une frontière (l'ensemble des points où son score est égal à un certain seuil). La combinaison de ces frontières permet de séparer l'espace des entrées en plusieurs régions, une pour chaque classe.
Exemple (2D, 3 classes) :
Imaginez que vous ayez des points en 2D appartenant à 3 classes différentes. Un modèle avec une seule couche de sortie et 3 neurones (avec softmax) pourrait apprendre à séparer ces classes en définissant 3 droites. L'intersection de ces droites délimiterait des régions correspondant à chaque classe.
Limitations
Même avec plusieurs neurones dans la couche de sortie, un modèle monocouche reste fondamentalement limité : il ne peut apprendre que des séparations linéaires. Si les classes ne sont pas linéairement séparables (comme dans le cas du XOR), le modèle ne pourra pas apprendre correctement. C'est là que les modèles multicouches deviennent nécessaires.
Exemple de Code (Keras) : Classification Multiclasse
Ce notebook illustre la classification multiclasse en utilisant un réseau de neurones simple dans TensorFlow/Keras. Nous allons créer des données synthétiques, entraîner un modèle, visualiser les résultats et évaluer ses performances.
1. Importation des librairies
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
2. Création des données synthétiques
Nous générons des données pour trois classes linéairement séparables. Chaque classe contient 100 points, représentés par des coordonnées (x1, x2). Nous décalons les classes pour assurer la séparabilité linéaire.
# Données synthétiques (3 classes, linéairement séparables)
X = np.random.randn(300, 2)
y = np.array([0]*100 + [1]*100 + [2]*100) # 3 classes (0, 1, 2)
# On décale les classes pour qu'elles soient linéairement séparables
X[y == 1] += [2, 2]
X[y == 2] += [4, 0]
3. Définition du modèle
Nous créons un modèle séquentiel Keras avec une seule couche dense. Cette couche a 3 neurones (un pour chaque classe) et utilise la fonction d'activation 'softmax' pour produire des probabilités d'appartenance à chaque classe. L'entrée du modèle a une dimension de 2 (x1, x2).
# Modèle Keras (une seule couche Dense avec 3 neurones)
model = tf.keras.Sequential([
tf.keras.layers.Dense(units=3, input_shape=(2,), activation='softmax')
])
4. Compilation du modèle
Nous compilons le modèle avec :
- Optimiseur: Stochastic Gradient Descent (SGD) avec un taux d'apprentissage de 0.1.
- Fonction de perte: 'sparse_categorical_crossentropy', adaptée à la classification multiclasse où les étiquettes sont des entiers.
- Métrique: 'accuracy' pour évaluer la performance du modèle.
# Compilation
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
5. Entraînement du modèle
Nous entraînons le modèle sur les données générées pendant 200 époques. verbose=0 supprime la sortie de progression de l'entraînement. L'historique de l'entrainement (perte et précision) est stocké dans la variable history.
# Entraînement
history = model.fit(X, y, epochs=200, verbose=0)
6. Visualisation de l'apprentissage
Nous visualisons l'évolution de la perte et de la précision au cours des époques.
# Visualisation
plt.plot(history.history['loss'])
plt.title('Loss au cours des époques')
plt.xlabel('Époque')
plt.ylabel('Loss')
plt.show()
plt.plot(history.history['accuracy'])
plt.title('Accuracy au cours des époques')
plt.xlabel('Époque')
plt.ylabel('Accuracy')
plt.show()
7. Visualisation des frontières de décision
Nous visualisons les données et les frontières de décision apprises par le modèle.
# Affichage des données et des frontières de décision
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis', marker='o')
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = np.argmax(Z, axis=1) # On prend l'index du plus grand
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, cmap='viridis', alpha=0.4)
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('Classification Multiclasse (3 classes)')
plt.show()



8. Évaluation du modèle
Nous évaluons les performances finales du modèle sur les données d'entraînement (dans un cas réel, nous utiliserions des données de test distinctes).
# Evaluation
loss, accuracy = model.evaluate(X, y, verbose=0)
print(f"Loss: {loss} et Accuracy: {accuracy}")
Dans cet exemple :
- On génère des données pour un problème de classification à trois classes et qui sont linéairement séparables.
- On utilise
input_shape=(2,)etDense(units=3, activation='softmax).
Ajouter des neurones à la couche de sortie permet de faire de la classification multiclasse, mais ne résout pas le problème de la séparabilité linéaire. Pour cela, il faut ajouter des couches cachées.
Limitations des Modèles Monocouches : Problèmes Non Linéairement Séparables
Nous avons vu qu'un modèle monocouche (comme la régression logistique, même avec plusieurs neurones dans la couche de sortie) ne peut apprendre que des séparations linéaires entre les classes. Cela signifie qu'il existe des problèmes que ces modèles ne peuvent tout simplement pas résoudre, quel que soit le nombre de neurones dans la couche de sortie.
Problèmes Non Linéairement Séparables
Un problème est dit non linéairement séparable si les classes ne peuvent pas être séparées par une ligne droite (en 2D), un plan (en 3D) ou un hyperplan (en dimensions supérieures).
Exemple classique : le XOR
La porte logique XOR (OU exclusif) est l'exemple le plus simple de problème non linéairement séparable. Rappelons sa table de vérité :
| x1 | x2 | y (XOR) |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Comme vous avez pu l'expérimenter précédemment en essayant de résoudre cet exercice, il est impossible de tracer une ligne droite qui sépare les points (0, 0) et (1, 1) des points (0, 1) et (1, 0).
Autres exemples :
- Données en forme de cercle : Des points à l'intérieur d'un cercle appartiennent à une classe, et des points à l'extérieur appartiennent à une autre classe.
- Données en forme de spirale : Deux spirales entrelacées représentent deux classes différentes.
- Données avec des relations complexes : De nombreux problèmes du monde réel impliquent des relations non linéaires entre les variables d'entrée et la sortie.
Explications exercice XOR
Réanalysons l'exercice que vous avez réalisé précédemment. Nous allons démontrer qu'un modèle Keras monocouche ne peut pas apprendre la porte XOR, même avec plusieurs neurones dans la couche de sortie.
Rappel : Solution Exercice 3 (Porte XOR)
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 1. Données
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.float32)
Y = np.array([0, 1, 1, 0], dtype=np.float32)
# 2. Modèle
model = tf.keras.Sequential([
tf.keras.layers.Dense(units=1, input_shape=(2,), activation='sigmoid')
])
# 3. Compilation
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),
loss='binary_crossentropy',
metrics=['accuracy'])
# 4. Entraînement
history = model.fit(X, Y, epochs=1000, verbose=0)
# 5. Courbes
plt.plot(history.history['loss'])
plt.title('Courbe de perte (XOR)')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
plt.plot(history.history['accuracy'])
plt.title('Courbe d\'exactitude (XOR)')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.show()
# 6. Tests
predictions = model.predict(X)
print(f"Prédictions (XOR) :\n{predictions}")
# 7. Visualisation des prédictions
plt.scatter(X[:, 0], X[:, 1], c=Y, cmap='viridis', marker='o', label='Données originales')
w0 = model.get_weights()[0][0]
w1 = model.get_weights()[0][1]
biais = model.get_weights()[1]
x = np.linspace(0, 1.1, 2)
y = (-w0 / w1) * x + (-biais / w1)
plt.plot(x, y, label='Droite de régression', c='orange')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Porte XOR')
plt.legend()
plt.show()
# Interprétation:
# Le modèle aura du mal à apprendre la porte XOR. Les prédictions ne correspondront pas aux valeurs attendues.
# L'exactitude ne dépassera pas 0.5 (50%), ce qui équivaut à des prédictions aléatoires.
# Cela démontre la limitation des modèles linéaires (comme une seule couche Dense) : ils ne peuvent pas résoudre des problèmes non linéairement séparables.


Analyse des Résultats :
- Courbe de perte : La perte ne descend pas vers zéro. Elle peut fluctuer, mais elle ne converge pas vers une valeur très basse.
- Courbe d'exactitude : L'exactitude ne dépasse pas 0.5 (50%), ce qui équivaut à des prédictions aléatoires pour un problème de classification binaire équilibré.
- Prédictions : Les prédictions ne correspondent pas à la table de vérité du XOR.
- Frontière de décision : La frontière de décision est une ligne droite (ou une combinaison de lignes droites, si vous avez plusieurs neurones dans la couche de sortie, mais cela ne change rien au problème fondamental), qui ne peut pas séparer les classes du XOR.
Conclusion :
Peu importe le nombre de neurones que vous ajoutez à la couche de sortie d'un modèle monocouche, vous ne pourrez pas résoudre le problème du XOR (ni aucun autre problème non linéairement séparable). L'ajout de neurones à la couche de sortie permet seulement de faire de la classification multiclasse, mais chaque classe doit être linéairement séparable des autres.
Pour résoudre des problèmes non linéairement séparables, il faut introduire des non-linéarités entre les couches, c'est-à-dire utiliser des couches cachées avec des fonctions d'activation non linéaires. C'est ce qui rend les réseaux de neurones multicouches si puissants.
Un modèle monocouche, quelle que soit sa largeur (nombre de neurones dans la couche de sortie), est fondamentalement limité à apprendre des séparations linéaires. Pour apprendre des séparations non linéaires, il faut des couches cachées.
Exercice : Classification d'Iris avec un Réseau Monocouche
Dans cet exercice, vous allez utiliser un jeu de données classique en Machine Learning, le jeu de données Iris, pour construire un modèle de classification d'espèces d'iris.
Jeu de Données Iris
Le jeu de données Iris contient des mesures de 150 fleurs d'iris, appartenant à trois espèces différentes :
- Iris setosa
- Iris versicolor
- Iris virginica

Pour chaque fleur, quatre caractéristiques (colonnes) ont été mesurées (en centimètres) :
- Longueur du sépale (
sepal length) - Largeur du sépale (
sepal width) - Longueur du pétale (
petal length) - Largeur du pétale (
petal width)
L'objectif est de construire un modèle capable de prédire l'espèce d'une iris en fonction de ces quatre caractéristiques.
Chargement des Données
Le jeu de données est fourni dans un fichier CSV (iris.csv). Voici les premières lignes du fichier :
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
...
Chaque ligne représente une fleur, et les colonnes représentent (dans l'ordre) : la longueur du sépale, la largeur du sépale, la longueur du pétale, la largeur du pétale, et l'espèce.
Ce que vous devez faire
-
Chargement et Prétraitement des Données :
- Chargez le fichier
iris.csvdans un DataFrame pandas. - Remplacez les noms des espèces par des valeurs numériques :
-
Iris-setosa: 0 -
Iris-versicolor: 1 -
Iris-virginica: 2
.replace()de pandas. -
- Séparez les données en deux tableaux NumPy :
X: Un tableau contenant les caractéristiques (les quatre premières colonnes).Y: Un tableau contenant les étiquettes (la dernière colonne, qui contient les valeurs numériques des espèces).
- Chargez le fichier
-
Modèle Monocouche (1 Neurone, Activation Linéaire) :
- Créez un modèle Keras séquentiel avec une seule couche
Denseayant un seul neurone. - N'utilisez aucune fonction d'activation dans la couche de sortie (laissez l'activation par défaut, qui est linéaire).
- Compilez le modèle :
- Choisissez l'optimiseur,
Adamavec unlearning_ratede0.01, - une fonction de perte
mse(erreur quadratique moyenne), - et la métrique
mae(erreur absolue moyenne)
- Choisissez l'optimiseur,
- Entraînez le modèle sur les données
XetY. - Observez les courbes de perte et d'exactitude.
- Faites des prédictions sur les données d'entraînement. Que constatez-vous ?
- Créez un modèle Keras séquentiel avec une seule couche
-
Modèle Monocouche (3 Neurones, Softmax) :
- Créez un nouveau modèle Keras séquentiel avec une seule couche
Denseayant trois neurones (un pour chaque espèce). - Utilisez la fonction d'activation
softmaxpour la couche de sortie. - Compilez le modèle :
- Choisissez l'optimiseur,
Adamavec unlearning_ratede0.01, - une fonction de perte
sparse_categorical_crossentropy, - et la métrique
accuracy
- Choisissez l'optimiseur,
- Entraînez le modèle.
- Observez les courbes de perte et d'exactitude. Y a-t-il une amélioration ?
- Faites des prédictions avec la méthode
model.predict(value). Comment interprétez-vous les sorties du modèle ?
- Créez un nouveau modèle Keras séquentiel avec une seule couche
-
Modèle Monocouche (3 Neurones, Softmax) avec One-Hot Encoding :
- Créez 3 nouvelles colonnes dans votre DataFrame, 'target_0', 'target_1', 'target_2', comme suit :
target_0vaudra 1 si l'espèce estIris-setosa(c'est-à-dire si la valeur numérique correspondante est 0), 0 sinon.target_1vaudra 1 si l'espèce estIris-versicolor(c'est-à-dire si la valeur numérique correspondante est 1), 0 sinon.target_2vaudra 1 si l'espèce estIris-virginica(c'est-à-dire si la valeur numérique correspondante est 2), 0 sinon.- Afin de représenter les iris comme ceci :
Iris-setosa:[1, 0, 0]Iris-versicolor:[0, 1, 0]Iris-virginica:[0, 0, 1]
- Créez un nouveau tableau NumPy
Y_ohecontenant ces trois colonnes.ohesignifie one-hot encoded. - Créez un nouveau modèle Keras séquentiel similaire au modèle précédent (une seule couche
Denseavec 3 neurones et une activation softmax). - Compilez le modèle :
- Choisissez l'optimiseur,
Adamavec unlearning_ratede0.01, - une fonction de perte
categorical_crossentropy, - et la métrique
accuracy
- Choisissez l'optimiseur,
- Entraînez le modèle avec
XetY_ohe. - Observez les courbes de perte et d'exactitude.
- Faites des prédictions avec la méthode
model.predict(value). Comment interprétez-vous les sorties du modèle ? Comment obtenez-vous la classe prédite à partir de ces sorties ?
- Créez 3 nouvelles colonnes dans votre DataFrame, 'target_0', 'target_1', 'target_2', comme suit :
-
Comparaison et Analyse :
- Comparez les performances (perte, exactitude, courbes) des trois modèles.
- Expliquez les différences de performance. Pourquoi le premier modèle ne fonctionne-t-il pas bien ? Pourquoi le troisième modèle est-il potentiellement meilleur que le deuxième ?
- Quel modèle utiliseriez-vous en pratique pour ce problème, et pourquoi ?
Cet exercice est difficile, il est normal de devoir rechercher de la documentation supplémentaire sur internet (ça fait partie de l'exercice) !
Je vous demande de comprendre pourquoi vous devez utiliser "sparse_categorical_crossentropy" ou "categorical_crossentropy".
Regardez uniquement la solution si vous avez terminé l'exercie !
Solution
# Importation des librairies nécessaires
import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Nous commençons par importer les librairies dont nous aurons besoin :
- TensorFlow (
tf) : Pour construire et entraîner nos modèles de Deep Learning. - NumPy (
np) : Pour manipuler les données sous forme de tableaux. - pandas (
pd) : Pour charger et manipuler facilement les données à partir du fichier CSV. - Matplotlib (
plt) : Pour créer des visualisations (graphiques).
# Chargement des données à partir du fichier CSV
df = pd.read_csv('iris.csv')
# Affichage des premières lignes du DataFrame pour vérification
df.head()
Nous chargeons le jeu de données Iris à partir du fichier CSV (iris.csv) dans un DataFrame pandas. La méthode df.head() nous permet d'afficher les 5 premières lignes du DataFrame, ce qui est utile pour vérifier que les données ont été chargées correctement et pour avoir un aperçu des colonnes.
# Remplacement des noms des espèces par des valeurs numériques
df = df.replace({"Iris-setosa": 0, 'Iris-versicolor': 1, 'Iris-virginica': 2})
# Affichage des premières lignes après la transformation pour vérification
df.head()
Pour faciliter l'entraînement de nos modèles, nous remplaçons les noms des espèces (chaînes de caractères) par des valeurs numériques :
Iris-setosadevient 0Iris-versicolordevient 1Iris-virginicadevient 2
Nous utilisons la méthode replace() de pandas pour effectuer cette transformation. Nous affichons à nouveau les premières lignes pour vérifier que la transformation a été effectuée correctement.
# Séparation des caractéristiques (X) et des étiquettes (Y)
X = df[['5.1', '3.5', '1.4', '0.2']].values # Toutes les lignes, les 4 premières colonnes
Y = df['Iris-setosa'].values # Toutes les lignes, la dernière colonne
Nous séparons maintenant nos données en deux tableaux NumPy :
X: Contient les caractéristiques (features) de nos fleurs d'iris (longueur et largeur des sépales et des pétales). Nous sélectionnons toutes les lignes (:) et les quatre premières colonnes (['5.1', '3.5', '1.4', '0.2'], qui correspondent aux noms originaux des colonnes dans le fichier CSV)..valuesconvertit le résultat en tableau NumPy.Y: Contient les étiquettes (labels) de nos fleurs d'iris (l'espèce, représentée par un entier : 0, 1 ou 2). Nous sélectionnons toutes les lignes et la dernière colonne ('Iris-setosa', qui a été renommée mais contient maintenant les valeurs numériques 0, 1, 2)..valuesconvertit le résultat en tableau NumPy.
# Visualisation des données (paires de caractéristiques)
plt.figure(figsize=(12, 6))
plt.subplot(2, 3, 1)
plt.scatter(X[:, 0], X[:, 1], c=Y, cmap='viridis')
plt.xlabel('Longueur Sépale')
plt.ylabel('Largeur Sépale')
plt.subplot(2, 3, 2)
plt.scatter(X[:, 0], X[:, 2], c=Y, cmap='viridis')
plt.xlabel('Longueur Sépale')
plt.ylabel('Longueur Pétale')
plt.subplot(2, 3, 3)
plt.scatter(X[:, 0], X[:, 3], c=Y, cmap='viridis')
plt.xlabel('Longueur Sépale')
plt.ylabel('Largeur Pétale')
plt.subplot(2, 3, 4)
plt.scatter(X[:, 1], X[:, 2], c=Y, cmap='viridis')
plt.xlabel('Largeur Sépale')
plt.ylabel('Longueur Pétale')
plt.subplot(2, 3, 5)
plt.scatter(X[:, 1], X[:, 3], c=Y, cmap='viridis')
plt.xlabel('Largeur Sépale')
plt.ylabel('Largeur Pétale')
plt.subplot(2, 3, 6)
plt.scatter(X[:, 2], X[:, 3], c=Y, cmap='viridis')
plt.xlabel('Longueur Pétale')
plt.ylabel('Largeur Pétale')
plt.suptitle('Visualisation des Données Iris (Paires de Caractéristiques)')
plt.show()
Avant de construire nos modèles, visualisons les données. Nous créons des nuages de points pour chaque paire de caractéristiques possible. Les points sont colorés en fonction de l'espèce réelle de l'iris (0, 1 ou 2, représentés par des couleurs différentes grâce à cmap='viridis').
Cette visualisation nous permet d'avoir une idée de la séparabilité des classes. On constate que :
- Les iris setosa (classe 0, en violet) semblent assez facilement séparables des deux autres espèces.
- Les iris versicolor (classe 1, en vert) et virginica (classe 2, en jaune) sont plus mélangées, en particulier en considérant les largeurs et longueurs de sépales.
Cela suggère qu'un modèle linéaire pourrait bien fonctionner pour séparer setosa des autres espèces, mais qu'il pourrait avoir plus de mal à distinguer versicolor et virginica.
# 2. Modèle monocouche (1 neurone, activation linéaire)
model1 = tf.keras.Sequential([
tf.keras.layers.Dense(units=1, input_shape=(4,)) # Pas d'activation explicite = activation linéaire
])
model1.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
loss='mse', # Erreur quadratique moyenne (régression)
metrics=['mae']) # Erreur absolue moyenne
history1 = model1.fit(X, Y, epochs=200, verbose=0)
plt.plot(history1.history['loss'])
plt.plot(history1.history['mae'])
plt.title('Courbe de perte et MAE (Modèle 1)')
plt.show()
predictions1 = model1.predict(X)
print(f"Quelques prédictions (Modèle 1) :\n{predictions1[:5]}")
Nous construisons ici notre premier modèle, une tentative de classification avec un seul neurone et une activation linéaire (c'est-à-dire, pas de fonction d'activation).
tf.keras.Sequential([...]): Nous créons un modèle séquentiel.tf.keras.layers.Dense(units=1, input_shape=(4,)): Une seule couche dense, avec un seul neurone.input_shape=(4,)indique que le modèle reçoit des entrées à 4 dimensions (les 4 caractéristiques des fleurs).model1.compile(...):optimizer=tf.keras.optimizers.Adam(learning_rate=0.01): Nous utilisons l'optimiseur Adam, une variante de la descente de gradient, avec un taux d'apprentissage de 0.01.loss='mse': Nous utilisons l'erreur quadratique moyenne (MSE) comme fonction de perte. C'est une fonction de perte typique pour la régression, pas pour la classification.metrics=['mae']: Nous évaluons le modèle avec l'erreur absolue moyenne (MAE).
model1.fit(X, Y, epochs=200, verbose=0): Nous entraînons le modèle pendant 200 époques.verbose=0supprime l'affichage des détails de l'entraînement.
Analyse des résultats (Modèle 1) :
- Courbes de perte et de MAE : La perte (MSE) et la MAE diminuent, mais restent relativement élevées.
- Prédictions : Les prédictions sont des nombres réels, pas des classes. Ce modèle essaie de prédire la valeur de l'étiquette (0, 1 ou 2) comme s'il s'agissait d'un problème de régression, ce qui n'a pas de sens.
Conclusion (Modèle 1) : Ce modèle n'est pas adapté au problème de classification. Il ne peut pas apprendre à classer correctement les iris.
# 3. Modèle monocouche (3 neurones, softmax)
model2 = tf.keras.Sequential([
tf.keras.layers.Dense(units=3, input_shape=(4,), activation='softmax')
])
model2.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
history2 = model2.fit(X, Y, epochs=200, verbose=0)
plt.plot(history2.history['loss'])
plt.plot(history2.history['accuracy'])
plt.title('Courbes de perte et d\'exactitude (Modèle 2)')
plt.show()
predictions2 = model2.predict(X)
print(f"Quelques prédictions (Modèle 2) :\n{predictions2[:5]}")
print(f"Classes prédites (Modèle 2) :\n{np.argmax(predictions2[:5], axis=1)}")
Nous construisons maintenant un deuxième modèle, plus adapté à la classification multiclasse :
tf.keras.layers.Dense(units=3, ..., activation='softmax'): Nous avons maintenant 3 neurones dans la couche de sortie (un pour chaque espèce d'iris), et nous utilisons la fonction d'activation softmax. Softmax transforme les sorties des neurones en une distribution de probabilité sur les classes.model2.compile(...):loss='sparse_categorical_crossentropy': Nous utilisons la perte d'entropie croisée catégorielle sparse. "Sparse" signifie que nos étiquettes sont des entiers (0, 1, 2) plutôt que des vecteurs one-hot.metrics=['accuracy']: Nous utilisons l'exactitude (accuracy) comme métrique, ce qui est approprié pour la classification.
Analyse des résultats (Modèle 2) :
- Courbes de perte et d'exactitude : La perte diminue et l'exactitude augmente, ce qui indique que le modèle apprend.
- Prédictions : Les prédictions sont maintenant des vecteurs de probabilités (une probabilité pour chaque classe). Par exemple,
[0.97, 0.02, 0.01]signifierait que le modèle prédit la classe 0 avec une probabilité de 97%, la classe 1 avec une probabilité de 2%, et la classe 2 avec une probabilité de 1%. - Classes prédites : Pour obtenir la classe prédite, on prend l'indice de la probabilité maximale (avec
np.argmax(predictions2, axis=1)).
Conclusion (Modèle 2) : Ce modèle est beaucoup plus adapté au problème. Il apprend à classer les iris, comme le montrent la diminution de la perte et l'augmentation de l'exactitude.
# 4. Modèle monocouche (3 neurones, softmax) avec one-hot encoding
# Création des colonnes one-hot
df['target_0'] = (df['Iris-setosa'] == 0).astype(int)
df['target_1'] = (df['Iris-setosa'] == 1).astype(int)
df['target_2'] = (df['Iris-setosa'] == 2).astype(int)
# Nouveau tableau Y
Y_ohe = df[['target_0', 'target_1', 'target_2']].values
model3 = tf.keras.Sequential([
tf.keras.layers.Dense(units=3, input_shape=(4,), activation='softmax')
])
model3.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),
loss='categorical_crossentropy', # Notez le changement ici
metrics=['accuracy'])
history3 = model3.fit(X, Y_ohe, epochs=200, verbose=0)
plt.plot(history3.history['loss'])
plt.plot(history3.history['accuracy'])
plt.title('Courbes de perte et d\'exactitude (Modèle 3)')
plt.show()
predictions3 = model3.predict(X)
print(f"Quelques prédictions (Modèle 3) :\n{predictions3[:5]}")
print(f"Classes prédites (Modèle 3) :\n{np.argmax(predictions3[:5], axis=1)}")
Ce troisième modèle est très similaire au deuxième, mais il utilise une représentation différente des étiquettes : le one-hot encoding.
-
One-Hot Encoding : Au lieu de représenter chaque espèce par un entier (0, 1 ou 2), nous la représentons par un vecteur de taille 3 :
Iris-setosa:[1, 0, 0]Iris-versicolor:[0, 1, 0]Iris-virginica:[0, 0, 1]
Nous créons trois nouvelles colonnes dans notre DataFrame (
target_0,target_1,target_2) pour représenter ces vecteurs one-hot, puis nous les extrayons dans un nouveau tableau NumPyY_ohe. -
model3.compile(...):loss='categorical_crossentropy': Nous utilisons maintenantcategorical_crossentropy(sans "sparse"), car nos étiquettes sont des vecteurs one-hot, pas des entiers.
Analyse des résultats (Modèle 3) :
- Courbes de perte et d'exactitude : Les courbes devraient être similaires à celles du modèle 2.
- Prédictions: Les prédictions et les classes sont similaires au modèle 2
Conclusion (Modèle 3) : Ce modèle est conceptuellement équivalent au modèle 2. Le one-hot encoding est souvent préféré car il est plus général et compatible avec certaines fonctions de similarité, mais pour un problème simple comme celui-ci, les deux approches fonctionnent bien.
Comparaison des Modèles
- Modèle 1 (Régression) : Totalement inadapté au problème de classification.
- Modèle 2 (Classification, étiquettes entières) : Adapté, fonctionne bien.
- Modèle 3 (Classification, one-hot encoding) : Adapté, fonctionne bien, conceptuellement équivalent au modèle 2.
Pour ce problème spécifique (classification d'iris), les modèles 2 et 3 donneront des résultats similaires. Le choix entre les deux dépend souvent de préférences personnelles ou de la compatibilité avec d'autres parties de votre pipeline de Machine Learning.