From aa4c6ee8a5d24450a55e771c7e58707fd775480e Mon Sep 17 00:00:00 2001 From: Emmanuel Dellandrea <emmanuel.dellandrea@ec-lyon.fr> Date: Wed, 22 Jan 2025 13:47:01 +0100 Subject: [PATCH] Ajout correction --- TD/TD4/rn_classification-complet.py | 417 ++++++++++++++++++++++++++++ TD/TD4/rn_regression-complet.py | 2 +- 2 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 TD/TD4/rn_classification-complet.py diff --git a/TD/TD4/rn_classification-complet.py b/TD/TD4/rn_classification-complet.py new file mode 100644 index 0000000..8b80e3b --- /dev/null +++ b/TD/TD4/rn_classification-complet.py @@ -0,0 +1,417 @@ +import matplotlib.pyplot as plt +import numpy as np + +def lecture_donnees(nom_fichier, delimiteur=','): + """ Lit le fichier contenant les données et renvoiee les matrices correspondant + + Parametres + ---------- + nom_fichier : nom du fichier contenant les données + delimiteur : caratère délimitant les colonne dans le fichier ("," par défaut) + + Retour + ------- + x : matrice des données de dimension [N, nb_var] + d : matrice contenant les valeurs de la variable cible de dimension [N, nb_cible] + N : nombre d'éléments + nb_var : nombre de variables prédictives + nb_cible : nombre de variables cibles + + """ + + data = np.loadtxt(nom_fichier, delimiter=delimiteur) + + nb_cible = 1 + nb_var = data.shape[1] - nb_cible + N = data.shape[0] + + x = data[:, :nb_var] + d = data[:, nb_var:].reshape(N,1) + + return x, d, N, nb_var, nb_cible + + +def normalisation(x): + """ Normalise les données par un centrage-réduction des variables prédictives + + Parametres + ---------- + X : matrice des données de dimension [N, nb_var] + + avec N : nombre d'éléments et nb_var : nombre de variables prédictives + + Retour + ------- + X_norm : matrice des données centrées-réduites de dimension [N, nb_var] + mu : moyenne des variables de dimension [1,nb_var] + sigma : écart-type des variables de dimension [1,nb_var] + + """ + + mu = np.mean(x, 0) + sigma = np.std(x, 0) + x_norm = (x - mu) / sigma + + return x_norm, mu, sigma + +def decoupage_donnees(x,d,prop_val=0.2, prop_test=0.2): + """ Découpe les données initiales en trois sous-ensembles distincts d'apprentissage, de validation et de test + + Parametres + ---------- + x : matrice des données de dimension [N, nb_var] + d : matrice des valeurs cibles [N, nb_cible] + prop_val : proportion des données de validation sur l'ensemble des données (entre 0 et 1) + prop_test : proportion des données de test sur l'ensemble des données (entre 0 et 1) + + avec N : nombre d'éléments, nb_var : nombre de variables prédictives, nb_cible : nombre de variables cibles + + Retour + ------- + x_app : matrice des données d'apprentissage + d_app : matrice des valeurs cibles d'apprentissage + x_val : matrice des données d'apprentissage + d_val : matrice des valeurs cibles d'apprentissage + x_test : matrice des données d'apprentissage + d_test : matrice des valeurs cibles d'apprentissage + + """ + assert prop_val + prop_test < 1.0 + + N = x.shape[0] + indices = np.arange(N) + np.random.shuffle(indices) + nb_val = int(N*prop_val) + nb_test = int(N*prop_test) + nb_app = N - nb_val - nb_test + + x = x[indices,:] + d = d[indices,:] + + x_app = x[:nb_app,:] + d_app = d[:nb_app,:] + + x_val = x[nb_app:nb_app+nb_val,:] + d_val = d[nb_app:nb_app+nb_val,:] + + x_test = x[N-nb_test:,:] + d_test = d[N-nb_test:,:] + + return x_app, d_app, x_val, d_val, x_test, d_test + +def calcule_cout_entropie_croisee(y, d): + """ Calcule la valeur de la fonction cout entropie croisée + + Parametres + ---------- + y : matrice des données prédites (softmax) + d : matrice des données réelles encodées par 1 + + Return + ------- + cout : nombre correspondant à la valeur de la fonction cout + + """ + + N = y.shape[1] + + cout = - np.sum(d*np.log(y)) / N + + return cout + +def passe_avant(x, W, b, activation): + """ Réalise une passe avant dans le réseau de neurones + + Parametres + ---------- + x : matrice des entrées, de dimension nb_var x N + W : liste contenant les matrices des poids du réseau + b : liste contenant les matrices des biais du réseau + activation : liste contenant les fonctions d'activation des couches du réseau + + avec N : nombre d'éléments, nb_var : nombre de variables prédictives + + Return + ------- + a : liste contenant les potentiels d'entrée des couches du réseau + h : liste contenant les sorties des couches du réseau + + """ + h = [x] + a = [] + for i in range(len(b)): + a.append( W[i].dot(h[i]) + b[i] ) + h.append( activation[i](a[i]) ) + + return a, h + +def passe_arriere(delta_h, a, h, W, activation): + """ Réalise une passe arrière dans le réseau de neurones (rétropropagation) + + Parametres + ---------- + delta_h : matrice contenant les valeurs du gradient du coût par rapport à la sortie du réseau + a : liste contenant les potentiels d'entrée des couches du réseau + h : liste contenant les sorties des couches du réseau + W : liste contenant les matrices des poids du réseau + activation : liste contenant les fonctions d'activation des couches du réseau + + Return + ------- + delta_W : liste contenant les matrice des gradients des poids des couches du réseau + delta_b : liste contenant les matrice des gradients des biais des couches du réseau + + """ + + delta_b = [] + delta_W = [] + + for i in range(len(W)-1,-1,-1): + + delta_a = delta_h * activation[i](a[i], True) + + delta_b.append( delta_a.mean(1).reshape(-1,1) ) + delta_W.append( delta_a.dot(h[i].T) ) + + delta_h = (W[i].T).dot(delta_a) + + delta_b = delta_b[::-1] + delta_W = delta_W[::-1] + + return delta_W, delta_b + +def sigmoide(z, deriv=False): + """ Calcule la valeur de la fonction sigmoide ou de sa dérivée appliquée à z + + Parametres + ---------- + z : peut être un scalaire ou une matrice + deriv : booléen. Si False renvoie la valeur de la fonction sigmoide, si True renvoie sa dérivée + + Return + ------- + s : valeur de la fonction sigmoide appliquée à z ou de sa dérivée. Même dimension que z + + """ + + s = 1 / (1 + np.exp(-z)) + if deriv: + return s * (1 - s) + else : + return s + +def lineaire(z, deriv=False): + """ Calcule la valeur de la fonction linéaire ou de sa dérivée appliquée à z + + Parametres + ---------- + z : peut être un scalaire ou une matrice + deriv : booléen. Si False renvoie la valeur de la fonction linéire, si True renvoie sa dérivée + + + Return + ------- + s : valeur de la fonction linéaire appliquée à z ou de sa dérivée. Même dimension que z + + """ + if deriv: + return np.ones(z.shape) + else : + return z + + +def relu(z, deriv=False): + """ Calcule la valeur de la fonction relu ou de sa dérivée appliquée à z + + Parametres + ---------- + z : peut être un scalaire ou une matrice + deriv : booléen. Si False renvoie la valeur de la fonction relu, si True renvoie sa dérivée + + Return + ------- + s : valeur de la fonction relu appliquée à z ou de sa dérivée. Même dimension que z + + """ + + r = np.zeros(z.shape) + if deriv: + pos = np.where(z>=0) + r[pos] = 1.0 + return r + else : + return np.maximum(r,z) + +def softmax(z, deriv=False): + """ Calcule la valeur de la fonction softmax ou de sa dérivée appliquée à z + + Parametres + ---------- + z : matrice des données + deriv : booléen. Si False renvoie la valeur de la fonction softmax, si True renvoie sa dérivée + + Return + ------- + s : valeur de la fonction softmax appliquée à z ou de sa dérivée. Même dimension que z + + """ + + if deriv: + return 1 + else : + return np.exp(z) / np.sum(np.exp(z),axis=0) + +def encodage_a_1(d): + """ Réalise un encodage 1 parmi n : sur les neurones de sortie du réseau, seul 1 aura la valeur 1, tous les autres à 0 + + Parametres + ---------- + d : matrice contenant les valeurs de la variable cible (classe des éléments) de dimension [N, 1] + avec N : nombre d'éléments + + Retour + ------- + e : matrice des données encodées de dimension [N, nb_classes] + avec N : nombre d'éléments et nb_classes le nombre de classes (maximum+1) des valeurs dans d + + """ + + d = d.astype(int).flatten() + N = d.shape[0] + nb_classes = d.max() + 1 + + e = np.zeros((N,nb_classes)) + e[range(N),d] = 1 + + return e + +def taux_classification(y,d): + """ Calcule le taux de classification (proportion d'éléments bien classés) + + Parametres + ---------- + y : matrice des sorties du réseau de dimension [nb_neurones_sortie x N] + d : matrice des vraies valeurs [nb_neurones_sortie x N] + + avec N : nombre d'éléments et nb_neurones_sortie : le nombre de neurones sur la couche de sortie + + Retour + ------- + t : taux de classification + + """ + + ind_y = np.argmax(y,axis=0) + ind_d = np.argmax(d,axis=0) + t = np.mean(ind_y == ind_d) + + return t + + +# ===================== Partie 1: Lecture et normalisation des données ===================== +print("Lecture des données ...") + +x, d, N, nb_var, nb_cible = lecture_donnees("iris.txt") +# x, d, N, nb_var, nb_cible = lecture_donnees("scores.txt") + +# Affichage des 10 premiers exemples du dataset +print("Affichage des 10 premiers exemples du dataset : ") +for i in range(0, 10): + print(f"x = {x[i,:]}, d = {d[i]}") + +# Normalisation des variables (centrage-réduction) +print("Normalisation des variables ...") +x, mu, sigma = normalisation(x) +d = encodage_a_1(d) + +# Découpage des données en sous-ensemble d'apprentissage, de validation et de test +x_app, d_app, x_val, d_val, x_test, d_test = decoupage_donnees(x,d) + +# ===================== Partie 2: Apprentissage ===================== + +# Choix du taux d'apprentissage et du nombre d'itérations +alpha = 0.0001 +nb_iters = 10000 +couts_apprentissage = np.zeros(nb_iters) +couts_validation = np.zeros(nb_iters) + +# Dimensions du réseau +D_c = [nb_var, 15, 15, d_app.shape[1]] # liste contenant le nombre de neurones pour chaque couche +activation = [relu, relu, softmax] # liste contenant les fonctions d'activation des couches cachées et de la couche de sortie + +# Initialisation aléatoire des poids du réseau +W = [] +b = [] +for i in range(len(D_c)-1): + W.append(2 * np.random.random((D_c[i+1], D_c[i])) - 1) + b.append(np.zeros((D_c[i+1],1))) + +x_app = x_app.T # Les données sont présentées en entrée du réseau comme des vecteurs colonnes +d_app = d_app.T + +x_val = x_val.T # Les données sont présentées en entrée du réseau comme des vecteurs colonnes +d_val = d_val.T + +x_test = x_test.T # Les données sont présentées en entrée du réseau comme des vecteurs colonnes +d_test = d_test.T + +for t in range(nb_iters): + + ############################################################################# + # Passe avant : calcul de la sortie prédite y sur les données de validation # + ############################################################################# + a, h = passe_avant(x_val, W, b, activation) + y_val = h[-1] # Sortie prédite + + ############################################################################### + # Passe avant : calcul de la sortie prédite y sur les données d'apprentissage # + ############################################################################### + a, h = passe_avant(x_app, W, b, activation) + y_app = h[-1] # Sortie prédite + + ########################################### + # Calcul de la fonction perte de type MSE # + ########################################### + couts_apprentissage[t] = calcule_cout_entropie_croisee(y_app,d_app) + couts_validation[t] = calcule_cout_entropie_croisee(y_val,d_val) + + #################################### + # Passe arrière : rétropropagation # + #################################### + delta_h = (y_app-d_app) # Pour la dernière couche + delta_W, delta_b = passe_arriere(delta_h, a, h, W, activation) + + ############################################# + # Mise à jour des poids et des biais ##### # + ############################################# + for i in range(len(b)-1,-1,-1): + b[i] -= alpha * delta_b[i] + W[i] -= alpha * delta_W[i] + +print("Coût final sur l'ensemble d'apprentissage : ", couts_apprentissage[-1]) +print("Taux de classification sur l'ensemble d'apprentissage : ", taux_classification(y_app, d_app)) + +print("Coût final sur l'ensemble de validation : ", couts_validation[-1]) +print("Taux de classification sur l'ensemble de validation : ", taux_classification(y_val, d_val)) + +# Affichage de l'évolution de la fonction de cout lors de la rétropropagation +plt.figure(0) +plt.title("Evolution de le fonction de coût lors de la retropropagation") +plt.plot(np.arange(couts_apprentissage.size), couts_apprentissage, label="Apprentissage") +plt.plot(np.arange(couts_validation.size), couts_validation, label="Validation") +plt.legend(loc="upper left") +plt.xlabel("Nombre d'iterations") +plt.ylabel("Coût") +plt.show() + +# ===================== Partie 3: Evaluation sur l'ensemble de test ===================== + +####################################################################### +# Passe avant : calcul de la sortie prédite y sur les données de test # +####################################################################### +a, h = passe_avant(x_test, W, b, activation) +y_test = h[-1] # Sortie prédite + +cout = calcule_cout_entropie_croisee(y_test,d_test) +print("Coût sur l'ensemble de test : ", cout) +print("Taux de classification sur l'ensemble de test : ", taux_classification(y_test, d_test)) diff --git a/TD/TD4/rn_regression-complet.py b/TD/TD4/rn_regression-complet.py index 0f685bf..9494ac5 100644 --- a/TD/TD4/rn_regression-complet.py +++ b/TD/TD4/rn_regression-complet.py @@ -213,7 +213,7 @@ def lineaire(z, deriv=False): """ if deriv: - return 1 + return np.ones(z.shape) else : return z -- GitLab