{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"# CHPS0942 : TD 2 : Working on MNIST\n\n<!-- > **Attention : sujet en cours de développement** -->\n\n## Objectifs\n> - Développer votre premier réseau de neurones\n> - Démarrer un challenge sur la base de MNIST\n\n## Présentation\n> * La base d'images de chiffres manuscrits américains est un ensemble mondialement reconnu et sur laquelle de très nombreux travaux ont été réalisés. \n> * Ces données sont issues d'un challenge \"Digit Recognizer\". Celui-ci fournit un fichier avec les images d'entrainement (_train.csv_), un fichier d'évaluation (_test.csv_), et un fichier de soumission des résultats de la classification obtenue sur les données de 'test'.\n> * Je vous fournis un fichier complémentaire (_test100.csv_) des 100 premiers items du fichier de test dans lequel j'ai ajouté la classe.\n> * Les images proposées sont des versions centrées et retaillée. Elles sont de dimension 28x28 pixels en niveau de gris \\[0-255\\].\n> * Les fichiers .csv d'entrainement et de test sont constitué d'une colonne _label_ dont la valeur est sémantiquement le nom de la classe (le chiffre associé/dessiné) et des 28x28 valeurs de pixel\n> * Le fichier .csv d'évaluation ne contient pas de label\n\n> * __Le travail à réaliser par vous même et à rendre se trouve à la fin de ce Notebook__\n"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"# Listage des données source\nimport os\nracine_data = \"../input\";\nrepertoires = os.listdir(racine_data)\nfor repertoire in repertoires:\n    print(repertoire)\n    fichiers = os.listdir(racine_data+\"/\"+repertoire)\n    for fichier in fichiers:\n        print(\"   > \" +fichier)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7cdb0d5954d3ccb21d2f7b18642cd7ee1355ae37"},"cell_type":"code","source":"# Chargement des données d'entrainement et de test\nimport pandas as pd\ntrain = pd.read_csv('../input/digits/train.csv')\ntest100 = pd.read_csv('../input/test-100-premiers/test100.csv')\nevaluation = pd.read_csv('../input/digits/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b9c59afb1e8b445c2cc7f730ab0128902e0995ad"},"cell_type":"code","source":"# Affichage des informations\nprint(\"Il y a {0} exemples d'apprentissage.\".format(train.shape[0]))\nprint(\"Il y a {0} exemples d'évaluation.\".format(evaluation.shape[0]))\nprint(\"Il y a {0} exemples de test.\".format(test100.shape[0]))\nprint(\"Nombre de colonnes : \" , train.shape[1])\nprint(\"Liste des colonnes :\")\ntrain.columns","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"9ad65d4b46fc7d17382f3aeef85f8612dcad9b7b"},"cell_type":"code","source":"# On sépare la classe (label) des données (pixels)\n\n# Le vecteur des numéros de classe\ntrain_label = train[\"label\"]\n\n# Suppression de cette colonne \ntrain.drop(\"label\", axis = 1 , inplace=True)\n\n# Nombre d'exemples d'entrainement par classe\ntrain_label.value_counts().sort_index()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b45d9b69e4f1b73b34221046f83e0abfd14e71f4"},"cell_type":"code","source":"# La même chose pour les tests\ntest100_label = test100[\"label\"]\ntest100.drop(\"label\", axis = 1 , inplace=True)\ntest100_label.value_counts().sort_index()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"33f8cf93665f17dad90c46fd24f8672ce9c182f7"},"cell_type":"code","source":"# Normalisation des valeurs des images [0-255] -> [0.0-1.0]\ntrain      = train/255.0\ntest100    = test100/255.0\nevaluation = evaluation/255.0\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"20279f8094eeaade1c61300b177bff51dc95f864"},"cell_type":"code","source":"# Affichage des images de l'ensemble de test\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\n# Dimension de l'affichage\nnb_cols = 10\nnb_ligs = 10\ntaille  = 20\n\n# Affichage de nb_ligs lignes de nb_cols images\nfig1, ax1 = plt.subplots(nb_ligs,nb_cols, figsize=(taille,taille))\nfor no_lig in range(nb_ligs):\n    for no_col in range(nb_cols):\n        ax1[no_lig][no_col].imshow(test100.iloc[no_lig*nb_cols+no_col].values.reshape((28,28)), cmap='gray')\n        ax1[no_lig][no_col].axis('off')\n        ax1[no_lig][no_col].set_title(test100_label[no_lig*nb_cols+no_col])  ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3650fdcfb0a6b07879c39aca2adf04a6318d6f4c"},"cell_type":"code","source":"import numpy as np\n# Restructuration des images en 3 dimensions (height = 28px, width = 28px , canal = 1)\ntrain_image =np.array(train).reshape(-1,28,28,1)\ntest100_image =np.array(test100).reshape(-1,28,28,1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c71211637b5abaf6d15881d321c71f42e9799691"},"cell_type":"code","source":"# Encodage du label de l'ensemble d'apprentissage\n#   par exemple le label '2' -> [ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.]\nfrom keras.utils.np_utils import to_categorical\n\ntrain_label_cat = to_categorical(train_label)\ntest100_label_cat = to_categorical(test100_label)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"48256aed5c3d4ca80551e9b22f896f124551eb04"},"cell_type":"markdown","source":"## Création du réseau de neurones\nNous allons utiliser un réseau en couches séquentielles : https://keras.io/getting-started/sequential-model-guide/ "},{"metadata":{"trusted":true,"_uuid":"c89eba961f3f0d2601b0fb3676e2f6bc34d99c7c"},"cell_type":"code","source":"# import de Keras \nfrom keras.models import Sequential\nfrom keras.layers import Conv2D\nfrom keras.layers import MaxPooling2D\nfrom keras.layers import Flatten\nfrom keras.layers import Dense","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5559b90fa8a13676acd63241d7d84388693031a8"},"cell_type":"code","source":"# Convolutional Neural Network (CNN)\n\n# Un réseau en couches séquentielles\nclassifier = Sequential()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7a5cffb8a6aa95e8f3820d27df8362556aab57dd"},"cell_type":"code","source":"# Premier étage de convolution - pooling\nclassifier.add(\n    Conv2D(32,                      # Taille du filtre\n           (3, 3),                  # Taille de la fenêtre\n           padding = 'Same',        # Remplissage des bords externes\n           activation=\"relu\",       # Fonction d'activation\n           input_shape=(28, 28, 1)  # Dimension de l'entrée (uniquement pour la 1ère couche)\n          )\n)\n\nclassifier.add(\n    MaxPooling2D(\n        pool_size = (2, 2)\n    )\n)\n\n# Deuxième niveau \nclassifier.add(Conv2D(32, (3, 3), activation=\"relu\"))\nclassifier.add(MaxPooling2D(pool_size = (2, 2)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a67e09517067e18e50d0f8328113ac064b0841c3"},"cell_type":"code","source":"# Applanissement (étranglement)\nclassifier.add(Flatten())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5104c7917746108d93cd09082229801e3f2ef62b"},"cell_type":"code","source":"# Couche totalement connectée \nclassifier.add(Dense(units = 256, activation = 'relu'))\n\n# Couche de sortie (nos 10 classes-chiffres)\nclassifier.add(Dense(units = 10, activation = 'softmax'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"cf7633867201e7ed0351b586fe58357d1d5e892d"},"cell_type":"code","source":"# Compilation du CNN décrit\nclassifier.compile(\n    optimizer = 'adam',\n    loss = 'categorical_crossentropy',\n    metrics = ['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7202239655cb8b408eecad3291b43a3faed2dbe5"},"cell_type":"code","source":"# Sauvegarde de la représentation graphique du réseau\n#     Ce fichier sera accessible dans l'onglet 'output' du kernel après un commit\nfrom keras.utils import plot_model\nplot_model(classifier, to_file='model_classifier_digit.png', show_shapes=True)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"90c6305aa1d34c8feb869e09e8c22cea23093a45"},"cell_type":"markdown","source":"## Création des fonctionnalités pour un affichage de l'évolution de l'apprentissage"},{"metadata":{"trusted":true,"_uuid":"094cb0087e218c94913e91939d1b11827c8658bc"},"cell_type":"code","source":"from keras.callbacks import Callback\nimport matplotlib.patches as mpatches\n\nclass AccLossPlotter(Callback):\n    \"\"\"Plot training Accuracy and Loss values on a Matplotlib graph. \n    The graph is updated by the 'on_epoch_end' event of the Keras Callback class\n    # Arguments\n        graphs: list with some or all of ('acc', 'loss')\n        save_graph: Save graph as an image on Keras Callback 'on_train_end' event \n    \"\"\"\n    \n    def __init__ (self, graphs=['acc','loss'], save_graph=False):\n        self.graphs = graphs\n        self.num_subplots = len(graphs)\n        self.save_graph = save_graph\n    \n    \n    def on_train_begin(self, logs={}):\n        self.acc = []\n        self.val_acc = []\n        self.loss = []\n        self.val_loss = []\n        self.epoch_count = 0\n        plt.ion()\n        plt.show()\n\n\n    def on_epoch_end(self, epoch, logs={}):\n        self.epoch_count += 1\n        self.val_acc.append(logs.get('val_acc'))\n        self.acc.append(logs.get('acc'))\n        self.loss.append(logs.get('loss'))\n        self.val_loss.append(logs.get('val_loss'))\n        epochs = [x for x in range(self.epoch_count)]\n\n        count_subplots = 0\n        \n        if 'acc' in self.graphs:\n            count_subplots += 1\n            plt.subplot(self.num_subplots, 1, count_subplots)\n            plt.title('Accuracy')\n            #plt.axis([0,100,0,1])\n            plt.plot(epochs, self.val_acc, color='r')\n            plt.plot(epochs, self.acc, color='b')\n            plt.ylabel('accuracy')\n\n            red_patch = mpatches.Patch(color='red', label='Test')\n            blue_patch = mpatches.Patch(color='blue', label='Train')\n\n            plt.legend(handles=[red_patch, blue_patch], loc=4)\n\n        if 'loss' in self.graphs:\n            count_subplots += 1\n            plt.subplot(self.num_subplots, 1, count_subplots)\n            plt.title('Loss')\n            #plt.axis([0,100,0,5])\n            plt.plot(epochs, self.val_loss, color='r')\n            plt.plot(epochs, self.loss, color='b')\n            plt.ylabel('loss')\n\n            red_patch = mpatches.Patch(color='red', label='Test')\n            blue_patch = mpatches.Patch(color='blue', label='Train')\n\n            plt.legend(handles=[red_patch, blue_patch], loc=4)\n        \n        plt.draw()\n        plt.pause(0.001)\n\n    def on_train_end(self, logs={}):\n        if self.save_graph:\n            plt.savefig('training_acc_loss.png')\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"93c60525aebc76af3cc7d4973e1f017c11e03954"},"cell_type":"code","source":"# http://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html\nfrom sklearn.metrics import confusion_matrix\n\nimport itertools\n\nclass ConfusionMatrixPlotter(Callback):\n    \"\"\"Plot the confusion matrix on a graph and update after each epoch\n    # Arguments\n        X_val: The input values \n        Y_val: The expected output values\n        classes: The categories as a list of string names\n        normalize: True - normalize to [0,1], False - keep as is\n        cmap: Specify matplotlib colour map\n        title: Graph Title\n    \"\"\"\n    def __init__(self, X_val, Y_val, classes, normalize=False, cmap=plt.cm.Blues, title='Confusion Matrix'):\n        self.X_val = X_val\n        self.Y_val = Y_val\n        self.title = title\n        self.classes = classes\n        self.normalize = normalize\n        self.cmap = cmap             # Color map\n        plt.ion()                    # Utilisation de pyplot en mode interactif\n        #plt.show()\n        plt.figure()\n\n        plt.title(self.title)\n        \n\n    def on_train_begin(self, logs={}):\n        pass\n\n    \n    def on_epoch_end(self, epoch, logs={}):\n        \"\"\" A la fin d'un cycle \"\"\"\n        plt.clf()\n        # On évalue le résultat du modèle sur toutes les entrées de X_val\n        pred = self.model.predict(self.X_val)\n        # On récupère l'index de la colonne de plus grande valeur pour toutes les lignes\n        #     du résultat\n        max_pred = np.argmax(pred, axis=1)\n        # On récupère l'index de la colonne de plus haute valeur pour toutes les lignes\n        max_y = np.argmax(self.Y_val, axis=1)\n        # On évalue la matrice de confusion\n        cnf_mat = confusion_matrix(max_y, max_pred)\n        \n        # S'il y a une demande de normalisation \n        if self.normalize:\n            cnf_mat = cnf_mat.astype('float') / cnf_mat.sum(axis=1)[:, np.newaxis]\n\n        # Seuil à la moitié du max\n        thresh = cnf_mat.max() / 2.\n        \n        # Pour tous les éléments du produit cartésien (tous les éléments de la matrice)\n        for i, j in itertools.product(range(cnf_mat.shape[0]), range(cnf_mat.shape[1])):\n            plt.text(j, i, cnf_mat[i, j],                                          \n                         horizontalalignment=\"center\",\n                         color=\"white\" if cnf_mat[i, j] > thresh else \"black\")\n\n        plt.imshow(cnf_mat, interpolation='nearest', cmap=self.cmap)\n\n        # Labels\n        tick_marks = np.arange(len(self.classes))\n        plt.xticks(tick_marks, self.classes, rotation=45)\n        plt.yticks(tick_marks, self.classes)\n\n        plt.colorbar()\n                                                                                                         \n        plt.tight_layout()                                                    \n        plt.ylabel('True label')                                              \n        plt.xlabel('Predicted label')\n            #plt.draw()\n        plt.show()\n        plt.pause(0.001)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"966059eb9c1262be041971bb8a7a11ab02415441"},"cell_type":"code","source":"# plotter : accuracy et loss à chaque cycle\nplotterAccLoss = AccLossPlotter(graphs=['acc', 'loss'], save_graph=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ec5d1dabefd7234e305d49e28818c42b9c276fd8"},"cell_type":"code","source":"# plotter : matrice de confusion à chaque cycle\nclass_names = ['0', '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9']\nplotterCM = ConfusionMatrixPlotter(\n    X_val=train_image,\n    classes=class_names,\n    Y_val=train_label_cat)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"cb1211834b297737a2679644cf44413f1c8f08c2"},"cell_type":"code","source":"# Entrainement du Réseau\nepochs= 10        # 20\nbatch_size=90\n\nclassifier.fit(train_image, train_label_cat,\n               batch_size=batch_size,\n               validation_data=(test100_image, test100_label_cat),\n               epochs=epochs, \n               callbacks=[plotterAccLoss,plotterCM])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"92f710946326ab5499b7cf0b56ed27a48e171ff5"},"cell_type":"code","source":"# Evaluation des résultats sur les données de test\nresults = classifier.predict(test100_image)\n# results","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bee26f66ed129463da04228631118a21c174a1db"},"cell_type":"code","source":"# Sauvegarde des résultats\npred = []\nnumTest = results.shape[0]\n# Pour chacun des items de test\nfor i in range(numTest):\n    # Neurone de sortie de plus grande valeur\n    pred.append(np.argmax(results[i])) \npredictions = np.array(pred) \n\nsample_submission = pd.read_csv('../input/test-100-premiers/sample_submission100.csv')\nresult=pd.DataFrame({'ImageId':sample_submission.ImageId, 'Label prédit':predictions, 'Classe réelle':test100_label})\nresult.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"30038dfc4a1f50699f0fe75dfd2a2774edb5952c","scrolled":true},"cell_type":"code","source":"print(result)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"329a662e5a51db2d09458a69977db8da4802214d"},"cell_type":"markdown","source":"# Travail à réaliser\n1. A partir de ce notebook, essayez plusieurs réseaux afin de comparer leurs résultats (_loss_ et _accuracy_) en changeant par exemple les fonctions d'activation, la taille des fenêtres de convolution... \n__Proposez 1 autre réseau avec les résultats obtenus sur la base de test.__\n1. On peut aussi utiliser des réseaux séquentiels auto-associatifs où ce que l'on cherche à apprendre c'est que le réseau fournisse en sortie une image qui soit le mieux possible identique à l'entrée. On a : au milieu du réseau une couche d'étranglement,  1 ou 2 couches de convolution en amont, 1 ou 2 couche de déconvolution en aval.\n__Proposez 2 réseaux et les résultats obtenus sur les tests.__"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}