Pas encore de compte ?
Conversion du dataset en tableau numpy :
Nous devons convertir nos images vers des matrices de valeurs qui vont être empilés.
Le réseau de neurones ne fonctionne qu’avec des tenseurs.
Les tenseurs de valeurs correspondent aux intensités de couleurs des 3 différents canaux RGB qui correspondant à chaque pixel qui compose chaque image.
Nous avons ainsi un fichier numpy par classe.
Nous avons 3 matrices qui correspondent à nos 3 canaux de couleurs RGB, qui sont empilés pour former un Tenseur d’ordre 3.
Tenseurs
Descente de gradient :
Le réseau de neurones va faire des prédictions, à partir d’une entrée via ses différents types de couches superposés.
C’est durant l’entrainement qu’il va apprendre, se tromper, et notamment s’auto-ajuster via l’étape de la rétro-propagation du gradient (backpropagation).
L' algorithme de descente du gradient va permettre de minimiser la fonction de coût, appelé aussi fonction d’objectif ou encore de perte.
Le but de cet algorithme est de chercher à résoudre la fonction suivante :
Ax = B, ou :
A est est une matrice d’entrée
x est un ensemble de variable contenu dans un tenseur qui représente l’ensemble des poids du réseau de neurone
B est un vecteur de sortie des labels
Degrés d’un tenseur
Deg 0 : c’est un produit scalaire
Deg 1 : c’est un vecteur
Deg 2 : c’est une matrice
Deg 3 : c’est un empilement de matrice, une sorte de matrice 3D. C’est cela que l’on envoie dans notre réseau de neurone
L' algorithme de descente du gradient va permettre de minimiser la fonction de coût, appelé aussi fonction d’objectif ou encore de perte.
Le but de cet algorithme est de chercher à résoudre la fonction suivante :
Ax = B, ou :
A est est une matrice d’entrée
x est un ensemble de variable contenu dans un tenseur qui représente l’ensemble des poids du réseau de neurone
B est un vecteur de sortie des labels
Pré traitement des données :
Nous allons générer deux types différents de dataset à partir de nos fichiers Numpy, le Dataset d’entrainement et le Dataset de validation.
le Dataset d’entrainement va permettre à notre réseau d’apprendre et d’extraire des caractéristiques distinctes de chacune de nos images.
Dataset de validation va servir à valider le modèle en fin de chaque itération au cours de l’entrainement.
En effet, en montrant de nouvelles images à notre réseau, il va lui permettre de se recalibrer pour éviter de sur-apprendre les images du jeu de données d’entrainement.
Il faudra respecter un certain ratio entre ces deux jeux de données.
A partir de notre dataset original, nous récupérons 80 à 90% des données pour le dataset d’entrainement, et donc de 10 à 20% pour le dataset de validation.
Notre réseau à convolution va avoir comme entrée un tenseur de la dimension suivante :
( n, w, h, c )
n : nombre total d’image de notre dataset
w : largeur en pixel de nos images
h : hauteur en pixel de nos images
c : nombre de canaux de nos images. Correspond donc à 1 pour du noir & blanc, et 3 pour des entrées en couleurs.
Il faudra donc bien faire attention de remodeler (reshape) nos données en les récupérant depuis nos fichiers numpy.
Création du modèle :
Nous devons respecter des templates concernant les empilement des différentes couches :
[ [Conv -> ReLU]*n -> Pool ] *q -> [FC -> ReLU]*k -> FC -> Softmax
Conv : couche de convolution
ReLU : fonction d’activation, Rectified Linear Unit
Pool : couche de convolution
FC : couche de neurones entièrement connecté
Softmax : fonction d’activation à sorties multiples
Entrainement du modèle :
Le réseau de neurones apprends.
Celui ci va se renforcer au fur et a mesure des itérations que va parcourir votre modèle sur votre jeu de donnée, devenant ainsi meilleur.
Réaliser une prédiction :
Le modèle est maintenant entraîné et l'on va enfin pouvoir réaliser des prédictions sur de nouvelles images.
Nous chargeons le modèle en mémoire.
Nous transformons l'image au format jpg vers un tableau numpy, puis nous remodelons (reshape) sa dimension.
Nous avons en sortie un tableau de 5 valeurs, correspondant aux 5 neurones de la couche de sortie du modèle, et donc à nos 5 classes d'images.
On aura pour chaque classe un pourcentage concernant sa prédiction.
Nous prenons la valeur la plus élevée des 5, qui correspond donc à la prédiction effectué par notre modèle.
Test de notre modèle sur un jeu de donnée entier :
Maintenant que nous avons un modèle, nous souhaitons savoir comment il va se comporter sur de grandes quantités de nouvelles données.
Nous allons recréer un dataset de nouvelles images, auxquelles notre réseau n’aura jamais vu auparavant, pour permettre de prédire au mieux comment notre réseau va se comporter en production.
Plus le nouveau dataset sera important, plus nous aurons une idée précise du comportement de votre réseau de neurones.
Le but de la matrice va nous permettre de mettre en évidence d’éventuel erreurs.
Code source du tutoriel de https://deeplylearning.fr/cours-pratiques-deep-learning/reconnaissance-dimages/ qui permet de creer avec Tensorflow et Keras une reconnaissance d'image entre 5 types de fleurs différentes, avec des algorithmes de deep learning.
Image-classification - GitHub
Testé sous Anaconda et Python 3.7
# IMPORT import os from PIL import Image import numpy as np from tqdm import tqdm """ # Classe permettant de convertir notre dataset d'images en tableaux Numpy """ def launchConversion(pathData, pathNumpy, resizeImg, imgSize): """ # Permet de lancer la conversion des images en tableau numpy :param pathData: chemin ou sont les :param pathNumpy: :param resizeImg: :param imgSize: """ #Pour chaque classe for flowerClasse in os.listdir(pathData): pathFlower = pathData + '\\' + flowerClasse imgs = [] #Pour chaque image d'une classe, on la charge, resize et transforme en tableau for imgFlower in tqdm(os.listdir(pathFlower), "Conversion de la classe : '{}'".format(flowerClasse)): imgFlowerPath = pathFlower + '\\' + imgFlower img = Image.open(imgFlowerPath) img.load() if resizeImg == True: img = img.resize(size=imgSize) data = np.asarray(img, dtype=np.float32) imgs.append(data) #Converti les gradients de pixels (allant de 0 à 255) vers des gradients compris entre 0 et 1 imgs = np.asarray(imgs) / 255. #Enregistre une classe entiere en un fichier numpy np.save(pathNumpy + '\\ ' + flowerClasse + '.npy', imgs) def main(): """ # Fonction main """ pathNumpy = '.\\numpy' pathData = '.\\dataset' resizeImg = True imgSize = (50, 50) launchConversion(pathData, pathNumpy, resizeImg, imgSize) if __name__ == '__main__': """ # MAIN """ main()
Testé sous Anaconda et Python 3.7
# IMPORT import numpy as np import os import keras from sklearn.model_selection import train_test_split from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D from keras.utils import to_categorical from keras.callbacks import EarlyStopping, ModelCheckpoint, CSVLogger from keras.optimizers import * from keras import regularizers """ # Classe permettant d'entrainer un modèle sur une jeu de données """ def get_labels(path): """ # Permet de recuperer les labels de nos classe, leurs indices dans le tableau et leur matrix binaire one hot encoder :param path: chemin ou sont stocké nos fichiers Numpy """ labels = [file.replace('.npy', '') for file in os.listdir(path) if file.endswith('.npy')] label_indices = np.arange(0, len(labels)) return labels, label_indices, to_categorical(label_indices) def get_train_test(train_ratio, pathData): """ # Retourner le dataset melanger en dataset d'entrainement et de validation selon un ratio :param train_ratio: permet de gerer la part entre dataset de train et de validation :param pathData: chemin des fichiers numpy """ labels, _, _ = get_labels(pathData) classNumber = 0 #On init avec le premier tableau pour avoir les bonnes dimensions pour la suite X = data = np.load(pathData + '\\' + labels[0] + '.npy') Y = np.zeros(X.shape[0]) dimension = X[0].shape classNumber += 1 #On ajoute le reste des fichiers numpy de nos classes for i, label in enumerate(labels[1:]): data = np.load(pathData + '\\' + label + '.npy') X = np.vstack((X, data)) Y = np.append(Y, np.full(data.shape[0], fill_value=(i+1))) classNumber += 1 X_train, X_test, Y_train, Y_test = train_test_split(X, Y, train_size=train_ratio) return X_train, X_test, to_categorical(Y_train), to_categorical(Y_test), classNumber, dimension def main(): """ # Fonction main """ #Definition des chemins et autres variables pathData = '.\\numpy' trainRatio = 0.8 epochs = 1000 batch_size = 16 earlyStopPatience = 5 #Definition des callbacks #Permet de retourner 4 metrics de suivi a chaque iteration csv_logger = CSVLogger('.\\logs\\log_moModel.csv', append=True, separator=',') #Permet de stopper l'entrainement quand le modèle n'entraine pluss early = EarlyStopping(monitor='val_loss', min_delta=0, patience=earlyStopPatience, verbose=0, mode='auto') #Permet de sauvegarder le model a chaque iteration si il est meilleur que le precedent check = ModelCheckpoint('.\\trainedModel\\moModel.hdf5', monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=False, mode='auto') #Recuperation de nos data pré traité x_train, x_test, y_train, y_test, classNumber, dimension = get_train_test(trainRatio, pathData) #On verifie les dimensions de nos données print('DIMENSION X TRAIN ' + str(x_train.shape)) print('DIMENSION X TEST ' + str(x_test.shape)) print('DIMENSION Y TRAIN ' + str(y_train.shape)) print('DIMENSION Y TEST ' + str(y_test.shape)) #On creer le modele model = Sequential() model.add(Conv2D(32, kernel_size=(2, 2), activation='relu', input_shape=(dimension[0], dimension[1], dimension[2]))) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(128, activation='relu')) model.add(Dropout(0.25)) model.add(Dense(classNumber, activation='softmax')) #On compile le modele model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adamax(lr=0.001, beta_1=0.9, beta_2=0.999, decay=0.0), metrics=['accuracy']) #On lance l'entrainement du modele trainning = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(x_test, y_test), callbacks=[early, check,csv_logger]) if __name__ == "__main__": """ # MAIN """ main()
Testé sous Anaconda et Python 3.7
# IMPORT import matplotlib.pyplot as plt import pandas as pd """ # Classe permettant de génerer 4 graphiques de suivit de métriques durant l'entrainement d'un modèle # Train accuracy, Train loss, Validation accuracy, Validation loss """ def displayGraph(pathLog,pathSaveGraph): """ # Fonction permettant de creer nos graph de suivi de metriques :param pathLog: chemin du CSV contenant nos metrics :param pathSaveGraph: chemin de destination pour sauvegarder nos 4 graphiques en jpg """ data = pd.read_csv(pathLog) # split into input (X) and output (Y) variables plot(data['epoch'], data['acc'], data['val_acc'], 'TRAIN_VAL_Accuracy', 'Epoch', 'Accuracy', 'upper left',pathSaveGraph) plot(data['epoch'], data['loss'], data['val_loss'], 'TRAIN_VAL_Loss', 'Epoch', 'Loss', 'upper left',pathSaveGraph) def plot(X, Y, Y2, title, xLabel, yLabel, legendLoc, pathSaveGraph): """ # Fonction d'affichage de graph :param X: correspond au nombre d'époch :param Y: correspond a la courbe accuracy :param Y2: correspond a la courbe loss :param title: titre du graphique :param xLabel: label des abcisses :param yLabel: label des ordonnees :param legendLoc: legende :param pathSaveGraph: chemin de sauvegarde pour les graphiques """ #On trace nos differentes courbes plt.plot(Y) plt.plot(Y2) #titre du graph, legende... plt.title(title) plt.xlabel(xLabel) plt.ylabel(yLabel) plt.legend(['train', 'val'], loc=legendLoc) #Pour avoir un courbe propre qui demarre à 0 plt.xlim(xmin=0.0, xmax=max(X)) plt.savefig(pathSaveGraph +'\\' + title) plt.figure() #plt.show() def main(): """ # Fonction main """ #Definition des chemins d'acces a notre fichier log pathLogs = '.\\logs\\log_moModel.csv' pathSaveGraph = '.\\graph' displayGraph(pathLogs,pathSaveGraph) if __name__ == "__main__": """ # MAIN """ main()
Testé sous Anaconda et Python 3.7
# IMPORT import matplotlib.pyplot as plt from tqdm import tqdm import os from PIL import Image import numpy as np from sklearn.metrics import confusion_matrix import itertools from keras.models import load_model """ # Classe permettant de génerer une matrice de confusion à partir d'un dataset de test et d'un modèle entrainé # au préalable """ def generateMatrix(model, datasetTestPath, imageSize, destinationMatrix): """ # Fonction qui va construire notre matrice de confusion :param model: chemin du modèle à charger pour realiser la prediction :param datasetTestPath: chemin du dataset contenant nos images de test :param imageSize: definit la taille de l'ensemble de nos images :param destinationMatrix: définit le chemin ou va être sauvegardé notre matrice sous format d'image :return: """ #Les tableaux contenanrt les predictions y_true = [] y_pred = [] total = 0 success = 0 index = 0 print('\nEvaluation :') #On parcours notre dataset de test for root, dirs, files in os.walk(datasetTestPath): for mydir in dirs: for sample in tqdm(os.listdir(root + '\\' + mydir), "Prediction de la classe '{}'".format(mydir)): sample_path = root + '\\' + mydir + '\\' + sample #Chargement et traitement de l'image img = Image.open(sample_path) img.load() img = img.resize(size=imageSize) img = np.asarray(img) / 255. #On reshape pour etre de la forme (nbImage,hauteurImage,largeurImage,nbCanaux) img = img.reshape(1, img.shape[0], img.shape[1], img.shape[2]) #Prediction de notre modele pred = np.argmax(model.predict(img)) total += 1 if pred == index: success += 1 y_true.append(index) y_pred.append(pred) index += 1 #Precision de notre modele sur notre jeu de test en entier accuracy = (success / total) * 100. print('\nPrecision : {0:.3f}%'.format(accuracy)) cnf_matrix = confusion_matrix(y_true, y_pred) np.set_printoptions(precision=2) # Plot normalized confusion matrix plt.figure() cmap = plt.cm.Blues classes = ['marguerite', 'pissenlit', 'rose', 'tournesol', 'tulipe'] title = 'Confusion matrix' cnf_matrix = cnf_matrix.astype('float') / cnf_matrix.sum(axis=1)[:, np.newaxis] #Legende de notre matrice plt.imshow(cnf_matrix, interpolation='nearest', cmap=cmap) plt.title(title) plt.colorbar() tick_marks = np.arange(len(classes)) plt.xticks(tick_marks, classes, rotation=45) plt.yticks(tick_marks, classes) fmt = '.2f' thresh = cnf_matrix.max() / 2. for i, j in itertools.product(range(cnf_matrix.shape[0]), range(cnf_matrix.shape[1])): plt.text(j, i, format(cnf_matrix[i, j], fmt), horizontalalignment="center", color="white" if cnf_matrix[i, j] > thresh else "black") plt.ylabel('True label') plt.xlabel('Predicted label') plt.tight_layout() #On sauvegarde notre matrice en image plt.savefig(destinationMatrix + '\\' + 'confusionMatrix') def main(): """ # Fonction main """ #On definit les chemins de nos divers ressources modelPath = '.\\trainedModel\\moModel.hdf5' datasetTestPath = '.\\datasetTest' destinationMatrix = '.\\graph' imageSize = (50, 50) model = load_model(modelPath) generateMatrix(model, datasetTestPath, imageSize, destinationMatrix) if __name__ == "__main__": """ # MAIN """ main()
Testé sous Anaconda et Python 3.7
#IMPORT from keras.models import load_model from PIL import Image import numpy as np import time """ # Classe permettant de réaliser une prédiction sur une nouvelle donnée """ def main(): """ # On definit les chemins d'acces au différentes hyper parametre """ modelPath = '.\\trainedModel\\moModel.hdf5' imagePath = '.\\testImage\\rose.jpg' imageSize = (50,50) label = ['marguerite', 'pissenlit', 'rose', 'tournesol', 'tulipe'] predict(modelPath, imagePath,imageSize, label) def predict(modelPath,imagePath, imageSize, label): """ # Fonction qui permet de convertir une image en array, de charger le modele et de lui injecter notre image pour une prediction :param modelPath: chemin du modèle au format hdf5 :param imagePath: chemin de l'image pour realiser une prediction :param imageSize: défini la taille de l'image. IMPORTANT : doit être de la même taille que celle des images du dataset d'entrainements :param label: nom de nos 5 classes de sortie """ start = time.time() # Chargement du modele print("Chargement du modèle :\n") model = load_model(modelPath) print("\nModel chargé.") #Chargement de notre image et traitement data = [] img = Image.open(imagePath) img.load() img = img.resize(size=imageSize) img = np.asarray(img) / 255. data.append(img) data = np.asarray(data) #On reshape pour correspondre aux dimensions de notre modele # Arg1 : correspond au nombre d'image que on injecte # Arg2 : correspond a la largeur de l'image # Arg3 : correspond a la hauteur de l'image # Arg4 : correspond au nombre de canaux de l'image (1 grayscale, 3 couleurs) dimension = data[0].shape #Reshape pour passer de 3 à 4 dimension pour notre réseau data = data.astype(np.float32).reshape(data.shape[0], dimension[0], dimension[1], dimension[2]) #On realise une prediction prediction = model.predict(data) #On recupere le numero de label qui a la plus haut prediction maxPredict = np.argmax(prediction) #On recupere le mot correspondant à l'indice precedent word = label[maxPredict] pred = prediction[0][maxPredict] * 100. end = time.time() #On affiche les prédictions print() print('----------') print(" Prediction :") for i in range(0, len(label)): print(' ' + label[i] + ' : ' + "{0:.2f}%".format(prediction[0][i] * 100.)) print() print('RESULTAT : ' + word + ' : ' + "{0:.2f}%".format(pred)) print('temps prediction : ' + "{0:.2f}secs".format(end-start)) print('----------') if __name__ == "__main__": """ # MAIN """ main()
Le classificateur de chiffres manuscrits : Le dataset MNIST
Testé sous Anaconda et Python 3.7
from sklearn.datasets import fetch_openml import numpy as np from matplotlib import pyplot as plt X, y = fetch_openml('mnist_784', version=1, return_X_y=True) X = X.to_numpy() y = y.astype(int) X_img = np.reshape(X,(X.shape[0],28*28)) #Recuperation du nombre d'exemples d'apprentissage ainsi que la dimension des vecteurs n_samples = X.shape[0] print("Nombre d'exemples d'apprentissage n_samples = %d " % n_samples) def plotImg(X): plt.figure(figsize=(7.195, 3.841), dpi=100) for i in range(200): plt.subplot(10,20,i+1) plt.imshow(X[i,:].reshape([28,28]), cmap='gray') plt.axis('off') plt.show() plt.close() plotImg(X_img) n_classes = np.max(y) + 1 print("Nombre de classes d'objets n_classes = %d " % n_classes)
Visualisation d’un exemple representatif du jeu de données
La classification comporte deux phases, une phase d'apprentissage et une phase d'évaluation.
Dans la phase d'apprentissage, le classificateur forme son modèle sur un ensemble de données donné et dans la phase d'évaluation, il teste les performances du classificateur.
Les performances sont évaluées sur la base de divers paramètres tels que l'exactitude, l'erreur, la précision et le rappel.
Bienvenu, je m’appelle Eric Soupet et je suis l'administrateur du site elodees.com. elodees.com est un état de l'art de l'Intelligence Artificielle et se veut collaboratif, vous pouvez dès à présent proposer du contenu tels que des articles, des événements, des tutoriels, ... alors n'hésitez pas !
Crédit des images de la plate-forme : Pixabay - Pixabay License | Pexels - Pexels License