diff --git a/TP4/TP4_ex1_sujet.ipynb b/TP4/TP4_ex1_sujet.ipynb new file mode 100755 index 0000000000000000000000000000000000000000..d4e1a784c3e730ca383da62682d0b2782c1c50bf --- /dev/null +++ b/TP4/TP4_ex1_sujet.ipynb @@ -0,0 +1,528 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction à l'apprentissage automatique - TP4 exercice 1\n", + "\n", + "### SVM sur données synthétiques\n", + "\n", + "<br> \n", + "\n", + "Dans la [documentation scikit-learn sur les SVM](http://scikit-learn.org/stable/modules/svm.html), lisez l'introduction.\n", + "\n", + "On utilisera la [classe SVC](http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html).\n", + "\n", + "Notez la valeur par défaut de l'hyperparamètre $C$, et les fonctions noyau disponibles nativement (ainsi que leurs paramètres).\n", + "\n", + "<br>\n", + "\n", + "Dans les questions suivantes, nous séparerons les bases de données\n", + "entre bases d'apprentissage (80% de la base initiale) et base de test (20%) en utilisant `model_selection.train_test_split`, et nous comparerons les performances des différents classifieurs en calculant le score de classification sur la base de test.\n", + "\n", + "<br>\n", + "\n", + "On commence par charger les bibliothèques utiles et définir une fonction de visualisation que nous utiliserons par la suite:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import datasets, model_selection, preprocessing, model_selection, svm\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.colors import ListedColormap\n", + "from matplotlib.colors import Normalize\n", + "\n", + "%matplotlib inline\n", + "\n", + "def plot_classif_result_SVM(X,y,clf,title):\n", + " cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA'])\n", + " cmap_bold = ListedColormap(['#FF0000', '#00FF00']) \n", + " cmap2 = ListedColormap(['#FF8888', '#FFAAAA', '#AAFFAA', '#88FF88']) \n", + " \n", + " h=0.01 # step size in the mesh\n", + " \n", + " x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1\n", + " y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1\n", + " xx, yy = np.meshgrid(np.arange(x_min, x_max, h),\n", + " np.arange(y_min, y_max, h))\n", + " Zdf = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])\n", + " Z=Zdf>0\n", + " Zdfbin = (np.abs(Zdf)<=1) # 0 if inside margin, 1 if outside \n", + " Color=np.zeros(Z.shape) # colors for each region \n", + " for i in range(len(Z)):\n", + " if (Z[i]):\n", + " if Zdfbin[i]: Color[i]=2\n", + " else: Color[i]=3\n", + " else:\n", + " if Zdfbin[i]: Color[i]=1 \n", + " else: Color[i]=0\n", + " \n", + " # Put the result into a color plot\n", + " Z = Z.reshape(xx.shape)\n", + " Color = Color.reshape(xx.shape) \n", + " plt.figure(figsize=[10,8])\n", + " plt.pcolormesh(xx, yy, Color, cmap=cmap2, shading='auto')\n", + " \n", + " # Plot also the training points\n", + " plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold, edgecolor='k', s=20)\n", + " \n", + " # Plot the support vectors (stars):\n", + " plt.scatter(X[clf.support_, 0], X[clf.support_, 1], c=y[clf.support_], cmap=cmap_bold,edgecolor='k',s=80, marker='*') \n", + " \n", + " plt.xlim(xx.min(), xx.max())\n", + " plt.ylim(yy.min(), yy.max())\n", + " plt.title(title);\n", + " plt.axis(\"equal\")\n", + " plt.show();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Jeu de données \"blobs\"\n", + "\n", + "Commençons par un jeu de données constitué de 1000 points du plan dans deux classes linéairement séparables, obtenu avec `make_blobs` (comme dans un des TP précédents). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# génération dataset\n", + "# on précise random_state pour travailler tous sur le même jeu de données\n", + "# avec cluster_std=1.5 dans la ligne suivante, les classes sont linéairement séparables\n", + "X_dataset, y_dataset = datasets.make_blobs(n_features=2, centers=2, cluster_std=1.5, n_samples=1000, random_state=1)\n", + "\n", + "X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2)\n", + "\n", + "# affichage dataset train (gros points) + test (petits points)\n", + "plt.figure(figsize=[10,8])\n", + "plt.scatter(X_train[:, 0], X_train[:, 1], marker='o', c=y_train, s=25, edgecolor='k')\n", + "plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_test, s=10)\n", + "plt.title('dataset')\n", + "plt.axis('equal')\n", + "plt.grid()\n", + "plt.show();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ensuite, nous entraînons une SVM à noyau linéaire, et nous visualisons les vecteurs supports à l'aide de la fonction `plot_classif_result_SVM`.\n", + "\n", + "On affiche également le score de classification sur la base test, ainsi que le nombre de vecteurs supports pour chaque classe.\n", + "\n", + "__Question 1__. Les résultats dans le cas où les classes sont linéairement séparables vous semblent-ils cohérents avec le cours?\n", + "\n", + "Que peut-on dire lorsque les deux classes ne sont plus linéairement séparables? (augmentez la valeur de `cluster_std`)\n", + "\n", + "En particulier, comparez la marge, les vecteurs supports, leur nombre, le score sur la base de test, et le temps de calcul.\n", + "\n", + "(pour obtenir deux classes non linéairement séparables, augmentez la valeur de `cluster_std` dans `make_blobs` dans la cellule précédente, et relancez la cellule suivante)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SVM=svm.SVC(kernel='linear')\n", + "print('Apprentissage...\\n')\n", + "%time SVM.fit(X_train,y_train)\n", + "print('\\nAffichage...\\n')\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM\")\n", + "%time print(\"score test SVM %.3f\\n\" % SVM.score(X_test, y_test) )\n", + "print(\"nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1\" % (SVM.n_support_[0],SVM.n_support_[1]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + " \n", + "Réponse:\n", + "\n", + "</font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Jeu de données \"moons\":\n", + "\n", + "Reprenons le jeu de données synthétique `make_moons`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# génération dataset\n", + "X_dataset, y_dataset = datasets.make_moons(noise=0.2, n_samples=1000, random_state=1)\n", + "\n", + "X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2)\n", + "\n", + "# affichage dataset train+test\n", + "plt.figure(figsize=[10,8])\n", + "plt.scatter(X_train[:, 0], X_train[:, 1], marker='o', c=y_train, s=25, edgecolor='k')\n", + "plt.scatter(X_test[:, 0], X_test[:, 1], marker='o', c=y_test, s=10)\n", + "plt.title('dataset')\n", + "plt.axis('equal')\n", + "plt.grid()\n", + "plt.show();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Noyau linéaire\n", + "\n", + "La celulle suivante permet l'apprentissage par SVM linéaire, avec différentes valeurs de l'hyperparamètre $C$.\n", + "\n", + "__Question 2__. Vérifiez que diminuer la valeur de l'hyperparamètre $C$ augmente le nombre de vecteurs supports (et change la surface de séparation). Est-ce cohérent avec la discussion du cours?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "print(\"\\nC=100\")\n", + "SVM=svm.SVC(kernel='linear',C=100)\n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=100\")\n", + "print(\"score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "print(\"nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1\" % (SVM.n_support_[0],SVM.n_support_[1]))\n", + "\n", + "print(\"\\nC=1\")\n", + "SVM=svm.SVC(kernel='linear') # par défaut, C=1\n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=1\")\n", + "print(\"score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "print(\"nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1\" % (SVM.n_support_[0],SVM.n_support_[1]))\n", + "\n", + "print(\"\\nC=1e-2\")\n", + "SVM=svm.SVC(kernel='linear',C=0.01)\n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=0.01\")\n", + "print(\"score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "print(\"nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1\" % (SVM.n_support_[0],SVM.n_support_[1]))\n", + "\n", + "print(\"\\nC=1e-4\")\n", + "SVM=svm.SVC(kernel='linear',C=1e-4)\n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=0.0001\")\n", + "print(\"score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "print(\"nombre de vecteurs supports: %d pour classe 0 et %d pour classe 1\" % (SVM.n_support_[0],SVM.n_support_[1]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + " \n", + "Réponse:\n", + " \n", + "</font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Noyau RBF\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Question 4__. Pour le noyau RBF et la valeur par défaut de $\\gamma$, la cellule suivante présente différentes classifications selon les valeurs de $C$. Retrouvez les situations identifiées dans le cas linéaire.\n", + "\n", + "__Rappel__ : d'après __[la documentation](http://scikit-learn.org/stable/auto_examples/svm/plot_rbf_parameters.html)__ : _\"The C parameter trades off misclassification of training examples against simplicity of the decision surface. A low C makes the decision surface smooth, while a high C aims at classifying all training examples correctly by giving the model freedom to select more samples as support vectors.\"_ " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "print(\"C=1e-2\")\n", + "SVM=svm.SVC(kernel='rbf',C=0.01)\n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=0.01\")\n", + "print(\"C=0.01, score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "\n", + "print(\"\\nC=1\")\n", + "SVM=svm.SVC(kernel='rbf',C=1) # valeur par défaut\n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=1\")\n", + "print(\"C=1, score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "\n", + "print(\"\\nC=100\")\n", + "SVM=svm.SVC(kernel='rbf',C=100) \n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=100\")\n", + "print(\"C=100, score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "\n", + "print(\"\\nC=1e6\")\n", + "SVM=svm.SVC(kernel='rbf',C=100000) \n", + "%time SVM.fit(X_train,y_train)\n", + "plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, C=100000\")\n", + "print(\"C=10000, score test SVM %.3f\" % SVM.score(X_test, y_test) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + "\n", + "Réponse:\n", + "\n", + "</font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "La cellule suivante permet de trouver la valeur optimale de l'hyperparamètre $C$ par validation croisée à 5 plis (5-fold cross validation) sur la base d'apprentissage." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# estimation de C par 5-fold cross validation \n", + "accuracy=[]\n", + "arrayC=10**(np.arange(-1,5.5,.5))\n", + "for C in arrayC:\n", + " SVM=svm.SVC(kernel='rbf',C=C) \n", + " scores = model_selection.cross_val_score(SVM, X_train, y_train, cv=5)\n", + " accuracy.append(scores.mean())\n", + " print('C=%.4f score de validation croisée = %.4f +/- %.4f'%(C,scores.mean(),scores.std()))\n", + "plt.figure()\n", + "plt.semilogx(arrayC,accuracy,'-*')\n", + "plt.title('score de validation croisée contre C')\n", + "plt.grid()\n", + "plt.show();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " Pour trouver une valeur optimale aux hyperparamètres $\\gamma$ et $C$ par validation croisée, on dispose de la fonction [GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html).\n", + "\n", + "Cette fonction va calculer le score de validation croisée pour différentes valeurs des paramètres: ici, $C$ et $\\gamma$ peuvent prendre des valeurs entre $10^{-3}$ et $10^3$. Rappelons, que par défaut, il s'agit de la validation croisée à 5 plis.\n", + "\n", + "Visualisez les résultats de la cellule suivante:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gamma_range=10**(np.arange(-3.,3.5,.5))\n", + "C_range=10**(np.arange(-3.,3.5,.5)) \n", + "parameters = { 'gamma': gamma_range, 'C': C_range }\n", + "SVM = svm.SVC(kernel='rbf')\n", + "gridsearch=model_selection.GridSearchCV(SVM, parameters,cv=5)\n", + "gridsearch.fit(X_train,y_train)\n", + "print(\"Meilleur estimateur trouvé:\")\n", + "print(gridsearch.best_estimator_)\n", + "print(\"avec un score:\")\n", + "print(gridsearch.best_score_)\n", + "print(\"Meilleurs paramètres:\")\n", + "print(gridsearch.best_params_)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Question 5__. Quel est le score de classification sur la base de test du meilleur classifieur SVM identifié?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code ici\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Question 6__. (révision): Comparez au résultat de la classification de la base de test par les algorithmes de classification aux 1,5, 10 plus proches voisins, au classifieur de la régression logistique, et au classifieur naïf gaussien.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import neighbors, naive_bayes, linear_model, neural_network\n", + "\n", + "# votre code ici\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + "\n", + "réponse:\n", + " \n", + "</font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Complément facultatif: discussion du rôle du paramètre $\\gamma$ du noyau RBF\n", + "\n", + "Observez le résultat de la classification avec un noyau\n", + " gaussien (RBF), et l'évolution selon différentes valeurs du\n", + " paramètre `gamma` (on gardera $C=1$, valeur par défaut de `SVC`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "SVM=svm.SVC(kernel='rbf',gamma=0.01)\n", + "SVM.fit(X_train,y_train)\n", + "%time plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, gamma=0.01\")\n", + "print(\"gamma=0.01, score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "\n", + "SVM=svm.SVC(kernel='rbf',gamma=1)\n", + "SVM.fit(X_train,y_train)\n", + "%time plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, gamma=1\")\n", + "print(\"gamma=1, score test SVM %.3f\" % SVM.score(X_test, y_test) )\n", + "\n", + "SVM=svm.SVC(kernel='rbf',gamma=100)\n", + "SVM.fit(X_train,y_train)\n", + "%time plot_classif_result_SVM(X_train,y_train,SVM,\"SVM, gamma=100\")\n", + "print(\"gamma=100, score test SVM %.3f\" % SVM.score(X_test, y_test) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Avec un noyau gaussien, $k(x,y)=\\exp(-\\gamma||x-y||^2)$ est différent de 0 seulement lorsque $x$ et $y$ sont proches relativement à $\\gamma$ (disons $||x-y||<1/\\sqrt{\\gamma}$). \n", + "\n", + "Le coefficient $\\sqrt{\\gamma}$ peut être vu comme l'inverse de la distance d'influence des vecteurs supports. En effet, le classifieur s'écrit: \n", + "$$f(x) = \\sum_i \\lambda_i y_i k(x_i,x) + b$$ \n", + "\n", + "__Lorsque $\\gamma$ est grand__, $k(x_i,x)$ a une contribution significative seulement pour $x$ proche d'un des vecteurs supports $x_i$. Ainsi, le signe de $f$ sera différent de celui de $b$ seulement pour $x$ proche d'un vecteur support $x_i$ associé à la classe de signe contraire à celui de $b$. On a intérêt à ce que toutes les observations soient des vecteurs supports de manière à minimiser le nombre de mauvaises classifications, donc la somme des variables d'écart. La surface de séparation est donc la superposition de disques autours des points d'une des classes, ce que l'on observe bien ici.\n", + "\n", + "__Lorsque $\\gamma$ est petit__, tous les $k(x_i,x)$ dans l'expression de $f$ ont une contribution. \n", + "Le modèle est alors trop \"moyenné\" et on a une surface entre classes très régulière (presque une droite ici). \n", + "\n", + "_Justification au passage_ (en complément): si $\\gamma$ est petit, on identifie l'exponentielle et son développement limité à l'ordre 1: $\\exp(-\\gamma||x-y||^2)=1-\\gamma||x-y||^2$. Ensuite:\n", + "$$f(x) = \\sum_i \\lambda_i y_i k(x_i,x) + b = \\sum_i \\lambda_i y_i (1-\\gamma||x_i-x||^2) + b = \\sum_i \\lambda_i y_i (1-\\gamma||x_i||^2-\\gamma||x||^2-2\\gamma x_i \\cdot x) + b $$ \n", + "Donc:\n", + "$$f(x) = \\sum_i \\lambda_i y_i (1-\\gamma ||x_i||^2) + b - \\gamma ||x||^2 \\sum_i \\lambda_i y_i - 2\\gamma \\left(\\sum_i \\lambda_i y_i x_i\\right) \\cdot x$$ \n", + "Comme $\\sum_i \\lambda_i y_i = 0$ (contrainte primale), $f(x)$ vérifie bien une relation du type $f(x) = B+ w\\cdot x$, où $B=b-\\sum_i \\lambda_i y_i \\gamma ||x_i||^2$ est une constante ne dépendant pas de $x$, et $w=- 2\\gamma \\left(\\sum_i \\lambda_i y_i x_i\\right)$.\n", + "Cela justifie que la frontière de séparation est linéaire!\n", + "\n", + "<br>\n", + "\n", + "Il y a sous-apprentissage avec $\\gamma$ petit, et sur-apprentissage avec $\\gamma$ grand.\n", + "\n", + "Lorsqu'on ne spécifie pas $\\gamma$ dans `svm.SVC`, une valeur est calculée à partir des observations. Voyez dans la documentation comment la valeur par défaut de $\\gamma$ est adaptée en fonction des observations.\n", + "\n", + "</font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Dans la [documentation](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html):\n", + " \n", + " \n", + "`if gamma='scale' (default) is passed then it uses 1 / (n_features * X.var()) as value of gamma`\n", + " \n", + "Le noyau RBF sans préciser gamma assure donc une normalisation. Attention, on normalise ici par rapport à la variance globale des caractéristiques. La normalisation par `StandardScaler` assure une normalisation caractéristique par caractéristique.\n", + " \n", + "\n", + "__Attention__ , si vous utilisez une version de `sklearn` inférieure à 0.22, passez à `svm.SVC` l'argument `gamma='scale'` pour observer le comportement de la version actuelle (la valeur par défaut était à l'époque `auto`).\n", + " \n", + "\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.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/TP4/TP4_ex2_sujet.ipynb b/TP4/TP4_ex2_sujet.ipynb new file mode 100755 index 0000000000000000000000000000000000000000..8899f212ba003190be877f1ac622b69e5090ba90 --- /dev/null +++ b/TP4/TP4_ex2_sujet.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction à l'apprentissage automatique: TP4 - Exercice 2 \n", + "\n", + "<br>\n", + "\n", + "### Reconnaissance d'images par SVM: la base de données Fashion-MNIST\n", + "\n", + "<br>\n", + "\n", + "Dans cet exercice, on travaille sur la base de données Fashion-MNIST, qui a l'\"avantage\" de fournir un problème de classification plus difficile que la base MNIST du TP précédent.\n", + "\n", + "Commencez par lire la description de la base ici: https://www.openml.org/d/40996\n", + "\n", + "Pour limiter les temps de calcul pendant le TP, nous allons nous limiter aux 10000 premières images de la base. La cellule suivante charge les données et prépare une base d'apprentissage et une base de test." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from sklearn import datasets, linear_model, metrics, model_selection\n", + "%matplotlib inline \n", + "\n", + "# Fashion-Mnist database sur openML: (il faut une dizaine de secondes pour charger la base)\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[:10000,:]/255. # normalisation des niveaux de gris entre 0 et 1\n", + "y_fashion=y_fashion[:10000]\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": [ + "La cellule suivante définit une fonction qui permet d'afficher les 150 premières images de la base de test, ainsi que la classe $c$ déterminée par l'algorithme de classification et la classe véritable $c'$ sous la forme $c/c'$." + ] + }, + { + "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();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__Questions__:\n", + "\n", + "Testez les classifieurs SVM à noyau linéaire et à noyau RBF. Pour vous éviter le temps de calcul de `GridSearchCV` (plusieurs minutes ici, étant donnée la taille de la base d'apprentissage):\n", + "- pour le noyau linéaire, vous utiliserez la valeur $C=0.1$ \n", + "- pour le noyau RBF, vous utiliserez la valeur de $\\gamma$ par défaut et $C=10$.\n", + "\n", + "Comparez les scores de classification sur la base de test de ces classifieurs aux scores de l'algorithme du plus proche voisin, des 5 plus proches voisins, à la classification bayésienne naïve gaussienne, et à la régression logistique (voir l'exercice 2 du TP3). Remarquez les bonnes performances des classifieurs linéaires, la mauvaise performance de GNB, et expliquez-les par les éléments du cours.\n", + "\n", + "Comparez également les temps de calcul: ajoutez `%time` (il s'agit d'une _magic command_ jupyter) devant les lignes où vous exécutez `fit` et `predict̀`). \n", + "\n", + "Sous Windows, vous n'obtenez que l'indication Wall: c'est l'heure réellement écoulée sur une horloge murale (_wall clock_). Sous MacOS et Windows, vous obtenez également la valeur \"CPU\" qui compte le temps de calcul total sur le CPU (en additionnant le temps de processus s'exécutant en parallèle sur les coeurs du processeurs). Comme toutes les fonctions `scikit-learn` ne bénéficient pas de la parallélisation sur plusieurs coeurs, seuls les temps d'exécution \"CPU\" sont réellement comparables.\n", + "\n", + "\n", + "Vous lirez dans la correction (à venir) une discussion sur le rôle des vecteurs supports (__facultatif__). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import svm, model_selection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code pour SVM linear\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code pour SVM rbf\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code pour 1-NN\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code pour 5-NN\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code pour GNB\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# votre code pour RL\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + " \n", + "Réponse:\n", + " \n", + "</font>" + ] + }, + { + "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/TP4/TP4_ex3_sujet.ipynb b/TP4/TP4_ex3_sujet.ipynb new file mode 100755 index 0000000000000000000000000000000000000000..ba1a47e244c7d46edd164cb5f2e8107f2ace9826 --- /dev/null +++ b/TP4/TP4_ex3_sujet.ipynb @@ -0,0 +1,167 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction à l'apprentissage automatique - TP4 exercice 3\n", + "\n", + "### SVM sur données réelles\n", + "\n", + "<br> \n", + "\n", + "On considère le jeu de données `breast-cancer` décrit __[ici](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_breast_cancer.html)__ et __[là](https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+(Diagnostic))__. \n", + "\n", + "__Questions__. Que contient ce jeu de données? Combien d'observations et d'attributs par observation? Que cherche-t-on à prédire?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + " \n", + "Votre réponse:\n", + " \n", + "</font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Réservez 20% des observations pour former une base de test.\n", + "\n", + "__Travail à faire__. Mettez en oeuvre la classification par une SVM à noyau gaussien (gardez la valeur par défaut \"scale\" pour le paramètre `gamma`) et une SVM linéaire ainsi que les autres méthodes de classification que vous connaissez bien à présent.\n", + "\n", + "Vous comparerez les scores de classification avec et sans normalisation ( _standardisation_ : les caractéristiques sont centrées et réduites). Vous fixerez les hyperparamètres de ces méthodes par validation croisée sur la base d'apprentissage, et vous comparerez les performances des méthodes sur la base de test." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn import datasets, metrics, model_selection, preprocessing, neighbors, naive_bayes, linear_model, svm\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "# on charge le jeu de données et on garde 30% comme base test:\n", + "dataset = datasets.load_breast_cancer()\n", + "X_dataset = dataset.data\n", + "y_dataset = dataset.target\n", + "X_train, X_test, y_train, y_test = model_selection.train_test_split(X_dataset,y_dataset,test_size=.2,random_state=1)\n", + "\n", + "print(\"Les 3 premières observations (non normalisées):\")\n", + "print(X_train[:3,:])\n", + "\n", + "print(\"et les étiquettes:\")\n", + "print(y_train[:3])\n", + "\n", + "# normalisation:\n", + "X_train_n = preprocessing.StandardScaler().fit_transform(X_train)\n", + "X_test_n = preprocessing.StandardScaler().fit(X_train).transform(X_test)\n", + "\n", + "print(\"\\nLes 3 premières observations (normalisées):\")\n", + "print(X_train_n[:3,:])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font color=red>\n", + "\n", + "Réponse\n", + "\n", + "</font>" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# choix de C pour SVM RBF \n", + "# on garde le coef gamma fixé par défaut\n", + "# faites deux gridsearch différentes: avec et sans normalisation du dataset\n", + "\n", + "# votre code ici:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# choix de C pour SVM linéaire \n", + "# faites deux gridsearch différentes: avec et sans normalisation du dataset\n", + "\n", + "# votre code ici:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# gridsearch KNN\n", + "# faites deux gridsearch différentes: avec et sans normalisation du dataset\n", + "\n", + "# votre code ici:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# gridsearch LR (paramètre de régularisation C)\n", + "# faites deux gridsearch différentes: avec et sans normalisation du dataset\n", + "\n", + "# votre code ici:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# calcul des scores sur la base de test pour les différents classifieurs\n", + "# (sans puis avec normalisation de la base)\n", + "\n", + "# votre code ici:\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.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}