diff --git a/TD/TD4/rn_regression-complet.py b/TD/TD4/rn_regression-complet.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f685bf91165bb3d473c5b681a5a0ddd92b01b9d
--- /dev/null
+++ b/TD/TD4/rn_regression-complet.py
@@ -0,0 +1,346 @@
+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_mse(y, d):
+    """ Calcule la valeur de la fonction cout MSE (moyenne des différences au carré)
+    
+    Parametres
+    ----------
+    y : matrice des données prédites 
+    d : matrice des données réelles 
+    
+    Return
+    -------
+    cout : nombre correspondant à la valeur de la fonction cout (moyenne des différences au carré)
+
+    """
+
+    N = y.shape[1]    
+    cout = np.square(y - d).sum() / 2 / 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 1     
+    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)
+
+
+# ===================== Partie 1: Lecture et normalisation des données =====================
+print("Lecture des données ...")
+
+x, d, N, nb_var, nb_cible = lecture_donnees("food_truck.txt")
+# x, d, N, nb_var, nb_cible = lecture_donnees("houses.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)
+dmax = d.max()
+d = d / dmax
+
+# 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.001
+nb_iters = 500
+couts_apprentissage = np.zeros(nb_iters)
+couts_validation = np.zeros(nb_iters)
+
+# Dimensions du réseau
+D_c = [nb_var, 5, 10, nb_cible] # liste contenant le nombre de neurones pour chaque couche 
+activation = [relu, sigmoide, lineaire] # 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_mse(y_app,d_app)
+    couts_validation[t] = calcule_cout_mse(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("Coût final sur l'ensemble de validation : ", couts_validation[-1])
+
+# 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_mse(y_test,d_test)
+print("Coût sur l'ensemble de test : ", cout)