From d1a3319cb755901f848acf905c218882783ccf52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric?= <frederic.sur@univ-lorraine.fr>
Date: Mon, 9 Jan 2023 08:50:36 +0100
Subject: [PATCH] TP6 2022-2023

---
 TP6/TP6_ex1_sujet.ipynb  | 428 +++++++++++++++++++++++++++++++++++++++
 TP6/TP6_ex3_correc.ipynb | 223 ++++++++++++++++++++
 TP6/TP6_ex3_sujet.ipynb  | 149 ++++++++++++++
 3 files changed, 800 insertions(+)
 create mode 100644 TP6/TP6_ex1_sujet.ipynb
 create mode 100644 TP6/TP6_ex3_correc.ipynb
 create mode 100644 TP6/TP6_ex3_sujet.ipynb

diff --git a/TP6/TP6_ex1_sujet.ipynb b/TP6/TP6_ex1_sujet.ipynb
new file mode 100644
index 0000000..edcbd93
--- /dev/null
+++ b/TP6/TP6_ex1_sujet.ipynb
@@ -0,0 +1,428 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Introduction à l'apprentissage automatique - TP6 exercice 1 \n",
+    "\n",
+    "\n",
+    "### Classification d'images à la fin du XXème siècle et au début du XXIème siècle \n",
+    "\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Le but de ce TP est de découvrir une application de la [vision par ordinateur](https://en.wikipedia.org/wiki/Computer_vision), la classification d'images.\n",
+    "\n",
+    "On utilisera un sous-ensemble de la [base de données Caltech256](https://data.caltech.edu/records/nyy15-4j048), constitué d'images appartenant à 7 catégories, disponible [à cette URL](https://members.loria.fr/FSur/enseignement/apprauto/Caltech256_small.zip) (disponible aussi sur Arche). La base de donnée est volontairement réduite pour rester gérable pendant un TP, mais vous pouvez bien entendu tenter de traiter la base entière sur votre temps libre.\n",
+    "\n",
+    "<br>\n",
+    "Commencez par charger et décompresser cette base d'images dans votre répertoire de travail, et visualisez les images qu'elle contient.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Le but est de prédire la catégorie d'une image de la base de test, connaissant les catégories auxquelles appartiennent les images de la base d'apprentissage. Il s'agit donc d'un problème de classification supervisée.\n",
+    "\n",
+    "Les images ont des tailles différentes, de l'ordre de 300x200 pixels (certaines images, rares, ont des tailles de plus de 1000x1000 pixels). Ce sont des images en couleur, un pixel est donc décrit par un triplet de composantes Rouge, Vert, Bleu. Les composantes prennent des valeurs entre 0 et 255 (codage sur 8 bits), de manière à ce que (0,0,0) soit le noir, (255,255,255) le blanc, (0,255,0) le vert \"pur\", etc. Chaque image est donc décrite par approximativement 3x300x200=120000 valeurs variant entre 0 et 255. \n",
+    "\n",
+    "Le défi est de réduire la dimension des observations, en extrayant de chaque image une information pertinente représentée dans un espace de dimension \"raisonnable\", commun à toutes les images. Nous allons utiliser une méthode inspirée de celle présentée dans [cet article](https://chapelle.cc/wp-content/uploads/2022/09/tnn99.pdf) très célèbre de 1999: chaque image sera représentée par un vecteur de ${\\mathbb R}^{8^3}={\\mathbb R}^{512}$ obtenu en concaténant les histogrammes tridimensionnels des triplets (R,V,B) en chaque pixel, chaque composante étant discrétisée sur 8 \"baquets\" ( _bins_ ). "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<br>\n",
+    "\n",
+    "On commence par importer des bibliothèques, et définir une fonction de visualisation de la base de test qui nous sera utile pour visualiser les performances des classifieurs."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from sklearn import svm, model_selection, neural_network, neighbors, linear_model, metrics, cluster\n",
+    "from sklearn.metrics.pairwise import chi2_kernel\n",
+    "import numpy as np\n",
+    "import os\n",
+    "import matplotlib.pyplot as plt\n",
+    "\n",
+    "%matplotlib notebook\n",
+    "\n",
+    "def display_test(X_image_test,Y_test,Y_test_pred,txt):\n",
+    "    # pour visualiser les résultats de 120 images tirées au hasard dans la base test\n",
+    "    # paramètres: \n",
+    "    # X_image_test: tableau des images de test \n",
+    "    # Y_test: classes d'appartenance réelles\n",
+    "    # Y_test_pred: classes prédites\n",
+    "    # txt: légende de la figure\n",
+    "    alea=np.random.choice(len(X_image_test),size=120,replace=False)\n",
+    "    plt.figure(figsize=[18,12])    \n",
+    "    for n in range(120):\n",
+    "        plt.subplot(12,10,n+1,xticks=[],yticks=[])\n",
+    "        plt.imshow(X_image_test[alea[n]],cmap='gray')\n",
+    "        if Y_test_pred[alea[n]]==Y_test[alea[n]]:\n",
+    "            plt.text(0.1,0.1,str(Y_test_pred[alea[n]])+' / '+str(Y_test[alea[n]]),fontsize=8,bbox=dict(facecolor='white', alpha=1))\n",
+    "        else:\n",
+    "            plt.text(0.1,0.1,str(Y_test_pred[alea[n]])+' / '+str(Y_test[alea[n]]),fontsize=8,bbox=dict(facecolor='red', alpha=1))\n",
+    "    plt.suptitle('prediction '+txt)\n",
+    "    plt.show();\n",
+    "    "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "La cellule suivante permet de lire les données et stocker l'information dans des tableaux. Notez le nombre d'observations dans chaque classe (80% pour la base d'apprentissage, 20 % pour la base test). Constatez que les classes ont des effectifs assez différents. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "path=\"./Caltech256_small/\" \n",
+    "y=0\n",
+    "X_image=[]\n",
+    "X=[]\n",
+    "Y=[]\n",
+    "X_train=[]\n",
+    "X_test=[]\n",
+    "\n",
+    "for directory in os.listdir(path):\n",
+    "    count = 0 # indice de l'image traitée dans le répertoire courant\n",
+    "    for file in os.listdir(path+directory):\n",
+    "        img = plt.imread(path+directory+\"/\"+file)\n",
+    "        if (len(img.shape)==2):  # au cas où on lit une image en niveaux de gris, on crée une image RBV ou R=V=B\n",
+    "            img=np.repeat(img[:,:,np.newaxis],3,axis=2)\n",
+    "        X_image.append(img)\n",
+    "        Y.append(y)\n",
+    "        count = count+1\n",
+    "    print(\"%s  - classe: %d  -  nombre d'observations: %d \" % (directory,y,count))\n",
+    "    y = y+1\n",
+    "    \n",
+    "X_image_train, X_image_test, Y_train, Y_test = model_selection.train_test_split(X_image, Y, test_size=0.2, random_state=1)\n",
+    "\n",
+    "for i in range(len(X_image_train)):\n",
+    "    img=X_image_train[i]\n",
+    "    h,b = np.histogramdd(img.reshape(-1,3),range=((0,255),(0,255),(0,255)),bins=8)  # on calcule l'histogramme 3D, chaque direction est discrétisée sur 8 bins\n",
+    "    h = h/(img.shape[0]*img.shape[1])  # on normalise les valeurs: (nombre de pixels dans un bin donné) / (nombre de pixels dans l'image)\n",
+    "    X_train.append(h.flatten())\n",
+    "\n",
+    "for i in range(len(X_image_test)):\n",
+    "    img=X_image_test[i]\n",
+    "    h,b = np.histogramdd(img.reshape(-1,3),range=((0,255),(0,255),(0,255)),bins=8)  # on calcule l'histogramme 3D, chaque direction est discrétisée sur 8 bins\n",
+    "    h = h/(img.shape[0]*img.shape[1])  # on normalise les valeurs: (nombre de pixels dans un bin donné) / (nombre de pixels dans l'image)\n",
+    "    X_test.append(h.flatten())\n",
+    "        \n",
+    " \n",
+    "print(\"\\n%d observations dans la base d'apprentissage, chacune est décrite par un vecteur de dimension %d\" % (len(X_train), len(X_train[0])) )\n",
+    "print(\"\\n%d observations dans la base de test, chacune est décrite par un vecteur de dimension %d\" % (len(X_test), len(X_test[0])) )\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "__Question 1__: `np.histogramdd` renvoie le nombre de pixels de l'image `img` dont les composantes (R,G,B) tombent dans un cube de la forme $[32i,32(i+1)]\\times[32j,32(j+1)]\\times[32k,32(k+1)]$, pour $i,j,k \\in \\{0,1,\\dots,7\\}$ (d'où les 8 \"baquets\" par composante couleur). Selon vous, pourquoi normalise-t-on les valeurs stockées dans les histogrammes, et quel est l'intérêt pour ces images de tailles différentes? "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<font color=red>\n",
+    "    \n",
+    "Réponse:\n",
+    "    \n",
+    "</font>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Les deux cellules suivantes permettent de visualiser une image et son histogramme normalisé. On représente l'histogramme 3D comme un _scatter plot_ tridimensionnel où chaque triplet (R,V,B) est représenté comme un disque de rayon proportionnel à la fréquence du triplet dans l'image, la couleur du disque étant déterminée par (R,V,B). Les couleurs les plus fréquentes dans l'image seront donc représentées par des \"gros\" disques. \n",
+    "\n",
+    "Le code est adapté de: https://staff.fnwi.uva.nl/r.vandenboomgaard/IPCV20172018/LectureNotes/IP/Images/ImageHistograms.html\n",
+    "\n",
+    "Constatez le lien entre l'allure de l'histogramme  (que vous pouvez \"tourner\" à la souris) et le contenu de l'image.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "_Remarque_: si vous avez un message d'erreur à l'exécution de la cellule suivante, mettez à jour `ipython`, en exécutant l'une ou l'autre de ces instructions dans un terminal:\n",
+    "\n",
+    "* si vous utilisez la distribution anaconda: `conda update ipython`\n",
+    "* sinon: `pip install --upgrade ipython`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from mpl_toolkits.mplot3d import Axes3D\n",
+    "\n",
+    "def histogram3dplot(h, e):\n",
+    "    \"\"\"\n",
+    "    Visualize a 3D histogram\n",
+    "    source: https://staff.fnwi.uva.nl/r.vandenboomgaard/IPCV20172018/LectureNotes/IP/Images/ImageHistograms.html\n",
+    "    \n",
+    "    Parameters\n",
+    "    ----------\n",
+    "\n",
+    "    h: histogram array of shape (M,N,O)\n",
+    "    e: list of bin edge arrays (for R, G and B)\n",
+    "    \"\"\"\n",
+    "    M, N, O = h.shape\n",
+    "    idxR = np.arange(M)\n",
+    "    idxG = np.arange(N)\n",
+    "    idxB = np.arange(O)\n",
+    "\n",
+    "    R, G, B = np.meshgrid(idxR, idxG, idxB, indexing='ij')   # change w.r.t. original code\n",
+    "    a = np.diff(e[0])[0]\n",
+    "    b = a/2\n",
+    "    R = a * R + b\n",
+    "\n",
+    "    a = np.diff(e[1])[0]\n",
+    "    b = a/2\n",
+    "    G = a * G + b\n",
+    "\n",
+    "    a = np.diff(e[2])[0]\n",
+    "    b = a/2\n",
+    "    B = a * B + b\n",
+    "\n",
+    "    colors = np.vstack((R.flatten(), G.flatten(), B.flatten())).T/255\n",
+    "    h = h / np.sum(h)\n",
+    "    f = plt.figure(figsize=[8,6])\n",
+    "    ax = f.add_subplot(111, projection='3d')     \n",
+    "    mxbins = np.array([M,N,O]).max()\n",
+    "    ax.scatter(R.flatten(), G.flatten(), B.flatten(), s=h.flatten()*(256/mxbins)**3/2, c=colors)\n",
+    "\n",
+    "    ax.set_xlabel('Rouge')\n",
+    "    ax.set_ylabel('Vert')\n",
+    "    ax.set_zlabel('Bleu')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# exemple: visualisation d'une image\n",
+    "n_image=100  # indice d'une image de la base d'apprentissage, essayez aussi d'autres valeurs\n",
+    "\n",
+    "plt.figure()\n",
+    "plt.imshow(X_image_train[n_image])\n",
+    "plt.title(\"image: %d - classe: %d\" % (n_image,Y_train[n_image]))\n",
+    "plt.show();\n",
+    "\n",
+    "histogram3dplot(X_train[n_image].reshape(8,8,8),b)\n",
+    "plt.show();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "__Question 2__: testez différents algorithmes de classification sur les histogrammes vus comme des vecteurs de ${\\mathbb R}^{256}$ (bases  `X_train` et `X_test` déjà construites): SVM, MLP, LR, NN:\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "* Pour les SVM, vous testerez les noyaux `linear`, `rbf`, et `chi2_kernel`.\n",
+    "\n",
+    "On peut utiliser une SVM avec un noyau du $\\chi^2$ (on sait que cette métrique est bien adaptée à la comparaison d'histogrammes d'après le cours de statistique) à l'aide de:\n",
+    "```python \n",
+    "SVM = svm.SVC(kernel=chi2_kernel)\n",
+    "``` \n",
+    "avec l'import préalable (déjà fait dans la première cellule de ce carnet):\n",
+    "```python\n",
+    "from sklearn.metrics.pairwise import chi2_kernel\n",
+    "```\n",
+    "Ce mode de définition d'un noyau rend la modification du paramètre $\\gamma$ plus compliquée, on se contentera donc de la valeur par défaut (idem pour `rbf`). Voir [cette page](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.chi2_kernel.html) pour la définition de ce noyau.\n",
+    "\n",
+    "Pour les trois noyaux, vous fixerez les valeurs de l'hyperparamètres $C$ à l'aide de `GridSearchCV`.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "* Pour les MLP, vous testerez un réseau à une couche cachée à 20 neurones et un autre avec deux couches cachées à 20 neurones, dont vous fixerez l'hyperparamètre de régularisation `alpha`  à l'aide de `GridSearchCV`. Vous fixerez `max_iter=500` et utiliserez `solver='lbfgs'` (bien adapté aux petites bases de données d'après la documentation de scikit-learn).\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "* pour LR, vous fixerez également l'hyperparamètre de régularisation par `GridSearchCV`.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "* pour les NN, vous testerez 1-ppv et 5-ppv.\n",
+    "\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Pour chaque classifieur, vous afficherez le score de classification sur la base de test, la matrice de confusion, et la visualisation de la classification obtenue à l'aide de la fonction `display_test` fournie.\n",
+    "\n",
+    "Quel est le score de classification \"minimal\" auquel se comparer ?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "\n",
+    "# gridsearch pour SVM\n",
+    "\n",
+    "C_range=10**np.arange(-4,4.5,1)\n",
+    "parameters = {'C':C_range}\n",
+    "\n",
+    "# votre code ici\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "# gridsearch pour MLP\n",
+    "\n",
+    "\n",
+    "alpha_range=10**np.arange(-4.,4.5,1)\n",
+    "parameters = {'alpha':alpha_range }\n",
+    "\n",
+    "# Vous pouvez avoir des avertissements sur des problèmes de convergence:\n",
+    "# n'y passez pas trop de temps (on améliorera les résultats dans l'exercice 2)\n",
+    "\n",
+    "# votre code ici:\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# gridsearch pour LR\n",
+    "\n",
+    "C_range=10**np.arange(-4,4.5,1)\n",
+    "parameters = {'C':C_range}\n",
+    "\n",
+    "# votre code ici:\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "%matplotlib inline  # on repasse à la présentation non-interactive des graphiques pour utilisation de la fonction display_test\n",
+    "\n",
+    "# classification avec les hyperparamètres optimaux\n",
+    "\n",
+    "# votre code ici"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "__Question 3__: \n",
+    "\n",
+    "Quelle est la nature de l'information permettant la classification? En quel sens reconnaît-on les différentes catégories?\n",
+    "\n",
+    "Quel modèle d'apprentissage présente les meilleures performances ? A partir de quel taux de classifications correctes\n",
+    "le modèle \"apprend\"-il quelque chose ?\n",
+    "\n",
+    "Quelle est la classe la plus \"facile\" à reconnaître, et pourquoi ? La plus difficile ?\n",
+    "\n",
+    "Que dire des biais de la base d'apprentissage ? En 2015, l'application photo de Google a fait les titres de la presse grand public pour avoir étiqueté des étudiants à la peau noire comme \"gorilles\". Voici la [\"solution\" de Google](https://www.wired.com/story/when-it-comes-to-gorillas-google-photos-remains-blind/).\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<font color=red>\n",
+    "\n",
+    "Réponse:\n",
+    "\n",
+    "</font>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<br>\n",
+    "\n",
+    "## Compléments d'information (après la séance de TD)\n",
+    "\n",
+    "L'[article](https://chapelle.cc/wp-content/uploads/2022/09/tnn99.pdf) de Chapelle et al. cité en introduction teste différents noyaux pour les SVM. On voit que la SVM avec noyau du $\\chi^2$ est parmi les plus performantes. Notons que la base de données de cet article est différente de celle que nous utilisons.\n",
+    "\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Cette méthode correspond à l'état de l'art scientifique à la fin des années 1990 / début des années 2000.\n",
+    "\n",
+    "En 2004-2005 sont apparus de nouveaux algorithmes basés sur la notion de \"sac de mots visuels\" et les descripteurs SIFT (évoqués en cours), dont une description accessible est [disponible ici](https://towardsdatascience.com/bag-of-visual-words-in-a-nutshell-9ceea97ce0fb) par exemple. Il s'agit d'une adaptation aux images des sacs de mots que l'on vous a présentés au TP2.\n",
+    "\n",
+    "Depuis 2012, les méthodes basées sur les [réseaux de neurones convolutifs](https://medium.com/@tifa2up/image-classification-using-deep-neural-networks-a-beginner-friendly-approach-using-tensorflow-94b0a090ccd4) obtiennent les meilleurs résultats. Elles font l'objet de l'exercice 2.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Notez la remarque dans l'article de Chapelle et al. de 1999, Section IV A p. 4 à propos du noyau du $\\chi^2$: \" _It is not known if the kernel satisfies Mercer’s condition_ \". Ceci a été démontré dans [cet article](https://people.eecs.berkeley.edu/~malik/papers/FBCM-nystrom.pdf) de 2004 (annexe B).\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "_Références bibliographiques:_\n",
+    "- O. Chapelle, P. Haffner, V. Vapnik. _SVMs for Histogram-Based Image Classification_, IEEE Transactions on Neural Networks, vol. 10, no. 5, 1999.\n",
+    "- C. Fowlkes, S. Belongie, F. Chung, J. Malik. _Spectral Grouping Using the Nyström Method_, IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 26, no. 2, 2004."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/TP6/TP6_ex3_correc.ipynb b/TP6/TP6_ex3_correc.ipynb
new file mode 100644
index 0000000..cc65150
--- /dev/null
+++ b/TP6/TP6_ex3_correc.ipynb
@@ -0,0 +1,223 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Introduction à l'apprentissage automatique - TP6 exercice 3 - <font color=red> CORRECTION </font>\n",
+    "\n",
+    "### CNN pour Fashion-MNIST\n",
+    "\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Dans cet exercice nous allons mettre en oeuvre un réseau de neurones convolutif pour la base d'images Fashion-MNIST.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "La cellule suivante importe les données et les sépare en base d'apprentissage et en base de test. Les niveaux de gris sont normalisés entre 0 et 1."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt\n",
+    "import time\n",
+    "from sklearn import datasets, metrics, model_selection\n",
+    "%matplotlib inline \n",
+    "\n",
+    "size_images=(28,28)\n",
+    "X_fashion, y_fashion = datasets.fetch_openml(data_id=40996, return_X_y=True, as_frame=False)\n",
+    "X_fashion=X_fashion/255.  # normalisation des niveaux de gris entre 0 et 1\n",
+    "\n",
+    "for i in range(10):\n",
+    "    n=np.sum(y_fashion==str(i))\n",
+    "    print(\"nombre d'observations dans la classe %d: %d\" %(i,n))\n",
+    "\n",
+    "n_samples = len(X_fashion)\n",
+    "print(\"nombre total d'observations (apprentissage + test): %d\" % n_samples)\n",
+    "\n",
+    "n_features = len(X_fashion[0])\n",
+    "print(\"nombre de caractéristiques par observation: %d\" % n_features)\n",
+    "\n",
+    "X_train, X_test, y_train, y_test = model_selection.train_test_split(X_fashion, y_fashion, test_size=0.2, random_state=1)\n",
+    "\n",
+    "print(\"nombre d'observations dans la base d'apprentissage: %d\" %len(X_train))\n",
+    "print(\"nombre d'observations dans la base de test: %d\" %len(X_test))\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On définit la fonction d'affichage habituelle:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def affichage_150_images(X_test,y_test,y_pred):\n",
+    "    plt.figure(figsize=[15,12])   \n",
+    "    for n in range(150):\n",
+    "        plt.subplot(10,15,n+1,xticks=[],yticks=[])\n",
+    "        plt.imshow(np.reshape(X_test[n,:],size_images),cmap='gray_r')\n",
+    "        if y_pred[n]==y_test[n]:\n",
+    "            plt.text(0.1,0.1,str(y_pred[n])+' / '+str(y_test[n]),fontsize=8,bbox=dict(facecolor='white', alpha=1))    \n",
+    "        else:\n",
+    "            plt.text(0.1,0.1,str(y_pred[n])+' / '+str(y_test[n]),fontsize=8,bbox=dict(facecolor='red', alpha=1))    \n",
+    "    plt.suptitle('classe predite / classe réelle')\n",
+    "    plt.show();\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Les CNN de Tensorflow exigent des bases de données sous la forme de tableaux numpy de dimension 4, de taille $(N,li,co,ch)$ où\n",
+    "- $N$ est le nombre d'observations\n",
+    "- $li$ est le nombre de lignes de chaque image\n",
+    "- $co$ est le nombre de colonnes de chaque image\n",
+    "- $ch$ est le nombre de canaux de chaque image (1 ici, car les images sont en noir-et-blanc)\n",
+    "\n",
+    "On reformate donc les tableaux `X_train` et `X_test`. Par ailleurs, `y_train` et `y_test` doivent être de type entier."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "X_train = X_train.reshape((len(X_train),)+size_images+(1,))\n",
+    "X_test = X_test.reshape((len(X_test),)+size_images+(1,))\n",
+    "y_train = y_train.astype(int)\n",
+    "y_test = y_test.astype(int)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "\n",
+    "<br>\n",
+    "\n",
+    "__Travail à faire__:\n",
+    "\n",
+    "en vous inspirant de la première partie de l'exercice 2, construisez un CNN permettant de classifier Fashion-MNIST.\n",
+    "\n",
+    "<br>\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from keras.models import Sequential\n",
+    "from keras.layers import Conv2D, MaxPooling2D\n",
+    "from keras.layers import Activation, Dropout, Flatten, Dense\n",
+    "\n",
+    "model = Sequential() \n",
+    "\n",
+    "# on part de la même architecture que dans l'exercice 2, mais on peut aussi innover:\n",
+    "# n'hésitez pas à m'envoyer vos résultats si vous améliorez le taux de classification sur la base test\n",
+    "# (à peu près 90% de succès ici) en introduisant une autre architecture\n",
+    "\n",
+    "model.add(Conv2D(32, (3, 3), input_shape=(28, 28, 1)))   \n",
+    "model.add(Activation('relu'))\n",
+    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
+    "\n",
+    "# on obtient de meilleurs résultats sans les deux couches suviantes:\n",
+    "#model.add(Conv2D(32, (3, 3)))\n",
+    "#model.add(Activation('relu'))\n",
+    "#model.add(MaxPooling2D(pool_size=(2, 2)))\n",
+    "\n",
+    "#model.add(Conv2D(64, (3, 3)))\n",
+    "#model.add(Activation('relu'))\n",
+    "#model.add(MaxPooling2D(pool_size=(2, 2)))\n",
+    "\n",
+    "model.add(Flatten())  # this converts our 3D feature maps to 1D feature vectors\n",
+    "model.add(Dense(64))  \n",
+    "model.add(Activation('relu'))\n",
+    "model.add(Dropout(0.2))  # semble meilleur que 0.5\n",
+    "model.add(Dense(10))  \n",
+    "model.add(Activation('softmax'))  \n",
+    "\n",
+    "model.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy'], optimizer='rmsprop')\n",
+    "\n",
+    "model.summary()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "model.fit(X_train,y_train,batch_size=16,epochs=5,validation_data=(X_test,y_test))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "y_proba = model.predict(X_test)\n",
+    "#print(y_proba) # les proba a posteriori de chaque classe\n",
+    "Y_test_pred=y_proba.argmax(axis=1)\n",
+    "\n",
+    "print(\"classes prédites:\")\n",
+    "print(Y_test_pred)\n",
+    "\n",
+    "\n",
+    "print(\"score de classification: %.3f\" %metrics.accuracy_score(y_test,Y_test_pred))\n",
+    "\n",
+    "print(\"matrice de confusion\")\n",
+    "print(metrics.confusion_matrix(y_test,Y_test_pred))\n",
+    "\n",
+    "affichage_150_images(X_test,y_test,Y_test_pred)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.12"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/TP6/TP6_ex3_sujet.ipynb b/TP6/TP6_ex3_sujet.ipynb
new file mode 100644
index 0000000..8518c19
--- /dev/null
+++ b/TP6/TP6_ex3_sujet.ipynb
@@ -0,0 +1,149 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Introduction à l'apprentissage automatique - TP6 exercice 3 \n",
+    "\n",
+    "### CNN pour Fashion-MNIST\n",
+    "\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "Dans cet exercice nous allons mettre en oeuvre un réseau de neurones convolutif pour la base d'images Fashion-MNIST.\n",
+    "\n",
+    "<br>\n",
+    "\n",
+    "La cellule suivante importe les données et les sépare en base d'apprentissage et en base de test. Les niveaux de gris sont normalisés entre 0 et 1."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt\n",
+    "import time\n",
+    "from sklearn import datasets, metrics, model_selection\n",
+    "%matplotlib inline \n",
+    "\n",
+    "size_images=(28,28)\n",
+    "X_fashion, y_fashion = datasets.fetch_openml(data_id=40996, return_X_y=True, as_frame=False)\n",
+    "X_fashion=X_fashion/255.  # normalisation des niveaux de gris entre 0 et 1\n",
+    "\n",
+    "for i in range(10):\n",
+    "    n=np.sum(y_fashion==str(i))\n",
+    "    print(\"nombre d'observations dans la classe %d: %d\" %(i,n))\n",
+    "\n",
+    "n_samples = len(X_fashion)\n",
+    "print(\"nombre total d'observations (apprentissage + test): %d\" % n_samples)\n",
+    "\n",
+    "n_features = len(X_fashion[0])\n",
+    "print(\"nombre de caractéristiques par observation: %d\" % n_features)\n",
+    "\n",
+    "X_train, X_test, y_train, y_test = model_selection.train_test_split(X_fashion, y_fashion, test_size=0.2, random_state=1)\n",
+    "\n",
+    "print(\"nombre d'observations dans la base d'apprentissage: %d\" %len(X_train))\n",
+    "print(\"nombre d'observations dans la base de test: %d\" %len(X_test))\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "On définit la fonction d'affichage habituelle:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def affichage_150_images(X_test,y_test,y_pred):\n",
+    "    plt.figure(figsize=[15,12])   \n",
+    "    for n in range(150):\n",
+    "        plt.subplot(10,15,n+1,xticks=[],yticks=[])\n",
+    "        plt.imshow(np.reshape(X_test[n,:],size_images),cmap='gray_r')\n",
+    "        if y_pred[n]==y_test[n]:\n",
+    "            plt.text(0.1,0.1,str(y_pred[n])+' / '+str(y_test[n]),fontsize=8,bbox=dict(facecolor='white', alpha=1))    \n",
+    "        else:\n",
+    "            plt.text(0.1,0.1,str(y_pred[n])+' / '+str(y_test[n]),fontsize=8,bbox=dict(facecolor='red', alpha=1))    \n",
+    "    plt.suptitle('classe predite / classe réelle')\n",
+    "    plt.show();\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Les CNN de Tensorflow exigent des bases de données sous la forme de tableaux numpy de dimension 4, de taille $(N,li,co,ch)$ où\n",
+    "- $N$ est le nombre d'observations\n",
+    "- $li$ est le nombre de lignes de chaque image\n",
+    "- $co$ est le nombre de colonnes de chaque image\n",
+    "- $ch$ est le nombre de canaux de chaque image (1 ici, car les images sont en noir-et-blanc)\n",
+    "\n",
+    "On reformate donc les tableaux `X_train` et `X_test`. Par ailleurs, `y_train` et `y_test` doivent être de type entier."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "X_train = X_train.reshape((len(X_train),)+size_images+(1,))\n",
+    "X_test = X_test.reshape((len(X_test),)+size_images+(1,))\n",
+    "y_train = y_train.astype(int)\n",
+    "y_test = y_test.astype(int)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "\n",
+    "<br>\n",
+    "\n",
+    "__Travail à faire__:\n",
+    "\n",
+    "en vous inspirant de la première partie de l'exercice 2, construisez un CNN permettant de classifier Fashion-MNIST.\n",
+    "\n",
+    "<br>\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# à vous de jouer!\n"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.12"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
-- 
GitLab