diff --git a/mlp.py b/mlp.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c33f0f8d43d9baad1872bfea5c31817c1e4a075
--- /dev/null
+++ b/mlp.py
@@ -0,0 +1,192 @@
+import numpy as np
+from read_cifar import read_cifar, split_dataset
+import matplotlib.pyplot as plt
+import os
+
+# Question 10: Implémentation avec MSE
+def learn_once_mse(w1, b1, w2, b2, data, targets, learning_rate):
+    
+    # On code la Forward pass: propagation des données à travers le réseau
+    a0 = data  # Couche d'entrée
+    z1 = np.matmul(a0, w1) + b1  # Première couche cachée (pré-activation)
+    a1 = 1 / (1 + np.exp(-z1))   # Activation sigmoid
+    z2 = np.matmul(a1, w2) + b2  # Couche de sortie (pré-activation)
+    a2 = 1 / (1 + np.exp(-z2))   # Activation sigmoid finale
+    predictions = a2
+
+    # On calcul l'erreur quadratique moyenne
+    loss = np.mean(np.square(predictions - targets))
+
+    # On code ensuite la Backward pass: calcul des gradients et mise à jour des poids
+    dC_da2 = 2 * (predictions - targets) / targets.shape[0]  # Dérivée par rapport à la sortie
+    dC_dz2 = dC_da2 * (a2 * (1 - a2))  # Dérivée de la sigmoid
+    dC_dw2 = np.matmul(a1.T, dC_dz2)   # Gradient pour w2
+    dC_db2 = np.mean(dC_dz2, axis=0)   # Gradient pour b2
+
+    # On propage l'erreur vers la première couche
+    dC_da1 = np.matmul(dC_dz2, w2.T)
+    dC_dz1 = dC_da1 * (a1 * (1 - a1))  # Dérivée de la sigmoid
+    dC_dw1 = np.matmul(a0.T, dC_dz1)   # Gradient pour w1
+    dC_db1 = np.mean(dC_dz1, axis=0)   # Gradient pour b1
+
+    # On met à jour les poids et biais avec le gradient descent
+    w1 = w1 - learning_rate * dC_dw1
+    b1 = b1 - learning_rate * dC_db1
+    w2 = w2 - learning_rate * dC_dw2
+    b2 = b2 - learning_rate * dC_db2
+
+    return w1, b1, w2, b2, loss
+
+# Question 11: One-hot encoding
+def one_hot(labels):
+    n_classes = 10  # CIFAR-10 a 10 classes
+    return np.eye(n_classes)[labels]
+
+# Question 12: Implémentation avec la Cross-Entropy
+def learn_once_cross_entropy(w1, b1, w2, b2, data, labels, learning_rate):
+    batch_size = data.shape[0]
+    
+    # On implemente la Forward pass
+    a0 = data
+    z1 = np.matmul(a0, w1) + b1
+    a1 = 1 / (1 + np.exp(-z1))
+    z2 = np.matmul(a1, w2) + b2
+    
+    # On implemente notre sofmax
+    z2 = z2 - np.max(z2, axis=1, keepdims=True)
+    exp_z2 = np.exp(z2)
+    a2 = exp_z2 / np.sum(exp_z2, axis=1, keepdims=True)
+    
+    # One-hot encoding
+    y = one_hot(labels)
+    
+    # Cross entropy loss avec réduction correcte
+    loss = -np.sum(y * np.log(a2 + 1e-15)) / batch_size
+    
+    # Backward pass avec normalisation correcte
+    dC_dz2 = (a2 - y) / batch_size
+    dC_dw2 = np.matmul(a1.T, dC_dz2)
+    dC_db2 = np.mean(dC_dz2, axis=0)
+    dC_da1 = np.matmul(dC_dz2, w2.T)
+    dC_dz1 = dC_da1 * (a1 * (1 - a1))
+    dC_dw1 = np.matmul(a0.T, dC_dz1)
+    dC_db1 = np.mean(dC_dz1, axis=0)
+    
+    # Update weights and biases
+    w1 = w1 - learning_rate * dC_dw1
+    b1 = b1 - learning_rate * dC_db1
+    w2 = w2 - learning_rate * dC_dw2
+    b2 = b2 - learning_rate * dC_db2
+    
+    return w1, b1, w2, b2, loss
+# Question 13: Training function
+def train_mlp(w1, b1, w2, b2, data_train, labels_train, learning_rate, num_epoch):
+    train_accuracies = []
+    batch_size = 128  # Taille de batch raisonnable
+    
+    for epoch in range(num_epoch):
+        # ON mélange aléatoirement les données à chaque époque
+        indices = np.random.permutation(len(data_train))
+        data_train = data_train[indices]
+        labels_train = labels_train[indices]
+        
+        epoch_losses = []
+        
+        # On réalise l'entrainement par batch
+        for i in range(0, len(data_train), batch_size):
+            # On récupère notre batch de données + les labels
+            batch_data = data_train[i:i+batch_size]
+            batch_labels = labels_train[i:i+batch_size]
+            
+            # On réalise l'entrainement avec la fonction précédement définit sur le batch
+            w1, b1, w2, b2, loss = learn_once_cross_entropy(
+                w1, b1, w2, b2, batch_data, batch_labels, learning_rate)
+            epoch_losses.append(loss)
+        
+        # On calcul finalement la précision de notre entrainement
+        predictions = predict_mlp(w1, b1, w2, b2, data_train) # fonction définit en dessous
+        # Pour l'accuracy on peut directement faire la moyenne du tableau contenant True si
+        # la prédiction est vrai (si prediction = label) le mean sur [True,False] renvois 0.5
+        accuracy = np.mean(predictions == labels_train)
+        train_accuracies.append(accuracy)
+        
+        # On affiche l'évolution de l'entrainement dans la console
+        if (epoch + 1) % 10 == 0:
+            print(f"Epoch {epoch+1}/{num_epoch}, Loss: {np.mean(epoch_losses):.4f}, Accuracy: {accuracy:.4f}")
+    
+    return w1, b1, w2, b2, train_accuracies
+
+def predict_mlp(w1, b1, w2, b2, data):
+    # On Forward pass jusqu'à la prédiction (on avait des soucis sans le faire)
+    z1 = np.matmul(data, w1) + b1
+    a1 = 1 / (1 + np.exp(-z1))
+    z2 = np.matmul(a1, w2) + b2
+    exp_z2 = np.exp(z2 - np.max(z2, axis=1, keepdims=True))
+    a2 = exp_z2 / np.sum(exp_z2, axis=1, keepdims=True)
+    return np.argmax(a2, axis=1)
+
+# Question 14: La fonction pour le test (comme a la fin de notre fonction train_mlp)
+# On la mise aussi dans la fonction du dessus pour avoir un suivit de la loss
+def test_mlp(w1, b1, w2, b2, data_test, labels_test):
+    predictions = predict_mlp(w1, b1, w2, b2, data_test)
+    return np.mean(predictions == labels_test)
+
+# Question 15: La fonction pour l'entrainement complet
+def run_mlp_training(data_train, labels_train, data_test, labels_test, d_h, learning_rate, num_epoch):
+    # On initialise tailles, poids ...
+    d_in = data_train.shape[1]
+    d_out = 10  # CIFAR-10 classes
+    w1 = 2 * np.random.rand(d_in, d_h) - 1
+    b1 = np.zeros((1, d_h))
+    w2 = 2 * np.random.rand(d_h, d_out) - 1
+    b2 = np.zeros((1, d_out))
+    
+    # On passe a l'entrainement 
+    w1, b1, w2, b2, train_accuracies = train_mlp(
+        w1, b1, w2, b2, data_train, labels_train, learning_rate, num_epoch)
+    
+    # On passe ensuite au test
+    test_accuracy = test_mlp(w1, b1, w2, b2, data_test, labels_test)
+    return train_accuracies, test_accuracy
+
+# Question 16: Fonction pour plot notre courbe d'entrainement
+def plot_learning_curve(train_accuracies, test_accuracies):
+    plt.figure(figsize=(10, 6))
+    plt.plot(range(1, len(train_accuracies) + 1), train_accuracies, label='Training Accuracy')
+    plt.plot(range(1, len(test_accuracies) + 1), test_accuracies, label='Test Accuracy', linestyle='--')
+    plt.xlabel('Epoch')
+    plt.ylabel('Accuracy')
+    plt.title('MLP Learning Curve')
+    plt.legend()
+    plt.grid(True)
+    
+    # On sauvegarder le graphique
+    os.makedirs('results', exist_ok=True)
+    plt.savefig('results/mlp.png')
+    plt.close()
+
+
+if __name__ == "__main__":
+    # On charge les données
+    cifar_dir = "data/cifar-10-batches-py"
+    all_data, all_labels = read_cifar(cifar_dir)
+    
+    # On normalise les données (pixel codé sur 8 bit)
+    all_data = all_data / 255.0
+    
+    # On split nos datas
+    data_train, labels_train, data_test, labels_test = split_dataset(all_data, all_labels, split=0.9)
+    
+    # On initialise les paramètres, ils sont fournit dans le sujet
+    d_h = 64
+    learning_rate = 0.1
+    num_epoch = 100
+    
+    # On démarre le training
+    train_accuracies, test_accuracy = run_mlp_training(
+        data_train, labels_train, data_test, labels_test,
+        d_h, learning_rate, num_epoch
+    )
+    
+    print(f"\nPrécision finale sur le test : {test_accuracy:.4f}")
+    plot_learning_curve(train_accuracies, test_accuracy)