From f333429567525c50ad87d69f8cdd12ad8b201bc4 Mon Sep 17 00:00:00 2001 From: unknown <you@example.com> Date: Fri, 10 Nov 2023 14:19:46 +0100 Subject: [PATCH] Ajout des fichiers --- knn.py | 86 ++++++++++++++++++++ mlp.py | 126 ++++++++++++++++++++++++++++++ read_cifar.py | 44 +++++++++++ results/._mlp.png | Bin 0 -> 4096 bytes results/mlp_training_accuracy.png | Bin 0 -> 19182 bytes 5 files changed, 256 insertions(+) create mode 100644 knn.py create mode 100644 mlp.py create mode 100644 read_cifar.py create mode 100644 results/._mlp.png create mode 100644 results/mlp_training_accuracy.png diff --git a/knn.py b/knn.py new file mode 100644 index 0000000..3a8bd65 --- /dev/null +++ b/knn.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 26 15:59:25 2023 + +@author: DE SIMEIS +""" + +from read_cifar import * +from collections import Counter +import matplotlib.pyplot as plt + + +# Définition de la fonction calculant la matrice des distances euclidiennes entre les données d'apprentissage et de test +def distance_matrix(data_train, data_test): + sum_of_squares_matrix1 = np.sum(data_train ** 2, axis=1, keepdims=True) + + + sum_of_squares_matrix2 = np.sum(data_test ** 2, axis=1, keepdims=True) + + + dot_product = np.dot(data_train, data_test.T) + + dists = np.sqrt(sum_of_squares_matrix1 - 2 * dot_product + sum_of_squares_matrix2.T) + + return dists +# Définition de la fonction k-NN pour faire des prédictions +def knn_predict(dists, labels_train, k): + + dists=dists.T + predictions = [] + for distances in dists: + min_indexes = np.argpartition(distances, k)[:k] + possible_pred = labels_train[min_indexes] + counted = Counter(possible_pred) + pred = counted.most_common(1)[0][0] + predictions.append(pred) + return predictions + +# Évaluation de la précision des prédictions +def evaluate_knn(predictions, labels_test): + sum=0 + for i in range(len(predictions)): + if predictions[i] == labels_test[i]: + sum+=1 + + return sum / len(predictions) + +'''def evaluate_knn(data_train , labels_train,data_test ,labels_test, k): + + return''' +# Fonction principale +def main(): + + print('#START#') + folder_path = 'data/cifar-10-batches-py' + data, labels = read_cifar(folder_path) + + + + data_train, data_test, labels_train, labels_test = split_dataset(data, labels, 0.9) + + + + num_epochs = 20 + accuracies=[] + dists = distance_matrix(data_train, data_test) + + for k in range(num_epochs): + prediction=knn_predict(dists, labels_train, k+1) + accuracy = evaluate_knn(prediction, labels_test) + print(accuracy) + accuracies.append(accuracy) + + plt.figure(figsize=(10, 6)) + x = range(1, num_epochs + 1) + plt.plot(x, accuracies) + plt.xlabel('Epochs') + plt.ylabel('Accuracy') + plt.title('Training Accuracy Evolution') + plt.grid() + + plt.savefig('KNN.png') + plt.show() + + +main() diff --git a/mlp.py b/mlp.py new file mode 100644 index 0000000..130bdff --- /dev/null +++ b/mlp.py @@ -0,0 +1,126 @@ +import numpy as np +import os +import matplotlib.pyplot as plt +from read_cifar import read_cifar, split_dataset + + +# Fonction d'activation sigmoid +def sigmoid(x): + return 1 / (1 + np.exp(-x)) + +# Dérivée de la fonction d'activation sigmoid +def sigmoid_derivative(x): + return x * (1 - x) +# Fonction d'erreur quadratique moyenne + +def mean_squared_error(predictions, targets): + return np.mean(np.square(predictions - targets)) + +# Conversion des étiquettes en one-hot encoding +def one_hot(labels, num_classes): + one_hot_labels = np.zeros((len(labels), num_classes)) + one_hot_labels[np.arange(len(labels)), labels] = 1 + return one_hot_labels + +# Fonction pour une itération d'apprentissage avec la perte de mean squared error (MSE) +def learn_once_mse(w1, b1, w2, b2, data, targets, learning_rate): + a0 = data + z1 = np.dot(a0, w1) + b1 + a1 = sigmoid(z1) + z2 = np.dot(a1, w2) + b2 + a2 = sigmoid(z2) + + dC_da2 = a2 - targets + dC_dz2 = dC_da2 * sigmoid_derivative(a2) + dC_da1 = np.dot(dC_dz2, w2.T) + dC_dz1 = dC_da1 * sigmoid_derivative(a1) + + dC_dw2 = np.dot(a1.T, dC_dz2) + dC_db2 = np.sum(dC_dz2, axis=0) + dC_dw1 = np.dot(a0.T, dC_dz1) + dC_db1 = np.sum(dC_dz1, axis=0) + + # moyenne des gradients + w1 -= learning_rate * dC_dw1 / len(data) + b1 -= learning_rate * dC_db1 / len(data) + w2 -= learning_rate * dC_dw2 / len(data) + b2 -= learning_rate * dC_db2 / len(data) + + loss = mean_squared_error(a2, targets) + + return w1, b1, w2, b2, loss + +# Initialisation des poids +def initialize_weights(input_size, hidden_size, output_size): + + w1 = np.random.randn(input_size, hidden_size) / np.sqrt(input_size) + b1 = np.zeros((1, hidden_size)) + w2 = np.random.randn(hidden_size, output_size) / np.sqrt(hidden_size) + b2 = np.zeros((1, output_size)) + + return w1, b1, w2, b2 + +# Ajout de la fonction de prédiction +def predict_mlp(w1, b1, w2, b2, data): + a0 = data + z1 = np.dot(a0, w1) + b1 + a1 = sigmoid(z1) + z2 = np.dot(a1, w2) + b2 + a2 = sigmoid(z2) + return a2 + + +# Fonction principale pour le training du MLP + +def run_mlp_training(data_train, labels_train, data_test, labels_test, d_h, learning_rate, num_epochs, results_dir='results'): + # Create results directory if it does not exist + if not os.path.exists(results_dir): + os.makedirs(results_dir) + + N_train = len(data_train) + N_test = len(data_test) + d_in = data_train.shape[1] + d_out = len(np.unique(labels_train)) + + w1, b1, w2, b2 = initialize_weights(d_in, d_h, d_out) + + train_accuracies = [] + losses = [] + + for epoch in range(num_epochs): + w1, b1, w2, b2, loss = learn_once_mse(w1, b1, w2, b2, data_train, one_hot(labels_train, d_out), learning_rate) + + predictions = predict_mlp(w1, b1, w2, b2, data_train) + accuracy = np.mean(np.argmax(predictions, axis=1) == labels_train) + train_accuracies.append(accuracy) + losses.append(loss) + print(f"Epoch {epoch + 1}/{num_epochs} - Loss: {loss:.4f} - Accuracy: {accuracy:.4f}") + + # Enregistrement du graphe training accuracy + plt.figure(figsize=(8, 6)) + plt.plot(range(1, num_epochs + 1), train_accuracies) + plt.xlabel("Epoch") + plt.ylabel("Training Accuracy") + plt.title("MLP Training Accuracy Evolution") + plt.grid(True) + + save_path = os.path.join(results_dir, "mlp_training_accuracy.png") + plt.savefig(save_path) + plt.show() + + +# Avec les données on a : +split_factor = 0.9 +folder_path = 'data/cifar-10-batches-py' +data, labels = read_cifar(folder_path) + +data_train, data_test, labels_train, labels_test = split_dataset(data, labels, split_factor) +d_h = 64 +learning_rate = 0.01 +num_epochs = 100 + +# Pour que ça aille dans le dossier results dans le même dossier que le code +results_directory = 'results' + +# Exécution de l'entraînement mlp +w1, b1, w2, b2, train_accuracies, losses = run_mlp_training(data_train, labels_train, data_test, labels_test, d_h, learning_rate, num_epochs, results_directory) diff --git a/read_cifar.py b/read_cifar.py new file mode 100644 index 0000000..f25b49c --- /dev/null +++ b/read_cifar.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Oct 26 15:53:56 2023 + +@author: DE SIMEIS +""" + +import pickle +import numpy as np +import os +from sklearn.model_selection import train_test_split +# Charge un batch CIFAR-10 depuis le chemin spécifié. +#Returns: +# - data (numpy.ndarray): Les données du batch. +# - labels (numpy.ndarray): Les étiquettes associées aux données. +def read_cifar_batch(batch): + with open(batch, 'rb') as fo: + dict = pickle.load(fo, encoding='bytes') + data = dict[b'data'] + labels = dict[b'labels'] + return data, labels + + +# Charge l'ensemble du dataset CIFAR-10 depuis le répertoire spécifié +def read_cifar(path): + batches_list = os.listdir(path) + data, labels = [], [] + for batch in batches_list: + if(batch == 'batches.meta' or batch == 'readme.html'): + continue + data_batch, labels_batch = read_cifar_batch(path + '/' + batch) + data.append(data_batch) + labels.append(labels_batch) + return np.array(data, dtype=np.float32).reshape((60000, 3072)), np.array(labels, dtype=np.int64).reshape(-1) + +#Divise l'ensemble de données en ensembles d'entraînement et de test. +# Returns: +# - data_train (numpy.ndarray): Les données d'entraînement. +# - data_test (numpy.ndarray): Les données de test. +# - labels_train (numpy.ndarray): Les étiquettes d'entraînement. +# - labels_test (numpy.ndarray): Les étiquettes de test. +def split_dataset(data, labels, split): + data_train, data_test, labels_train, labels_test = train_test_split(data, labels, test_size=1-split, shuffle=True) + return data_train, data_test, labels_train,labels_test diff --git a/results/._mlp.png b/results/._mlp.png new file mode 100644 index 0000000000000000000000000000000000000000..114a742c3cedf3b0d023b2b2efb2a81f4fb82862 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3Cj$e65d#C?1_lNYpYIU^1EU-RLEsn?5@bE= z<bMVRkPe0e3=9lvXquQ97{KCU3=E>l`MG+D1qC^&dWEHlMTvPOnR%%o<<}S(7z7v? zq!98siNz(Q#i=PNi6yDZ0l^?e1_l;J)@G(AN#+)oN!9^Hsb!g|<<<s`-e4ngBJw~s zffSD7(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S``F0*nj{3?K~ls9Z=!X0bw1 zYH@yPQF5w6T7FTsLS|k`YF<fZeqLfuPNhOlYFddxQchxCwt|8JSkDkAKz;{dus0C> KcNqq`|NjB^pgPt7 literal 0 HcmV?d00001 diff --git a/results/mlp_training_accuracy.png b/results/mlp_training_accuracy.png new file mode 100644 index 0000000000000000000000000000000000000000..65d1741acd1b30b71f783da6820d730109fe1358 GIT binary patch literal 19182 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 literal 0 HcmV?d00001 -- GitLab