diff --git a/TP1/TP1_ex1_sujet.ipynb b/TP1/TP1_ex1_sujet.ipynb index 40371b40a4af525c1285095790e1c58d42d988ee..baab3a532a1b88bb5668185ce166d43ce0ad3c1e 100755 --- a/TP1/TP1_ex1_sujet.ipynb +++ b/TP1/TP1_ex1_sujet.ipynb @@ -8,7 +8,7 @@ "\n", "<br>\n", "\n", - "Le but de ce premier exercice est de se familiariser avec l'environnement __[Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/)__ et l'utilisation de quelques fonctionalités de la bibliothèque Python __[scikit-learn](http://scikit-learn.org)__ et de sa __[documentation](https://scikit-learn.org/stable/user_guide.html)__ (utilisez la recherche en haut à droite sur cette dernière page). Tous les modèles d'apprentissage de scikit-learn s'utilisent selon le même principe: le TP aujourd'hui est représentatif de notre travail dans les prochaines séances.\n", + "Le but de ce premier exercice est de se familiariser avec l'environnement __[Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/)__ et l'utilisation de quelques fonctionnalités de la bibliothèque Python __[scikit-learn](http://scikit-learn.org)__ et de sa __[documentation](https://scikit-learn.org/stable/user_guide.html)__ (utilisez la recherche en haut à droite sur cette dernière page). Tous les modèles d'apprentissage de scikit-learn s'utilisent selon le même principe: le TP aujourd'hui est représentatif de notre travail dans les prochaines séances.\n", "\n", "Nous allons nous intéresser à un problème de régression linéaire sous l'angle de l'apprentissage automatique. Rappelons que les problèmes de régression font partie de l'apprentissage supervisé. Vous avez étudié la régression linéaire en cours d'analyse de données, nous introduisons à présent la notion de régularisation qui interviendra à plusieurs reprises dans la suite de ce cours.\n", "\n", @@ -16,7 +16,8 @@ "\n", "Exécutez les cellules de ce carnet Jupyter les unes après les autres.\n", "En cas de problème d'exécution du code Python, vous pouvez redémarrer le noyau / kernel (onglet `Noyau` ou `Kernel` dans la barre du carnet Jupyter en haut).\n", - "Pour ajouter vos commentaires personnels, créez une nouvelle cellule (onglet `Insert`) de type Markdown (à sélectionner dans le menu déroulant en haut du carnet). Un aide-mémoire pour Markdown est disponible __[ici](https://www.ibm.com/support/knowledgecenter/en/SSGNPV_2.0.0/dsx/markd-jupyter.html)__. Vous pouvez aussi examiner le code source des cellules de cette page à l'aide du \"double clic\". Nous vous encourageons à écrire vos commentaires en couleur, comme dans l'exemple de la cellule suivante.\n" + "Pour ajouter vos commentaires personnels, créez une nouvelle cellule (onglet `Insert`) de type Markdown (à sélectionner dans le menu déroulant en haut du carnet). Un aide-mémoire pour Markdown est disponible __[ici](https://www.ibm.com/support/knowledgecenter/en/SSGNPV_2.0.0/dsx/markd-jupyter.html)__. Vous pouvez aussi examiner le code source des cellules de cette page en \"double cliquant\". Nous vous encourageons à écrire vos commentaires en couleur, comme dans l'exemple de la cellule suivante.\n", + "\n" ] }, { @@ -52,11 +53,9 @@ "import numpy as np\n", "import sklearn.linear_model as lm\n", "import sklearn.metrics as metrics \n", + "import sklearn.preprocessing as preprocessing \n", "import matplotlib.pyplot as plt\n", "\n", - "# on ignore les avertissements \"future warning\" \n", - "from warnings import simplefilter\n", - "simplefilter(action='ignore', category=FutureWarning)\n", "\n", "# \"magic function\" Jupyter pour l'affichage des graphiques dans le carnet de manière interactive (zoom possible par exemple):\n", "# (voir plus loin)\n", @@ -75,7 +74,7 @@ "\n", "L'objectif de cet exercice est de prédire la distance d'arrêt d'un véhicule en fonction de sa vitesse, dans une approche \"basée données\".\n", "\n", - "Commencez par sauvegarder le fichier `freinage.txt` dans votre espace de travail.\n", + "Commencez par sauvegarder le fichier `freinage.txt` dans votre espace de travail, dans le même dossier que ce carnet.\n", "\n", "Dans la cellule ci-dessous, on charge les données (les _observations_ ): `Y_data` représente la distance de freinage d'un véhicule en fonction de la vitesse `X_data` du véhicule.\n", "\n", @@ -97,14 +96,14 @@ "Y_data = data[:,1].reshape(len(data),1) # deuxième colonne\n", "print(\"\\nnombre d'observations : %d\" %len(X_data))\n", "\n", - "\n", - "plt.figure(figsize=(10,6))\n", + "# représentation graphique\n", + "plt.figure(figsize=(9,6))\n", "plt.scatter(X_data, Y_data)\n", "plt.xlabel(\"X: vitesse (km/h)\")\n", "plt.ylabel(\"Y: distance d'arrêt (m)\")\n", "plt.grid()\n", "plt.xlim(0, 130)\n", - "plt.ylim(0, 100); # le ; permet d'éviter un affichage intempestif dans le carnet" + "plt.ylim(0, 100); # le ; final permet d'éviter un affichage intempestif dans le carnet" ] }, { @@ -123,7 +122,7 @@ "$$ y_{pred} = a_0 + a_1 x$$\n", "\n", "Les valeurs de $a_0$ et $a_1$ sont estimées par la méthode des moindres carrés: on cherche les paramètres $a_0$ et $a_1$ qui minimisent\n", - "$$\\sum_{i=1}^n \\bigl\\lvert y_{data}[i] - a_0 - a_1 x_{data}[i]\\bigr\\rvert^2$$\n", + "$$\\sum_{i=1}^n \\bigl\\lvert y_{data}[i] - a_0 - a_1 x_{data}[i]\\bigr\\lvert^2$$\n", "sur l'ensemble des $n=20$ observations $(x_{data}[i],y_{data}[i])_{1\\leq i\\leq n}$." ] }, @@ -142,7 +141,7 @@ "# (remarque: sklearn attend des données sous forme de vecteurs colonnes)\n", "lr.fit(X_data, Y_data) \n", "# de manière générale, la méthode fit permet l'apprentissage des paramètres du modèle \n", - "# (ici par la méthodes des moindres carrés)\n", + "# (ici, estimation par la méthodes des moindres carrés)\n", "# Ils sont stockés dans les attributs suivants de l'objet lr:\n", "print(lr.intercept_) \n", "print(lr.coef_)" @@ -160,7 +159,9 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + "\n", + "_Votre réponse._\n", + " \n", "</font>" ] }, @@ -177,13 +178,14 @@ "metadata": {}, "outputs": [], "source": [ - "# On \"prédit\" les valeurs de y pour 30 valeurs de x réparties régulièrement entre 0 et 130\n", + "# On \"prédit\" les valeurs de y pour 30 valeurs de x réparties régulièrement entre 0 et 130:\n", "X = np.linspace(0,130,num=30).reshape(30,1)\n", "Y_pred_lr = lr.predict(X) \n", "# la méthode predict permet de prédire les valeurs y pour les valeurs de x passées en argument\n", "# (à l'aide du modèle lr défini précédemment)\n", "\n", - "plt.figure(figsize=(10,6))\n", + "# représentation graphique\n", + "plt.figure(figsize=(9,6))\n", "plt.plot(X_data, Y_data,'o')\n", "plt.plot(X, Y_pred_lr, '-g')\n", "plt.xlim(0, 130)\n", @@ -208,8 +210,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "<font color=red>\n", - "Réponse:\n", + "<font color=\"red\">\n", + "\n", + "_Votre réponse._\n", + "\n", + " \n", "</font>" ] }, @@ -236,7 +241,9 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + "\n", + "_Votre réponse._\n", + "\n", "</font>" ] }, @@ -248,13 +255,13 @@ "\n", "Nous allons à présent construire des modèles prédictifs polynomiaux:\n", "$$ y_{pred} = a_0 + \\sum_{j=1}^d a_j x^j$$\n", - "pour des entiers $d>1$. On se limitera à deux modèles: $d=2$ et $d=6$.\n", + "pour des entiers $d>1$. On se limitera à deux modèles: $d=2$ et $d=6$.\n", "\n", "Autrement dit, on prédit la distance d'arrêt comme une fonction polynomiale de la vitesse.\n", "\n", "Les coefficients $a_0, \\dots, a_d$ sont toujours estimés par la méthode des moindres carrés.\n", "\n", - "Pour ce faire, on utilise une régression linéaire _multivariée_ comme dans le cours d'analyse de données: au lieu d'utiliser comme régresseur la seule variable $x$, on utilise également les variables $x^2, x^3,\\dots, x^d$. Tout se passe comme si la $i$-ème observation est le vecteur $x[i],x[i]^2,x[i]^3,\\dots, x[i]^d$." + "Pour ce faire, on utilise une régression linéaire _multivariée_ comme dans le cours d'analyse de données: au lieu d'utiliser comme régresseur la seule variable $x$, on utilise également les variables $x^2, x^3,\\dots, x^d$. Tout se passe comme si la $i$-ème observation est le vecteur $x[i],x[i]^2,x[i]^3,\\dots, x[i]^d$." ] }, { @@ -265,9 +272,9 @@ "source": [ "# création des vecteurs \"puissance\"\n", "from sklearn.preprocessing import PolynomialFeatures\n", - "poly2 = PolynomialFeatures(degree=2,include_bias=False)\n", + "poly2 = PolynomialFeatures(degree=2,include_bias=False) # degré 2\n", "X_data2 = poly2.fit_transform(X_data)\n", - "poly6 = PolynomialFeatures(degree=6,include_bias=False)\n", + "poly6 = PolynomialFeatures(degree=6,include_bias=False) # degré 6\n", "X_data6 = poly6.fit_transform(X_data)\n", "print(\"les cinq premières observations dans X_data6: \\n(les colonnes contiennent les puissances successives de la première)\")\n", "print(X_data6[:5,:])\n", @@ -291,15 +298,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "__Question 4__. Comment s'écrivent les deux modèles de prédiction de $y$ en fonction de $x$? (ne reportez que quelques chiffres significatifs)" + "__Question 4__. Comment s'écrivent les deux modèles de prédiction de $y$ en fonction de $x$? (ne reportez que quelques chiffres significatifs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "<font color=red>\n", - "Réponse:\n", + "<font color='red'>\n", + "\n", + "_Votre réponse._\n", + "\n", + " \n", "</font>" ] }, @@ -316,13 +326,13 @@ "metadata": {}, "outputs": [], "source": [ - "# prédiction des deux modèles sur les valeurs X définies plus haut, pour le graphique\n", + "# prédiction des deux modèles sur les valeurs X définies plus haut, pour faire le graphique\n", "X2=poly2.fit_transform(X)\n", "X6=poly6.fit_transform(X)\n", - "Y_pred_lrp2=lrp2.predict(X2) \n", + "Y_pred_lrp2=lrp2.predict(X2) \n", "Y_pred_lrp6=lrp6.predict(X6)\n", "\n", - "plt.figure(figsize=(10,6))\n", + "plt.figure(figsize=(9,6))\n", "plt.plot(X_data, Y_data,'o')\n", "plt.plot(X, Y_pred_lr, '-g')\n", "plt.plot(X, Y_pred_lrp2, '-b')\n", @@ -340,7 +350,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "__Question 5__. Comment interprêter ces résultats, ainsi que les calculs de MSE ci-dessous, dans le vocabulaire de l'apprentissage automatique?" + "__Question 5.__ Comment interprêter ces résultats, ainsi que les calculs de MSE ci-dessous, dans le vocabulaire de l'apprentissage automatique?" ] }, { @@ -360,9 +370,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "<font color=red>\n", - "Réponse:\n", - "</font>" + "<font color='red'>\n", + " \n", + "_Votre réponse._\n", + "\n", + " \n", + "</font> " ] }, { @@ -380,16 +393,15 @@ "On voit apparaître un compromis entre l'adéquation aux données (mesurée par la MSE, premier terme de l'expression) et la valeur des paramètres $a_j$ (qui interviennent par le carré de la norme euclidienne du vecteur $(a_1,\\dots,a_d) $). On remarque que la régression linéaire classique correspond au cas particulier $\\alpha=0$.\n", "\n", "Rappelons deux points discutés dans le polycopié:\n", - "- le coefficient $a_0$ n'est pas inclus dans la régularisation;\n", - "- il vaut mieux normaliser les caractéristiques (les composantes des observations) de manière à ce qu'elles varient dans le même intervalle, sinon la régularisation n'aura pas le même effet sur le coefficient $a_i$ devant chacune d'entre elles.\n", + "- le paramètre $a_0$ n'est pas inclus dans la régularisation;\n", + "- comme les paramètres $a_1,\\dots, a_j$ ont le même poids dans la régularisation, il vaut mieux normaliser les caractéristiques (les composantes des observations) de manière à ce qu'elles varient dans le même intervalle. Dans le cas contraire, la régularisation n'aurait pas le même effet sur chaque caractéristique.\n", "\n", "Cette méthode est la régression _ridge_.\n", "\n", "<br>\n", "\n", - "L'influence du paramètre $\\alpha$ est illustrée par la cellule suivante, dans le cas d'une régression polynomiale.\n", "\n", - "Remarquez `normalize=True`, et notez que vous retrouvez bien les valeurs de la régression linéaire classique pour $\\alpha=0$.\n" + "Dans un premier temps, on normalise les observations en divisant chaque vecteur de caractéristique par son écart-type empirique.\n" ] }, { @@ -398,76 +410,147 @@ "metadata": {}, "outputs": [], "source": [ - "ridgealpha0 = lm.Ridge(normalize=True,alpha=0)\n", - "ridgealpha0.fit(X_data6,Y_data)\n", + "# on définit un objet \"scaler6\" sur les données de X_data6\n", + "scaler6 = preprocessing.StandardScaler(with_mean=False).fit(X_data6)\n", + "\n", + "# les coefficients suivants sont les quantités par lesquelles sont divisées les 6 caractéristiques:\n", + "print(scaler6.scale_)\n", + "\n", + "# on constate que ces coefficients sont bien l'écart-type de la caractéristique:\n", + "print(np.std(X_data6,axis=0))\n", + "\n", + "# la procédure suivante permet de normaliser les caractéristiques de X_data6 et X6 en les divisant par leur écart-type: \n", + "X_data6_n = scaler6.transform(X_data6)\n", + "X6_n = scaler6.transform(X6)\n", + "\n", + "# remarquons que l'on ne normalise pas les Y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nous allons estimer les paramètres $a'_j$ de modèles de régression sur les données normalisées (où la $j$-ème caractéristique du dataset de départ est divisée par $\\sigma_j$). Si $x$ est une vitesse en km/h, il faut donc diviser $x^j$ par $\\sigma_j$ pour prédire la distance d'arrêt, selon:\n", + "$$ y_{pred} = a'_0 + \\sum_{j=1}^d a'_j \\frac{x^j}{\\sigma_j}$$\n", + "\n", + "La prédiction s'exprime donc en fonction de la vitesse en km/h de la manière suivante:\n", + "$$ y_{pred} = a_0 + \\sum_{j=1}^d a_j x^j$$\n", + "où $a_0=a'_0$, et $\\;\\forall 1\\leqslant j \\leqslant d, \\, a_j=a'_j/\\sigma_j$.\n", + "\n", + "Dans la suite, ce sont ces coefficients $a_j$ que l'on affichera pour pouvoir comparer aux coefficients obtenus avec la régression simple (section 3).\n", + "\n", + "<br>\n", + "\n", + "L'influence du paramètre $\\alpha$ est illustrée par la cellule suivante, dans le cas de la régression polynomiale de degré 6: on effectue des regressions ridges pour différentes valeurs de $\\alpha$. \n", + "\n", + "Notez que vous retrouvez bien les valeurs de la régression linéaire classique pour $\\alpha=0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ridgealpha0 = lm.Ridge(alpha=0)\n", + "ridgealpha0.fit(X_data6_n,Y_data)\n", "print(\"ridge regression alpha=0\")\n", "print(ridgealpha0.intercept_)\n", - "print(ridgealpha0.coef_)\n", - "Y_pred_ridgealpha0 = ridgealpha0.predict(X6)\n", + "print(ridgealpha0.coef_/scaler6.scale_) # on affiche bien les a_j, pas les a'_j\n", + "Y_pred_ridgealpha0 = ridgealpha0.predict(X6_n) # on prédit avec les a'_j, donc sur les données normalisées\n", "\n", - "ridgealpha01 = lm.Ridge(normalize=True,alpha=0.1)\n", - "ridgealpha01.fit(X_data6,Y_data)\n", + "ridgealpha01 = lm.Ridge(alpha=0.1)\n", + "ridgealpha01.fit(X_data6_n,Y_data)\n", "print(\"\\nridge regression alpha=0.1\")\n", "print(ridgealpha01.intercept_)\n", - "print(ridgealpha01.coef_)\n", - "Y_pred_ridgealpha01 = ridgealpha01.predict(X6)\n", + "print(ridgealpha01.coef_/scaler6.scale_)\n", + "Y_pred_ridgealpha01 = ridgealpha01.predict(X6_n)\n", "\n", - "ridgealpha1 = lm.Ridge(normalize=True,alpha=1)\n", - "ridgealpha1.fit(X_data6,Y_data)\n", + "ridgealpha1 = lm.Ridge(alpha=1)\n", + "ridgealpha1.fit(X_data6_n,Y_data)\n", "print(\"\\nridge regression alpha=1\")\n", "print(ridgealpha1.intercept_)\n", - "print(ridgealpha1.coef_)\n", - "Y_pred_ridgealpha1 = ridgealpha1.predict(X6)\n", + "print(ridgealpha1.coef_/scaler6.scale_)\n", + "Y_pred_ridgealpha1 = ridgealpha1.predict(X6_n)\n", "\n", - "ridgealpha10 = lm.Ridge(normalize=True,alpha=10)\n", - "ridgealpha10.fit(X_data6,Y_data)\n", + "ridgealpha10 = lm.Ridge(alpha=10)\n", + "ridgealpha10.fit(X_data6_n,Y_data)\n", "print(\"\\nridge regression alpha=10\")\n", "print(ridgealpha10.intercept_)\n", - "print(ridgealpha10.coef_)\n", - "Y_pred_ridgealpha10 = ridgealpha10.predict(X6)\n", + "print(ridgealpha10.coef_/scaler6.scale_)\n", + "Y_pred_ridgealpha10 = ridgealpha10.predict(X6_n)\n", "\n", - "ridgealpha100 = lm.Ridge(normalize=True,alpha=100)\n", - "ridgealpha100.fit(X_data6,Y_data)\n", + "ridgealpha100 = lm.Ridge(alpha=100)\n", + "ridgealpha100.fit(X_data6_n,Y_data)\n", "print(\"\\nridge regression alpha=100\")\n", "print(ridgealpha100.intercept_)\n", - "print(ridgealpha100.coef_)\n", - "Y_pred_ridgealpha100 = ridgealpha100.predict(X6)\n", + "print(ridgealpha100.coef_/scaler6.scale_)\n", + "Y_pred_ridgealpha100 = ridgealpha100.predict(X6_n)\n", + "\n", + "ridgealpha1000 = lm.Ridge(alpha=1000)\n", + "ridgealpha1000.fit(X_data6_n,Y_data)\n", + "print(\"\\nridge regression alpha=1000\")\n", + "print(ridgealpha1000.intercept_)\n", + "print(ridgealpha1000.coef_/scaler6.scale_)\n", + "Y_pred_ridgealpha1000 = ridgealpha1000.predict(X6_n)\n", "\n", - "plt.figure(figsize=(10,6))\n", + "plt.figure(figsize=(9,6))\n", "plt.plot(X_data, Y_data,'o')\n", "plt.plot(X, Y_pred_ridgealpha0, '-g')\n", "plt.plot(X, Y_pred_ridgealpha01, '-b')\n", "plt.plot(X, Y_pred_ridgealpha1, '-c')\n", "plt.plot(X, Y_pred_ridgealpha10, '-r')\n", - "plt.plot(X, Y_pred_ridgealpha100, '-k')\n", + "plt.plot(X, Y_pred_ridgealpha100, '-y')\n", + "plt.plot(X, Y_pred_ridgealpha1000, '-k')\n", "plt.xlim(0, 130)\n", "plt.ylim(-10, 80)\n", "plt.xlabel(\"X: vitesse (km/h)\")\n", "plt.ylabel(\"Y: distance d'arrêt (m)\")\n", "plt.grid()\n", "plt.title('régression ridge, d=6')\n", - "plt.legend([\"observations\",\"alpha=0\",\"alpha=0.1\",\"alpha=1\",\"alpha=10\",\"alpha=100\"]);" + "plt.legend([\"observations\",\"alpha=0\",\"alpha=0.1\",\"alpha=1\",\"alpha=10\",\"alpha=100\",\"alpha=1000\"]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Notez que les coefficients $a_j$ ($1\\leq j \\leq 6$) diminuent et que $a_0$ tend vers la moyenne des $Y_\\text{data}[i]$ lorsque $\\alpha$ augmente. Il faut maintenant choisir l'hyperparamètre $\\alpha$.\n", + "Notez que les coefficients $a_j$ ($1\\leq j \\leq 6$) diminuent et que $a_0$ tend vers la moyenne des $Y_\\text{data}[i]$ lorsque $\\alpha$ augmente. \n", + "\n", + "Il faut maintenant choisir l'hyperparamètre $\\alpha$ le plus adapté au problème.\n", "\n", - "Un _hyperparamètre_ est un paramètre du modèle qui n'est pas déterminé sur les données mais qui doit être fixé par l'utilisateur.\n", + "Un _hyperparamètre_ est un paramètre du modèle qui n'est pas déterminé par apprentissage mais qui doit être fixé par l'utilisateur ou de manière à optimiser un critère.\n", "\n", "<br>\n", "\n", - "La méthode `RidgeCV` (à utiliser à la place de `Ridge`) permet de faire une régression ridge en sélectionnant un hyperparamètre par _validation croisée_. Dans cette question, nous l'utilisons en \"boîte noire\"; on verra des explications en fin d'exercice.\n", - "Une partie de la prochaine séance sera consacrée à cette méthode. " + "La méthode `RidgeCV` (à utiliser à la place de `Ridge`) permet de faire une régression ridge en sélectionnant un hyperparamètre par _validation croisée_. **Dans cette question, nous l'utilisons en \"boîte noire\"**.\n", + "Une partie de la prochaine séance sera consacrée à cette méthode de sélection de modèle. \n", + "\n", + "<br>\n", + "\n", + "On commence par normaliser les datasets pour la régression linéaire et polynomiale de degré 2, comme précédemment pour la régression polynomiale de degré 6." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scaler1 = preprocessing.StandardScaler(with_mean=False).fit(X_data)\n", + "X_data_n = scaler1.transform(X_data)\n", + "X_n = scaler1.transform(X)\n", + "\n", + "scaler2 = preprocessing.StandardScaler(with_mean=False).fit(X_data2)\n", + "X_data2_n = scaler2.transform(X_data2)\n", + "X2_n = scaler2.transform(X2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Par exemple, pour la régression ridge dont la valeur de $\\alpha$ est choisie par validation croisée à 5 plis parmi 10 valeurs de la forme $10^{-i}$ avec $i$ prenant 11 valeurs régulièrement espacées entre entre -6 et 6: " + "Par exemple, pour la régression ridge dont la valeur de $\\alpha$ est choisie par validation croisée parmi 11 valeurs de la forme $10^{-i}$ avec $i$ prenant 11 valeurs régulièrement espacées entre entre -5 et 5: " ] }, { @@ -476,11 +559,11 @@ "metadata": {}, "outputs": [], "source": [ - "ridge1 = lm.RidgeCV(normalize=True, alphas=np.logspace(-6, 6, 11), cv=5)\n", - "ridge1.fit(X_data,Y_data)\n", + "ridge1 = lm.RidgeCV(alphas=np.logspace(-5, 5, 11))\n", + "ridge1.fit(X_data_n,Y_data)\n", "print(\"ridge regression, polynome degré 1\")\n", "print(ridge1.intercept_)\n", - "print(ridge1.coef_)\n", + "print(ridge1.coef_/scaler1.scale_)\n", "print(\"alpha sélectionné: %.5f\" %ridge1.alpha_)" ] }, @@ -488,7 +571,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Avec des données normalisées (`normalize=True`), il est d'usage de chercher $\\alpha$ autour de la valeur 1 comme ici. \n", + "Avec des données normalisées, il est d'usage de chercher $\\alpha$ autour de la valeur 1 comme ici. \n", "\n", "<br>\n", "\n", @@ -501,25 +584,25 @@ "metadata": {}, "outputs": [], "source": [ - "ridge1 = lm.RidgeCV(normalize=True, alphas=np.logspace(-5, 5, 11), cv=5)\n", - "ridge1.fit(X_data,Y_data)\n", + "ridge1 = lm.RidgeCV(alphas=np.logspace(-5, 5, 11), cv=5)\n", + "ridge1.fit(X_data_n,Y_data)\n", "print(\"ridge regression, polynome degré 1\")\n", "print(ridge1.intercept_)\n", - "print(ridge1.coef_)\n", + "print(ridge1.coef_/scaler1.scale_)\n", "print(\"alpha sélectionné: %.5f\" %ridge1.alpha_)\n", "\n", - "ridge2 = lm.RidgeCV(normalize=True, alphas=np.logspace(-5, 5, 11), cv=5)\n", - "ridge2.fit(X_data2,Y_data)\n", + "ridge2 = lm.RidgeCV(alphas=np.logspace(-5, 5, 11), cv=5)\n", + "ridge2.fit(X_data2_n,Y_data)\n", "print(\"\\nridge regression, polynome degré 2\")\n", "print(ridge2.intercept_)\n", - "print(ridge2.coef_)\n", + "print(ridge2.coef_/scaler2.scale_)\n", "print(\"alpha sélectionné: %.5f\" %ridge2.alpha_)\n", "\n", - "ridge6 = lm.RidgeCV(normalize=True, alphas=np.logspace(-5, 5, 11), cv=5)\n", - "ridge6.fit(X_data6,Y_data)\n", + "ridge6 = lm.RidgeCV(alphas=np.logspace(-5, 5, 11), cv=5)\n", + "ridge6.fit(X_data6_n,Y_data)\n", "print(\"\\nridge regression, polynome degré 6\")\n", "print(ridge6.intercept_)\n", - "print(ridge6.coef_)\n", + "print(ridge6.coef_/scaler6.scale_)\n", "print(\"alpha sélectionné: %.5f\" %ridge6.alpha_)" ] }, @@ -529,11 +612,11 @@ "metadata": {}, "outputs": [], "source": [ - "Y_pred_lrr1=ridge1.predict(X)\n", - "Y_pred_lrr2=ridge2.predict(X2)\n", - "Y_pred_lrr6=ridge6.predict(X6)\n", + "Y_pred_lrr1=ridge1.predict(X_n)\n", + "Y_pred_lrr2=ridge2.predict(X2_n)\n", + "Y_pred_lrr6=ridge6.predict(X6_n)\n", "\n", - "plt.figure(figsize=(10,6))\n", + "plt.figure(figsize=(9,6))\n", "plt.plot(X_data, Y_data,'o')\n", "plt.plot(X, Y_pred_lrr1, '-g')\n", "plt.plot(X, Y_pred_lrr2, '-b')\n", @@ -553,11 +636,11 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"régression ridge polynomiale degré 1, MSE = %.2f\" %metrics.mean_squared_error(Y_data,ridge1.predict(X_data)))\n", + "print(\"régression ridge polynomiale degré 1, MSE = %.2f\" %metrics.mean_squared_error(Y_data,ridge1.predict(X_data_n)))\n", "\n", - "print(\"régression ridge polynomiale degré 2, MSE = %.2f\" %metrics.mean_squared_error(Y_data,ridge2.predict(X_data2)))\n", + "print(\"régression ridge polynomiale degré 2, MSE = %.2f\" %metrics.mean_squared_error(Y_data,ridge2.predict(X_data2_n)))\n", "\n", - "print(\"régression ridge polynomiale degré 6, MSE = %.2f\" %metrics.mean_squared_error(Y_data,ridge6.predict(X_data6)))\n" + "print(\"régression ridge polynomiale degré 6, MSE = %.2f\" %metrics.mean_squared_error(Y_data,ridge6.predict(X_data6_n)))\n" ] }, { @@ -572,7 +655,10 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + "\n", + "_Votre réponse._\n", + "\n", + " \n", "</font>" ] }, @@ -584,11 +670,10 @@ "\n", "Expérimentez le _Lasso_ , décrit __[dans la documentation](https://scikit-learn.org/stable/modules/linear_model.html#lasso)__. \n", "\n", - "__Question 7__. Quelle est la différence essentielle avec la régression ridge? On utilisera __[LassoCV](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LassoCV.html#sklearn.linear_model.LassoCV)__. Comment comprenez-vous la remarque _\"As the Lasso regression yields sparse models, it can thus be used to perform feature selection\"_ dans la documentation?\n", + "__Question 7__. Quelle est la différence essentielle avec la régression ridge? On utilisera __[LassoCV](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LassoCV.html#sklearn.linear_model.LassoCV)__ qui sélectionne l'hyperparamètre. Comment comprenez-vous la remarque _\"As the Lasso regression yields sparse models, it can thus be used to perform feature selection\"_ dans la documentation?\n", "\n", - "_Remarques_ :\n", - "- observez dans la documentation que la stratégie pour fixer l'hyperparamètre $\\alpha$ n'est pas la même que pour `RidgeCV`\n", - "- on change la valeur de `max_iter` pour éviter un problème de convergence dans le dernier modèle (essayez sans cette option) " + "_Remarque_ :\n", + "observez dans la documentation que la stratégie pour fixer l'hyperparamètre $\\alpha$ n'est pas la même que pour `RidgeCV`" ] }, { @@ -596,7 +681,10 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + "\n", + "_Votre réponse._\n", + "\n", + " \n", "</font>" ] }, @@ -606,25 +694,25 @@ "metadata": {}, "outputs": [], "source": [ - "lasso1 = lm.LassoCV(normalize=True)\n", - "lasso1.fit(X_data,np.ravel(Y_data)) # le lasso attend un 1D array pour y, d'où \"np_ravel\"\n", + "lasso1 = lm.LassoCV()\n", + "lasso1.fit(X_data_n,np.ravel(Y_data)) # le lasso attend un 1D array pour y, d'où \"np_ravel\"\n", "print(\"lasso regression, fonction affine\")\n", "print(lasso1.intercept_)\n", - "print(lasso1.coef_)\n", + "print(lasso1.coef_/scaler1.scale_)\n", "print(\"alpha sélectionné: %.5f\" %lasso1.alpha_)\n", "\n", - "lasso2 = lm.LassoCV(normalize=True)\n", - "lasso2.fit(X_data2,np.ravel(Y_data)) \n", + "lasso2 = lm.LassoCV()\n", + "lasso2.fit(X_data2_n,np.ravel(Y_data)) \n", "print(\"\\nlasso regression, polynome degré 2\")\n", "print(lasso2.intercept_)\n", - "print(lasso2.coef_)\n", + "print(lasso2.coef_/scaler2.scale_)\n", "print(\"alpha sélectionné: %.5f\" %lasso2.alpha_)\n", "\n", - "lasso6 = lm.LassoCV(normalize=True, max_iter=10000) # (max_iter pour éviter des problèmes numériques ensuite) \n", - "lasso6.fit(X_data6,np.ravel(Y_data))\n", + "lasso6 = lm.LassoCV() \n", + "lasso6.fit(X_data6_n,np.ravel(Y_data))\n", "print(\"\\nlasso regression, polynome degré 6\")\n", "print(lasso6.intercept_)\n", - "print(lasso6.coef_)\n", + "print(lasso6.coef_/scaler6.scale_)\n", "print(\"alpha sélectionné: %.5f\" %lasso6.alpha_)" ] }, @@ -640,7 +728,10 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + "\n", + "_Votre réponse._\n", + "\n", + " \n", "</font>" ] }, @@ -650,11 +741,11 @@ "metadata": {}, "outputs": [], "source": [ - "Y_pred_lasso1=lasso1.predict(X)\n", - "Y_pred_lasso2=lasso2.predict(X2)\n", - "Y_pred_lasso6=lasso6.predict(X6)\n", + "Y_pred_lasso1=lasso1.predict(X_n)\n", + "Y_pred_lasso2=lasso2.predict(X2_n)\n", + "Y_pred_lasso6=lasso6.predict(X6_n)\n", "\n", - "plt.figure(figsize=(10,6))\n", + "plt.figure(figsize=(9,6))\n", "plt.plot(X_data, Y_data,'o')\n", "plt.plot(X, Y_pred_lr, '-g')\n", "plt.plot(X, Y_pred_lasso2, '-b')\n", @@ -710,56 +801,10 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", - "</font>" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 6. Sélection de modèle par validation croisée\n", - "\n", - "Nous allons à présent chercher à déterminer le meilleur modèle par _validation croisée_.\n", - "\n", - "La validation croisée à $K$ plis (_$K$-fold cross-validation_) consiste à:\n", - "* former aléatoirement $K>2$ sous-ensembles (on parle de plis ou de blocs) de la base d'apprentissage \n", - "* faire l'apprentissage du modèle sur les données de $K-1$ plis, et calculer un score de prédiction sur le $K$-ème pli restant (pli de test)\n", - "* répéter l'opération précédente $K$ fois en faisant jouer à chaque pli le rôle de pli de test\n", - "* le score de validation croisée est la moyenne des $K$ scores de prédiction obtenu\n", - "\n", - "Une illustration est disponible sur [wikipedia](https://fr.wikipedia.org/wiki/Validation_crois%C3%A9e#Techniques_de_validation).\n", + " \n", + "_Votre réponse._\n", "\n", - "L'idée générale est de calculer un score de prédiction sur un pli de test n'ayant pas servi à l'apprentissage, et de faire une moyenne pour éviter les fluctuations d'échantillonnage (le hasard pourrait favoriser un modèle particulier).\n", - "\n", - "<br>\n", - "\n", - "\n", - "__Question 10__. En utilisant la fonction `cross_val_score` expliquée sur [cette page](https://scikit-learn.org/stable/modules/cross_validation.html) et en vous inspirant de l'exemple ci-dessous pour la régression linéaire, déterminez quel est le meilleur modèle parmi ceux que vous avez envisagés (c'est-à-dire lr, lrp2, lrp6, ridge1, ridge2, ridge6, lasso1, lasso2, lasso6). Utilisez la même syntaxe que dans `fit` pour spécifier les données en argument de `cross_val_score`. Par défaut, le score calculé pour tous ces modèles est le [coefficient de détermination](https://fr.wikipedia.org/wiki/Coefficient_de_d%C3%A9termination) $R^2$: plus il est proche de 1, meilleur est le modèle sur le pli de test. On voit d'après la définition sur la page wikipedia que si l'écart entre la vraie valeur et la valeur prédite sur le pli de test est grand, alors $R^2$ peut être négatif. C'est le cas en particulier s'il y a surapprentissage. Remarquons que maximiser $R^2$ est équivalent à minimiser MSE.\n", - "\n", - "_Remarque_ : on utilise une validation croisée à 5 plis (valeur par défaut dans la dernière version de `scikit-learn`) par cohérence avec ce que l'on a utilisé pour sélectionner l'hyperparamètre de la régression ridge et du lasso. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# votre code ici:\n", - "\n", - "from sklearn.model_selection import cross_val_score\n", - "\n", - "scores_lr = cross_val_score(lr, X_data, Y_data, cv=5)\n", - "print(\"lr - Accuracy: %0.2f (+/- %0.2f)\" % (scores_lr.mean(), scores_lr.std() * 2))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "<font color=red>\n", - "Réponse:\n", + " \n", "</font>" ] }, @@ -787,7 +832,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.11.5" } }, "nbformat": 4, diff --git a/TP1/TP1_ex2_sujet.ipynb b/TP1/TP1_ex2_sujet.ipynb index 8176ac42ae70b9c5d97f0d30d774bed7ee84b433..ae970f3dca1792b9a81aa082f104ba3d92e2717f 100755 --- a/TP1/TP1_ex2_sujet.ipynb +++ b/TP1/TP1_ex2_sujet.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Introduction à l'apprentissage automatique: TP1 - Exercice 2\n", + "# Introduction à l'apprentissage automatique: TP1 - Exercice 2 \n", "\n", "<br>\n", "\n", @@ -16,7 +16,12 @@ "\n", "On cherche à prédire l'influence de dix indicateurs $x_1,x_2,\\dots,x_{10}$ (âge, sexe, et diverses mesures physiologiques) sur un indicateur $y$ de la progression du diabète, à l'aide d'un modèle linéaire. Une étude complète nécessiterait de justifier ce modèle par des graphiques, les coefficients de corrélation linéaire, etc., comme vous l'avez fait en cours d'Analyse de données. Nous ne le ferons pas faute de temps.\n", "\n", - "On se base sur un jeu de données intégré à scikit-learn et [décrit ici](https://scikit-learn.org/stable/datasets/toy_dataset.html#diabetes-dataset)." + "On se base sur un jeu de données intégré à scikit-learn et [décrit ici](https://scikit-learn.org/stable/datasets/toy_dataset.html#diabetes-dataset).\n", + "\n", + "Notez la remarque:\n", + "> Note: Each of these 10 feature variables have been mean centered and scaled by the standard deviation times the square root of n_samples (i.e. the sum of squares of each column totals 1).\n", + "\n", + "Il est donc inutile de normaliser les caractéristiques.\n" ] }, { @@ -31,11 +36,7 @@ "\n", "import matplotlib.pyplot as plt\n", "\n", - "# on ignore les avertissements \"future warning\" \n", - "from warnings import simplefilter\n", - "simplefilter(action='ignore', category=FutureWarning)\n", - "\n", - "# \"magic function\" Jupyter pour l'affichage des graphiques dans le carnet:\n", + "# \"magic function\" Jupyter pour l'affichage des graphiques interactifs dans le carnet:\n", "%matplotlib notebook" ] }, @@ -62,7 +63,7 @@ "source": [ "Les observations (chacune est composée de 10 indicateurs) forment les colonnes de `diabetes_X` et l'indicateur à prédire est stocké dans `diabetes_y`.\n", "\n", - "On commence par séparer la base de données entre ensemble d'apprentissage et ensemble de test (20% des observations pour ce dernier ensemble). La répartition est faite de manière aléatoire par la cellule suivante. \n", + "On commence par séparer la base de données entre un ensemble d'apprentissage et un ensemble de test qui nous servira à évaluer les modèles de régression (20% des observations pour ce dernier ensemble). La répartition est faite de manière aléatoire par la cellule suivante. \n", "\n", "__Remarque__: dans la cellule suivante, `random_state=42` (nombre arbitraire) permet de fixer la graine du générateur aléatoire de manière à ce que nous ayons tous la même répartition aléatoire, ce qui facilitera la discussion." ] @@ -105,7 +106,9 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + " \n", + "Votre réponse.\n", + " \n", "</font>" ] }, @@ -140,7 +143,7 @@ "plt.xlabel('alpha')\n", "plt.ylabel('MSE')\n", "plt.title('MSE régression linéaire et ridge vs. alpha ')\n", - "plt.axis([1e-4,1e4,2750,3050])\n", + "plt.axis([1e-4,1e4,2750,5500])\n", "plt.legend(['MSE lr','MSE ridge'])\n", "plt.grid()\n", "plt.show();" @@ -151,14 +154,19 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + " \n", + "Votre réponse.\n", + "\n", + " \n", "</font>" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "n_alphas = 100\n", @@ -185,11 +193,12 @@ "\n", "plt.figure(figsize=(8,6))\n", "plt.semilogx(alphas, MSE_lasso)\n", + "plt.semilogx(alphas, MSE_ridge, '--')\n", "plt.xlabel('alpha')\n", "plt.ylabel('MSE')\n", - "plt.axis([1e-4,1e4,2750,3050])\n", - "plt.title('MSE régression linéaire et lasso vs. alpha ')\n", - "plt.legend(['MSE lr','MSE lasso'])\n", + "plt.axis([1e-4,1e4,2750,5500])\n", + "plt.title('MSE régression linéaire, lasso, et ridge vs. alpha ')\n", + "plt.legend(['MSE lr','MSE lasso','MSE ridge (rappel)'])\n", "plt.grid()\n", "plt.show()" ] @@ -199,7 +208,9 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + " \n", + "Votre réponse.\n", + " \n", "</font>" ] }, @@ -207,17 +218,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "__Question 2__. Proposez des modèles de régression multivariée pour prédire $y$ en fonction des 10 indicateurs dans $X$. Vous testerez régression linéaire, ridge, lasso et fixerez l'hyperparamètre de ces deux dernières méthodes par validation croisée sur la _base d'apprentissage_ , conformément à la démarche vue dans l'exercice 1.\n", + "__Question 2__. Proposez des modèles de régression multivariée pour prédire $y$ en fonction des 10 indicateurs dans $X$. Vous testerez régression linéaire, ridge, lasso et fixerez l'hyperparamètre de ces deux dernières méthodes par validation croisée sur la _base d'apprentissage_ , conformément à la démarche vue dans l'exercice 1 (utilisez `RidgeCV` et `LassoCV`).\n", "\n", "<br>\n", "\n", - "Quel est finalement le meilleur modèle ? \n", + "Quel est finalement le meilleur modèle? Quelles sont les variables sélectionnées et leur influence quantitative?\n", "\n", "_Indication_ : calculez la valeur de MSE sur la _base de test_ et comparez à la variance des étiquettes à prédire.\n", "\n", "<br>\n", "\n", - "Quelles variables semblent les plus pertinentes dans l'étude? Quelles sont les variables sélectionnées et leur influence quantitative?\n" + "Quelles variables semblent les plus pertinentes dans l'étude?\n" ] }, { @@ -226,8 +237,7 @@ "metadata": {}, "outputs": [], "source": [ - "# votre code ici (il faut pour l'essentiel faire des copier/coller depuis l'énoncé de l'exercice 1)\n", - "\n", + "# votre code ici:\n", "\n" ] }, @@ -236,7 +246,9 @@ "metadata": {}, "source": [ "<font color=red>\n", - "Réponse:\n", + "\n", + "Votre réponse.\n", + " \n", "</font>" ] }, @@ -264,7 +276,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.11.5" } }, "nbformat": 4,