diff --git a/ISEN/IA/CIPA4/Cours/CM2-IA-ApprentissageSupervise-V-KO-CIPA4_2025.pdf b/ISEN/IA/CIPA4/Cours/CM2-IA-ApprentissageSupervise-V-KO-CIPA4_2025.pdf new file mode 100644 index 0000000..9dc20fd Binary files /dev/null and b/ISEN/IA/CIPA4/Cours/CM2-IA-ApprentissageSupervise-V-KO-CIPA4_2025.pdf differ diff --git a/ISEN/IA/CIPA4/Cours/CM3-IA-ApprentissageNonSupervise-KOA.pdf b/ISEN/IA/CIPA4/Cours/CM3-IA-ApprentissageNonSupervise-KOA.pdf new file mode 100644 index 0000000..262997d Binary files /dev/null and b/ISEN/IA/CIPA4/Cours/CM3-IA-ApprentissageNonSupervise-KOA.pdf differ diff --git a/ISEN/IA/CIPA4/TP/TP2/tp2_IA.ipynb b/ISEN/IA/CIPA4/TP/TP2/tp2_IA.ipynb index 48f9e9d..14e47dd 100644 --- a/ISEN/IA/CIPA4/TP/TP2/tp2_IA.ipynb +++ b/ISEN/IA/CIPA4/TP/TP2/tp2_IA.ipynb @@ -1,2709 +1,3822 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "33e76083", - "metadata": {}, - "source": [ - "# I - Préparation de données" - ] - }, - { - "cell_type": "markdown", - "id": "1ccf6f5b", - "metadata": {}, - "source": [ - "## 0-Téléchargement de données" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "5a9a29a0", - "metadata": {}, - "outputs": [ + "cells": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: pandas in ./.venv/lib/python3.13/site-packages (2.3.2)\n", - "Requirement already satisfied: numpy>=1.26.0 in ./.venv/lib/python3.13/site-packages (from pandas) (2.3.3)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in ./.venv/lib/python3.13/site-packages (from pandas) (2.9.0.post0)\n", - "Requirement already satisfied: pytz>=2020.1 in ./.venv/lib/python3.13/site-packages (from pandas) (2025.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in ./.venv/lib/python3.13/site-packages (from pandas) (2025.2)\n", - "Requirement already satisfied: six>=1.5 in ./.venv/lib/python3.13/site-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n", - "Note: you may need to restart the kernel to use updated packages.\n", - "Requirement already satisfied: matplotlib in ./.venv/lib/python3.13/site-packages (3.10.6)\n", - "Requirement already satisfied: contourpy>=1.0.1 in ./.venv/lib/python3.13/site-packages (from matplotlib) (1.3.3)\n", - "Requirement already satisfied: cycler>=0.10 in ./.venv/lib/python3.13/site-packages (from matplotlib) (0.12.1)\n", - "Requirement already satisfied: fonttools>=4.22.0 in ./.venv/lib/python3.13/site-packages (from matplotlib) (4.60.0)\n", - "Requirement already satisfied: kiwisolver>=1.3.1 in ./.venv/lib/python3.13/site-packages (from matplotlib) (1.4.9)\n", - "Requirement already satisfied: numpy>=1.23 in ./.venv/lib/python3.13/site-packages (from matplotlib) (2.3.3)\n", - "Requirement already satisfied: packaging>=20.0 in ./.venv/lib/python3.13/site-packages (from matplotlib) (25.0)\n", - "Requirement already satisfied: pillow>=8 in ./.venv/lib/python3.13/site-packages (from matplotlib) (11.3.0)\n", - "Requirement already satisfied: pyparsing>=2.3.1 in ./.venv/lib/python3.13/site-packages (from matplotlib) (3.2.5)\n", - "Requirement already satisfied: python-dateutil>=2.7 in ./.venv/lib/python3.13/site-packages (from matplotlib) (2.9.0.post0)\n", - "Requirement already satisfied: six>=1.5 in ./.venv/lib/python3.13/site-packages (from python-dateutil>=2.7->matplotlib) (1.17.0)\n", - "Note: you may need to restart the kernel to use updated packages.\n", - "Requirement already satisfied: scikit-learn in ./.venv/lib/python3.13/site-packages (1.7.2)\n", - "Requirement already satisfied: numpy>=1.22.0 in ./.venv/lib/python3.13/site-packages (from scikit-learn) (2.3.3)\n", - "Requirement already satisfied: scipy>=1.8.0 in ./.venv/lib/python3.13/site-packages (from scikit-learn) (1.16.2)\n", - "Requirement already satisfied: joblib>=1.2.0 in ./.venv/lib/python3.13/site-packages (from scikit-learn) (1.5.2)\n", - "Requirement already satisfied: threadpoolctl>=3.1.0 in ./.venv/lib/python3.13/site-packages (from scikit-learn) (3.6.0)\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "%pip install pandas\n", - "%pip install matplotlib\n", - "%pip install scikit-learn" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "5be8782b", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.datasets import fetch_openml\n", - "mnist = fetch_openml('mnist_784', version=1)" - ] - }, - { - "cell_type": "markdown", - "id": "5c06bdc5", - "metadata": {}, - "source": [ - "## 1- Informations sur les données" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "70575266", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Clés disponibles dans le dataset MNIST:\n", - "['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url']\n" - ] - } - ], - "source": [ - "X = mnist.keys()\n", - "print(\"Clés disponibles dans le dataset MNIST:\")\n", - "print(list(X))" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "eaffc443", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "a. Variable X créée avec les données\n", - "b. Taille des données X: (70000, 784)\n", - " - Nombre d'échantillons: 70000\n", - " - Nombre de features par échantillon: 784\n", - "\n", - "c. Variable Y créée avec les classes\n", - "d. Taille des labels Y: (70000,)\n", - " - Nombre total de labels: 70000\n", - "\n", - "e. Classes uniques dans la base de données: ['0' '1' '2' '3' '4' '5' '6' '7' '8' '9']\n", - " - Nombre de classes différentes: 10\n", - "\n", - "f. Description détaillée de la base de données:\n", - "**Author**: Yann LeCun, Corinna Cortes, Christopher J.C. Burges \n", - "**Source**: [MNIST Website](http://yann.lecun.com/exdb/mnist/) - Date unknown \n", - "**Please cite**: \n", - "\n", - "The MNIST database of handwritten digits with 784 features, raw data available at: http://yann.lecun.com/exdb/mnist/. It can be split in a training set of the first 60,000 examples, and a test set of 10,000 examples \n", - "\n", - "It is a subset of a larger set available from NIST. The digits have been size-normalized and centered in a fixed-size image. It is a good database for people who want to try learning techniques and pattern recognition methods on real-world data while spending minimal efforts on preprocessing and formatting. The original black and white (bilevel) images from NIST were size normalized to fit in a 20x20 pixel box while preserving their aspect ratio. The resulting images contain grey levels as a result of the anti-aliasing technique used by the normalization algorithm. the images were centered in a 28x28 image by computing the center of mass of the pixels, and translating the image so as to position this point at the center of the 28x28 field. \n", - "\n", - "With some classification methods (particularly template-based methods, such as SVM and K-nearest neighbors), the error rate improves when the digits are centered by bounding box rather than center of mass. If you do this kind of pre-processing, you should report it in your publications. The MNIST database was constructed from NIST's NIST originally designated SD-3 as their training set and SD-1 as their test set. However, SD-3 is much cleaner and easier to recognize than SD-1. The reason for this can be found on the fact that SD-3 was collected among Census Bureau employees, while SD-1 was collected among high-school students. Drawing sensible conclusions from learning experiments requires that the result be independent of the choice of training set and test among the complete set of samples. Therefore it was necessary to build a new database by mixing NIST's datasets. \n", - "\n", - "The MNIST training set is composed of 30,000 patterns from SD-3 and 30,000 patterns from SD-1. Our test set was composed of 5,000 patterns from SD-3 and 5,000 patterns from SD-1. The 60,000 pattern training set contained examples from approximately 250 writers. We made sure that the sets of writers of the training set and test set were disjoint. SD-1 contains 58,527 digit images written by 500 different writers. In contrast to SD-3, where blocks of data from each writer appeared in sequence, the data in SD-1 is scrambled. Writer identities for SD-1 is available and we used this information to unscramble the writers. We then split SD-1 in two: characters written by the first 250 writers went into our new training set. The remaining 250 writers were placed in our test set. Thus we had two sets with nearly 30,000 examples each. The new training set was completed with enough examples from SD-3, starting at pattern # 0, to make a full set of 60,000 training patterns. Similarly, the new test set was completed with SD-3 examples starting at pattern # 35,000 to make a full set with 60,000 test patterns. Only a subset of 10,000 test images (5,000 from SD-1 and 5,000 from SD-3) is available on this site. The full 60,000 sample training set is available.\n", - "\n", - "Downloaded from openml.org.\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "\n", - "# a. Stocker les données dans la variable X\n", - "X = mnist.data\n", - "print(\"a. Variable X créée avec les données\")\n", - "\n", - "# b. Afficher la taille des données (nombre de features et taille de chaque feature)\n", - "print(f\"b. Taille des données X: {X.shape}\")\n", - "print(f\" - Nombre d'échantillons: {X.shape[0]}\")\n", - "print(f\" - Nombre de features par échantillon: {X.shape[1]}\")\n", - "\n", - "# c. Stocker les classes dans la variable Y\n", - "Y = mnist.target\n", - "print(\"\\nc. Variable Y créée avec les classes\")\n", - "\n", - "# d. Afficher la taille des labels/classes\n", - "print(f\"d. Taille des labels Y: {Y.shape}\")\n", - "print(f\" - Nombre total de labels: {Y.shape[0]}\")\n", - "\n", - "# e. Afficher les différentes classes de la base de données\n", - "classes_uniques = np.unique(Y)\n", - "print(f\"\\ne. Classes uniques dans la base de données: {classes_uniques}\")\n", - "print(f\" - Nombre de classes différentes: {len(classes_uniques)}\")\n", - "\n", - "# f. Description détaillée de la base de données\n", - "print(\"\\nf. Description détaillée de la base de données:\")\n", - "print(mnist.DESCR)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "d333dc4d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Analyse de la première instance de MNIST ===\n", - "\n", - "a. Affichage de la première instance:\n", - " i. Première instance extraite avec X.values[0]\n", - " Forme originale: (784,)\n", - " ii. Image redimensionnée en 28x28: (28, 28)\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAGcCAYAAAA2+rwbAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAHA9JREFUeJzt3QeUXGX5P/CbQgkhECLSi4KIhN4EAekCIgEERUSliBIUCGoQ0XDAAiL1KEURkKJIkRIUC0gEVIo06ULOIYIU6TGEJnX+57n/332YnZ3d7J2EZU0+n3P2ZJmdd25/v+/73vcOgxqNRqMAgKIoBr/TKwDAwCEUAEhCAYAkFABIQgGAJBQASEIBgCQUAEhCoaZrrrmmmGeeeYp99tnnnV4VgFlOKLRx7bXXFoMGDSq+/e1vd/vbZpttVvz85z8vzjjjjOKcc84pBoJNN920XF/630MPPVTu+z333HNAnacwIEKhukCaf+aee+5i6aWXLnbbbbfirrvuKmYHn/rUp4rjjz+++NKXvlTceeedxZwmjmsEEf/73slQqyOCL9YzgrCTcvFz0EEH9fi+b3zjG/m+b7eEbNXomnfeeYt//etfbct/4AMf6NYwq0J733337fb+66+/vvjkJz9ZLLnkkmUdudBCC5WfEfVk1dhsV5/29vOe97ynmBWGFm+D5ZdfvvjsZz9b/v7CCy8Uf/vb34rzzz+/uPTSS4s//elPxYYbblgMZB/84AeL++67r1h44YV7fM9Xv/rVYsEFFyyDbvXVVy/eSdFzeemll97RdYCBbOjQocW5555b/OAHPyh/b/b666+X11C8/vrrr/f4Ga+88kpx6KGHFr/4xS9mal3OPvvs4vOf/3y5vG233bZYYYUVykp98uTJxe9///viL3/5S7HHHnsUI0eOLA4//PBu5b/zne+Udc9XvvKVLq/H+wdsKLzvfe/rlraxM4888shiwoQJtdO+v80333xlas9IHNiBYJlllnmnVwEGtI9+9KPF5ZdfXvz2t78tdtxxxy5/i4r4iSeeKLbffvviN7/5Ta+N3fPOO6/4+te/Xqy22modrUc03saNG1eMGDGiuOGGG4qVV165y99fe+21rB+jkm83NBih0NPf/qfuKRxwwAHlv7fccku3YYjHHnus2H333YvFFlusGDx4cJfQiNQcM2ZM2WqPG7yRqhEwrS3j5vHV2Nkx9h87/t3vfnfx5S9/uXj55ZfL9/3ud78rPvShDxXDhw8vFl100eLggw/u1jrobaz2qaeeKnsJEXyxPqNGjSpPuD//+c/d3hvdufiZNm1asf/++5fDaNE6iJZCJXoau+66a7H44ouX3chll1223FfPPvvsTN1TiGXEa/HvH//4x2KDDTYow+5d73pX2Qpp9/lxEz22ZYkllii3LfbPhz/84eK0007rsl9CbG9z17Xapueee644+uiji0022aT8nNim+DeO75QpU3odFogLbo011iiGDRtW7o8DDzwwj1urOC/i4o51jHWNfbvTTjsV1113XZf3xZcAn3nmmWXvdIEFFij3wTrrrFO+Vscbb7xRblcc9xhGiH+POuqo4s0336w9xFadF30V++CQQw4ptzGWvcoqqxSnn356j++fOHFi8elPf7pcx9jeaFXGcbzkkku6vC+O2Xvf+97y9xiyaD6e1TX473//u2ytrr/++sUiiyxS7utY97im4lpoFcf/sMMOK0aPHl3MP//85T6P9YhzrnXopa/HJvZjVIQhrutOhkvi3IiKtN1xj9di+ObjH/94r59xxBFHlMc7hpo6dc899xTPP/98uR2tgRDmmmuu4iMf+UjxTnpbegq9aa28onKKSjoq16gc//vf/5YnSPjJT35S7LfffuXBjGCIk/LWW28texxRgcVPVDrNbrrppvLi3XrrrYuxY8eW74nPmT59evkZMXa6ww47lMuMgDj22GPLkzdO5BmJSi1O0LgYtttuu2LnnXcupk6dWl6Em2++edmtjDHB1i5n/C2G0aIlEqEQFVmIVskuu+xSBmGsU1z0//jHP4qTTz65uPLKK8ttiZN1ZsQyYjtj2yMYojKNrnJsS3MFWr0n9nWsS1TKTz/9dHnPJLYrZlvFRRgVRFygEV7N49BRmYcYdot9GSd9XGQRvvfff39Z4ccy/v73v5dlW8U2X3HFFeWyY3/F7yeeeGLxzDPPFL/85S+7vPdHP/pRGcwRHrGM6ClFwyK25+KLLy422mijrHQ+85nPlEOX0ZiIYxPny1VXXVXsvffe5b4+7rjj+rQfY/uj8ohKNM7JOE9POOGEsgHydopKKM6bSZMmFauuumq5DXHNxPbHPm7nm9/8ZrmdsR+q4xjnwSc+8Ylyn1YNtDhmEbyxP2MItLkFXVW4cb7E/bMtttiiWG+99cpK6/bbby+vqThH43hG6FT7O667OG+jot9mm23KczvCIJb/uc99Lo99nWNTnWfREIlwqdatznBJhGkEZYTpk08+mddg/B7nZRzfeE9v4tqPRtMf/vCHsl7paf/3Jhpl4Z///GfZ0BgyZEgx4DRmoQcffDD+3wyNrbfeutvfDjvssPJvm222Wb4W/x0/e+21V+P111/v8v577723MXTo0Mbqq6/eeOaZZ7r87aijjirLHXfccfnaNddck5932WWX5euvvvpqY7XVVmsMGjSosfDCCzduvvnm/Nv06dMbiyyySGPUqFHl+1o/6/DDD++y3A022KCx6KKLNiZPntzl9alTpzbe//73l5/z4osv5uvLLrts7o+XXnqpS5nYpgUWWKCx5JJLNh566KEufzv//PPLcvvvv3+jLzbZZJPy/c3OOuus8rXYh9ddd12+Hvt50003Lf9244035us77bRT+dodd9zR7fNb93+8L5bZzrRp0xrPPvtst9evvvrqxuDBgxtf+MIXurwe+zg+b8EFF2zcf//9+Xrsr9inUeaxxx7L12P94rUllliiPN+avfnmm13ee9ppp+X51Xx8X3nllcaYMWPKv916662NGanOhzgXX3jhhXz90UcfLc+p+Nsee+zR530U50X89EV1HLfZZpsu18hdd93VmHvuuduep1OmTOn2Oc8//3xj1VVXLfdz8zlaXbOt61958skny7KtzjnnnLLcEUcc0WWd4rUdd9yx2/v/+9//dvmcusemOk/iWNRRlYtrKj4vfj/mmGPy7/F7vHbbbbfldXd4y/6srq/HH3+8ceedd5bn37rrrlueb5UVV1yx2zVYnTdjx47N16LM2muvXb6+0UYbNU4//fTG3Xff3a3+602U7ev504m3ZfjogQceKIcF4ifG3zbeeOPiu9/9bpnE0cpvFq2DY445plti/vSnPy2HdU466aRM10oM+cSwULQyWkV6R2uzEi2baCHFvoyW8Lrrrpt/i+GlaPFHa//RRx/tdZuidRStwr322qvs1UQLtvqJxI8ZSfE57VqOsX3Rqm0WrfXovcQQRGvLOXpMa621VnHBBRcUMytaYM039mM/R2urdSiv0rqeoXX/9yZajbF/WlXd5WjxthMt1hVXXLHLekTLLlrKt912W5fzIl6Lrnzr8EH0QmOoqrn3ET2VU045pTwPms+5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mJUZ11TUc/E5sZ/ieYl4BiWG1dqFbr/pr+cU2ultLnclnivYddddy3npc801VzkvfK211moccsghjfvuu2+GzxY0z/WOf1u1m//c22fFMwmHHnpoY5VVVmkMGzasMf/88zdWWGGFxm677da49NJLa89Hj7n5e++9d/m+mHe+0EILlfPJx40b1+WZik6fU2i3ze2274ILLmjssssujeWXX74x33zzlfPZY17+0Ucf3W2eeszXjvfGsYg5283LiXnYp556amPllVduzDvvvI3FFlus3L6nnnqq7Xr2Nv98Rtuw3Xbblc+GxH5baqmlGjvvvHPj+uuv7/beCy+8sLHllluW+zbOoXg2JJ7VOP744xtPP/10n/ZxzCOP52OWW265cnnx7/e///3GAw880OM8/4suuqg8lvH+2A8HHHBAuS/rPKcQ4rmCgw8+uFzveeaZpzF69Ohynn9P52k8y7HVVluV2ztixIhyv0+aNKnH/RnP3Wy77baNkSNHls/zNB+PeIbgyCOPLM/xWPYyyyzTGD9+fNvteOSRR8rrcv311y+f/4ntjvfHMzDNz8R0emzOPvvscn/GevR1rn7zcwoz0pfnFNqdFyuttFI+IzWj5xTieY1LLrmksc8++5TXV1xDQ4YMKbc/nls44YQTGi+//HLjnXxOYdD/LQQABu49BQD6n1AAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgDX3rVxiY3njjjdplnnvuuWKgOvnkkzsq99JLL9UuM3ny5NplTjnllNplDjrooNplzj///KIT8847b+0yhxxySO0yhx9+eDEn0lMAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAki/Em808/PDDtcu8+uqrtcvccMMNtctc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", - "text/plain": [ - "
" + "cell_type": "markdown", + "id": "33e76083", + "metadata": { + "id": "33e76083" + }, + "source": [ + "# I - Préparation de données" ] - }, - "metadata": {}, - "output_type": "display_data" }, { - "name": "stdout", - "output_type": "stream", - "text": [ - " iii. Image affichée en niveau de gris\n", - " Valeurs min/max des pixels: 0.0 à 255.0\n", - "\n", - "b. Analyse de la première instance:\n", - " - Classe de la première instance: 5\n", - " - Type de la classe: \n", - " - Type de l'instance (données): \n", - "\n", - "=== Conclusion ===\n", - "La première image du dataset MNIST représente le chiffre: 5\n" - ] - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import matplotlib as mpl\n", - "import numpy as np\n", - "\n", - "print(\"=== Analyse de la première instance de MNIST ===\\n\")\n", - "\n", - "# a. Affichage de la première instance de la base de données\n", - "print(\"a. Affichage de la première instance:\")\n", - "\n", - "# i. Utiliser l'attribut \"values\" du dictionnaire X\n", - "premiere_instance = X.values[0]\n", - "print(f\" i. Première instance extraite avec X.values[0]\")\n", - "print(f\" Forme originale: {premiere_instance.shape}\")\n", - "\n", - "# ii. Redimensionner via la fonction \"reshape\" de numpy en taille 28,28\n", - "image_2d = premiere_instance.reshape(28, 28)\n", - "print(f\" ii. Image redimensionnée en 28x28: {image_2d.shape}\")\n", - "\n", - "# iii. Utiliser imshow avec cmap=mpl.cm.binary pour affichage en niveau de gris\n", - "plt.figure()\n", - "plt.imshow(image_2d, cmap=mpl.cm.binary)\n", - "plt.title(\"Première instance du dataset MNIST\", fontsize=14)\n", - "plt.axis('off') # Supprimer les axes pour une meilleure visualisation\n", - "plt.show()\n", - "\n", - "print(f\" iii. Image affichée en niveau de gris\")\n", - "print(f\" Valeurs min/max des pixels: {premiere_instance.min():.1f} à {premiere_instance.max():.1f}\")\n", - "\n", - "# b. Afficher la classe et le type de la première instance\n", - "print(f\"\\nb. Analyse de la première instance:\")\n", - "classe_premiere = Y.iloc[0] # Utiliser iloc pour pandas Series\n", - "print(f\" - Classe de la première instance: {classe_premiere}\")\n", - "print(f\" - Type de la classe: {type(classe_premiere)}\")\n", - "print(f\" - Type de l'instance (données): {type(premiere_instance)}\")\n", - "\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(f\"La première image du dataset MNIST représente le chiffre: {classe_premiere}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "c0966e7f", - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "id": "1ccf6f5b", + "metadata": { + "id": "1ccf6f5b" + }, + "source": [ + "## 0-Téléchargement de données" + ] + }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Conversion des labels en valeurs numériques ===\n", - "\n", - "Type actuel de Y: \n", - "Type des éléments de Y: \n", - "Premiers labels (avant casting): ['5', '0', '4', '1', '9']\n", - "\n", - "Après casting:\n", - "Type de Y: \n", - "Type des éléments de Y: \n", - "Premiers labels (après casting): [5, 0, 4, 1, 9]\n", - "\n", - "Classe de la première instance (numérique): 5\n", - "Type de la classe: \n", - "\n", - "=== Conclusion ===\n", - "Les labels ont été convertis en valeurs numériques avec succès.\n" - ] - } - ], - "source": [ - "print(\"=== Conversion des labels en valeurs numériques ===\\n\")\n", - "\n", - "# Afficher le type actuel des labels\n", - "print(f\"Type actuel de Y: {type(Y)}\")\n", - "print(f\"Type des éléments de Y: {type(Y.iloc[0])}\")\n", - "print(f\"Premiers labels (avant casting): {list(Y.head())}\")\n", - "\n", - "# Appliquer le casting sur les labels\n", - "Y = Y.astype(np.uint8)\n", - "\n", - "# Vérifier le résultat du casting\n", - "print(f\"\\nAprès casting:\")\n", - "print(f\"Type de Y: {type(Y)}\")\n", - "print(f\"Type des éléments de Y: {type(Y.iloc[0])}\")\n", - "print(f\"Premiers labels (après casting): {list(Y.head())}\")\n", - "\n", - "# Vérifier que la classe de la première instance est maintenant numérique\n", - "classe_premiere_numerique = Y.iloc[0]\n", - "print(f\"\\nClasse de la première instance (numérique): {classe_premiere_numerique}\")\n", - "print(f\"Type de la classe: {type(classe_premiere_numerique)}\")\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(\"Les labels ont été convertis en valeurs numériques avec succès.\")" - ] - }, - { - "cell_type": "markdown", - "id": "05f6129e", - "metadata": {}, - "source": [ - "## 2- Répartition des données" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0560d20", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Répartition des données MNIST ===\n", - "\n", - "Taille totale des données: 70000 échantillons\n", - "Taille totale des labels: 70000 échantillons\n", - "\n", - "=== Résultats de la répartition ===\n", - "Données d'apprentissage:\n", - " - X_train: (60000, 784) échantillons\n", - " - Y_train: (60000,) labels\n", - "\n", - "Données de test:\n", - " - X_test: (10000, 784) échantillons\n", - " - Y_test: (10000,) labels\n", - "\n", - "=== Vérification ===\n", - "Total apprentissage + test: 70000 échantillons\n", - "Cohérent avec le total original: True\n", - "\n", - "Répartition terminée!\n", - "60 000 échantillons pour l'apprentissage\n", - "10000 échantillons pour le test\n" - ] - } - ], - "source": [ - "print(\"=== Répartition des données MNIST ===\\n\")\n", - "\n", - "# Vérification de la taille totale des données\n", - "print(f\"Taille totale des données: {X.shape[0]} échantillons\")\n", - "print(f\"Taille totale des labels: {Y.shape[0]} échantillons\")\n", - "\n", - "# Répartition des données en une seule ligne (indexing sur ndarrays)\n", - "# a. Les 60 000 premières images composeront la base d'apprentissage\n", - "# b. Le reste des images constitue la base de test\n", - "X_train, X_test = X.iloc[:60000], X.iloc[60000:]\n", - "Y_train, Y_test = Y.iloc[:60000], Y.iloc[60000:]\n", - "\n", - "print(f\"\\n=== Résultats de la répartition ===\")\n", - "print(f\"Données d'apprentissage:\")\n", - "print(f\" - X_train: {X_train.shape} échantillons\")\n", - "print(f\" - Y_train: {Y_train.shape} labels\")\n", - "\n", - "print(f\"\\nDonnées de test:\")\n", - "print(f\" - X_test: {X_test.shape} échantillons\") \n", - "print(f\" - Y_test: {Y_test.shape} labels\")\n", - "\n", - "print(f\"\\n=== Vérification ===\")\n", - "print(f\"Total apprentissage + test: {X_train.shape[0] + X_test.shape[0]} échantillons\")\n", - "print(f\"Cohérent avec le total original: {X_train.shape[0] + X_test.shape[0] == X.shape[0]}\")\n", - "\n", - "print(f\"\\nRépartition terminée!\")\n", - "print(f\"60 000 échantillons pour l'apprentissage\")\n", - "print(f\"{X_test.shape[0]} échantillons pour le test\")" - ] - }, - { - "cell_type": "markdown", - "id": "6515e1aa", - "metadata": {}, - "source": [ - "# II- Apprentissage d'un classifieur binaire" - ] - }, - { - "cell_type": "markdown", - "id": "47d18544", - "metadata": {}, - "source": [ - "## 2 - Apprentissage des données" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "bff45d57", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== Résultats de la répartition ===\n", - "\n", - "y_train_5: 0 True\n", - "1 False\n", - "2 False\n", - "3 False\n", - "4 False\n", - " ... \n", - "59995 False\n", - "59996 False\n", - "59997 True\n", - "59998 False\n", - "59999 False\n", - "Name: class, Length: 60000, dtype: bool\n", - "\n", - "y_test_5: 60000 False\n", - "60001 False\n", - "60002 False\n", - "60003 False\n", - "60004 False\n", - " ... \n", - "69995 False\n", - "69996 False\n", - "69997 False\n", - "69998 True\n", - "69999 False\n", - "Name: class, Length: 10000, dtype: bool\n" - ] - } - ], - "source": [ - "y_train_5 = (Y_train == 5)\n", - "y_test_5 = (Y_test == 5)\n", - "\n", - "print(f\"\\n=== Résultats de la répartition ===\")\n", - "print(f\"\\ny_train_5: {y_train_5}\")\n", - "print(f\"\\ny_test_5: {y_test_5}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "e02058de", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
SGDClassifier()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "cell_type": "code", + "execution_count": 1, + "id": "5a9a29a0", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5a9a29a0", + "outputId": "624e697a-5fba-46df-ebbe-7b9c120013cf" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting pandas\n", + " Downloading pandas-2.3.3-cp313-cp313-win_amd64.whl.metadata (19 kB)\n", + "Collecting numpy>=1.26.0 (from pandas)\n", + " Downloading numpy-2.3.3-cp313-cp313-win_amd64.whl.metadata (60 kB)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in c:\\users\\breizhhardware\\appdata\\roaming\\python\\python313\\site-packages (from pandas) (2.9.0.post0)\n", + "Collecting pytz>=2020.1 (from pandas)\n", + " Using cached pytz-2025.2-py2.py3-none-any.whl.metadata (22 kB)\n", + "Collecting tzdata>=2022.7 (from pandas)\n", + " Using cached tzdata-2025.2-py2.py3-none-any.whl.metadata (1.4 kB)\n", + "Requirement already satisfied: six>=1.5 in c:\\users\\breizhhardware\\appdata\\roaming\\python\\python313\\site-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)\n", + "Downloading pandas-2.3.3-cp313-cp313-win_amd64.whl (11.0 MB)\n", + " ---------------------------------------- 0.0/11.0 MB ? eta -:--:--\n", + " ---------------------------------------- 11.0/11.0 MB 96.0 MB/s 0:00:00\n", + "Downloading numpy-2.3.3-cp313-cp313-win_amd64.whl (12.8 MB)\n", + " ---------------------------------------- 0.0/12.8 MB ? eta -:--:--\n", + " ---------------------------------------- 12.8/12.8 MB 179.2 MB/s 0:00:00\n", + "Using cached pytz-2025.2-py2.py3-none-any.whl (509 kB)\n", + "Using cached tzdata-2025.2-py2.py3-none-any.whl (347 kB)\n", + "Installing collected packages: pytz, tzdata, numpy, pandas\n", + "\n", + " ---------------------------------------- 0/4 [pytz]\n", + " ---------- ----------------------------- 1/4 [tzdata]\n", + " ---------- ----------------------------- 1/4 [tzdata]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " -------------------- ------------------- 2/4 [numpy]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ------------------------------ --------- 3/4 [pandas]\n", + " ---------------------------------------- 4/4 [pandas]\n", + "\n", + "Successfully installed numpy-2.3.3 pandas-2.3.3 pytz-2025.2 tzdata-2025.2\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Collecting matplotlib\n", + " Using cached matplotlib-3.10.6-cp313-cp313-win_amd64.whl.metadata (11 kB)\n", + "Collecting contourpy>=1.0.1 (from matplotlib)\n", + " Using cached contourpy-1.3.3-cp313-cp313-win_amd64.whl.metadata (5.5 kB)\n", + "Collecting cycler>=0.10 (from matplotlib)\n", + " Using cached cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)\n", + "Collecting fonttools>=4.22.0 (from matplotlib)\n", + " Downloading fonttools-4.60.1-cp313-cp313-win_amd64.whl.metadata (114 kB)\n", + "Collecting kiwisolver>=1.3.1 (from matplotlib)\n", + " Using cached kiwisolver-1.4.9-cp313-cp313-win_amd64.whl.metadata (6.4 kB)\n", + "Requirement already satisfied: numpy>=1.23 in c:\\users\\breizhhardware\\appdata\\local\\programs\\python\\python313\\lib\\site-packages (from matplotlib) (2.3.3)\n", + "Requirement already satisfied: packaging>=20.0 in c:\\users\\breizhhardware\\appdata\\roaming\\python\\python313\\site-packages (from matplotlib) (25.0)\n", + "Collecting pillow>=8 (from matplotlib)\n", + " Using cached pillow-11.3.0-cp313-cp313-win_amd64.whl.metadata (9.2 kB)\n", + "Collecting pyparsing>=2.3.1 (from matplotlib)\n", + " Downloading pyparsing-3.2.5-py3-none-any.whl.metadata (5.0 kB)\n", + "Requirement already satisfied: python-dateutil>=2.7 in c:\\users\\breizhhardware\\appdata\\roaming\\python\\python313\\site-packages (from matplotlib) (2.9.0.post0)\n", + "Requirement already satisfied: six>=1.5 in c:\\users\\breizhhardware\\appdata\\roaming\\python\\python313\\site-packages (from python-dateutil>=2.7->matplotlib) (1.17.0)\n", + "Using cached matplotlib-3.10.6-cp313-cp313-win_amd64.whl (8.1 MB)\n", + "Using cached contourpy-1.3.3-cp313-cp313-win_amd64.whl (226 kB)\n", + "Using cached cycler-0.12.1-py3-none-any.whl (8.3 kB)\n", + "Downloading fonttools-4.60.1-cp313-cp313-win_amd64.whl (2.3 MB)\n", + " ---------------------------------------- 0.0/2.3 MB ? eta -:--:--\n", + " ---------------------------------------- 2.3/2.3 MB 43.8 MB/s 0:00:00\n", + "Using cached kiwisolver-1.4.9-cp313-cp313-win_amd64.whl (73 kB)\n", + "Using cached pillow-11.3.0-cp313-cp313-win_amd64.whl (7.0 MB)\n", + "Downloading pyparsing-3.2.5-py3-none-any.whl (113 kB)\n", + "Installing collected packages: pyparsing, pillow, kiwisolver, fonttools, cycler, contourpy, matplotlib\n", + "\n", + " ---------------------------------------- 0/7 [pyparsing]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----- ---------------------------------- 1/7 [pillow]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ----------------- ---------------------- 3/7 [fonttools]\n", + " ---------------------- ----------------- 4/7 [cycler]\n", + " ---------------------------- ----------- 5/7 [contourpy]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------- ----- 6/7 [matplotlib]\n", + " ---------------------------------------- 7/7 [matplotlib]\n", + "\n", + "Successfully installed contourpy-1.3.3 cycler-0.12.1 fonttools-4.60.1 kiwisolver-1.4.9 matplotlib-3.10.6 pillow-11.3.0 pyparsing-3.2.5\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Collecting scikit-learn\n", + " Downloading scikit_learn-1.7.2-cp313-cp313-win_amd64.whl.metadata (11 kB)\n", + "Requirement already satisfied: numpy>=1.22.0 in c:\\users\\breizhhardware\\appdata\\local\\programs\\python\\python313\\lib\\site-packages (from scikit-learn) (2.3.3)\n", + "Collecting scipy>=1.8.0 (from scikit-learn)\n", + " Downloading scipy-1.16.2-cp313-cp313-win_amd64.whl.metadata (60 kB)\n", + "Collecting joblib>=1.2.0 (from scikit-learn)\n", + " Using cached joblib-1.5.2-py3-none-any.whl.metadata (5.6 kB)\n", + "Collecting threadpoolctl>=3.1.0 (from scikit-learn)\n", + " Using cached threadpoolctl-3.6.0-py3-none-any.whl.metadata (13 kB)\n", + "Downloading scikit_learn-1.7.2-cp313-cp313-win_amd64.whl (8.7 MB)\n", + " ---------------------------------------- 0.0/8.7 MB ? eta -:--:--\n", + " ---------------------------------------- 8.7/8.7 MB 92.6 MB/s 0:00:00\n", + "Using cached joblib-1.5.2-py3-none-any.whl (308 kB)\n", + "Downloading scipy-1.16.2-cp313-cp313-win_amd64.whl (38.5 MB)\n", + " ---------------------------------------- 0.0/38.5 MB ? eta -:--:--\n", + " --------------------------------------- 38.3/38.5 MB 236.7 MB/s eta 0:00:01\n", + " ---------------------------------------- 38.5/38.5 MB 186.0 MB/s 0:00:00\n", + "Using cached threadpoolctl-3.6.0-py3-none-any.whl (18 kB)\n", + "Installing collected packages: threadpoolctl, scipy, joblib, scikit-learn\n", + "\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " ---------- ----------------------------- 1/4 [scipy]\n", + " -------------------- ------------------- 2/4 [joblib]\n", + " -------------------- ------------------- 2/4 [joblib]\n", + " -------------------- ------------------- 2/4 [joblib]\n", + " -------------------- ------------------- 2/4 [joblib]\n", + " -------------------- ------------------- 2/4 [joblib]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ------------------------------ --------- 3/4 [scikit-learn]\n", + " ---------------------------------------- 4/4 [scikit-learn]\n", + "\n", + "Successfully installed joblib-1.5.2 scikit-learn-1.7.2 scipy-1.16.2 threadpoolctl-3.6.0\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } ], - "text/plain": [ - "SGDClassifier()" + "source": [ + "%pip install pandas\n", + "%pip install matplotlib\n", + "%pip install scikit-learn" ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from sklearn.linear_model import SGDClassifier\n", - "\n", - "# a. Création d'un objet SGDClassifier\n", - "sgd_classifier = SGDClassifier()\n", - "\n", - "# b. Application de la méthode fit avec les données de la question 14 (classification binaire)\n", - "sgd_classifier.fit(X_train, y_train_5)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "e73e18a3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Prédiction de la première instance avec le modèle SGD ===\n", - "\n", - "Forme de la première instance: (1, 784)\n", - "\n", - "Prédiction du modèle:\n", - "- Résultat: True\n", - "- Type: \n", - "\n", - "Comparaison avec la réalité:\n", - "- Vraie classe de la première instance: 5\n", - "- Est-ce vraiment un '5'?: True\n", - "- Prédiction du modèle: True\n", - "\n", - "Le modèle prédit: 'C'est un chiffre 5'\n", - "\n", - "=== Conclusion ===\n", - "Prédiction: True\n", - "Exactitude: Correct\n" - ] - } - ], - "source": [ - "print(\"=== Prédiction de la première instance avec le modèle SGD ===\\n\")\n", - "\n", - "# Extraire la première instance (celle de la question 8)\n", - "premiere_instance = X.iloc[0:1] # Utiliser [0:1] pour garder la forme 2D nécessaire\n", - "print(f\"Forme de la première instance: {premiere_instance.shape}\")\n", - "\n", - "# Prédire avec le modèle SGD entraîné\n", - "prediction = sgd_classifier.predict(premiere_instance)\n", - "print(f\"\\nPrédiction du modèle:\")\n", - "print(f\"- Résultat: {prediction[0]}\")\n", - "print(f\"- Type: {type(prediction[0])}\")\n", - "\n", - "# Vérification avec la vraie classe\n", - "vraie_classe = Y.iloc[0]\n", - "est_vraiment_5 = (vraie_classe == 5)\n", - "\n", - "print(f\"\\nComparaison avec la réalité:\")\n", - "print(f\"- Vraie classe de la première instance: {vraie_classe}\")\n", - "print(f\"- Est-ce vraiment un '5'?: {est_vraiment_5}\")\n", - "print(f\"- Prédiction du modèle: {prediction[0]}\")\n", - "\n", - "# Résultat de la prédiction\n", - "if prediction[0]:\n", - " if est_vraiment_5:\n", - " print(f\"\\nLe modèle prédit: 'C'est un chiffre 5'\")\n", - " else :\n", - " print(\"\\nLe modèle prédit: 'C'est un chiffre 5' (ERREUR)\")\n", - "else:\n", - " if est_vraiment_5:\n", - " print(f\"\\nLe modèle prédit: 'Ce n'est PAS un chiffre 5' (ERREUR)\")\n", - " else:\n", - " print(\"\\nLe modèle prédit: 'Ce n'est PAS un chiffre 5'\")\n", - "\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(f\"Prédiction: {'True' if prediction[0] else 'False'}\")\n", - "print(f\"Exactitude: {'Correct' if prediction[0] == est_vraiment_5 else 'Incorrect'}\")" - ] - }, - { - "cell_type": "markdown", - "id": "8aa1ea52", - "metadata": {}, - "source": [ - "### 2-1- Taux de classification" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "acaeacd9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Taux de classification (accuracy) pour chaque fold: [0.9542 0.96765 0.96125]\n", - "Moyenne du taux de classification: 0.9610\n" - ] - } - ], - "source": [ - "from sklearn.model_selection import cross_val_score\n", - "\n", - "# Opter pour la valeur accuracy pour l'argument scoring\n", - "# a. Afficher le taux de classification (accuracy) de chaque fold\n", - "scores = cross_val_score(sgd_classifier, X_train, y_train_5, cv=3, scoring='accuracy')\n", - "print(f\"Taux de classification (accuracy) pour chaque fold: {scores}\")\n", - "# b. Afficher la moyenne du taux de classification\n", - "print(f\"Moyenne du taux de classification: {scores.mean():.4f}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "e5949779", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Taux de classification (accuracy) pour chaque fold avec Never5Classifier: [0.91125 0.90855 0.90915]\n", - "Moyenne du taux de classification avec Never5Classifier: 0.9096\n" - ] - } - ], - "source": [ - "from sklearn.base import BaseEstimator\n", - "\n", - "class Never5Classifier(BaseEstimator):\n", - " def fit(self, data, labels):\n", - " pass\n", - "\n", - " def predict(self, data):\n", - " '''Prend en argument les données et retourne une \n", - "structure de données ayant la taille des données et qui contient que la \n", - "valeur False (= non-5)'''\n", - " return np.zeros((data.shape[0],), dtype=bool)\n", - " \n", - "# c. Créez un objet de la classe Never5Classifier\n", - "never_5_classifier = Never5Classifier()\n", - "\n", - "# d. Testez le classifieur Never5Classifier en utilisant une validation croisée de type 3-fold cross-validation.\n", - "# i. Afficher le taux de classification (accuracy) de chaque fold\n", - "scores_never5 = cross_val_score(never_5_classifier, X_train, y_train_5, cv=3, scoring='accuracy')\n", - "print(f\"\\nTaux de classification (accuracy) pour chaque fold avec Never5Classifier: {scores_never5}\")\n", - "# ii. Afficher la moyenne du taux de classification\n", - "print(f\"Moyenne du taux de classification avec Never5Classifier: {scores_never5.mean():.4f}\")" - ] - }, - { - "cell_type": "markdown", - "id": "776962ff", - "metadata": {}, - "source": [ - "### 2-2- Matrice de confusion" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "5733c7b1", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Évaluation du classifieur SGD avec cross_val_predict ===\n", - "\n", - "Type des prédictions: \n", - "Forme des prédictions: (60000,)\n", - "Type des éléments: \n", - "\n", - "Premières 20 prédictions: [ True False False False False False False False False False False True\n", - " False False False False False False False False]\n", - "Dernières 20 prédictions: [False False False False False False False False False False False False\n", - " False True False False False True False False]\n", - "\n", - "=== Statistiques des prédictions ===\n", - "Nombre total de prédictions: 60000\n", - "Prédictions 'True' (chiffre 5): 5645\n", - "Prédictions 'False' (non-5): 54355\n", - "Pourcentage de 'True': 9.41%\n", - "Pourcentage de 'False': 90.59%\n", - "\n", - "=== Comparaison avec la réalité ===\n", - "Vraies instances de '5' dans l'entraînement: 5421\n", - "Vraies instances de 'non-5' dans l'entraînement: 54579\n", - "\n", - "=== Résultats de cross_val_predict ===\n", - "Les prédictions ont été obtenues pour tous les échantillons d'entraînement\n", - "Chaque échantillon a été prédit exactement une fois lors de la validation croisée\n" - ] - } - ], - "source": [ - "from sklearn.model_selection import cross_val_predict\n", - "\n", - "print(\"=== Évaluation du classifieur SGD avec cross_val_predict ===\\n\")\n", - "\n", - "# Utiliser cross_val_predict pour obtenir les classes prédites\n", - "# Cette fonction retourne les prédictions pour chaque échantillon lors de la validation croisée 3-fold\n", - "y_train_pred = cross_val_predict(sgd_classifier, X_train, y_train_5, cv=3)\n", - "\n", - "print(f\"Type des prédictions: {type(y_train_pred)}\")\n", - "print(f\"Forme des prédictions: {y_train_pred.shape}\")\n", - "print(f\"Type des éléments: {type(y_train_pred[0])}\")\n", - "\n", - "# Afficher quelques exemples de prédictions\n", - "print(f\"\\nPremières 20 prédictions: {y_train_pred[:20]}\")\n", - "print(f\"Dernières 20 prédictions: {y_train_pred[-20:]}\")\n", - "\n", - "# Compter les prédictions True et False\n", - "nb_true = np.sum(y_train_pred)\n", - "nb_false = len(y_train_pred) - nb_true\n", - "\n", - "print(f\"\\n=== Statistiques des prédictions ===\")\n", - "print(f\"Nombre total de prédictions: {len(y_train_pred)}\")\n", - "print(f\"Prédictions 'True' (chiffre 5): {nb_true}\")\n", - "print(f\"Prédictions 'False' (non-5): {nb_false}\")\n", - "print(f\"Pourcentage de 'True': {(nb_true / len(y_train_pred) * 100):.2f}%\")\n", - "print(f\"Pourcentage de 'False': {(nb_false / len(y_train_pred) * 100):.2f}%\")\n", - "\n", - "# Comparer avec les vraies valeurs\n", - "nb_vraies_5 = np.sum(y_train_5)\n", - "print(f\"\\n=== Comparaison avec la réalité ===\")\n", - "print(f\"Vraies instances de '5' dans l'entraînement: {nb_vraies_5}\")\n", - "print(f\"Vraies instances de 'non-5' dans l'entraînement: {len(y_train_5) - nb_vraies_5}\")\n", - "\n", - "print(f\"\\n=== Résultats de cross_val_predict ===\")\n", - "print(\"Les prédictions ont été obtenues pour tous les échantillons d'entraînement\")\n", - "print(\"Chaque échantillon a été prédit exactement une fois lors de la validation croisée\")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "970fce9c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Matrice de confusion du classifieur SGD ===\n", - "\n", - "Matrice de confusion (valeurs absolues):\n", - "[[53253 1326]\n", - " [ 1102 4319]]\n", - "\n", - "Matrice de confusion normalisée:\n", - "[[0.97570494 0.02429506]\n", - " [0.20328353 0.79671647]]\n" - ] }, { - "data": { - "image/png": "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", - "text/plain": [ - "
" + "cell_type": "code", + "execution_count": 2, + "id": "5be8782b", + "metadata": { + "id": "5be8782b" + }, + "outputs": [], + "source": [ + "from sklearn.datasets import fetch_openml\n", + "mnist = fetch_openml('mnist_784', version=1)" ] - }, - "metadata": {}, - "output_type": "display_data" }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== Interprétation de la matrice de confusion ===\n", - "\n", - "Valeurs de la matrice (absolues):\n", - "• Vrais Négatifs (TN): 53253 - Correctement classés comme 'Non-5'\n", - "• Faux Positifs (FP): 1326 - Incorrectement classés comme '5' (Erreur Type I)\n", - "• Faux Négatifs (FN): 1102 - Incorrectement classés comme 'Non-5' (Erreur Type II)\n", - "• Vrais Positifs (TP): 4319 - Correctement classés comme '5'\n", - "\n", - "Pourcentages (normalisés):\n", - "• Spécificité (TN rate): 0.976 - 97.6% des 'Non-5' bien identifiés\n", - "• Taux FP (FP rate): 0.024 - 2.4% des 'Non-5' mal classés comme '5'\n", - "• Taux FN (FN rate): 0.203 - 20.3% des '5' mal classés comme 'Non-5'\n", - "• Sensibilité (TP rate): 0.797 - 79.7% des '5' bien identifiés\n" - ] - } - ], - "source": [ - "from sklearn.metrics import confusion_matrix\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "print(\"=== Matrice de confusion du classifieur SGD ===\\n\")\n", - "\n", - "# Calculer la matrice de confusion normale\n", - "cm = confusion_matrix(y_train_5, y_train_pred)\n", - "print(\"Matrice de confusion (valeurs absolues):\")\n", - "print(cm)\n", - "\n", - "# Calculer la matrice de confusion normalisée\n", - "cm_normalized = confusion_matrix(y_train_5, y_train_pred, normalize='true')\n", - "print(\"\\nMatrice de confusion normalisée:\")\n", - "print(cm_normalized)\n", - "\n", - "# Visualisation des matrices de confusion avec matplotlib uniquement\n", - "fig, axes = plt.subplots(1, 2, figsize=(12, 5))\n", - "\n", - "# Matrice de confusion absolue\n", - "im1 = axes[0].imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)\n", - "axes[0].set_title('Matrice de confusion (valeurs absolues)')\n", - "axes[0].set_xlabel('Classe prédite')\n", - "axes[0].set_ylabel('Classe réelle')\n", - "\n", - "# Ajouter les annotations manuellement\n", - "for i in range(cm.shape[0]):\n", - " for j in range(cm.shape[1]):\n", - " axes[0].text(j, i, format(cm[i, j], 'd'), \n", - " ha=\"center\", va=\"center\", color=\"black\")\n", - "\n", - "# Ajouter les labels des axes\n", - "axes[0].set_xticks([0, 1])\n", - "axes[0].set_yticks([0, 1])\n", - "axes[0].set_xticklabels(['Non-5', '5'])\n", - "axes[0].set_yticklabels(['Non-5', '5'])\n", - "\n", - "# Matrice de confusion normalisée\n", - "im2 = axes[1].imshow(cm_normalized, interpolation='nearest', cmap=plt.cm.Blues)\n", - "axes[1].set_title('Matrice de confusion normalisée')\n", - "axes[1].set_xlabel('Classe prédite')\n", - "axes[1].set_ylabel('Classe réelle')\n", - "\n", - "# Ajouter les annotations pour la matrice normalisée\n", - "for i in range(cm_normalized.shape[0]):\n", - " for j in range(cm_normalized.shape[1]):\n", - " axes[1].text(j, i, format(cm_normalized[i, j], '.3f'), \n", - " ha=\"center\", va=\"center\", color=\"black\")\n", - "\n", - "# Ajouter les labels des axes\n", - "axes[1].set_xticks([0, 1])\n", - "axes[1].set_yticks([0, 1])\n", - "axes[1].set_xticklabels(['Non-5', '5'])\n", - "axes[1].set_yticklabels(['Non-5', '5'])\n", - "\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "# Interprétation détaillée de la matrice de confusion\n", - "print(\"\\n=== Interprétation de la matrice de confusion ===\")\n", - "tn, fp, fn, tp = cm.ravel()\n", - "\n", - "print(f\"\\nValeurs de la matrice (absolues):\")\n", - "print(f\"• Vrais Négatifs (TN): {tn} - Correctement classés comme 'Non-5'\")\n", - "print(f\"• Faux Positifs (FP): {fp} - Incorrectement classés comme '5' (Erreur Type I)\")\n", - "print(f\"• Faux Négatifs (FN): {fn} - Incorrectement classés comme 'Non-5' (Erreur Type II)\")\n", - "print(f\"• Vrais Positifs (TP): {tp} - Correctement classés comme '5'\")\n", - "\n", - "print(f\"\\nPourcentages (normalisés):\")\n", - "print(f\"• Spécificité (TN rate): {cm_normalized[0,0]:.3f} - {cm_normalized[0,0]*100:.1f}% des 'Non-5' bien identifiés\")\n", - "print(f\"• Taux FP (FP rate): {cm_normalized[0,1]:.3f} - {cm_normalized[0,1]*100:.1f}% des 'Non-5' mal classés comme '5'\")\n", - "print(f\"• Taux FN (FN rate): {cm_normalized[1,0]:.3f} - {cm_normalized[1,0]*100:.1f}% des '5' mal classés comme 'Non-5'\")\n", - "print(f\"• Sensibilité (TP rate): {cm_normalized[1,1]:.3f} - {cm_normalized[1,1]*100:.1f}% des '5' bien identifiés\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "b1b979f7", - "metadata": {}, - "source": [ - "### 2-3- Précision et rappel" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "b4a6c352", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Calcul des métriques de performance avec sklearn.metrics ===\n", - "\n", - "Précision (Precision): 0.7651\n", - "Rappel (Recall): 0.7967\n", - "Score F1: 0.7806\n", - "\n", - "=== Interprétation des résultats ===\n", - "\n", - "1. PRÉCISION = 0.7651 (76.51%)\n", - " • Sur toutes les images que le modèle a classées comme '5',\n", - " 76.51% sont réellement des '5'\n", - " • 23.49% sont des faux positifs (erreurs)\n", - "\n", - "2. RAPPEL = 0.7967 (79.67%)\n", - " • Sur toutes les vraies images de '5' dans la base,\n", - " le modèle en a détecté 79.67%\n", - " • Il a manqué 20.33% des vrais '5' (faux négatifs)\n", - "\n", - "3. SCORE F1 = 0.7806 (78.06%)\n", - " • Moyenne harmonique entre précision et rappel\n", - " • Mesure équilibrée des performances globales\n", - "\n", - "=== Analyse comparative ===\n", - "✓ Rappel > Précision : Le modèle est plutôt libéral\n", - " → Il détecte bien les '5' mais fait quelques erreurs en trop\n", - "\n", - "✅ CONCLUSION: Performances BONNES (F1 ≥ 60%)\n" - ] - } - ], - "source": [ - "from sklearn.metrics import precision_score, recall_score, f1_score\n", - "\n", - "print(\"=== Calcul des métriques de performance avec sklearn.metrics ===\\n\")\n", - "\n", - "# Calculer la précision\n", - "precision = precision_score(y_train_5, y_train_pred)\n", - "print(f\"Précision (Precision): {precision:.4f}\")\n", - "\n", - "# Calculer le rappel\n", - "recall = recall_score(y_train_5, y_train_pred)\n", - "print(f\"Rappel (Recall): {recall:.4f}\")\n", - "\n", - "# Calculer le score F1\n", - "f1 = f1_score(y_train_5, y_train_pred)\n", - "print(f\"Score F1: {f1:.4f}\")\n", - "\n", - "print(f\"\\n=== Interprétation des résultats ===\")\n", - "\n", - "print(f\"\\n1. PRÉCISION = {precision:.4f} ({precision*100:.2f}%)\")\n", - "print(f\" • Sur toutes les images que le modèle a classées comme '5',\")\n", - "print(f\" {precision*100:.2f}% sont réellement des '5'\")\n", - "print(f\" • {(1-precision)*100:.2f}% sont des faux positifs (erreurs)\")\n", - "\n", - "print(f\"\\n2. RAPPEL = {recall:.4f} ({recall*100:.2f}%)\")\n", - "print(f\" • Sur toutes les vraies images de '5' dans la base,\")\n", - "print(f\" le modèle en a détecté {recall*100:.2f}%\")\n", - "print(f\" • Il a manqué {(1-recall)*100:.2f}% des vrais '5' (faux négatifs)\")\n", - "\n", - "print(f\"\\n3. SCORE F1 = {f1:.4f} ({f1*100:.2f}%)\")\n", - "print(f\" • Moyenne harmonique entre précision et rappel\")\n", - "print(f\" • Mesure équilibrée des performances globales\")\n", - "\n", - "print(f\"\\n=== Analyse comparative ===\")\n", - "if precision > recall:\n", - " print(f\"✓ Précision > Rappel : Le modèle est plutôt conservateur\")\n", - " print(f\" → Il évite les faux positifs mais manque quelques vrais '5'\")\n", - "elif recall > precision:\n", - " print(f\"✓ Rappel > Précision : Le modèle est plutôt libéral\")\n", - " print(f\" → Il détecte bien les '5' mais fait quelques erreurs en trop\")\n", - "else:\n", - " print(f\"✓ Précision ≈ Rappel : Modèle équilibré\")\n", - "\n", - "# Évaluation globale\n", - "if f1 >= 0.8:\n", - " print(f\"\\n🎯 CONCLUSION: Performances EXCELLENTES (F1 ≥ 80%)\")\n", - "elif f1 >= 0.6:\n", - " print(f\"\\n✅ CONCLUSION: Performances BONNES (F1 ≥ 60%)\")\n", - "elif f1 >= 0.4:\n", - " print(f\"\\n⚠️ CONCLUSION: Performances MOYENNES (F1 ≥ 40%)\")\n", - "else:\n", - " print(f\"\\n❌ CONCLUSION: Performances FAIBLES (F1 < 40%)\")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "1ad09da3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Calcul des scores de décision avec cross_val_predict ===\n", - "\n", - "Type des scores: \n", - "Forme des scores: (60000,)\n", - "Type des éléments: \n", - "\n", - "Premiers 20 scores: [ 2.13224940e+03 -1.45293316e+04 -2.75430465e+04 -8.82172041e+03\n", - " -1.53541937e+04 -5.84835647e+03 -1.44072632e+04 -1.43372113e+04\n", - " -7.78103125e+03 -6.39475338e+03 -1.51267776e+04 1.02144924e+04\n", - " -2.39928686e+04 -8.38870002e+03 -7.36395439e+03 -1.18529978e+04\n", - " -1.39203417e+04 -9.48505167e+03 -2.01806124e+01 -1.55160951e+04]\n", - "Derniers 20 scores: [ -5166.76610699 -13399.56368524 -18515.00077271 -36167.18870608\n", - " -14514.01056646 -43469.63093711 -12955.36686545 -19963.18692214\n", - " -19227.49174077 -13500.94507705 -23222.74448091 -19466.07239252\n", - " -10684.51870424 7313.16701388 -7142.61203242 -14237.61818576\n", - " -24229.33576969 6360.50352131 -6384.98145829 -10661.12738376]\n", - "\n", - "=== Statistiques des scores de décision ===\n", - "Nombre total de scores: 60000\n", - "Score minimum: -132535.1477\n", - "Score maximum: 32014.4380\n", - "Score moyen: -15415.1039\n", - "Écart-type: 12174.2137\n", - "\n", - "=== Répartition selon le seuil par défaut (0) ===\n", - "Scores positifs (> 0): 4173 - Prédits comme '5'\n", - "Scores négatifs (≤ 0): 55827 - Prédits comme 'Non-5'\n", - "Pourcentage de scores positifs: 6.96%\n", - "Pourcentage de scores négatifs: 93.05%\n", - "\n", - "=== Vérification de cohérence ===\n", - "Les prédictions binaires (score > 0) correspondent aux prédictions précédentes: False\n", - "\n", - "=== Conclusion ===\n", - "Les scores de décision permettent de comprendre la 'confiance' du modèle:\n", - "• Plus le score est élevé, plus le modèle est confiant que c'est un '5'\n", - "• Plus le score est faible (négatif), plus le modèle est confiant que ce n'est PAS un '5'\n", - "• Le seuil par défaut de 0 sépare les deux classes\n" - ] - } - ], - "source": [ - "from sklearn.model_selection import cross_val_predict\n", - "import numpy as np\n", - "\n", - "print(\"=== Calcul des scores de décision avec cross_val_predict ===\\n\")\n", - "\n", - "# Utiliser cross_val_predict avec method='decision_function' pour obtenir les scores\n", - "# Cette fonction retourne les scores de décision pour chaque échantillon lors de la validation croisée 3-fold\n", - "y_train_scores = cross_val_predict(sgd_classifier, X_train, y_train_5, cv=3, method='decision_function')\n", - "\n", - "print(f\"Type des scores: {type(y_train_scores)}\")\n", - "print(f\"Forme des scores: {y_train_scores.shape}\")\n", - "print(f\"Type des éléments: {type(y_train_scores[0])}\")\n", - "\n", - "# Afficher quelques exemples de scores\n", - "print(f\"\\nPremiers 20 scores: {y_train_scores[:20]}\")\n", - "print(f\"Derniers 20 scores: {y_train_scores[-20:]}\")\n", - "\n", - "# Statistiques des scores\n", - "print(f\"\\n=== Statistiques des scores de décision ===\")\n", - "print(f\"Nombre total de scores: {len(y_train_scores)}\")\n", - "print(f\"Score minimum: {y_train_scores.min():.4f}\")\n", - "print(f\"Score maximum: {y_train_scores.max():.4f}\")\n", - "print(f\"Score moyen: {y_train_scores.mean():.4f}\")\n", - "print(f\"Écart-type: {y_train_scores.std():.4f}\")\n", - "\n", - "# Compter les scores positifs et négatifs (seuil par défaut = 0)\n", - "scores_positifs = np.sum(y_train_scores > 0)\n", - "scores_negatifs = np.sum(y_train_scores <= 0)\n", - "\n", - "print(f\"\\n=== Répartition selon le seuil par défaut (0) ===\")\n", - "print(f\"Scores positifs (> 0): {scores_positifs} - Prédits comme '5'\")\n", - "print(f\"Scores négatifs (≤ 0): {scores_negatifs} - Prédits comme 'Non-5'\")\n", - "print(f\"Pourcentage de scores positifs: {(scores_positifs / len(y_train_scores) * 100):.2f}%\")\n", - "print(f\"Pourcentage de scores négatifs: {(scores_negatifs / len(y_train_scores) * 100):.2f}%\")\n", - "\n", - "# Comparaison avec les prédictions binaires précédentes\n", - "predictions_binaires = (y_train_scores > 0)\n", - "coherence = np.array_equal(predictions_binaires, y_train_pred)\n", - "print(f\"\\n=== Vérification de cohérence ===\")\n", - "print(f\"Les prédictions binaires (score > 0) correspondent aux prédictions précédentes: {coherence}\")\n", - "\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(\"Les scores de décision permettent de comprendre la 'confiance' du modèle:\")\n", - "print(\"• Plus le score est élevé, plus le modèle est confiant que c'est un '5'\")\n", - "print(\"• Plus le score est faible (négatif), plus le modèle est confiant que ce n'est PAS un '5'\")\n", - "print(\"• Le seuil par défaut de 0 sépare les deux classes\")" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "0a4ec288", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Calcul des précisions, rappels et seuils avec precision_recall_curve ===\n", - "\n", - "Nombre de seuils calculés: 60000\n", - "Nombre de valeurs de précision: 60001\n", - "Nombre de valeurs de rappel: 60001\n", - "\n", - "=== Analyse des seuils ===\n", - "Seuil minimum: -132535.1477\n", - "Seuil maximum: 32014.4380\n", - "Premiers 10 seuils: [-132535.14773956 -121873.47955627 -118483.73475821 -118296.60869282\n", - " -116277.14187267 -116148.58641667 -114865.02362754 -113825.16819122\n", - " -113408.34216882 -113265.71984976]\n", - "Derniers 10 seuils: [22366.02990715 22438.59825572 22595.72296806 24738.75768502\n", - " 25217.88677776 25793.95761186 26702.46673458 26857.71452807\n", - " 30389.44578544 32014.43798322]\n", - "\n", - "=== Analyse des précisions ===\n", - "Précision minimum: 0.0903\n", - "Précision maximum: 1.0000\n", - "Précision moyenne: 0.2857\n", - "\n", - "=== Analyse des rappels ===\n", - "Rappel minimum: 0.0000\n", - "Rappel maximum: 1.0000\n", - "Rappel moyen: 0.9187\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" + "cell_type": "markdown", + "id": "5c06bdc5", + "metadata": { + "id": "5c06bdc5" + }, + "source": [ + "## 1- Informations sur les données" ] - }, - "metadata": {}, - "output_type": "display_data" }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== Analyse de points clés ===\n", - "Seuil optimal (F1 max): -1605.2029\n", - " - Précision: 0.7945\n", - " - Rappel: 0.7602\n", - " - F1-score: 0.7770\n", - "\n", - "Seuil par défaut (≈ 0):\n", - " - Précision: 0.0903\n", - " - Rappel: 1.0000\n", - " - F1-score: 0.1657\n", - "\n", - "=== Conclusion ===\n", - "La courbe précision-rappel montre le compromis entre précision et rappel:\n", - "• Seuil élevé → Précision élevée, Rappel faible (peu de faux positifs)\n", - "• Seuil faible → Précision faible, Rappel élevé (peu de faux négatifs)\n", - "• Le seuil optimal pour F1 est: -1605.2029\n" - ] - } - ], - "source": [ - "from sklearn.metrics import precision_recall_curve\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "print(\"=== Calcul des précisions, rappels et seuils avec precision_recall_curve ===\\n\")\n", - "\n", - "# Calculer les précisions, rappels et seuils pour chaque instance\n", - "# Utilise les scores de décision calculés précédemment\n", - "precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_train_scores)\n", - "\n", - "print(f\"Nombre de seuils calculés: {len(thresholds)}\")\n", - "print(f\"Nombre de valeurs de précision: {len(precisions)}\")\n", - "print(f\"Nombre de valeurs de rappel: {len(recalls)}\")\n", - "\n", - "print(f\"\\n=== Analyse des seuils ===\")\n", - "print(f\"Seuil minimum: {thresholds.min():.4f}\")\n", - "print(f\"Seuil maximum: {thresholds.max():.4f}\")\n", - "print(f\"Premiers 10 seuils: {thresholds[:10]}\")\n", - "print(f\"Derniers 10 seuils: {thresholds[-10:]}\")\n", - "\n", - "print(f\"\\n=== Analyse des précisions ===\")\n", - "print(f\"Précision minimum: {precisions.min():.4f}\")\n", - "print(f\"Précision maximum: {precisions.max():.4f}\")\n", - "print(f\"Précision moyenne: {precisions.mean():.4f}\")\n", - "\n", - "print(f\"\\n=== Analyse des rappels ===\")\n", - "print(f\"Rappel minimum: {recalls.min():.4f}\")\n", - "print(f\"Rappel maximum: {recalls.max():.4f}\")\n", - "print(f\"Rappel moyen: {recalls.mean():.4f}\")\n", - "\n", - "plt.plot(recalls, precisions, linewidth=2)\n", - "plt.xlabel(\"Rappel\")\n", - "plt.ylabel(\"Précision\")\n", - "plt.title(\"Courbe Précision-Rappel\")\n", - "plt.grid(True, alpha=0.3)\n", - "\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "# Analyse de points spécifiques\n", - "print(f\"\\n=== Analyse de points clés ===\")\n", - "\n", - "# Trouver le seuil optimal (F1-score maximum)\n", - "f1_scores = 2 * (precisions[:-1] * recalls[:-1]) / (precisions[:-1] + recalls[:-1])\n", - "f1_scores = np.nan_to_num(f1_scores) # Remplacer NaN par 0\n", - "optimal_idx = np.argmax(f1_scores)\n", - "optimal_threshold = thresholds[optimal_idx]\n", - "optimal_precision = precisions[optimal_idx]\n", - "optimal_recall = recalls[optimal_idx]\n", - "optimal_f1 = f1_scores[optimal_idx]\n", - "\n", - "print(f\"Seuil optimal (F1 max): {optimal_threshold:.4f}\")\n", - "print(f\" - Précision: {optimal_precision:.4f}\")\n", - "print(f\" - Rappel: {optimal_recall:.4f}\")\n", - "print(f\" - F1-score: {optimal_f1:.4f}\")\n", - "\n", - "# Seuil par défaut (0)\n", - "default_idx = np.where(thresholds <= 0)[0]\n", - "if len(default_idx) > 0:\n", - " default_idx = default_idx[0]\n", - " default_precision = precisions[default_idx]\n", - " default_recall = recalls[default_idx]\n", - " default_f1 = 2 * (default_precision * default_recall) / (default_precision + default_recall)\n", - " \n", - " print(f\"\\nSeuil par défaut (≈ 0):\")\n", - " print(f\" - Précision: {default_precision:.4f}\")\n", - " print(f\" - Rappel: {default_recall:.4f}\")\n", - " print(f\" - F1-score: {default_f1:.4f}\")\n", - "\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(\"La courbe précision-rappel montre le compromis entre précision et rappel:\")\n", - "print(\"• Seuil élevé → Précision élevée, Rappel faible (peu de faux positifs)\")\n", - "print(\"• Seuil faible → Précision faible, Rappel élevé (peu de faux négatifs)\")\n", - "print(f\"• Le seuil optimal pour F1 est: {optimal_threshold:.4f}\")" - ] - }, - { - "cell_type": "markdown", - "id": "da6f1f8b", - "metadata": {}, - "source": [ - "# III- Apprentissage d'un classifieur multi-classes\n" - ] - }, - { - "cell_type": "markdown", - "id": "f4510e3b", - "metadata": {}, - "source": [ - "## 1 - Apprentissage des données" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "7dc960b0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Apprentissage d'un classifieur SGD multi-classes ===\n", - "\n" - ] + "cell_type": "code", + "execution_count": 3, + "id": "70575266", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "70575266", + "outputId": "47418e75-a843-4ec7-94c2-66ddf07096e2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Clés disponibles dans le dataset MNIST:\n", + "['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url']\n" + ] + } + ], + "source": [ + "X = mnist.keys()\n", + "print(\"Clés disponibles dans le dataset MNIST:\")\n", + "print(list(X))" + ] }, { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[18]\u001b[39m\u001b[32m, line 9\u001b[39m\n\u001b[32m 6\u001b[39m sgd_multiclass = SGDClassifier()\n\u001b[32m 8\u001b[39m \u001b[38;5;66;03m# Entraîner le modèle avec toutes les classes (Y_train au lieu de y_train_5)\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m \u001b[43msgd_multiclass\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfit\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX_train\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mY_train\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mEntraînement terminé\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 12\u001b[39m \u001b[38;5;66;03m# Afficher les informations sur le modèle\u001b[39;00m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/base.py:1365\u001b[39m, in \u001b[36m_fit_context..decorator..wrapper\u001b[39m\u001b[34m(estimator, *args, **kwargs)\u001b[39m\n\u001b[32m 1358\u001b[39m estimator._validate_params()\n\u001b[32m 1360\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m config_context(\n\u001b[32m 1361\u001b[39m skip_parameter_validation=(\n\u001b[32m 1362\u001b[39m prefer_skip_nested_validation \u001b[38;5;129;01mor\u001b[39;00m global_skip_validation\n\u001b[32m 1363\u001b[39m )\n\u001b[32m 1364\u001b[39m ):\n\u001b[32m-> \u001b[39m\u001b[32m1365\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfit_method\u001b[49m\u001b[43m(\u001b[49m\u001b[43mestimator\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:920\u001b[39m, in \u001b[36mBaseSGDClassifier.fit\u001b[39m\u001b[34m(self, X, y, coef_init, intercept_init, sample_weight)\u001b[39m\n\u001b[32m 891\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Fit linear model with Stochastic Gradient Descent.\u001b[39;00m\n\u001b[32m 892\u001b[39m \n\u001b[32m 893\u001b[39m \u001b[33;03mParameters\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 916\u001b[39m \u001b[33;03m Returns an instance of self.\u001b[39;00m\n\u001b[32m 917\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 918\u001b[39m \u001b[38;5;28mself\u001b[39m._more_validate_params()\n\u001b[32m--> \u001b[39m\u001b[32m920\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_fit\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 921\u001b[39m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 922\u001b[39m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 923\u001b[39m \u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43malpha\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 924\u001b[39m \u001b[43m \u001b[49m\u001b[43mC\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m1.0\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 925\u001b[39m \u001b[43m \u001b[49m\u001b[43mloss\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mloss\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 926\u001b[39m \u001b[43m \u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 927\u001b[39m \u001b[43m \u001b[49m\u001b[43mcoef_init\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcoef_init\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 928\u001b[39m \u001b[43m \u001b[49m\u001b[43mintercept_init\u001b[49m\u001b[43m=\u001b[49m\u001b[43mintercept_init\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 929\u001b[39m \u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m=\u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 930\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:707\u001b[39m, in \u001b[36mBaseSGDClassifier._fit\u001b[39m\u001b[34m(self, X, y, alpha, C, loss, learning_rate, coef_init, intercept_init, sample_weight)\u001b[39m\n\u001b[32m 704\u001b[39m \u001b[38;5;66;03m# Clear iteration count for multiple call to fit.\u001b[39;00m\n\u001b[32m 705\u001b[39m \u001b[38;5;28mself\u001b[39m.t_ = \u001b[32m1.0\u001b[39m\n\u001b[32m--> \u001b[39m\u001b[32m707\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_partial_fit\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 708\u001b[39m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 709\u001b[39m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 710\u001b[39m \u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 711\u001b[39m \u001b[43m \u001b[49m\u001b[43mC\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 712\u001b[39m \u001b[43m \u001b[49m\u001b[43mloss\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 713\u001b[39m \u001b[43m \u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 714\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmax_iter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 715\u001b[39m \u001b[43m \u001b[49m\u001b[43mclasses\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 716\u001b[39m \u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 717\u001b[39m \u001b[43m \u001b[49m\u001b[43mcoef_init\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 718\u001b[39m \u001b[43m \u001b[49m\u001b[43mintercept_init\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 719\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 721\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[32m 722\u001b[39m \u001b[38;5;28mself\u001b[39m.tol \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 723\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m.tol > -np.inf\n\u001b[32m 724\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m.n_iter_ == \u001b[38;5;28mself\u001b[39m.max_iter\n\u001b[32m 725\u001b[39m ):\n\u001b[32m 726\u001b[39m warnings.warn(\n\u001b[32m 727\u001b[39m (\n\u001b[32m 728\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mMaximum number of iteration reached before \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m (...)\u001b[39m\u001b[32m 732\u001b[39m ConvergenceWarning,\n\u001b[32m 733\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:641\u001b[39m, in \u001b[36mBaseSGDClassifier._partial_fit\u001b[39m\u001b[34m(self, X, y, alpha, C, loss, learning_rate, max_iter, classes, sample_weight, coef_init, intercept_init)\u001b[39m\n\u001b[32m 639\u001b[39m \u001b[38;5;66;03m# delegate to concrete training procedure\u001b[39;00m\n\u001b[32m 640\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m n_classes > \u001b[32m2\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m641\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_fit_multiclass\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 642\u001b[39m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 643\u001b[39m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 644\u001b[39m \u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m=\u001b[49m\u001b[43malpha\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 645\u001b[39m \u001b[43m \u001b[49m\u001b[43mC\u001b[49m\u001b[43m=\u001b[49m\u001b[43mC\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 646\u001b[39m \u001b[43m \u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 647\u001b[39m \u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m=\u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 648\u001b[39m \u001b[43m \u001b[49m\u001b[43mmax_iter\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmax_iter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 649\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 650\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m n_classes == \u001b[32m2\u001b[39m:\n\u001b[32m 651\u001b[39m \u001b[38;5;28mself\u001b[39m._fit_binary(\n\u001b[32m 652\u001b[39m X,\n\u001b[32m 653\u001b[39m y,\n\u001b[32m (...)\u001b[39m\u001b[32m 658\u001b[39m max_iter=max_iter,\n\u001b[32m 659\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:786\u001b[39m, in \u001b[36mBaseSGDClassifier._fit_multiclass\u001b[39m\u001b[34m(self, X, y, alpha, C, learning_rate, sample_weight, max_iter)\u001b[39m\n\u001b[32m 784\u001b[39m random_state = check_random_state(\u001b[38;5;28mself\u001b[39m.random_state)\n\u001b[32m 785\u001b[39m seeds = random_state.randint(MAX_INT, size=\u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m.classes_))\n\u001b[32m--> \u001b[39m\u001b[32m786\u001b[39m result = \u001b[43mParallel\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 787\u001b[39m \u001b[43m \u001b[49m\u001b[43mn_jobs\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mn_jobs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mverbose\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mverbose\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrequire\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43msharedmem\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\n\u001b[32m 788\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 789\u001b[39m \u001b[43m \u001b[49m\u001b[43mdelayed\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfit_binary\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 790\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 791\u001b[39m \u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 792\u001b[39m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 793\u001b[39m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 794\u001b[39m \u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 795\u001b[39m \u001b[43m \u001b[49m\u001b[43mC\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 796\u001b[39m \u001b[43m \u001b[49m\u001b[43mlearning_rate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 797\u001b[39m \u001b[43m \u001b[49m\u001b[43mmax_iter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 798\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_expanded_class_weight\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 799\u001b[39m \u001b[43m \u001b[49m\u001b[32;43m1.0\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 800\u001b[39m \u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 801\u001b[39m \u001b[43m \u001b[49m\u001b[43mvalidation_mask\u001b[49m\u001b[43m=\u001b[49m\u001b[43mvalidation_mask\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 802\u001b[39m \u001b[43m \u001b[49m\u001b[43mrandom_state\u001b[49m\u001b[43m=\u001b[49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 803\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 804\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43menumerate\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mseeds\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 805\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 807\u001b[39m \u001b[38;5;66;03m# take the maximum of n_iter_ over every binary fit\u001b[39;00m\n\u001b[32m 808\u001b[39m n_iter_ = \u001b[32m0.0\u001b[39m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/utils/parallel.py:82\u001b[39m, in \u001b[36mParallel.__call__\u001b[39m\u001b[34m(self, iterable)\u001b[39m\n\u001b[32m 73\u001b[39m warning_filters = warnings.filters\n\u001b[32m 74\u001b[39m iterable_with_config_and_warning_filters = (\n\u001b[32m 75\u001b[39m (\n\u001b[32m 76\u001b[39m _with_config_and_warning_filters(delayed_func, config, warning_filters),\n\u001b[32m (...)\u001b[39m\u001b[32m 80\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m delayed_func, args, kwargs \u001b[38;5;129;01min\u001b[39;00m iterable\n\u001b[32m 81\u001b[39m )\n\u001b[32m---> \u001b[39m\u001b[32m82\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__call__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43miterable_with_config_and_warning_filters\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/joblib/parallel.py:1986\u001b[39m, in \u001b[36mParallel.__call__\u001b[39m\u001b[34m(self, iterable)\u001b[39m\n\u001b[32m 1984\u001b[39m output = \u001b[38;5;28mself\u001b[39m._get_sequential_output(iterable)\n\u001b[32m 1985\u001b[39m \u001b[38;5;28mnext\u001b[39m(output)\n\u001b[32m-> \u001b[39m\u001b[32m1986\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m output \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.return_generator \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43moutput\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1988\u001b[39m \u001b[38;5;66;03m# Let's create an ID that uniquely identifies the current call. If the\u001b[39;00m\n\u001b[32m 1989\u001b[39m \u001b[38;5;66;03m# call is interrupted early and that the same instance is immediately\u001b[39;00m\n\u001b[32m 1990\u001b[39m \u001b[38;5;66;03m# reused, this id will be used to prevent workers that were\u001b[39;00m\n\u001b[32m 1991\u001b[39m \u001b[38;5;66;03m# concurrently finalizing a task from the previous call to run the\u001b[39;00m\n\u001b[32m 1992\u001b[39m \u001b[38;5;66;03m# callback.\u001b[39;00m\n\u001b[32m 1993\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m._lock:\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/joblib/parallel.py:1914\u001b[39m, in \u001b[36mParallel._get_sequential_output\u001b[39m\u001b[34m(self, iterable)\u001b[39m\n\u001b[32m 1912\u001b[39m \u001b[38;5;28mself\u001b[39m.n_dispatched_batches += \u001b[32m1\u001b[39m\n\u001b[32m 1913\u001b[39m \u001b[38;5;28mself\u001b[39m.n_dispatched_tasks += \u001b[32m1\u001b[39m\n\u001b[32m-> \u001b[39m\u001b[32m1914\u001b[39m res = \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1915\u001b[39m \u001b[38;5;28mself\u001b[39m.n_completed_tasks += \u001b[32m1\u001b[39m\n\u001b[32m 1916\u001b[39m \u001b[38;5;28mself\u001b[39m.print_progress()\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/utils/parallel.py:147\u001b[39m, in \u001b[36m_FuncWrapper.__call__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 145\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m config_context(**config), warnings.catch_warnings():\n\u001b[32m 146\u001b[39m warnings.filters = warning_filters\n\u001b[32m--> \u001b[39m\u001b[32m147\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfunction\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[36mFile \u001b[39m\u001b[32m/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:465\u001b[39m, in \u001b[36mfit_binary\u001b[39m\u001b[34m(est, i, X, y, alpha, C, learning_rate, max_iter, pos_weight, neg_weight, sample_weight, validation_mask, random_state)\u001b[39m\n\u001b[32m 462\u001b[39m tol = est.tol \u001b[38;5;28;01mif\u001b[39;00m est.tol \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m -np.inf\n\u001b[32m 464\u001b[39m _plain_sgd = _get_plain_sgd_function(input_dtype=coef.dtype)\n\u001b[32m--> \u001b[39m\u001b[32m465\u001b[39m coef, intercept, average_coef, average_intercept, n_iter_ = \u001b[43m_plain_sgd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 466\u001b[39m \u001b[43m \u001b[49m\u001b[43mcoef\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 467\u001b[39m \u001b[43m \u001b[49m\u001b[43mintercept\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 468\u001b[39m \u001b[43m \u001b[49m\u001b[43maverage_coef\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 469\u001b[39m \u001b[43m \u001b[49m\u001b[43maverage_intercept\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 470\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_loss_function_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 471\u001b[39m \u001b[43m \u001b[49m\u001b[43mpenalty_type\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 472\u001b[39m \u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 473\u001b[39m \u001b[43m \u001b[49m\u001b[43mC\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 474\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_get_l1_ratio\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 475\u001b[39m \u001b[43m \u001b[49m\u001b[43mdataset\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 476\u001b[39m \u001b[43m \u001b[49m\u001b[43mvalidation_mask\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 477\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mearly_stopping\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 478\u001b[39m \u001b[43m \u001b[49m\u001b[43mvalidation_score_cb\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 479\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mn_iter_no_change\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 480\u001b[39m \u001b[43m \u001b[49m\u001b[43mmax_iter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 481\u001b[39m \u001b[43m \u001b[49m\u001b[43mtol\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 482\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mfit_intercept\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 483\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mverbose\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 484\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mshuffle\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 485\u001b[39m \u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[43m \u001b[49m\u001b[43mpos_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 487\u001b[39m \u001b[43m \u001b[49m\u001b[43mneg_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 488\u001b[39m \u001b[43m \u001b[49m\u001b[43mlearning_rate_type\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 489\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43meta0\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 490\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpower_t\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 491\u001b[39m \u001b[43m \u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 492\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mt_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 493\u001b[39m \u001b[43m \u001b[49m\u001b[43mintercept_decay\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 494\u001b[39m \u001b[43m \u001b[49m\u001b[43mest\u001b[49m\u001b[43m.\u001b[49m\u001b[43maverage\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 495\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 497\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m est.average:\n\u001b[32m 498\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(est.classes_) == \u001b[32m2\u001b[39m:\n", - "\u001b[31mKeyboardInterrupt\u001b[39m: " - ] - } - ], - "source": [ - "from sklearn.linear_model import SGDClassifier\n", - "\n", - "print(\"=== Apprentissage d'un classifieur SGD multi-classes ===\\n\")\n", - "\n", - "# Création d'un objet SGDClassifier pour classification multi-classes\n", - "sgd_multiclass = SGDClassifier()\n", - "\n", - "# Entraîner le modèle avec toutes les classes (Y_train au lieu de y_train_5)\n", - "sgd_multiclass.fit(X_train, Y_train)\n", - "print(\"Entraînement terminé\")\n", - "\n", - "# Afficher les informations sur le modèle\n", - "print(f\"\\n=== Informations sur le modèle multi-classes ===\")\n", - "print(f\"Classes apprises: {sgd_multiclass.classes_}\")\n", - "print(f\"Nombre de classes: {len(sgd_multiclass.classes_)}\")\n", - "\n", - "# Test de prédiction sur la première instance\n", - "print(f\"\\n=== Test de prédiction ===\")\n", - "premiere_instance = X.iloc[0:1]\n", - "prediction_multiclass = sgd_multiclass.predict(premiere_instance)\n", - "vraie_classe = Y.iloc[0]\n", - "\n", - "print(f\"Vraie classe de la première instance: {vraie_classe}\")\n", - "print(f\"Prédiction du modèle multi-classes: {prediction_multiclass[0]}\")\n", - "print(f\"Prédiction correcte: {prediction_multiclass[0] == vraie_classe}\")\n", - "\n", - "# Obtenir les scores de décision pour toutes les classes\n", - "scores_decision = sgd_multiclass.decision_function(premiere_instance)\n", - "print(f\"\\nScores de décision pour chaque classe:\")\n", - "for i, (classe, score) in enumerate(zip(sgd_multiclass.classes_, scores_decision[0])):\n", - " marker = \" ←\" if classe == prediction_multiclass[0] else \"\"\n", - " print(f\" Classe {classe}: {score:.4f}{marker}\")\n", - "\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(\"Le modèle SGD a été entraîné avec succès pour classifier les 10 chiffres (0-9)\")\n", - "print(\"Il peut maintenant prédire n'importe quel chiffre manuscrit du dataset MNIST\")" - ] - }, - { - "cell_type": "markdown", - "id": "82287df9", - "metadata": {}, - "source": [ - "## 2- Evaluation du modèle d'apprentissage sur les données d'apprentissage" - ] - }, - { - "cell_type": "markdown", - "id": "3f807144", - "metadata": {}, - "source": [ - "### 2-1- Taux de classification " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f487ebf", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Évaluation du classifieur SGD multi-classes avec validation croisée ===\n", - "\n", - "a. Taux de classification (accuracy) de chaque fold:\n", - " Fold 1: 0.8576 (85.76%)\n", - " Fold 2: 0.8788 (87.88%)\n", - " Fold 3: 0.8800 (88.00%)\n", - "\n", - "b. Moyenne des taux de classification: 0.8721 (87.21%)\n" - ] - } - ], - "source": [ - "from sklearn.model_selection import cross_val_score\n", - "\n", - "print(\"=== Évaluation du classifieur SGD multi-classes avec validation croisée ===\\n\")\n", - "\n", - "# Utiliser cross_val_score avec 3-fold cross-validation sur le modèle multi-classes\n", - "# Évaluer avec toutes les classes (Y_train) au lieu de la classification binaire\n", - "scores_multiclass = cross_val_score(sgd_multiclass, X_train, Y_train, cv=3, scoring='accuracy')\n", - "\n", - "# a. Afficher le taux de classification (accuracy) de chaque fold\n", - "for i, score in enumerate(scores_multiclass, 1):\n", - " print(f\" Fold {i}: {score:.4f} ({score*100:.2f}%)\")\n", - "\n", - "# b. Afficher la moyenne des taux de classification\n", - "moyenne_accuracy = scores_multiclass.mean()\n", - "print(f\"\\nb. Moyenne des taux de classification: {moyenne_accuracy:.4f} ({moyenne_accuracy*100:.2f}%)\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60f56dbb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Amélioration avec StandardScaler ===\n", - "\n", - "Forme des données standardisées: (60000, 784)\n" - ] + "cell_type": "code", + "execution_count": 4, + "id": "eaffc443", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eaffc443", + "outputId": "e92b6f04-69b5-4a62-c784-0548004504d6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a. Variable X créée avec les données\n", + "b. Taille des données X: (70000, 784)\n", + " - Nombre d'échantillons: 70000\n", + " - Nombre de features par échantillon: 784\n", + "\n", + "c. Variable Y créée avec les classes\n", + "d. Taille des labels Y: (70000,)\n", + " - Nombre total de labels: 70000\n", + "\n", + "e. Classes uniques dans la base de données: ['0' '1' '2' '3' '4' '5' '6' '7' '8' '9']\n", + " - Nombre de classes différentes: 10\n", + "\n", + "f. Description détaillée de la base de données:\n", + "**Author**: Yann LeCun, Corinna Cortes, Christopher J.C. Burges \n", + "**Source**: [MNIST Website](http://yann.lecun.com/exdb/mnist/) - Date unknown \n", + "**Please cite**: \n", + "\n", + "The MNIST database of handwritten digits with 784 features, raw data available at: http://yann.lecun.com/exdb/mnist/. It can be split in a training set of the first 60,000 examples, and a test set of 10,000 examples \n", + "\n", + "It is a subset of a larger set available from NIST. The digits have been size-normalized and centered in a fixed-size image. It is a good database for people who want to try learning techniques and pattern recognition methods on real-world data while spending minimal efforts on preprocessing and formatting. The original black and white (bilevel) images from NIST were size normalized to fit in a 20x20 pixel box while preserving their aspect ratio. The resulting images contain grey levels as a result of the anti-aliasing technique used by the normalization algorithm. the images were centered in a 28x28 image by computing the center of mass of the pixels, and translating the image so as to position this point at the center of the 28x28 field. \n", + "\n", + "With some classification methods (particularly template-based methods, such as SVM and K-nearest neighbors), the error rate improves when the digits are centered by bounding box rather than center of mass. If you do this kind of pre-processing, you should report it in your publications. The MNIST database was constructed from NIST's NIST originally designated SD-3 as their training set and SD-1 as their test set. However, SD-3 is much cleaner and easier to recognize than SD-1. The reason for this can be found on the fact that SD-3 was collected among Census Bureau employees, while SD-1 was collected among high-school students. Drawing sensible conclusions from learning experiments requires that the result be independent of the choice of training set and test among the complete set of samples. Therefore it was necessary to build a new database by mixing NIST's datasets. \n", + "\n", + "The MNIST training set is composed of 30,000 patterns from SD-3 and 30,000 patterns from SD-1. Our test set was composed of 5,000 patterns from SD-3 and 5,000 patterns from SD-1. The 60,000 pattern training set contained examples from approximately 250 writers. We made sure that the sets of writers of the training set and test set were disjoint. SD-1 contains 58,527 digit images written by 500 different writers. In contrast to SD-3, where blocks of data from each writer appeared in sequence, the data in SD-1 is scrambled. Writer identities for SD-1 is available and we used this information to unscramble the writers. We then split SD-1 in two: characters written by the first 250 writers went into our new training set. The remaining 250 writers were placed in our test set. Thus we had two sets with nearly 30,000 examples each. The new training set was completed with enough examples from SD-3, starting at pattern # 0, to make a full set of 60,000 training patterns. Similarly, the new test set was completed with SD-3 examples starting at pattern # 35,000 to make a full set with 60,000 test patterns. Only a subset of 10,000 test images (5,000 from SD-1 and 5,000 from SD-3) is available on this site. The full 60,000 sample training set is available.\n", + "\n", + "Downloaded from openml.org.\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# a. Stocker les données dans la variable X\n", + "X = mnist.data\n", + "print(\"a. Variable X créée avec les données\")\n", + "\n", + "# b. Afficher la taille des données (nombre de features et taille de chaque feature)\n", + "print(f\"b. Taille des données X: {X.shape}\")\n", + "print(f\" - Nombre d'échantillons: {X.shape[0]}\")\n", + "print(f\" - Nombre de features par échantillon: {X.shape[1]}\")\n", + "\n", + "# c. Stocker les classes dans la variable Y\n", + "Y = mnist.target\n", + "print(\"\\nc. Variable Y créée avec les classes\")\n", + "\n", + "# d. Afficher la taille des labels/classes\n", + "print(f\"d. Taille des labels Y: {Y.shape}\")\n", + "print(f\" - Nombre total de labels: {Y.shape[0]}\")\n", + "\n", + "# e. Afficher les différentes classes de la base de données\n", + "classes_uniques = np.unique(Y)\n", + "print(f\"\\ne. Classes uniques dans la base de données: {classes_uniques}\")\n", + "print(f\" - Nombre de classes différentes: {len(classes_uniques)}\")\n", + "\n", + "# f. Description détaillée de la base de données\n", + "print(\"\\nf. Description détaillée de la base de données:\")\n", + "print(mnist.DESCR)" + ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:726: ConvergenceWarning: Maximum number of iteration reached before convergence. Consider increasing max_iter to improve the fit.\n", - " warnings.warn(\n", - "/Volumes/SSD/Nextcloud/Documents/ISEN/Cours/Obsidian Vault/ISEN/IA/CIPA4/TP/TP2/.venv/lib/python3.13/site-packages/sklearn/linear_model/_stochastic_gradient.py:726: ConvergenceWarning: Maximum number of iteration reached before convergence. Consider increasing max_iter to improve the fit.\n", - " warnings.warn(\n" - ] + "cell_type": "code", + "execution_count": 5, + "id": "d333dc4d", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 707 + }, + "id": "d333dc4d", + "outputId": "6606173c-65ee-47ca-83fb-9674a7865f84" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Analyse de la première instance de MNIST ===\n", + "\n", + "a. Affichage de la première instance:\n", + " i. Première instance extraite avec X.values[0]\n", + " Forme originale: (784,)\n", + " ii. Image redimensionnée en 28x28: (28, 28)\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAGcCAYAAAA2+rwbAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAHA9JREFUeJzt3QeUXGX5P/CbQgkhECLSi4KIhN4EAekCIgEERUSliBIUCGoQ0XDAAiL1KEURkKJIkRIUC0gEVIo06ULOIYIU6TGEJnX+57n/332YnZ3d7J2EZU0+n3P2ZJmdd25/v+/73vcOgxqNRqMAgKIoBr/TKwDAwCEUAEhCAYAkFABIQgGAJBQASEIBgCQUAEhCoaZrrrmmmGeeeYp99tnnnV4VgFlOKLRx7bXXFoMGDSq+/e1vd/vbZpttVvz85z8vzjjjjOKcc84pBoJNN920XF/630MPPVTu+z333HNAnacwIEKhukCaf+aee+5i6aWXLnbbbbfirrvuKmYHn/rUp4rjjz+++NKXvlTceeedxZwmjmsEEf/73slQqyOCL9YzgrCTcvFz0EEH9fi+b3zjG/m+b7eEbNXomnfeeYt//etfbct/4AMf6NYwq0J733337fb+66+/vvjkJz9ZLLnkkmUdudBCC5WfEfVk1dhsV5/29vOe97ynmBWGFm+D5ZdfvvjsZz9b/v7CCy8Uf/vb34rzzz+/uPTSS4s//elPxYYbblgMZB/84AeL++67r1h44YV7fM9Xv/rVYsEFFyyDbvXVVy/eSdFzeemll97RdYCBbOjQocW5555b/OAHPyh/b/b666+X11C8/vrrr/f4Ga+88kpx6KGHFr/4xS9mal3OPvvs4vOf/3y5vG233bZYYYUVykp98uTJxe9///viL3/5S7HHHnsUI0eOLA4//PBu5b/zne+Udc9XvvKVLq/H+wdsKLzvfe/rlraxM4888shiwoQJtdO+v80333xlas9IHNiBYJlllnmnVwEGtI9+9KPF5ZdfXvz2t78tdtxxxy5/i4r4iSeeKLbffvviN7/5Ta+N3fPOO6/4+te/Xqy22modrUc03saNG1eMGDGiuOGGG4qVV165y99fe+21rB+jkm83NBih0NPf/qfuKRxwwAHlv7fccku3YYjHHnus2H333YvFFlusGDx4cJfQiNQcM2ZM2WqPG7yRqhEwrS3j5vHV2Nkx9h87/t3vfnfx5S9/uXj55ZfL9/3ud78rPvShDxXDhw8vFl100eLggw/u1jrobaz2qaeeKnsJEXyxPqNGjSpPuD//+c/d3hvdufiZNm1asf/++5fDaNE6iJZCJXoau+66a7H44ouX3chll1223FfPPvvsTN1TiGXEa/HvH//4x2KDDTYow+5d73pX2Qpp9/lxEz22ZYkllii3LfbPhz/84eK0007rsl9CbG9z17Xapueee644+uiji0022aT8nNim+DeO75QpU3odFogLbo011iiGDRtW7o8DDzwwj1urOC/i4o51jHWNfbvTTjsV1113XZf3xZcAn3nmmWXvdIEFFij3wTrrrFO+Vscbb7xRblcc9xhGiH+POuqo4s0336w9xFadF30V++CQQw4ptzGWvcoqqxSnn356j++fOHFi8elPf7pcx9jeaFXGcbzkkku6vC+O2Xvf+97y9xiyaD6e1TX473//u2ytrr/++sUiiyxS7utY97im4lpoFcf/sMMOK0aPHl3MP//85T6P9YhzrnXopa/HJvZjVIQhrutOhkvi3IiKtN1xj9di+ObjH/94r59xxBFHlMc7hpo6dc899xTPP/98uR2tgRDmmmuu4iMf+UjxTnpbegq9aa28onKKSjoq16gc//vf/5YnSPjJT35S7LfffuXBjGCIk/LWW28texxRgcVPVDrNbrrppvLi3XrrrYuxY8eW74nPmT59evkZMXa6ww47lMuMgDj22GPLkzdO5BmJSi1O0LgYtttuu2LnnXcupk6dWl6Em2++edmtjDHB1i5n/C2G0aIlEqEQFVmIVskuu+xSBmGsU1z0//jHP4qTTz65uPLKK8ttiZN1ZsQyYjtj2yMYojKNrnJsS3MFWr0n9nWsS1TKTz/9dHnPJLYrZlvFRRgVRFygEV7N49BRmYcYdot9GSd9XGQRvvfff39Z4ccy/v73v5dlW8U2X3HFFeWyY3/F7yeeeGLxzDPPFL/85S+7vPdHP/pRGcwRHrGM6ClFwyK25+KLLy422mijrHQ+85nPlEOX0ZiIYxPny1VXXVXsvffe5b4+7rjj+rQfY/uj8ohKNM7JOE9POOGEsgHydopKKM6bSZMmFauuumq5DXHNxPbHPm7nm9/8ZrmdsR+q4xjnwSc+8Ylyn1YNtDhmEbyxP2MItLkFXVW4cb7E/bMtttiiWG+99cpK6/bbby+vqThH43hG6FT7O667OG+jot9mm23KczvCIJb/uc99Lo99nWNTnWfREIlwqdatznBJhGkEZYTpk08+mddg/B7nZRzfeE9v4tqPRtMf/vCHsl7paf/3Jhpl4Z///GfZ0BgyZEgx4DRmoQcffDD+3wyNrbfeutvfDjvssPJvm222Wb4W/x0/e+21V+P111/v8v577723MXTo0Mbqq6/eeOaZZ7r87aijjirLHXfccfnaNddck5932WWX5euvvvpqY7XVVmsMGjSosfDCCzduvvnm/Nv06dMbiyyySGPUqFHl+1o/6/DDD++y3A022KCx6KKLNiZPntzl9alTpzbe//73l5/z4osv5uvLLrts7o+XXnqpS5nYpgUWWKCx5JJLNh566KEufzv//PPLcvvvv3+jLzbZZJPy/c3OOuus8rXYh9ddd12+Hvt50003Lf9244035us77bRT+dodd9zR7fNb93+8L5bZzrRp0xrPPvtst9evvvrqxuDBgxtf+MIXurwe+zg+b8EFF2zcf//9+Xrsr9inUeaxxx7L12P94rUllliiPN+avfnmm13ee9ppp+X51Xx8X3nllcaYMWPKv916662NGanOhzgXX3jhhXz90UcfLc+p+Nsee+zR530U50X89EV1HLfZZpsu18hdd93VmHvuuduep1OmTOn2Oc8//3xj1VVXLfdz8zlaXbOt61958skny7KtzjnnnLLcEUcc0WWd4rUdd9yx2/v/+9//dvmcusemOk/iWNRRlYtrKj4vfj/mmGPy7/F7vHbbbbfldXd4y/6srq/HH3+8ceedd5bn37rrrlueb5UVV1yx2zVYnTdjx47N16LM2muvXb6+0UYbNU4//fTG3Xff3a3+602U7ev504m3ZfjogQceKIcF4ifG3zbeeOPiu9/9bpnE0cpvFq2DY445plti/vSnPy2HdU466aRM10oM+cSwULQyWkV6R2uzEi2baCHFvoyW8Lrrrpt/i+GlaPFHa//RRx/tdZuidRStwr322qvs1UQLtvqJxI8ZSfE57VqOsX3Rqm0WrfXovcQQRGvLOXpMa621VnHBBRcUMytaYM039mM/R2urdSiv0rqeoXX/9yZajbF/WlXd5WjxthMt1hVXXLHLekTLLlrKt912W5fzIl6Lrnzr8EH0QmOoqrn3ET2VU045pTwPms+5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mJUZ11TUc/E5sZ/ieYl4BiWG1dqFbr/pr+cU2ultLnclnivYddddy3npc801VzkvfK211moccsghjfvuu2+GzxY0z/WOf1u1m//c22fFMwmHHnpoY5VVVmkMGzasMf/88zdWWGGFxm677da49NJLa89Hj7n5e++9d/m+mHe+0EILlfPJx40b1+WZik6fU2i3ze2274ILLmjssssujeWXX74x33zzlfPZY17+0Ucf3W2eeszXjvfGsYg5283LiXnYp556amPllVduzDvvvI3FFlus3L6nnnqq7Xr2Nv98Rtuw3Xbblc+GxH5baqmlGjvvvHPj+uuv7/beCy+8sLHllluW+zbOoXg2JJ7VOP744xtPP/10n/ZxzCOP52OWW265cnnx7/e///3GAw880OM8/4suuqg8lvH+2A8HHHBAuS/rPKcQ4rmCgw8+uFzveeaZpzF69Ohynn9P52k8y7HVVluV2ztixIhyv0+aNKnH/RnP3Wy77baNkSNHls/zNB+PeIbgyCOPLM/xWPYyyyzTGD9+fNvteOSRR8rrcv311y+f/4ntjvfHMzDNz8R0emzOPvvscn/GevR1rn7zcwoz0pfnFNqdFyuttFI+IzWj5xTieY1LLrmksc8++5TXV1xDQ4YMKbc/nls44YQTGi+//HLjnXxOYdD/LQQABu49BQD6n1AAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgDX3rVxiY3njjjdplnnvuuWKgOvnkkzsq99JLL9UuM3ny5NplTjnllNplDjrooNplzj///KIT8847b+0yhxxySO0yhx9+eDEn0lMAIAkFAJJQACAJBQCSUAAgCQUAklAAIAkFAJJQACAJBQCSUAAgCQUAki/Em808/PDDtcu8+uqrtcvccMMNtctc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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " iii. Image affichée en niveau de gris\n", + " Valeurs min/max des pixels: 0.0 à 255.0\n", + "\n", + "b. Analyse de la première instance:\n", + " - Classe de la première instance: 5\n", + " - Type de la classe: \n", + " - Type de l'instance (données): \n", + "\n", + "=== Conclusion ===\n", + "La première image du dataset MNIST représente le chiffre: 5\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib as mpl\n", + "import numpy as np\n", + "\n", + "print(\"=== Analyse de la première instance de MNIST ===\\n\")\n", + "\n", + "# a. Affichage de la première instance de la base de données\n", + "print(\"a. Affichage de la première instance:\")\n", + "\n", + "# i. Utiliser l'attribut \"values\" du dictionnaire X\n", + "premiere_instance = X.values[0]\n", + "print(f\" i. Première instance extraite avec X.values[0]\")\n", + "print(f\" Forme originale: {premiere_instance.shape}\")\n", + "\n", + "# ii. Redimensionner via la fonction \"reshape\" de numpy en taille 28,28\n", + "image_2d = premiere_instance.reshape(28, 28)\n", + "print(f\" ii. Image redimensionnée en 28x28: {image_2d.shape}\")\n", + "\n", + "# iii. Utiliser imshow avec cmap=mpl.cm.binary pour affichage en niveau de gris\n", + "plt.figure()\n", + "plt.imshow(image_2d, cmap=mpl.cm.binary)\n", + "plt.title(\"Première instance du dataset MNIST\", fontsize=14)\n", + "plt.axis('off') # Supprimer les axes pour une meilleure visualisation\n", + "plt.show()\n", + "\n", + "print(f\" iii. Image affichée en niveau de gris\")\n", + "print(f\" Valeurs min/max des pixels: {premiere_instance.min():.1f} à {premiere_instance.max():.1f}\")\n", + "\n", + "# b. Afficher la classe et le type de la première instance\n", + "print(f\"\\nb. Analyse de la première instance:\")\n", + "classe_premiere = Y.iloc[0] # Utiliser iloc pour pandas Series\n", + "print(f\" - Classe de la première instance: {classe_premiere}\")\n", + "print(f\" - Type de la classe: {type(classe_premiere)}\")\n", + "print(f\" - Type de l'instance (données): {type(premiere_instance)}\")\n", + "\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(f\"La première image du dataset MNIST représente le chiffre: {classe_premiere}\")" + ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "c. Évaluation avec données standardisées:\n", - "Fold 1: 0.9013 (90.14%)\n", - "Fold 2: 0.8939 (89.39%)\n", - "Fold 3: 0.9055 (90.55%)\n", - "Moyenne: 0.9002 (90.02%)\n", - "\n", - "=== Comparaison des résultats ===\n", - "Sans standardisation: 0.8721 (87.21%)\n", - "Avec standardisation: 0.9002 (90.02%)\n", - "Amélioration: +0.0281 (+2.81 points de %)\n", - "✅ Les résultats sont MEILLEURS avec la standardisation\n" - ] - } - ], - "source": [ - "from sklearn.preprocessing import StandardScaler\n", - "from sklearn.model_selection import cross_val_score\n", - "\n", - "print(\"=== Amélioration avec StandardScaler ===\\n\")\n", - "\n", - "# a. Instancier un objet de la classe StandardScaler\n", - "scaler = StandardScaler()\n", - "\n", - "# b. Appliquer fit_transform sur les données d'apprentissage\n", - "X_train_scaled = scaler.fit_transform(X_train)\n", - "print(f\"Forme des données standardisées: {X_train_scaled.shape}\")\n", - "\n", - "# c. Évaluer le classifieur SGD avec les données standardisées\n", - "scores_scaled = cross_val_score(sgd_multiclass, X_train_scaled, Y_train, cv=3, scoring='accuracy')\n", - "\n", - "print(\"\\nÉvaluation avec données standardisées:\")\n", - "for i, score in enumerate(scores_scaled, 1):\n", - " print(f\"Fold {i}: {score:.4f} ({score*100:.2f}%)\")\n", - "\n", - "moyenne_scaled = scores_scaled.mean()\n", - "print(f\"Moyenne: {moyenne_scaled:.4f} ({moyenne_scaled*100:.2f}%)\")\n", - "\n", - "# Comparaison avec les résultats précédents\n", - "print(f\"\\n=== Comparaison des résultats ===\")\n", - "print(f\"Sans standardisation: {moyenne_accuracy:.4f} ({moyenne_accuracy*100:.2f}%)\")\n", - "print(f\"Avec standardisation: {moyenne_scaled:.4f} ({moyenne_scaled*100:.2f}%)\")\n", - "\n", - "amelioration = moyenne_scaled - moyenne_accuracy\n", - "print(f\"Amélioration: {amelioration:+.4f} ({amelioration*100:+.2f} points de %)\")\n", - "\n", - "if amelioration > 0:\n", - " print(\"✅ Les résultats sont MEILLEURS avec la standardisation\")\n", - "else:\n", - " print(\"❌ Les résultats ne sont PAS meilleurs avec la standardisation\")" - ] - }, - { - "cell_type": "markdown", - "id": "ab0c9c23", - "metadata": {}, - "source": [ - "### 2-2- Matrice de confusion" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55bf66d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== Matrice de confusion du classifieur SGD multi-classes ===\n", - "\n", - "Prédiction des classes avec cross_val_predict...\n" - ] + "cell_type": "code", + "execution_count": 6, + "id": "c0966e7f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c0966e7f", + "outputId": "da7a65ba-42fe-4a01-a2f7-e19a5023c8c1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Conversion des labels en valeurs numériques ===\n", + "\n", + "Type actuel de Y: \n", + "Type des éléments de Y: \n", + "Premiers labels (avant casting): ['5', '0', '4', '1', '9']\n", + "\n", + "Après casting:\n", + "Type de Y: \n", + "Type des éléments de Y: \n", + "Premiers labels (après casting): [5, 0, 4, 1, 9]\n", + "\n", + "Classe de la première instance (numérique): 5\n", + "Type de la classe: \n", + "\n", + "=== Conclusion ===\n", + "Les labels ont été convertis en valeurs numériques avec succès.\n" + ] + } + ], + "source": [ + "print(\"=== Conversion des labels en valeurs numériques ===\\n\")\n", + "\n", + "# Afficher le type actuel des labels\n", + "print(f\"Type actuel de Y: {type(Y)}\")\n", + "print(f\"Type des éléments de Y: {type(Y.iloc[0])}\")\n", + "print(f\"Premiers labels (avant casting): {list(Y.head())}\")\n", + "\n", + "# Appliquer le casting sur les labels\n", + "Y = Y.astype(np.uint8)\n", + "\n", + "# Vérifier le résultat du casting\n", + "print(f\"\\nAprès casting:\")\n", + "print(f\"Type de Y: {type(Y)}\")\n", + "print(f\"Type des éléments de Y: {type(Y.iloc[0])}\")\n", + "print(f\"Premiers labels (après casting): {list(Y.head())}\")\n", + "\n", + "# Vérifier que la classe de la première instance est maintenant numérique\n", + "classe_premiere_numerique = Y.iloc[0]\n", + "print(f\"\\nClasse de la première instance (numérique): {classe_premiere_numerique}\")\n", + "print(f\"Type de la classe: {type(classe_premiere_numerique)}\")\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(\"Les labels ont été convertis en valeurs numériques avec succès.\")" + ] }, { - "ename": "", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[1;31mLe noyau s’est bloqué lors de l’exécution du code dans une cellule active ou une cellule précédente. \n", - "\u001b[1;31mVeuillez vérifier le code dans la ou les cellules pour identifier une cause possible de l’échec. \n", - "\u001b[1;31mCliquez ici pour plus d’informations. \n", - "\u001b[1;31mPour plus d’informations, consultez Jupyter log." - ] + "cell_type": "markdown", + "id": "05f6129e", + "metadata": { + "id": "05f6129e" + }, + "source": [ + "## 2- Répartition des données" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0560d20", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b0560d20", + "outputId": "60f2b446-fc88-4850-af04-110449e3505b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Répartition des données MNIST ===\n", + "\n", + "Taille totale des données: 70000 échantillons\n", + "Taille totale des labels: 70000 échantillons\n", + "\n", + "=== Résultats de la répartition ===\n", + "Données d'apprentissage:\n", + " - X_train: (60000, 784) échantillons\n", + " - Y_train: (60000,) labels\n", + "\n", + "Données de test:\n", + " - X_test: (10000, 784) échantillons\n", + " - Y_test: (10000,) labels\n", + "\n", + "=== Vérification ===\n", + "Total apprentissage + test: 70000 échantillons\n", + "Cohérent avec le total original: True\n", + "\n", + "Répartition terminée!\n", + "60 000 échantillons pour l'apprentissage\n", + "10000 échantillons pour le test\n" + ] + } + ], + "source": [ + "print(\"=== Répartition des données MNIST ===\\n\")\n", + "\n", + "# Vérification de la taille totale des données\n", + "print(f\"Taille totale des données: {X.shape[0]} échantillons\")\n", + "print(f\"Taille totale des labels: {Y.shape[0]} échantillons\")\n", + "\n", + "# Répartition des données en une seule ligne (indexing sur ndarrays)\n", + "# a. Les 60 000 premières images composeront la base d'apprentissage\n", + "# b. Le reste des images constitue la base de test\n", + "X_train, X_test = X.iloc[:60000], X.iloc[60000:]\n", + "Y_train, Y_test = Y.iloc[:60000], Y.iloc[60000:]\n", + "\n", + "print(f\"\\n=== Résultats de la répartition ===\")\n", + "print(f\"Données d'apprentissage:\")\n", + "print(f\" - X_train: {X_train.shape} échantillons\")\n", + "print(f\" - Y_train: {Y_train.shape} labels\")\n", + "\n", + "print(f\"\\nDonnées de test:\")\n", + "print(f\" - X_test: {X_test.shape} échantillons\")\n", + "print(f\" - Y_test: {Y_test.shape} labels\")\n", + "\n", + "print(f\"\\n=== Vérification ===\")\n", + "print(f\"Total apprentissage + test: {X_train.shape[0] + X_test.shape[0]} échantillons\")\n", + "print(f\"Cohérent avec le total original: {X_train.shape[0] + X_test.shape[0] == X.shape[0]}\")\n", + "\n", + "print(f\"\\nRépartition terminée!\")\n", + "print(f\"60 000 échantillons pour l'apprentissage\")\n", + "print(f\"{X_test.shape[0]} échantillons pour le test\")" + ] + }, + { + "cell_type": "markdown", + "id": "6515e1aa", + "metadata": { + "id": "6515e1aa" + }, + "source": [ + "# II- Apprentissage d'un classifieur binaire" + ] + }, + { + "cell_type": "markdown", + "id": "47d18544", + "metadata": { + "id": "47d18544" + }, + "source": [ + "## 2 - Apprentissage des données" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "bff45d57", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bff45d57", + "outputId": "0eefa7be-9ad8-4215-8361-2451cea28ae8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Résultats de la répartition ===\n", + "\n", + "y_train_5: 0 True\n", + "1 False\n", + "2 False\n", + "3 False\n", + "4 False\n", + " ... \n", + "59995 False\n", + "59996 False\n", + "59997 True\n", + "59998 False\n", + "59999 False\n", + "Name: class, Length: 60000, dtype: bool\n", + "\n", + "y_test_5: 60000 False\n", + "60001 False\n", + "60002 False\n", + "60003 False\n", + "60004 False\n", + " ... \n", + "69995 False\n", + "69996 False\n", + "69997 False\n", + "69998 True\n", + "69999 False\n", + "Name: class, Length: 10000, dtype: bool\n" + ] + } + ], + "source": [ + "y_train_5 = (Y_train == 5)\n", + "y_test_5 = (Y_test == 5)\n", + "\n", + "print(f\"\\n=== Résultats de la répartition ===\")\n", + "print(f\"\\ny_train_5: {y_train_5}\")\n", + "print(f\"\\ny_test_5: {y_test_5}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e02058de", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 80 + }, + "id": "e02058de", + "outputId": "60656543-f39f-4afb-eb25-e27a7ede47f1" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
SGDClassifier()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "SGDClassifier()" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.linear_model import SGDClassifier\n", + "\n", + "# a. Création d'un objet SGDClassifier\n", + "sgd_classifier = SGDClassifier()\n", + "\n", + "# b. Application de la méthode fit avec les données de la question 14 (classification binaire)\n", + "sgd_classifier.fit(X_train, y_train_5)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e73e18a3", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e73e18a3", + "outputId": "6121b55c-787c-42c1-a167-e6e7c9679c79" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Prédiction de la première instance avec le modèle SGD ===\n", + "\n", + "Forme de la première instance: (1, 784)\n", + "\n", + "Prédiction du modèle:\n", + "- Résultat: True\n", + "- Type: \n", + "\n", + "Comparaison avec la réalité:\n", + "- Vraie classe de la première instance: 5\n", + "- Est-ce vraiment un '5'?: True\n", + "- Prédiction du modèle: True\n", + "\n", + "Le modèle prédit: 'C'est un chiffre 5'\n", + "\n", + "=== Conclusion ===\n", + "Prédiction: True\n", + "Exactitude: Correct\n" + ] + } + ], + "source": [ + "print(\"=== Prédiction de la première instance avec le modèle SGD ===\\n\")\n", + "\n", + "# Extraire la première instance (celle de la question 8)\n", + "premiere_instance = X.iloc[0:1] # Utiliser [0:1] pour garder la forme 2D nécessaire\n", + "print(f\"Forme de la première instance: {premiere_instance.shape}\")\n", + "\n", + "# Prédire avec le modèle SGD entraîné\n", + "prediction = sgd_classifier.predict(premiere_instance)\n", + "print(f\"\\nPrédiction du modèle:\")\n", + "print(f\"- Résultat: {prediction[0]}\")\n", + "print(f\"- Type: {type(prediction[0])}\")\n", + "\n", + "# Vérification avec la vraie classe\n", + "vraie_classe = Y.iloc[0]\n", + "est_vraiment_5 = (vraie_classe == 5)\n", + "\n", + "print(f\"\\nComparaison avec la réalité:\")\n", + "print(f\"- Vraie classe de la première instance: {vraie_classe}\")\n", + "print(f\"- Est-ce vraiment un '5'?: {est_vraiment_5}\")\n", + "print(f\"- Prédiction du modèle: {prediction[0]}\")\n", + "\n", + "# Résultat de la prédiction\n", + "if prediction[0]:\n", + " if est_vraiment_5:\n", + " print(f\"\\nLe modèle prédit: 'C'est un chiffre 5'\")\n", + " else :\n", + " print(\"\\nLe modèle prédit: 'C'est un chiffre 5' (ERREUR)\")\n", + "else:\n", + " if est_vraiment_5:\n", + " print(f\"\\nLe modèle prédit: 'Ce n'est PAS un chiffre 5' (ERREUR)\")\n", + " else:\n", + " print(\"\\nLe modèle prédit: 'Ce n'est PAS un chiffre 5'\")\n", + "\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(f\"Prédiction: {'True' if prediction[0] else 'False'}\")\n", + "print(f\"Exactitude: {'Correct' if prediction[0] == est_vraiment_5 else 'Incorrect'}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8aa1ea52", + "metadata": { + "id": "8aa1ea52" + }, + "source": [ + "### 2-1- Taux de classification" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "acaeacd9", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "acaeacd9", + "outputId": "2355029c-3718-4452-edc6-260a54fd184f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Taux de classification (accuracy) pour chaque fold: [0.95335 0.9652 0.9699 ]\n", + "Moyenne du taux de classification: 0.9628\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import cross_val_score\n", + "\n", + "# Opter pour la valeur accuracy pour l'argument scoring\n", + "# a. Afficher le taux de classification (accuracy) de chaque fold\n", + "scores = cross_val_score(sgd_classifier, X_train, y_train_5, cv=3, scoring='accuracy')\n", + "print(f\"Taux de classification (accuracy) pour chaque fold: {scores}\")\n", + "# b. Afficher la moyenne du taux de classification\n", + "print(f\"Moyenne du taux de classification: {scores.mean():.4f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e5949779", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e5949779", + "outputId": "a3c4632f-4bbb-41df-92d9-20910e94d0d9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Taux de classification (accuracy) pour chaque fold avec Never5Classifier: [0.91125 0.90855 0.90915]\n", + "Moyenne du taux de classification avec Never5Classifier: 0.9096\n" + ] + } + ], + "source": [ + "from sklearn.base import BaseEstimator\n", + "\n", + "class Never5Classifier(BaseEstimator):\n", + " def fit(self, data, labels):\n", + " pass\n", + "\n", + " def predict(self, data):\n", + " '''Prend en argument les données et retourne une\n", + "structure de données ayant la taille des données et qui contient que la\n", + "valeur False (= non-5)'''\n", + " return np.zeros((data.shape[0],), dtype=bool)\n", + "\n", + "# c. Créez un objet de la classe Never5Classifier\n", + "never_5_classifier = Never5Classifier()\n", + "\n", + "# d. Testez le classifieur Never5Classifier en utilisant une validation croisée de type 3-fold cross-validation.\n", + "# i. Afficher le taux de classification (accuracy) de chaque fold\n", + "scores_never5 = cross_val_score(never_5_classifier, X_train, y_train_5, cv=3, scoring='accuracy')\n", + "print(f\"\\nTaux de classification (accuracy) pour chaque fold avec Never5Classifier: {scores_never5}\")\n", + "# ii. Afficher la moyenne du taux de classification\n", + "print(f\"Moyenne du taux de classification avec Never5Classifier: {scores_never5.mean():.4f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "776962ff", + "metadata": { + "id": "776962ff" + }, + "source": [ + "### 2-2- Matrice de confusion" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "5733c7b1", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5733c7b1", + "outputId": "a32a7620-14e0-42ad-8438-1616ba491cd2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Évaluation du classifieur SGD avec cross_val_predict ===\n", + "\n", + "Type des prédictions: \n", + "Forme des prédictions: (60000,)\n", + "Type des éléments: \n", + "\n", + "Premières 20 prédictions: [ True False False False False False False False False False False True\n", + " False False False False False False False False]\n", + "Dernières 20 prédictions: [ True False False False False False False False False False False False\n", + " False True False False False True False False]\n", + "\n", + "=== Statistiques des prédictions ===\n", + "Nombre total de prédictions: 60000\n", + "Prédictions 'True' (chiffre 5): 5867\n", + "Prédictions 'False' (non-5): 54133\n", + "Pourcentage de 'True': 9.78%\n", + "Pourcentage de 'False': 90.22%\n", + "\n", + "=== Comparaison avec la réalité ===\n", + "Vraies instances de '5' dans l'entraînement: 5421\n", + "Vraies instances de 'non-5' dans l'entraînement: 54579\n", + "\n", + "=== Résultats de cross_val_predict ===\n", + "Les prédictions ont été obtenues pour tous les échantillons d'entraînement\n", + "Chaque échantillon a été prédit exactement une fois lors de la validation croisée\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import cross_val_predict\n", + "\n", + "print(\"=== Évaluation du classifieur SGD avec cross_val_predict ===\\n\")\n", + "\n", + "# Utiliser cross_val_predict pour obtenir les classes prédites\n", + "# Cette fonction retourne les prédictions pour chaque échantillon lors de la validation croisée 3-fold\n", + "y_train_pred = cross_val_predict(sgd_classifier, X_train, y_train_5, cv=3)\n", + "\n", + "print(f\"Type des prédictions: {type(y_train_pred)}\")\n", + "print(f\"Forme des prédictions: {y_train_pred.shape}\")\n", + "print(f\"Type des éléments: {type(y_train_pred[0])}\")\n", + "\n", + "# Afficher quelques exemples de prédictions\n", + "print(f\"\\nPremières 20 prédictions: {y_train_pred[:20]}\")\n", + "print(f\"Dernières 20 prédictions: {y_train_pred[-20:]}\")\n", + "\n", + "# Compter les prédictions True et False\n", + "nb_true = np.sum(y_train_pred)\n", + "nb_false = len(y_train_pred) - nb_true\n", + "\n", + "print(f\"\\n=== Statistiques des prédictions ===\")\n", + "print(f\"Nombre total de prédictions: {len(y_train_pred)}\")\n", + "print(f\"Prédictions 'True' (chiffre 5): {nb_true}\")\n", + "print(f\"Prédictions 'False' (non-5): {nb_false}\")\n", + "print(f\"Pourcentage de 'True': {(nb_true / len(y_train_pred) * 100):.2f}%\")\n", + "print(f\"Pourcentage de 'False': {(nb_false / len(y_train_pred) * 100):.2f}%\")\n", + "\n", + "# Comparer avec les vraies valeurs\n", + "nb_vraies_5 = np.sum(y_train_5)\n", + "print(f\"\\n=== Comparaison avec la réalité ===\")\n", + "print(f\"Vraies instances de '5' dans l'entraînement: {nb_vraies_5}\")\n", + "print(f\"Vraies instances de 'non-5' dans l'entraînement: {len(y_train_5) - nb_vraies_5}\")\n", + "\n", + "print(f\"\\n=== Résultats de cross_val_predict ===\")\n", + "print(\"Les prédictions ont été obtenues pour tous les échantillons d'entraînement\")\n", + "print(\"Chaque échantillon a été prédit exactement une fois lors de la validation croisée\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "970fce9c", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 906 + }, + "id": "970fce9c", + "outputId": "f2f89ef9-d9f4-4330-abb9-a4b404f4ba7d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Matrice de confusion du classifieur SGD ===\n", + "\n", + "Matrice de confusion (valeurs absolues):\n", + "[[53265 1314]\n", + " [ 868 4553]]\n", + "\n", + "Matrice de confusion normalisée:\n", + "[[0.97592481 0.02407519]\n", + " [0.16011806 0.83988194]]\n" + ] + }, + { + "data": { + "image/png": "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+3qh1u27HhpIADcbvVB7nue5c+dO6yIBABJBbASkPmIj+5HUgN8IDAw0F92JEyfKP//8k2D+unXrpEOHDtKpUyfZsmWLqS726quvyqxZs7yW04t57dq1ZcOGDdK7d2954oknZMeOHVfcfp8+fcyXWN26deX9998Xp9OZovsH+INdu3ZJwYIFpWTJktK1a1c5cOBAWhcJAJAIYiMg9REb2Y+kBvxKu3btpHr16jJ48OAE88aNGyd33nmnuViXLVvWtDXt27evjBkzxmu5Vq1amQt26dKl5YUXXjAX42XLliW53ddee03mzp0rP/74o7Rv3968XgMI4GZSr149E+guXLhQpk6dKnv37pVbb71Vzp07l9ZFAwAkgtgISD3ERjcHGgzB72jb0SZNmkj//v29pm/fvl3uvfder2kNGzaU8ePHS0xMjLmboapWreqer539aDWyY8eOmectW7aU3377zfxdrFgx2bp1q/lbgwGXGjVqyIULF0xA8NRTT6XingI3lp7/Lvo50Qu5fg40aO3Ro0ealg0AkDhiIyB1EBvdHKipAb/TuHFjad68uQwcOPCaXh8cHOz1XC/esbGx5u8ZM2bIxo0bzeP7779PdB36habVPCMiIq6pDIANtK203tnbvXt3WhcFAJAEYiPgxiA2shM1NeCXdPgyrWpZrlw597QKFSrI8uXLvZbT5/rF47oTcSWFChVK1nJ6YddexrXTLeBmdf78edmzZ4889NBDaV0UAMAVEBsBqY/YyE4kNeCXqlSpYjrqmTBhgntav379pE6dOjJs2DDp2LGjrFy5UiZNmiRTpky5rm198803cvToUalfv77p1VvbjmqnXPGreAK203O6devWplrloUOHTPtsDXo7d+6c1kUDAFwBsRGQ8oiNbg4kNeC3tIOqOXPmuJ/XrFnTtG8bNGiQuXgXKFDALKOdYl0PrZI5efJkefbZZ02v3tqJlna81bNnzxTYC8B/aLVhvUifPHlS8uTJI40aNZJVq1aZvwEA/o/YCEhZxEY3B4eTsZkAAAAAAICF6CgUAAAAAABYiaQGAAAAAACwEkkNAAAAAABgJZIaAAAAAADASiQ1AAAAAACAlUhqAAAAAAAAK5HUAAAAAAAAViKpAcAqp0+flqFDh8rhw4fTuigAAABpjtgI6R1JDeAm4nA4ZMGCBXKzcjqd0r17d7l48aIU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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Interprétation de la matrice de confusion ===\n", + "\n", + "Valeurs de la matrice (absolues):\n", + "• Vrais Négatifs (TN): 53265 - Correctement classés comme 'Non-5'\n", + "• Faux Positifs (FP): 1314 - Incorrectement classés comme '5' (Erreur Type I)\n", + "• Faux Négatifs (FN): 868 - Incorrectement classés comme 'Non-5' (Erreur Type II)\n", + "• Vrais Positifs (TP): 4553 - Correctement classés comme '5'\n", + "\n", + "Pourcentages (normalisés):\n", + "• Spécificité (TN rate): 0.976 - 97.6% des 'Non-5' bien identifiés\n", + "• Taux FP (FP rate): 0.024 - 2.4% des 'Non-5' mal classés comme '5'\n", + "• Taux FN (FN rate): 0.160 - 16.0% des '5' mal classés comme 'Non-5'\n", + "• Sensibilité (TP rate): 0.840 - 84.0% des '5' bien identifiés\n" + ] + } + ], + "source": [ + "from sklearn.metrics import confusion_matrix\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "print(\"=== Matrice de confusion du classifieur SGD ===\\n\")\n", + "\n", + "# Calculer la matrice de confusion normale\n", + "cm = confusion_matrix(y_train_5, y_train_pred)\n", + "print(\"Matrice de confusion (valeurs absolues):\")\n", + "print(cm)\n", + "\n", + "# Calculer la matrice de confusion normalisée\n", + "cm_normalized = confusion_matrix(y_train_5, y_train_pred, normalize='true')\n", + "print(\"\\nMatrice de confusion normalisée:\")\n", + "print(cm_normalized)\n", + "\n", + "# Visualisation des matrices de confusion avec matplotlib uniquement\n", + "fig, axes = plt.subplots(1, 2, figsize=(12, 5))\n", + "\n", + "# Matrice de confusion absolue\n", + "im1 = axes[0].imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)\n", + "axes[0].set_title('Matrice de confusion (valeurs absolues)')\n", + "axes[0].set_xlabel('Classe prédite')\n", + "axes[0].set_ylabel('Classe réelle')\n", + "\n", + "# Ajouter les annotations manuellement\n", + "for i in range(cm.shape[0]):\n", + " for j in range(cm.shape[1]):\n", + " axes[0].text(j, i, format(cm[i, j], 'd'),\n", + " ha=\"center\", va=\"center\", color=\"black\")\n", + "\n", + "# Ajouter les labels des axes\n", + "axes[0].set_xticks([0, 1])\n", + "axes[0].set_yticks([0, 1])\n", + "axes[0].set_xticklabels(['Non-5', '5'])\n", + "axes[0].set_yticklabels(['Non-5', '5'])\n", + "\n", + "# Matrice de confusion normalisée\n", + "im2 = axes[1].imshow(cm_normalized, interpolation='nearest', cmap=plt.cm.Blues)\n", + "axes[1].set_title('Matrice de confusion normalisée')\n", + "axes[1].set_xlabel('Classe prédite')\n", + "axes[1].set_ylabel('Classe réelle')\n", + "\n", + "# Ajouter les annotations pour la matrice normalisée\n", + "for i in range(cm_normalized.shape[0]):\n", + " for j in range(cm_normalized.shape[1]):\n", + " axes[1].text(j, i, format(cm_normalized[i, j], '.3f'),\n", + " ha=\"center\", va=\"center\", color=\"black\")\n", + "\n", + "# Ajouter les labels des axes\n", + "axes[1].set_xticks([0, 1])\n", + "axes[1].set_yticks([0, 1])\n", + "axes[1].set_xticklabels(['Non-5', '5'])\n", + "axes[1].set_yticklabels(['Non-5', '5'])\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Interprétation détaillée de la matrice de confusion\n", + "print(\"\\n=== Interprétation de la matrice de confusion ===\")\n", + "tn, fp, fn, tp = cm.ravel()\n", + "\n", + "print(f\"\\nValeurs de la matrice (absolues):\")\n", + "print(f\"• Vrais Négatifs (TN): {tn} - Correctement classés comme 'Non-5'\")\n", + "print(f\"• Faux Positifs (FP): {fp} - Incorrectement classés comme '5' (Erreur Type I)\")\n", + "print(f\"• Faux Négatifs (FN): {fn} - Incorrectement classés comme 'Non-5' (Erreur Type II)\")\n", + "print(f\"• Vrais Positifs (TP): {tp} - Correctement classés comme '5'\")\n", + "\n", + "print(f\"\\nPourcentages (normalisés):\")\n", + "print(f\"• Spécificité (TN rate): {cm_normalized[0,0]:.3f} - {cm_normalized[0,0]*100:.1f}% des 'Non-5' bien identifiés\")\n", + "print(f\"• Taux FP (FP rate): {cm_normalized[0,1]:.3f} - {cm_normalized[0,1]*100:.1f}% des 'Non-5' mal classés comme '5'\")\n", + "print(f\"• Taux FN (FN rate): {cm_normalized[1,0]:.3f} - {cm_normalized[1,0]*100:.1f}% des '5' mal classés comme 'Non-5'\")\n", + "print(f\"• Sensibilité (TP rate): {cm_normalized[1,1]:.3f} - {cm_normalized[1,1]*100:.1f}% des '5' bien identifiés\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b1b979f7", + "metadata": { + "id": "b1b979f7" + }, + "source": [ + "### 2-3- Précision et rappel" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "b4a6c352", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b4a6c352", + "outputId": "ff327828-d729-4313-8124-a58892527c26" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Calcul des métriques de performance avec sklearn.metrics ===\n", + "\n", + "Précision (Precision): 0.7760\n", + "Rappel (Recall): 0.8399\n", + "Score F1: 0.8067\n", + "\n", + "=== Interprétation des résultats ===\n", + "\n", + "1. PRÉCISION = 0.7760 (77.60%)\n", + " • Sur toutes les images que le modèle a classées comme '5',\n", + " 77.60% sont réellement des '5'\n", + " • 22.40% sont des faux positifs (erreurs)\n", + "\n", + "2. RAPPEL = 0.8399 (83.99%)\n", + " • Sur toutes les vraies images de '5' dans la base,\n", + " le modèle en a détecté 83.99%\n", + " • Il a manqué 16.01% des vrais '5' (faux négatifs)\n", + "\n", + "3. SCORE F1 = 0.8067 (80.67%)\n", + " • Moyenne harmonique entre précision et rappel\n", + " • Mesure équilibrée des performances globales\n", + "\n", + "=== Analyse comparative ===\n", + "✓ Rappel > Précision : Le modèle est plutôt libéral\n", + " → Il détecte bien les '5' mais fait quelques erreurs en trop\n", + "\n", + "🎯 CONCLUSION: Performances EXCELLENTES (F1 ≥ 80%)\n" + ] + } + ], + "source": [ + "from sklearn.metrics import precision_score, recall_score, f1_score\n", + "\n", + "print(\"=== Calcul des métriques de performance avec sklearn.metrics ===\\n\")\n", + "\n", + "# Calculer la précision\n", + "precision = precision_score(y_train_5, y_train_pred)\n", + "print(f\"Précision (Precision): {precision:.4f}\")\n", + "\n", + "# Calculer le rappel\n", + "recall = recall_score(y_train_5, y_train_pred)\n", + "print(f\"Rappel (Recall): {recall:.4f}\")\n", + "\n", + "# Calculer le score F1\n", + "f1 = f1_score(y_train_5, y_train_pred)\n", + "print(f\"Score F1: {f1:.4f}\")\n", + "\n", + "print(f\"\\n=== Interprétation des résultats ===\")\n", + "\n", + "print(f\"\\n1. PRÉCISION = {precision:.4f} ({precision*100:.2f}%)\")\n", + "print(f\" • Sur toutes les images que le modèle a classées comme '5',\")\n", + "print(f\" {precision*100:.2f}% sont réellement des '5'\")\n", + "print(f\" • {(1-precision)*100:.2f}% sont des faux positifs (erreurs)\")\n", + "\n", + "print(f\"\\n2. RAPPEL = {recall:.4f} ({recall*100:.2f}%)\")\n", + "print(f\" • Sur toutes les vraies images de '5' dans la base,\")\n", + "print(f\" le modèle en a détecté {recall*100:.2f}%\")\n", + "print(f\" • Il a manqué {(1-recall)*100:.2f}% des vrais '5' (faux négatifs)\")\n", + "\n", + "print(f\"\\n3. SCORE F1 = {f1:.4f} ({f1*100:.2f}%)\")\n", + "print(f\" • Moyenne harmonique entre précision et rappel\")\n", + "print(f\" • Mesure équilibrée des performances globales\")\n", + "\n", + "print(f\"\\n=== Analyse comparative ===\")\n", + "if precision > recall:\n", + " print(f\"✓ Précision > Rappel : Le modèle est plutôt conservateur\")\n", + " print(f\" → Il évite les faux positifs mais manque quelques vrais '5'\")\n", + "elif recall > precision:\n", + " print(f\"✓ Rappel > Précision : Le modèle est plutôt libéral\")\n", + " print(f\" → Il détecte bien les '5' mais fait quelques erreurs en trop\")\n", + "else:\n", + " print(f\"✓ Précision ≈ Rappel : Modèle équilibré\")\n", + "\n", + "# Évaluation globale\n", + "if f1 >= 0.8:\n", + " print(f\"\\n🎯 CONCLUSION: Performances EXCELLENTES (F1 ≥ 80%)\")\n", + "elif f1 >= 0.6:\n", + " print(f\"\\n✅ CONCLUSION: Performances BONNES (F1 ≥ 60%)\")\n", + "elif f1 >= 0.4:\n", + " print(f\"\\n⚠️ CONCLUSION: Performances MOYENNES (F1 ≥ 40%)\")\n", + "else:\n", + " print(f\"\\n❌ CONCLUSION: Performances FAIBLES (F1 < 40%)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "1ad09da3", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1ad09da3", + "outputId": "c8bf023d-152f-4184-c389-6a73970896ab" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Calcul des scores de décision avec cross_val_predict ===\n", + "\n", + "Type des scores: \n", + "Forme des scores: (60000,)\n", + "Type des éléments: \n", + "\n", + "Premiers 20 scores: [ -4623.91768182 -29346.42102807 -34236.70184275 -16138.71839177\n", + " -21171.63889063 -22009.80344991 -14964.09692292 -26696.68263781\n", + " -6819.04675803 -16964.3306227 -22432.720243 4412.72876249\n", + " -32333.85710508 -20597.06268605 -8276.30040315 -17367.45521284\n", + " -29269.23384584 -19067.33952242 -4755.35724385 -22693.61472589]\n", + "Derniers 20 scores: [ -6147.65113121 -15312.77185557 -9620.37637257 -26420.40769421\n", + " -14148.43527715 -26976.56161717 -3968.54435326 -16082.07592535\n", + " -13972.69019004 -8555.29681656 -15571.86317708 -9900.93296428\n", + " -4133.63759523 6095.01731827 -5444.55339566 -12910.74832263\n", + " -10589.39874089 6317.74072977 -9602.73377933 -9875.91846006]\n", + "\n", + "=== Statistiques des scores de décision ===\n", + "Nombre total de scores: 60000\n", + "Score minimum: -133315.7300\n", + "Score maximum: 36765.4381\n", + "Score moyen: -16088.3185\n", + "Écart-type: 13106.2289\n", + "\n", + "=== Répartition selon le seuil par défaut (0) ===\n", + "Scores positifs (> 0): 4508 - Prédits comme '5'\n", + "Scores négatifs (≤ 0): 55492 - Prédits comme 'Non-5'\n", + "Pourcentage de scores positifs: 7.51%\n", + "Pourcentage de scores négatifs: 92.49%\n", + "\n", + "=== Vérification de cohérence ===\n", + "Les prédictions binaires (score > 0) correspondent aux prédictions précédentes: False\n", + "\n", + "=== Conclusion ===\n", + "Les scores de décision permettent de comprendre la 'confiance' du modèle:\n", + "• Plus le score est élevé, plus le modèle est confiant que c'est un '5'\n", + "• Plus le score est faible (négatif), plus le modèle est confiant que ce n'est PAS un '5'\n", + "• Le seuil par défaut de 0 sépare les deux classes\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import cross_val_predict\n", + "import numpy as np\n", + "\n", + "print(\"=== Calcul des scores de décision avec cross_val_predict ===\\n\")\n", + "\n", + "# Utiliser cross_val_predict avec method='decision_function' pour obtenir les scores\n", + "# Cette fonction retourne les scores de décision pour chaque échantillon lors de la validation croisée 3-fold\n", + "y_train_scores = cross_val_predict(sgd_classifier, X_train, y_train_5, cv=3, method='decision_function')\n", + "\n", + "print(f\"Type des scores: {type(y_train_scores)}\")\n", + "print(f\"Forme des scores: {y_train_scores.shape}\")\n", + "print(f\"Type des éléments: {type(y_train_scores[0])}\")\n", + "\n", + "# Afficher quelques exemples de scores\n", + "print(f\"\\nPremiers 20 scores: {y_train_scores[:20]}\")\n", + "print(f\"Derniers 20 scores: {y_train_scores[-20:]}\")\n", + "\n", + "# Statistiques des scores\n", + "print(f\"\\n=== Statistiques des scores de décision ===\")\n", + "print(f\"Nombre total de scores: {len(y_train_scores)}\")\n", + "print(f\"Score minimum: {y_train_scores.min():.4f}\")\n", + "print(f\"Score maximum: {y_train_scores.max():.4f}\")\n", + "print(f\"Score moyen: {y_train_scores.mean():.4f}\")\n", + "print(f\"Écart-type: {y_train_scores.std():.4f}\")\n", + "\n", + "# Compter les scores positifs et négatifs (seuil par défaut = 0)\n", + "scores_positifs = np.sum(y_train_scores > 0)\n", + "scores_negatifs = np.sum(y_train_scores <= 0)\n", + "\n", + "print(f\"\\n=== Répartition selon le seuil par défaut (0) ===\")\n", + "print(f\"Scores positifs (> 0): {scores_positifs} - Prédits comme '5'\")\n", + "print(f\"Scores négatifs (≤ 0): {scores_negatifs} - Prédits comme 'Non-5'\")\n", + "print(f\"Pourcentage de scores positifs: {(scores_positifs / len(y_train_scores) * 100):.2f}%\")\n", + "print(f\"Pourcentage de scores négatifs: {(scores_negatifs / len(y_train_scores) * 100):.2f}%\")\n", + "\n", + "# Comparaison avec les prédictions binaires précédentes\n", + "predictions_binaires = (y_train_scores > 0)\n", + "coherence = np.array_equal(predictions_binaires, y_train_pred)\n", + "print(f\"\\n=== Vérification de cohérence ===\")\n", + "print(f\"Les prédictions binaires (score > 0) correspondent aux prédictions précédentes: {coherence}\")\n", + "\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(\"Les scores de décision permettent de comprendre la 'confiance' du modèle:\")\n", + "print(\"• Plus le score est élevé, plus le modèle est confiant que c'est un '5'\")\n", + "print(\"• Plus le score est faible (négatif), plus le modèle est confiant que ce n'est PAS un '5'\")\n", + "print(\"• Le seuil par défaut de 0 sépare les deux classes\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "0a4ec288", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "0a4ec288", + "outputId": "3d6bcd2a-eece-43b3-9b76-75d08cd363e5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Calcul des précisions, rappels et seuils avec precision_recall_curve ===\n", + "\n", + "Nombre de seuils calculés: 60000\n", + "Nombre de valeurs de précision: 60001\n", + "Nombre de valeurs de rappel: 60001\n", + "\n", + "=== Analyse des seuils ===\n", + "Seuil minimum: -133315.7300\n", + "Seuil maximum: 36765.4381\n", + "Premiers 10 seuils: [-133315.73000991 -128627.16177203 -120211.44750976 -117647.94028586\n", + " -117646.74175979 -117013.48576985 -114717.78599486 -114508.12335627\n", + " -114065.45351097 -113581.47813317]\n", + "Derniers 10 seuils: [28353.20710671 28483.68062929 28531.95014512 28559.86367441\n", + " 28687.45932799 28835.83098215 28877.6800515 31516.36497999\n", + " 36066.23752443 36765.43808511]\n", + "\n", + "=== Analyse des précisions ===\n", + "Précision minimum: 0.0903\n", + "Précision maximum: 1.0000\n", + "Précision moyenne: 0.2834\n", + "\n", + "=== Analyse des rappels ===\n", + "Rappel minimum: 0.0000\n", + "Rappel maximum: 1.0000\n", + "Rappel moyen: 0.9155\n" + ] + }, + { + "data": { + "image/png": "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pcvA5KazaN0BV7tQdTyjzu7MvVxL7nLnlLv82mLYpEkT8zj3MjNu2kKmAWbSpEmWc7S1T8e+5ayrgtLgqoHIvZ0t2Gn3vM4W1pZGbXHM2TKY8/vV7ldtgczPnj17zDjHnONCP/jgAzOTOHerm/585ZyBrLNv9fN3/5zorF9tpR47dqzlvJxdzQBosQP8mgYwXUNNl5TQVricd574+eefzcQA97pzTZs2lVtuucX8AdWwogPely5daroDdVmJnK0lGhR1ML4uB6KTClavXm3+wOtaaoWl68hdeeWVZkyZtizqenV9+/Y15XF/D08//bRZPkTHU7nv06rXaSjQsV+6Jpu36OegAUYDmwYYDXA6rk9bkPTz02CsEwg0VGh3o3422q2p34O2bOpno2PNTtetqufr8ija8qbj+7Q7VMOthpucLac56fvrch667IqWTycMaHDXsmhLl69ud6ZrDuo/BHSJFl2LTz8bd+ujfmbaqqatehpC82vN1RbYO+64w9y9Qsc9TpkyxXwf+QVB/ZnVbl4N2toqqYsi6zIm+rOj9PPXsHfzzTeb5Xp0gox2k+/YscNMJtHg+vrrr/vkcwEcze5puQDO3caNG81yD9WrV3dFRkaaZTnat2/veu2111ypqame8zIyMsyyFjVq1HBFRES4EhISXMOHD7eco7KyslyPPvqoq2zZsq7Y2FiznMXmzZtPu9zJb7/9lqdM7iUn1q5d67ruuutMmUqVKuUaNGhQnuVClC6LocumFCtWzGz169d33Xfffa4NGzac8Xs/Uxly0nLr657OW2+9ZZbc0CVStKyNGzd2PfLII649e/ZYzps+fbqrXbt25jxdxqR169aujz/++LTLnXz22WeuHj16mOVLtG4uuOAC19133+3au3fvaZc7cZs2bZpZGiQqKspVunRpV79+/TzLt5zt+yrokh/u5U5eeOGFfJ+/9dZbzXI0Wv/u779Jkyau6Oho8/P23HPPuaZMmWJeQ1/LTT+D3r17u2bPnm3O1+9B6zT38inu+vvhhx9cd911l/kZKV68uPleDx8+nKc8+hnpz6MucaJlqFWrlinjsmXLCv29A4EoRP9jd7gEAAQWbVnU1uNvv/32rAsxa8uktuqdbfILgLNjjB0AAECAINgBAAAECIIdAABAgGCMHQAAQICgxQ4AACBAEOwAAAACBMEOAAAgQATdnSf0Po16mxtd2f5cboYOAADgCzodIjk5WSpXrmxuS3gmQRfsNNQlJCTYXQwAAIBC2blzp7k14ZkEXbDTljr3h6P3HvRmy6DelFrvZXi2dA3foE6ciXpxHurEmaiX4K2XpKQk0yjlzjBnEnTBzt39qqHO28EuNTXVvAe/gM5AnTgT9eI81IkzUS/OlO3DeinIEDJ+MgAAAAIEwQ4AACBAEOwAAAACBMEOAAAgQBDsAAAAAgTBDgAAIEAQ7AAAAAIEwQ4AACBAEOwAAAACBMEOAAAgQNga7H788Ue54oorpHLlyuY2GV999dVZr1m4cKG0aNFCoqKipHbt2vLee+/5pKwAAABOZ2uwS0lJkaZNm8rEiRMLdP7WrVuld+/e0rlzZ1m1apU88MADcuedd8rs2bO9XlYAAACnC7fzzS+//HKzFdSkSZOkRo0a8uKLL5r9Bg0ayKJFi+Tll1+Wnj17ilP8lZIuqemZkpGZbXdRAABAELE12BXWkiVLpFu3bpZjGui05e500tLSzOaWlJRkvmZnZ5vNG4ZMWyk/bDwkJaLC5P/uiJYmVUt65X1QOFrfLpfLa/WOoqFenIc6cSbqJXjrJbsQr+1XwW7fvn1SoUIFyzHd17B28uRJiYmJyXPNuHHjZMyYMXmOHzx4UFJTU71Szs37/w6PyWlZ8uVviVIxsqpX3geF/8U4duyY+QUMDWXekFNQL85DnTgT9RK89ZKcnByYwa4ohg8fLkOHDvXsawhMSEiQcuXKSVxcnFfes0a5ErL72GHzOCo6VsqXL++V90Hhf/l0ko7WPf9TdA7qxXmoE2eiXpzJF/USHR0dmMGuYsWKsn//fssx3deAll9rndLZs7rlph++typgUJc6smjz38FOK5tfQOdw1wd14izUi/NQJ85EvQRnvYQW4nX96iejbdu2Mn/+fMuxuXPnmuMAAADBztZgd/z4cbNsiW7u5Uz08Y4dOzzdqP379/ecf88990hiYqI88sgjsn79ennjjTfkk08+kSFDhtj2PQAAADiFrcFu2bJl0rx5c7MpHQunj0eOHGn29+7d6wl5Spc6mTFjhmml0/XvdNmTt99+21FLnQAAANjF1jF2nTp1MrNITie/u0roNStXrhR/89XK3fLkt2vlyqaVZfSVF9pdHAAAEID8aoydv9qflCoPTFtlFi5+f8k2ychiDSIAAHD+Eex8YOKCzZ7H2kCZfYZWSgAAgKLyq+VO/FFKWqZ8uXK33cUAAABBgGDnZd9vOCAp6Vm2lmHzgWSZ/ed+KRYZJhfVKC0LNxyURZsOSdkSUfL8tU0kJjJMnCgpNUMOJafJBaVjJTk1U0JCROJjIuTIiQzZ8deJv7fDKbL10AnpVK+cNK4SL9XLFrO8ho7h1PWFVGpGlqSk2VsXAAB4E8HOy/YczXvbsrTMbIkKzxumNu1Plsk/JcrFNctIrXLFZe+xk9K1QQX5vyXb5fMVu0x4aVW9tFxco0y+YUyDy/TVe2Tabztl4/5k6d6ggqzdmyTr953+ViTlS0TJja0TTNj7YeNB+WnTIXO8fsUScuREuvRqXMl0H/dvW00SSsfKn3uSZNm2v2TFjiMyc80+qRgXLfUqljDX6usM6FhTqpaKNS2V+r5VS8VIpfhoCQ8LNWMLEw+myPp9f5dJX+vHjQfl2hZVZV/SSVm546icSM+Si6qXMmHt0PFT9/g9G/183Lo3rGDGM+46ckL2J/39GmWKRcrhlPT/nfH38jq1yxeXzQeOm8flSkTJ/V1qy9XNq0iJqHBJSs2UfcdSTR38/TXVfNWg3qVeedlz7KS0r11W0jKypWPdstKocrxEhjOyAQBgrxDXmaalBiC9pVh8fLy5r5u3bin2S+JhueGtX077/NDudeXfXetYjq3ZdUyueH1RgV6/SskYWfRoZ1mSeFiKRYZL5ZIx8vZPiTL1t51y7GSGOFVEWIhkZAXuj1tc9N+B8OU+TeWa5twfuDC34zlw4IC59R6r6TsDdeJM1Evw1ktSIbILLXY2eGnuRmlVvZS0q1XW01LX9+3TB8Hcdh89KTWGzyzw+RoE9RrVsU5ZiYuJkBm/7xVfK0yo0xa0g8l/t7Zpq+C+pL9bPquXiTUth9o9q1+XbDksx9MyZfn2I6d9rdAQkQpx0abVzZs01Kkh01abTZe2qVYmVupUKCF1KxSX9MxsKREdYVr+tJu5VbVSUqZ43tvdAQBQVAQ7m/Sd/Kt8cHtrubBynNz+/m9mDNm50q7A3o0rycodR0x469Gwgula1K5R7QbVtlk9RxtptQv23cXbPGHv0rrlTND8Y/cxyXK5ZMX2IxIWGmJaAXMGRA2kGkiqlSkmG/Ylm9bCzOxsGTz17+5NFRsZJrGR4ZauVH2tmmWLSf1Kcaabt0GlEnLoeLpkZbvMce0W1W5rl7jMOLm46Ig831/O8XJu91xay/N47Z4kOZCcar5fLat2V2uXsH7PEWGh5l9Vem/hzMg4KRYdISWiw2Xv0VT5YdNBGT97g2ntjI4INd+Tdh9XjPvf1/hoqVwy2tSRdr2u3nVUapQtZgLa24u2nrY+tFu8ICb0aSata5SW0sUiJTrCmeMdAQD+ga5YH3XF/qNJJfm2CK1klzeqKMMvbyAz/9hrXlfHwuXn9vY15J5La0r5uOgCv/bJ9CwzIeFsYULH7unYNw0eZ6NBSl9Pg5z72j1HT0qVUjH5jit0WnN5fuGxIJ/j4ZQ02XXk5Bm74AtKw6SG4y0HU0wo1ZY+dfPF1aR3k0qSne2Sg8fTpG6FEqZONKBq66ZuGug1oOraiev2JkmNssVl++EUM7Gkba0ypsXQaehech7qxJmoF2fKpis2OLWpWeaMwU4H9391X3vTCqaTEbSFSf84N6kab4KGtkzpNuLLNfLLlsNyfasE050XFhJiJjYUJtC5FXQ2rAa1grYkFYuy/kjpdTXLFRd/UdhQ5/4cq0bGmpbCDU9fJl+v3CM/bzkkpYtFyZTFW6VD7bKyaPMh01Kp3bKz/th7xm7pnF3G7lCn/u+X7WY7V2OvaSx921xwzq8DAHAegp2P6JiwM3n66kZmzJhubtoFmN8fZTiXtkr+66IEs6mRVzTM56zmnoWrX5i9wcclFPOPA910sod2jWs3uE7eGdy1jnRtUN6cc/REhhnXqGMTtUUwPDSkSKEXAOBbBDsfiIkIk5Ixp+8C0+U5Lm9cyadlgv3u61zbbG46CUTH7ZUrHmXCVHJapglfa3Yfk1HT/zTLweiEjIUbDngmaihdHkbHK+q1Gsw0jOnj8LAQs27fvHX7831/fY2lW/8ym7rzg2VnLbNmOx28cX3LqnJty6pmaR4AgHMQ7HygfFyU1K9UwjI7NadHL6tnS7ngLMWjwk3rmZsuxqyaVC0pXw5sf86vr13A93y44pxewz0i99Plu8x2Jtr1fEXTymYcoD5ufkGpc3pvAMDZEex8oGzxKNNFN2fIJbJ651F5Y+EWM+ZKDehYQ2qXL2F3EREELmtUSbY921sSDx6XxVsOS1pGlmRmu2TZtiOWVr16FUqY2cGrdx07p7UHdRHq9fvydjXrOES7J9IAQKAi2PmAToxwTyxoV7us2XRQ/I6/UqRmWf+ZWIDAoJNZLBNaLj37Ne7Zwos3HzJ3+fhiRdHvf1zv8VmWfV1up3KxULnmonAzyYixfABQdAQ7H8hvEVpdxoKWOvgLd9jS26jp9tK/muU5R9dKNGv9ZWbJ699vNmMDdekWvU/xmbhvYzdt1QHPMZ3Iobez0xniAICCI9j5QMlY560dBpxvukSPe63DZ3LN3tbQV+ex7wr8Wq/M32Q2t7svrWkmhOiSQToW8e1bWpmZu7/vOmqW1LmsUUWz0DWtfQCCHcHOB/QPERDsoU/H9+Wkdzl5c+EW+WPPMdl++MQZr//PD4mex7oA8/WTlliedy8b8+hl9eXeTqfuRgIAwYbE4QMEOyCvRlXiZWK/Fp5V20uWLit3fLBMVmw/Kiczsor0ms/NWi8fLNkmix7t4rn7CQAEExKHDxDsgLPTcacf3XmxZ1/vI/yfH7dIiIRI3QrFzZ1WhkxbbWbttqhWSg4mp8q8dafG5eW8c0etETPNcjFv3tRCLq5RRkIJeQCCBInDB4pH8zEDhaUtbgM7nVrAWV3TvGq+51735s+ybPsRyzHtsu07+VfzWBd6vqNDTRnYuRZ30QAQ0EgcPlCCFjvAqz67t50cPZEuzZ6ce9q7bLw8b6PZ3D6/t620rFbah6UEAO8jcfiArl8HwLtKxkaaCRp7j52Ubi/+ICnpZx6nd+2bpyZgXHZhRbmhdYK5RZrOsgUAf0Xi8IHYSP5QAL5SKT5G/nzyMs/9dxesPyD3f7zyjNfM+nOf2dymD2pvbuUGAP6GYOcD3D4JsG/ikt6vVje3F2avl4kLtpzxuitfX2y+bhnbi9m1APwKwc4HoiJC7S4CgP95uGd9s6n3Fm+V/y7dIRv3H8/3XJ1dqy4oHSvzH7zUrMcHAE5GsPOBqHD+GABOdGv7GmZze23+Jnlx7qkJFm47/jrhuXPGB7e3lgsrx0lMZJjERIQxwxaAoxDsfICuWMA/3N+1jtm6vrhQthxMyfec/lOW5nv83dsukkvrlGPNPAC2Itj5AC12gH+Z/2Anyc52yTe/75HBU1cV6Jrb3v3N83jruF605AGwBcHOB/gXPOCfv7dXNatithPpmdJw5OwCX1tj+N9j89QdHWrIxv3J8njvhlKvYgkvlRYA/kawA4CziI0MN2vk5eRyuSQ5LVN6vPSj7EtKPe217yzaar72nPCj5XiVkjHyYI+6ck3zKrTuAThvCHYAUAQaxuKiI+SXEV09x6oPm1Hg63cfPSlDP1ltNreKcdHm/rbNEkoS9gAUCcEOAM4TbdXTljwNba/O3yQn0rPk29/3Fvh6bfm75o2fzeMOtctKqWKRUqNsMenRsII0qhLvxZIDCBQEOwA4j7SlrWqpWHn+uqZm//W+p55LScsUbYj7fv0BGfTfM98NY9HmQ57HGhJViehwGXZ5fbmkTjlJSc+U8iWipXSxSG99KwD8EMEOAHx83+h/NKlsNjdt5Vuw4YDc/t6yM16fnJopj335R57jl9QtJ6/e0MzcLxdAcCPYAYADWvm61K9gunLTMrNkxfaj8ueeY7JhX7J8unzXWa//ceNBafbkXIkMC5VJN+sYvVK05AFBimAHAA5b0LxtrTJmUy9c/3eX7i+Jh+WRz343d8FoU6O0/Lr1rzzXpmdlW1r9dMbty32a+bD0AOxGsAMAP3BxzTLy4yOdLce2H06RS19YeNprvly522x3X1JThnSvK9ER3AUHCHQEOwDwU9XKFDPdt5lZ2TJh3iZ5fcHmfM/7z4+JZlNNE0rKwz3qSfvaZVhSBQhABDsvyHa57C4CgCASHhYqD/WsZzaVnpktdR//Lt9zV+88Kje982u+z30zqIM0rsqyKoA/I9h5QWYWwQ6AfSLDQ01L3h+7j8m/P14piYdSCnTdFa8v8jwe27um/KtsOQnlVteAXyHYeUFWNsEOgP10UePvH+pkHu86ckJm/L5Xxn23vkDXjpiRaLaSsRFy9ESG3H1pTXmga12JiWScHuBkBDsvyCTYAXAYXTT57ktrmS3nP0IPJKfK4I9XydJteWfZKg116j8/JJrt8d4N5M6ONX1WbgCFQ7DzAh3IDABOFxYaIpXiY+STe9p6FkoeP2eDTFyw5bTXPD1jnWzcn+y5swYAZyHYeQEtdgD8kc6SfbhnfXmwe13ZvGOP7EuPlP1JaWa27fbDJzznfbJsl9le7tNUejWuZNbeA+AMBDsvYIwdAH8XFx0utS8oK6GhoXJ9qwQzRq/Dcwss5wyZttpsasvYXqYFEIC9mO/kBRl0xQIIwDF6r97Y/LTP1xoxUwZ8sMx05wKwD8HOC2ixAxCIrmxa2SyjMuXWVvk+P3ftfqkxfKZZKw+APQh2XsAYOwCBrEv9CibgbR3XK9/nr5q4WKoPmyGf/LbT52UDgh3Bzgu48wSAYJlsoQHvp1z3sHV75PPfpdGo2XLoeJrPywYEK4KdF2TTYgcgiCSUjjUB74XrmuR57nhaprR6ep50Hr9Qlmw5bEv5gGBCsPMC7igGIBjp7FkNeF/f1z7Pc1sPpciNk38xXbQAvIdg5wXMCgMQzJomlJTEsb2kd5NK+T6v4e6b1XskJS3T52UDAh3r2HkBs2IBBLvQ0BCZ2LeFDL/8hHR8foHk/vfu/R+v9Dxe+2RPiY3kzxFwPtBi5wVZtNgBgGf9u63jest3gzue9pyGI2ebVryvV+1mjDJwjgh2XkCuAwCrBpXizPi7T+5uKzXLFcv3nMFTV0nNETNNyFu+/S+flxEIBLR9ewFdsQCQv9Y1Ssv3D3YyY5GbjJkjyan5j7O79s0l5uv1LavKC9c39XEpAf9Fi50XdGtQwfP4nktr2loWAHDqGnhrRveUjU9fLs9d2/i05326fJc0Hj3btOBlcrtG4KxosfOChpXj5PUbm8m6nQdlYOfadhcHABwrMjxU+lx0gdk0uI37br28s2ir5Rxt1XO34HWoXVZeuL6JVIqPsanEgLMR7LykV+NK0qpCmMREhtldFADwC+FhofLEPxqaLSk1Q5qMnpPnnEWbD0nbcd+bx3pLM235A3AKXbEAAMeJi44wtyq7sHLcac+pMXymXP7KT7L32Emflg1wMlrsAACOvVXZjH//vUzK/qRUGfPNnzJzzT7LOev2Jnla8D4ecLG0rVXGlrICTkGLHQDA8SrERcsb/VrKn2N6SqnYiHzP0VuWNXhils/LBjgJwQ4A4DeKRYXLypE9ZNnj3aRJ1fg8z5/MyOJ+tAhqBDsAgN8pWzxKpg/qIKtH9pAHu9fN87yGu22HUmwpG2Angh0AwG/Fx0bI/V3ryC/Du+Z5rtP4hXLfRytsKRcQtMFu4sSJUr16dYmOjpY2bdrI0qVLz3j+hAkTpF69ehITEyMJCQkyZMgQSU1N9Vl5AQDOUzE+WjY/c3me4zPW7KX1DkHF1mA3bdo0GTp0qIwaNUpWrFghTZs2lZ49e8qBAwfyPf+///2vDBs2zJy/bt06eeedd8xrjBgxwudlBwA4bx08vR9ty2ql8m2904CXnsndKxDYbA12L730kgwYMEBuu+02adiwoUyaNEliY2NlypQp+Z7/888/S/v27aVv376mla9Hjx5y4403nrWVDwAQPD6/t538PrpHvs/Vffw7+WP3MXOvWiAQ2baOXXp6uixfvlyGDx/uORYaGirdunWTJUv+vnVMbu3atZMPP/zQBLnWrVtLYmKizJw5U26++ebTvk9aWprZ3JKSkszX7Oxss3mLvrb+j8Ob74HCoU6ciXpxnkCok+KRYbJmVHfp+PxCOXoyw/LcP15bZL6ufKKbxMfkv3SKEwVCvQSibB/US2Fe27Zgd+jQIcnKypIKFSpYjuv++vXr871GW+r0ug4dOpgPMTMzU+65554zdsWOGzdOxowZk+f4wYMHvTo2Tyvh2LG//1WogRX2o06ciXpxnkCqk1l3N5GUtCzp+uaqPM81f2qe+bro3y0kPNT5tyYLpHoJJNk+qJfk5OTAvPPEwoULZezYsfLGG2+YiRabN2+WwYMHy1NPPSVPPPFEvtdoi6CO48vZYqeTLsqVKydxcae/Vc35qGi9h6G+D7+AzkCdOBP14jyBWCeJYyvJN6v3yOBpq/M81+HVFfLn6B6Ov7d3INZLIMj2Qb3oBFPHB7uyZctKWFiY7N+/33Jc9ytWrJjvNRretNv1zjvvNPuNGzeWlJQUueuuu+Sxxx7L9wONiooyW256rrd/MbSiffE+KDjqxJmoF+cJxDq5qnlVs73+/SYZP2ej5bkLR88xEy+cLhDrJRB4u14K87q2/WRERkZKy5YtZf78+ZbUq/tt27bN95oTJ07k+eY0HCoGwgIACmJQlzqy4onueY7rrNnDx0+NyQb8ka2RX7tIJ0+eLO+//75ZvuTee+81LXA6S1b179/fMrniiiuukDfffFOmTp0qW7dulblz55pWPD3uDngAAJxN6WKRsnVcrzzHWz49T26ZwkoL8F+2jrHr06ePmcQwcuRI2bdvnzRr1kxmzZrlmVCxY8cOSwvd448/bpo79evu3btNf7aGumeeecbG7wIA4I/078m6Jy+TBiNnWY7/sPGgab2b8e8OcmHlvPejBZwsxBVkfZg6eSI+Pt7MYPH25AldaLl8+fKMhXAI6sSZqBfnCcY6GfjRcpm5Zl+e4zpbdvPYvC17dgjGevEH2T6ol8JkF34yAABB741+LeWPMT3zHM/MdsngqSttKRNQFAQ7AAB0UeOocDMz9p1bWlmOf71qj+ma/W7NXtvKBhQUwQ4AgBy6NqggSx/rmuf4vR+tkKsmLpZ1e/++gxHgRAQ7AAByKV8iWmb+u2Oe46t3HpXLX/nJtODpJAvAaQh2AADko2HlONM1+9y1jfN9XpdF0YAXZHMQ4XAEOwAAzqDPRRfIT490lquaVc73+RrDZ8qXK3f5vFxAfgh2AACcRULpWHnlhuamBe+xXg3yPD9k2mpa7+AIBDsAAAphwCU1ZcPTl0lcdN41/q9+42dbygS4EewAACikqPAw+X10T1n0aOc8kyu05e7oiXTbyobgRrADAKCIqpaKNa13uTV7cq68s2irLWVCcCPYAQBwjq13+S2N8tS3a+Ufr/1kS5kQvAh2AACcp6VRLq1bznL8j91Jpms2O5tJFfANgh0AAOfJ+7e3lnlDL8lzvOaImZKUmmFLmRBcCHYAAJxHtcuXkIUPdcpzvMnoOfJXCpMq4F0EOwAAzrPqZYvJ+qfyTqpo8dRcumXhVQQ7AAC8IDoizIy7qxgXnadbVsfd0TULbyDYAQDgRb+M6Jrvce2a3XLwuM/Lg8BGsAMAwMu05a5amdg8x7u++IN8smynLWVCYCLYAQDgAz883FnWjO6R5/gjn/0uz89ab0uZEHgIdgAA+EiJ6AjTejeiV33L8TcWbpFX52+yrVwIHAQ7AAB87K5Lasl3g613q3hp7kb5eOkO28qEwECwAwDABg0qxcn0Qe0tx4Z/sUbmrd1vW5ng/wh2AADYpEnVkvLJ3W0tx+78YJks3HDAtjLBvxHsAACwUesapeWF65pYjt367m+yePMh28oE/0WwAwDAZte3SpAHu9e1HOv39q/y9LdrbSsT/BPBDgAAB7i/ax0pERVuOfb2oq3Sd/IvtpUJ/odgBwCAQ6wZ01Mm3dTCcuznLYdl8NSVtpUJ/oVgBwCAg1zWqJJ8ObCd5djXq/bI2j1JtpUJ/oNgBwCAwzS/oJR8fZ91KZR/vL5YXC6XbWWCfyDYAQDgQE0TSsqYKy+0HGv7ygo5dDzNtjLB+Qh2AAA41C3tquc51nrs95KUmmFLeeB8BDsAABxM7y17TfMqlmNNRs+RjKxs28oE5yLYAQDgcC/3aSbdG5S3HKvz2He2lQfORbADAMAP/OfmlnmOpWZk2VIWOBfBDgAAP/HLA9ZwV/+JWXKYyRTIgWAHAIAf6VyvnGW/5dPzZNYf+2wrD5yFYAcAgB+ZfHNLKRUbYTl2z4fLZcqirbaVCc5BsAMAwI+EhobIypE98hx/8tu1snrnUVvKBOcg2AEA4KfLoEy5tZXl2FUTF8ukH7bYVibYj2AHAICf6lK/gkzo08xy7Nnv1kv1YTNsKxPsRbADAMCPXd28inTKNaFCLdv2ly3lgb0IdgAA+Ln3bmstvwzvajl23aQlrHMXhAh2AAAEgIrx0fL5vW3zrHOH4EKwAwAgQLSsVlrqVyxhOXbsZIZt5YHvEewAAAggsx64xLLfdMwcumSDCMEOAIAA8+hl9fN0ybpcLtvKA98h2AEAEGDu7VRLypeIshyrMXymbeWB7xDsAAAIQL+O6CqV46Mtx+58f5lt5YFvEOwAAAhAISEh8tOjXSzH5q3bbxYvpls2cBHsAAAIUGGhIbJqZPc8x0d8ucaW8sD7CHYAAASwkrGRsnpkD8uxj5fulBPpmbaVCd5DsAMAIMDFx0bIlrG9LMcajpxtW3ngPQQ7AACCpFu2d5NKlmPZ2Yy1CzQEOwAAgsTrNza37N84+RfbygLvINgBABBEM2Xb1Srj2f9161+y868TtpYJ5xfBDgCAIDLl1oss+x2fX2BbWXD+EewAAAgi0RFh0q/NBZZjizYdsq08OL8IdgAABJlnrmls2b/pnV9tKwvOL4IdAABB6IuB7Sz7CzYcsK0sOH8IdgAABKEWF5Sy7N/27m+2lQXnD8EOAIAg9fm91la7xIPHbSsLzg+CHQAAQaplNWurXZcXf7CtLDg/CHYAAASxF65rYtl/bf4m28qCc0ewAwAgiF3fKsGy/+LcjbaVBecuvCgXZWVlyXvvvSfz58+XAwcOSHZ2tuX577///jwUDQAA+MLiYV2k/bOn/nZXHzZDNj59uUSG0/7jb4pUY4MHDzabBrxGjRpJ06ZNLVthTJw4UapXry7R0dHSpk0bWbp06RnPP3r0qNx3331SqVIliYqKkrp168rMmTOL8m0AAAARqVIyRmqULWY51vzJObaVBz5usZs6dap88skn0qtXr3N4a5Fp06bJ0KFDZdKkSSbUTZgwQXr27CkbNmyQ8uXL5zk/PT1dunfvbp777LPPpEqVKrJ9+3YpWbLkOZUDAIBg9839HaTRqNme/ZT0LEnLzJKo8DBbywUftNhFRkZK7dq15Vy99NJLMmDAALntttukYcOGJuDFxsbKlClT8j1fj//111/y1VdfSfv27U1L36WXXlroVkIAAGBVPCpcto6zNtjUe3yWbeWBD4Pdgw8+KK+88oq4XK4ivu3frW/Lly+Xbt26nSpMaKjZX7JkSb7XTJ8+Xdq2bWu6YitUqGC6gceOHWu6hAEAwLkJCQmRh3vWsxx76NPVtpUHPuqKXbRokSxYsEC+++47ufDCCyUiIsLy/BdffHHW1zh06JAJZBrQctL99evX53tNYmKimZjRr18/M65u8+bNMnDgQMnIyJBRo0ble01aWprZ3JKSksxXnfCRe9LH+aSvrcHXm++BwqFOnIl6cR7qJLjr5Z5LasgLszd49j9bvkue+2cjE/pgT70U5rWLFOx0TNs111wjvqbfmI6ve+uttyQsLExatmwpu3fvlhdeeOG0wW7cuHEyZsyYPMcPHjwoqampXi3rsWPHTGVrSyTsR504E/XiPNSJM/myXqbf2ViufHuNZ7/WY7PklwdaevU9/VW2D+olOTnZu8Hu3XfflXNVtmxZE872799vOa77FStWzPcanQmrrYN6nVuDBg1k3759pmtXx/7lNnz4cDNBI2eLXUJCgpQrV07i4uLEmxWt/7rR9+F/jM5AnTgT9eI81Ikz+bJedP5ipfhNsvfYqQaQ2PjSZhwefF8vunJIQZ1TDWmrl85gVfXq1TPfVEFpCNMWN10L7+qrr/Z8OLo/aNCgfK/RCRP//e9/zXnuD2/jxo0m8OUX6pQuiaJbbnq9t38xtKJ98T4oOOrEmagX56FOnMmX9fLzsC5SY/ip5cRue29ZnnvLwjf1UpjXLVIJUlJS5PbbbzeB6pJLLjFb5cqV5Y477pATJ04U+HW0JW3y5Mny/vvvy7p16+Tee+81r62zZFX//v1Ni5ubPq+zYnUNPQ10M2bMMJMndDIFAAA4v2Hlxtan7kqxfPsROZnOZEWnK1Kw00D2ww8/yDfffGMWDNbt66+/Nsd0xmxB9enTR8aPHy8jR46UZs2ayapVq2TWrFmeCRU7duyQvXv3es7XLtTZs2fLb7/9Jk2aNJF///vfJuQNGzasKN8GAAA4gyevamTZf3rGWtvKgoIJcRVhzRIdH6cLBHfq1MlyXGfK/utf/zJdtE6lY+zi4+PNQEdvj7HT263pZA+6MpyBOnEm6sV5qBNnsqteLpvwo6zfd2rwvq51xwxZ39ZLYbJLkUqg3a25lylR+k0VpisWAAA42xcDrePqco67g/MUKdjpIsG6vEjO5UJOnjxplhXR5wAAQGCIjQyXxlXiLceqD5shx9MybSsTznOw07tOLF68WKpWrSpdu3Y1m45/+/nnn81zAAAgcHxyd95Gm5z3lYVzFGm5E72V16ZNm+Sjjz7y3CXixhtvNHeEiImJOd9lBAAANoqJDJP1T10m9Z+w3jtWW+1Y285ZilwbsbGxMmDAgPNbGgAA4EjREWGy7dnephvW7Z9vLJY5Qy61tVwoYrCbPn26XH755ebOD/r4TK688sqCviwAAPAjd11SU976MdE83rj/OK12DlPgmtC7Q+itu3Tmq/tOEfnRKdBZWSxgCABAIHq4Zz1PsHOPtdOWPPjZ5Aldp0VDnfvx6TZCHQAAgSsiLNS02uW0aX/Bb1IP7zpvK+np3ScAAEDgG9GrgWX/xsm/2lYWnIdg99xzz8m0adM8+9dff72ULl1aqlSpIqtXry7KSwIAAD/yf3e09jw+dDxNinAjKzgl2E2aNMmsW6fmzp0r8+bNM/d41ckVDz/88PkuIwAAcJiOdcpZ9q98fbFtZcEpRZrGopMo3MHu22+/NfeH7dGjh1SvXl3atGlTlJcEAAB+5p8tqsgXK3abx2t2H7O7OChqi12pUqVk586d5rG21HXr1s081mZYJk8AABAcnru2iWX/3g+X21YWnEOw++c//yl9+/aV7t27y+HDh00XrFq5cqXUrl27KC8JAAD8cIZsiwtKeva/+2OfreVBEYPdyy+/LIMGDZKGDRuaMXbFixc3x/fu3SsDBw4832UEAAAO9eGd1iFY6/cl2VYWFHGMnd594qGHHspzfMiQIeejTAAAwE/ERlqjxKSFW2TCDc1tK0+w45ZiAADgnAztXldemrvRPD56MsPu4gQ1bikGAADOSdcG5T3BbuGGg3YXJ6gVONjp7cLyewwAAIJbnfIlLPv7k1KlQly0beUJZuftlmIAACA4RYZb40SbsfNtK0uwK1Kw+/e//y2vvvpqnuOvv/66PPDAA+ejXAAAwI8Mu7y+ZX/yj4m2lSWYFSnYff7559K+ffs8x9u1ayefffbZ+SgXAADwI7e2q27Zf2bmOtvKEsyKFOx0UeL4+Pg8x+Pi4uTQoUPno1wAAMCPREeEydwhl1iO6R2p4AfBTu8uobcSy+27776TmjVrno9yAQAAP1OngnUSRY3hM20rS7Aq0gLFQ4cONXeeOHjwoHTp0sUcmz9/vrz44osyYcKE811GAADgJy6sHCd/7jl194mk1AyJi46wtUzBpEgtdrfffrsJce+884507tzZbB9++KG8+eabMmDAgPNfSgAA4Be+vb+DZX/M9LW2lSUYFXm5k3vvvVd27dol+/fvl6SkJElMTJT+/fuf39IBAAC/ojcq6NW4omf/8xW7bC1PsClysMvMzJR58+bJF1984RkcuWfPHjl+/Pj5LB8AAPAzI3o1sOyfSM+0rSzBpkDB7sSJE5b97du3S+PGjeWqq66S++67z4y1U88995w89NBD3ikpAADwC1VLxVr2v161x7ayBJsCBbuXX35Z3nrrLc/+4MGDpVWrVnLkyBGJiYnxHL/mmmvMJAoAABDc2tYs43k8/Is1tpYlmBQo2N10003y9ttvy8iRI83+Tz/9JI8//rhERkZazqtevbrs3r3bOyUFAAB+45HL6ln20zO5z7xjgl21atVMmNOFiVV2drZkZWXlOU8nU5QoYV3DBgAABJ/mF5Sy7LcZO8+2sgSTAk+eiIqKkokTJ5rHPXr0sKxXpzNgdNLEqFGjpFevXt4pKQAA8Cu9G1fyPD5yIsPWsgSLIs2KHT9+vCxevFgaNmwoqamp0rdvX083rE6gAAAAmNivhWV//OwNtpUlWBTpzhMJCQmyevVqmTZtmvmqrXV33HGH9OvXzzKZAgAAwO31BZvloZ7WsXewOdhlZGRI/fr15dtvvzVBTjcAAID8/Dysi7R79nu7ixE0Ct0VGxERYbpfAQAAzqZySWtP3t5jJ20rSzAo0hg7XZRYx9Lp3ScAAADOpEqOcNd2HK13jhtj99tvv5mFiOfMmWPuQFGsWDHL83qbMQAAANXv4gvk+VlMnHBssCtZsqRce+215780AAAg4AzsVNsS7F6cs0Ee7MEkCtuDnS5M/MILL8jGjRslPT1dunTpIqNHj2YmLAAAOKOqpWJk15G/x9e99v1mGdKtroSGhthdrOAeY/fMM8/IiBEjpHjx4lKlShV59dVXzXg7AACAM5lxf0fL/v1TV9pWlkBWqGD3wQcfyBtvvCGzZ8+Wr776Sr755hv56KOPTEseAADA6cTHRkitcqfG5M/4fa+t5QlUhQp2O3bssNwyrFu3buZ2Ynv27PFG2QAAQAD5YmB7y35Wtsu2sgSqQgU7Xd4kOjo6z7p2umgxAADAmcTHRFj2n/p2rW1lCVSFmjzhcrnk1ltvlaioKM8xXaz4nnvusSx5wnInAADgbN77eZuMvvJCu4sRvMHulltuyXPspptuOp/lAQAAAWzOkEukx8s/evYPH0+TMsVPNRjBh8Hu3XffPce3AwAAwaxuhRKW/ZZPz5Ntz/a2rTyBpki3FAMAACiq0sUiLfvPfrfetrIEGoIdAADwqRVPdLfsT/phi21lCTQEOwAA4HNLhnfxPG5VrZStZQkkBDsAAOBzleJjJOR/dxRbtv2IpGZk2V2kgECwAwAAtnDlWJ+42ZNz7CxKwCDYAQAA26VmcHvS84FgBwAAbJE49tRtStWm/cm2lSVQEOwAAIAtQkNDpEyOpU+651i4GEVDsAMAALaZdHNLy/6033bYVpZAQLADAAC2uah6acv+o5+vsa0sgYBgBwAAbPXt/R08jyvGRdtaFn9HsAMAALZqVCXe83hfUqqtZfF3BDsAAGC7qqViPI/3HSPcFRXBDgAA2G7XkZOexxePm29rWfwZwQ4AANjulRuaWfYPJqfZVhZ/RrADAAC2u6pZFcv+Rc/Ms60s/oxgBwAAHOHlPk0t+0dS0m0ri78i2AEAAEe4pnlVy/5bPyXaVhZ/5YhgN3HiRKlevbpER0dLmzZtZOnSpQW6burUqRISEiJXX32118sIAAC879Z21T2P31y4xday+CPbg920adNk6NChMmrUKFmxYoU0bdpUevbsKQcOHDjjddu2bZOHHnpIOnbs6LOyAgAA7xrctY7dRfBrtge7l156SQYMGCC33XabNGzYUCZNmiSxsbEyZcqU016TlZUl/fr1kzFjxkjNmjV9Wl4AAOA9pYpFWvaTUzNsK4s/CrfzzdPT02X58uUyfPhwz7HQ0FDp1q2bLFmy5LTXPfnkk1K+fHm544475Keffjrje6SlpZnNLSkpyXzNzs42m7foa7tcLq++BwqHOnEm6sV5qBNnCqZ66VinrPy06ZB53Hj0HEkce7kEc71kF+K1bQ12hw4dMq1vFSpUsBzX/fXr1+d7zaJFi+Sdd96RVatWFeg9xo0bZ1r2cjt48KCkpqZ6tRKOHTtmKlvDKuxHnTgT9eI81IkzBVO9NC4fJT9tOrW/atNOqRwfJcFaL8nJyf4R7Iryjd18880yefJkKVu2bIGu0dZAHcOXs8UuISFBypUrJ3FxcV6taJ3Yoe8T6L+A/oI6cSbqxXmoE2cKpnp5qHd5eWPxbs/+W0sPyhv9Wkiw1kt0dLR/BDsNZ2FhYbJ//37Lcd2vWLFinvO3bNliJk1cccUVeZonw8PDZcOGDVKrVi3LNVFRUWbLTT98b/9iaEX74n1QcNSJM1EvzkOdOFMw1cuQbnXl5XkbzeNZf+6XLJdIRFhoUNZLaCFe19ZPKDIyUlq2bCnz58+3BDXdb9u2bZ7z69evL2vWrDHdsO7tyiuvlM6dO5vH2hIHAAD8312XWCdH3vXBMtvK4k9s74rVbtJbbrlFWrVqJa1bt5YJEyZISkqKmSWr+vfvL1WqVDFj5bQpslGjRpbrS5Ysab7mPg4AAPxXTGSYXFK3nPy48aDZX7DhoPy27S+5qHppu4vmaLa3afbp00fGjx8vI0eOlGbNmpmWt1mzZnkmVOzYsUP27t1rdzEBAICPvXVzS8v+9ZNOv2IGHNJipwYNGmS2/CxcuPCM17733nteKhUAALBTdESY3HNpLZn0w6k7UBw7mSHxMRG2lsvJbG+xAwAAOJ1hl9eXuOhT7VB3/x9j7c6EYAcAABztxjYXeB7/kviXrWVxOoIdAABwtId71LPsn0zPsq0sTkewAwAAjhaea/26XxIP21YWpyPYAQAAx+vduJLn8Ue/7rC1LE5GsAMAAI53ad1ynsfz1lnvWIVTCHYAAMDxejc51WKnDiSl2lYWJyPYAQAAxysWZV16t/XYU7cjxSkEOwAA4Bc+uL21ZX/09D9tK4tTEewAAIBf0HvH5vTez9vE5XLZVh4nItgBAAC/sfbJnpZ9Wu2sCHYAAMBvxEaGS7OEkp79VbuO2VoepyHYAQAAv/LqDc09j1fvPGprWZyGYAcAAPxKQukYy35aJrcYcyPYAQAAvxISEiIRYSGe/bd+SLS1PE5CsAMAAH6nXsUSnsfvL9lma1mchGAHAAD8zoQ+p8bZHTqebmtZnIRgBwAA/H6c3bZDKbaVxUkIdgAAwO9EhYdZ9l+YvcG2sjgJwQ4AAPilQZ1rex7PWLOX2bEEOwAA4K9u71DDsn/NxJ8l2BHsAACAXypdLNKyXyI6XIIdwQ4AAPithQ918jz+detfEuwIdgAAwG9VKxNr2f9h40EJZgQ7AADgt/QuFDlNWrhFghnBDgAA+LVJN7X0PF6SeFiCGcEOAAD4tS71y1v2DyanSbAi2AEAAL8WGR4qsZGnFiweH8SLFRPsAACA37utfXXP4+83HJBgRbADAAB+7/qWCZ7HdMUCAAD4sQtKW5c9CVYEOwAA4PdCQ63Lnuz864QEI4IdAAAICM0SSnoe3zJlqQQjgh0AAAgI/dtW8zxOPJQiwYhgBwAAAsI1zatIsCPYAQCAgLm9WPkSUZ79IynpEmwIdgAAIGCcTM/yPL78lZ8k2BDsAABAwBjcrY7n8b6kVHG5XBJMCHYAACBg3Nru1B0o1JETGRJMCHYAACBghIeFStuaZTz7P206KMGEYAcAAAJKzXLFPI+/Wb1XggnBDgAABJT2tct6Hs9bt1+CCcEOAAAElM71ylv2s7ODZwIFwQ4AAASUmMgwy/6SxMMSLAh2AAAg4LSpUdrzeP66AxIsCHYAACDg9G5SyfN4yuKtkppxauHiQEawAwAAAecfTSpb9t9cuEWCAcEOAAAEnNLFIqV+xRKe/Vfmb5JgQLADAAAB6ZUbmlv2DySnSqAj2AEAgIBUt0Jxy37rZ+ZLoCPYAQCAgBQSEiLPXNPIcmzEl2skkBHsAABAwOrb+gLL/n9/3SFZAbxgMcEOAAAEdKvdJ3e3tRzr/vIPEqgIdgAAIKC1rlFabr64mmc/8WCKnEjPlEBEsAMAAAFv5BUNLfuvfb9ZAhHBDgAABLyIsFC5Mcd4u0BdsJhgBwAAgsLQ7nUl0BHsAABAUChXIsqyfzA5TQINwQ4AAASli56ZJ4GGYAcAAILGpJtaWPYzsrIlkBDsAABA0LisUSXL/vRVeySQEOwAAEBQ6VSvnOfxg5+ulkBCsAMAAEFl2OX1LfsrdhyRQEGwAwAAQaV+xTjLfr/Jv0qgINgBAICg898BbTyPT2ZkicvlkkBAsAMAAEGnXa2ylv01u49JIHBEsJs4caJUr15doqOjpU2bNrJ06dLTnjt58mTp2LGjlCpVymzdunU74/kAAAD5qVIyxvP4gyXbJRDYHuymTZsmQ4cOlVGjRsmKFSukadOm0rNnTzlw4EC+5y9cuFBuvPFGWbBggSxZskQSEhKkR48esnv3bp+XHQAA+K++bU7dO/az5bskENge7F566SUZMGCA3HbbbdKwYUOZNGmSxMbGypQpU/I9/6OPPpKBAwdKs2bNpH79+vL2229Ldna2zJ8/3+dlBwAA/qtv61PBLlCE2/nm6enpsnz5chk+fLjnWGhoqOle1da4gjhx4oRkZGRI6dKl830+LS3NbG5JSUnmq4ZB3bxFX1sHYnrzPVA41IkzUS/OQ504E/Vy/sXHWGPQe4u3Sv+21RxXL4V5bVuD3aFDhyQrK0sqVKhgOa7769evL9BrPProo1K5cmUTBvMzbtw4GTNmTJ7jBw8elNTUVPFmJRw7dsxUtoZV2I86cSbqxXmoE2eiXryjcaVismZvink8+pu1clmtU+PunFIvycnJ/hHsztWzzz4rU6dONePudOJFfrQ1UMfw5Wyx03F55cqVk7g46zo255NWdEhIiHkffgGdgTpxJurFeagTZ6JevGPyLXFy8bMLPPshMfFSrkSUo+rldBnHccGubNmyEhYWJvv377cc1/2KFSue8drx48ebYDdv3jxp0qTJac+LiooyW2764Xv7F0Mr2hfvg4KjTpyJenEe6sSZqJfzr2LJWMv+Le/+JrMeuMRR9VKY17X1JyMyMlJatmxpmfjgngjRtm3b0173/PPPy1NPPSWzZs2SVq1a+ai0AAAgED3eu4Hn8fp9Be/2dCLbI792k+radO+//76sW7dO7r33XklJSTGzZFX//v0tkyuee+45eeKJJ8ysWV37bt++fWY7fvy4jd8FAADwV3d0qGHZz87237tQ2B7s+vTpY7pVR44caZYwWbVqlWmJc0+o2LFjh+zdu9dz/ptvvmlm01533XVSqVIlz6avAQAAUFjaldq0arxn//MV/rumnSMmTwwaNMhs+dGJETlt27bNR6UCAADBIiw0xPP44c9+l8sbV5LiUY6ISf7VYgcAAGC31/u2sOw3GjVb/BHBDgAABL3KJWNkcNc64u8IdgAAACIypHtdy/6xExnibwh2AAAA/9OoyqmbF1z9xmLxNwQ7AACA/2lU+dTs2K2H/r7VmD8h2AEAAPzP6CsvlJxem79J/AnBDgAA4H+iI8IkPMfSJy/O3Sj+hGAHAACQw68juoq/ItgBAADkUKZ4VM5dWb8vSfwFwQ4AACCXjnXKeh4/8dUf4i8IdgAAALncfHE1z+Pfth2RoyfSxR8Q7AAAAHLp1qCCZf+rlbvFHxDsAAAAcgkNDbGEuzHfrhV/QLADAADIx8M963keu1ziFwh2AAAA+ahXsYRl//DxNHE6gh0AAMBpxESEeR6/9VOiOB3BDgAA4DT+1aqq5/HbP20VpyPYAQAAnMa9nWp7HmdlO3+gHcEOAADgNCrGR1v20zOzxckIdgAAAGdQvUys5/Hvu46KkxHsAAAAzqB4dLjn8cvzNoqTEewAAAAKeHuxxZsPi5MR7AAAAM7g6uZVLPspaZniVAQ7AACAM4gKP7WWneo/Zak4FcEOAADgLO6+tKbn8fLtR8SpCHYAAABnMaRbXcu+U9e0I9gBAACcRXSOW4upXxOdOYmCYAcAAFAAjarEeR5/8/secSKCHQAAQAHc1q6G5/HHS3eKExHsAAAACqBTvXKW/cws591ejGAHAABQAGWKR1n2//NjojgNwQ4AAKCAbm9/qjv2hdkbxGkIdgAAAAX0YA/rsictnponLpdzlj4h2AEAABRQsahwy/7RkxnynyXOmSFLsAMAACiEVSO7W/bfW7pPnIJgBwAAUAglYyNlw9OXWY7tOXpSnMDanggAAICzigoPk8Fd65g7ULSsEiNR4c5oKyPYAQAAFMGQ7nUlOztbDhw4kGcpFLs4I14CAADgnBHsAAAAAgTBDgAAIEAQ7AAAAAIEwQ4AACBAEOwAAAACBMEOAAAgQBDsAAAAAgTBDgAAIEAQ7AAAAAIEwQ4AACBAEOwAAAACBMEOAAAgQBDsAAAAAkS4BBmXy2W+JiUlefV9srOzJTk5WaKjoyU0lPzsBNSJM1EvzkOdOBP1Erz1kvS/zOLOMGcSdMFOP3yVkJBgd1EAAAAKlWHi4+PPeE6IqyDxL8CS9Z49e6REiRISEhLitffRdK3hcefOnRIXF+e190HBUSfORL04D3XiTNRL8NaLy+Uyoa5y5cpnbRUMuhY7/UCqVq3qs/fTSuYX0FmoE2eiXpyHOnEm6iU46yX+LC11bnTSAwAABAiCHQAAQIAg2HlJVFSUjBo1ynyFM1AnzkS9OA914kzUizNFOaxegm7yBAAAQKCixQ4AACBAEOwAAAACBMEOAAAgQBDszsHEiROlevXq5jYibdq0kaVLl57x/E8//VTq169vzm/cuLHMnDnTZ2UNFoWpk8mTJ0vHjh2lVKlSZuvWrdtZ6xC++V1xmzp1qllI/Oqrr/Z6GYNNYevk6NGjct9990mlSpXMIPG6devy/zAH1MuECROkXr16EhMTYxbJHTJkiKSmpvqsvIHuxx9/lCuuuMIsDKz/L/rqq6/Oes3ChQulRYsW5vekdu3a8t5774lP6eQJFN7UqVNdkZGRrilTprj+/PNP14ABA1wlS5Z07d+/P9/zFy9e7AoLC3M9//zzrrVr17oef/xxV0REhGvNmjU+L3ugKmyd9O3b1zVx4kTXypUrXevWrXPdeuutrvj4eNeuXbt8XvZAVth6cdu6daurSpUqro4dO7quuuoqn5U3GBS2TtLS0lytWrVy9erVy7Vo0SJTNwsXLnStWrXK52UPZIWtl48++sgVFRVlvmqdzJ4921WpUiXXkCFDfF72QDVz5kzXY4895vriiy90oqnryy+/POP5iYmJrtjYWNfQoUPN3/rXXnvN/O2fNWuWz8pMsCui1q1bu+677z7PflZWlqty5cqucePG5Xv+v/71L1fv3r0tx9q0aeO6++67vV7WYFHYOsktMzPTVaJECdf777/vxVIGn6LUi9ZFu3btXG+//bbrlltuIdjZXCdvvvmmq2bNmq709HQfljL4FLZe9NwuXbpYjmmgaN++vdfLGoykAMHukUcecV144YWWY3369HH17NnT5St0xRZBenq6LF++3HTd5bxVme4vWbIk32v0eM7zVc+ePU97PrxfJ7mdOHFCMjIypHTp0l4saXApar08+eSTUr58ebnjjjt8VNLgUZQ6mT59urRt29Z0xVaoUEEaNWokY8eOlaysLB+WPLAVpV7atWtnrnF31yYmJpru8V69evms3HDe3/qgu1fs+XDo0CHzPzT9H1xOur9+/fp8r9m3b1++5+tx2FMnuT366KNmHEXuX0r4tl4WLVok77zzjqxatcpHpQwuRakTDQzff/+99OvXzwSHzZs3y8CBA80/hHRhVthTL3379jXXdejQwdwkPjMzU+655x4ZMWKEj0qNgv6tT0pKkpMnT5qxkN5Gix0gIs8++6wZqP/ll1+aQcuwR3Jystx8881mYkvZsmXtLg7+Jzs727SgvvXWW9KyZUvp06ePPPbYYzJp0iS7ixbUdJC+tpy+8cYbsmLFCvniiy9kxowZ8tRTT9ldNNiIFrsi0D84YWFhsn//fstx3a9YsWK+1+jxwpwP79eJ2/jx402wmzdvnjRp0sTLJQ0uha2XLVu2yLZt28wstJyhQoWHh8uGDRukVq1aPih54CrK74rOhI2IiDDXuTVo0MC0TmgXYmRkpNfLHeiKUi9PPPGE+YfQnXfeafZ1tYWUlBS56667TPDWrlz41un+1sfFxfmktU5R60Wg/xPTf7XOnz/f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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Analyse de points clés ===\n", + "Seuil optimal (F1 max): -1297.4468\n", + " - Précision: 0.7622\n", + " - Rappel: 0.7504\n", + " - F1-score: 0.7563\n", + "\n", + "Seuil par défaut (≈ 0):\n", + " - Précision: 0.0903\n", + " - Rappel: 1.0000\n", + " - F1-score: 0.1657\n", + "\n", + "=== Conclusion ===\n", + "La courbe précision-rappel montre le compromis entre précision et rappel:\n", + "• Seuil élevé → Précision élevée, Rappel faible (peu de faux positifs)\n", + "• Seuil faible → Précision faible, Rappel élevé (peu de faux négatifs)\n", + "• Le seuil optimal pour F1 est: -1297.4468\n" + ] + } + ], + "source": [ + "from sklearn.metrics import precision_recall_curve\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "print(\"=== Calcul des précisions, rappels et seuils avec precision_recall_curve ===\\n\")\n", + "\n", + "# Calculer les précisions, rappels et seuils pour chaque instance\n", + "# Utilise les scores de décision calculés précédemment\n", + "precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_train_scores)\n", + "\n", + "print(f\"Nombre de seuils calculés: {len(thresholds)}\")\n", + "print(f\"Nombre de valeurs de précision: {len(precisions)}\")\n", + "print(f\"Nombre de valeurs de rappel: {len(recalls)}\")\n", + "\n", + "print(f\"\\n=== Analyse des seuils ===\")\n", + "print(f\"Seuil minimum: {thresholds.min():.4f}\")\n", + "print(f\"Seuil maximum: {thresholds.max():.4f}\")\n", + "print(f\"Premiers 10 seuils: {thresholds[:10]}\")\n", + "print(f\"Derniers 10 seuils: {thresholds[-10:]}\")\n", + "\n", + "print(f\"\\n=== Analyse des précisions ===\")\n", + "print(f\"Précision minimum: {precisions.min():.4f}\")\n", + "print(f\"Précision maximum: {precisions.max():.4f}\")\n", + "print(f\"Précision moyenne: {precisions.mean():.4f}\")\n", + "\n", + "print(f\"\\n=== Analyse des rappels ===\")\n", + "print(f\"Rappel minimum: {recalls.min():.4f}\")\n", + "print(f\"Rappel maximum: {recalls.max():.4f}\")\n", + "print(f\"Rappel moyen: {recalls.mean():.4f}\")\n", + "\n", + "plt.plot(recalls, precisions, linewidth=2)\n", + "plt.xlabel(\"Rappel\")\n", + "plt.ylabel(\"Précision\")\n", + "plt.title(\"Courbe Précision-Rappel\")\n", + "plt.grid(True, alpha=0.3)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Analyse de points spécifiques\n", + "print(f\"\\n=== Analyse de points clés ===\")\n", + "\n", + "# Trouver le seuil optimal (F1-score maximum)\n", + "f1_scores = 2 * (precisions[:-1] * recalls[:-1]) / (precisions[:-1] + recalls[:-1])\n", + "f1_scores = np.nan_to_num(f1_scores) # Remplacer NaN par 0\n", + "optimal_idx = np.argmax(f1_scores)\n", + "optimal_threshold = thresholds[optimal_idx]\n", + "optimal_precision = precisions[optimal_idx]\n", + "optimal_recall = recalls[optimal_idx]\n", + "optimal_f1 = f1_scores[optimal_idx]\n", + "\n", + "print(f\"Seuil optimal (F1 max): {optimal_threshold:.4f}\")\n", + "print(f\" - Précision: {optimal_precision:.4f}\")\n", + "print(f\" - Rappel: {optimal_recall:.4f}\")\n", + "print(f\" - F1-score: {optimal_f1:.4f}\")\n", + "\n", + "# Seuil par défaut (0)\n", + "default_idx = np.where(thresholds <= 0)[0]\n", + "if len(default_idx) > 0:\n", + " default_idx = default_idx[0]\n", + " default_precision = precisions[default_idx]\n", + " default_recall = recalls[default_idx]\n", + " default_f1 = 2 * (default_precision * default_recall) / (default_precision + default_recall)\n", + "\n", + " print(f\"\\nSeuil par défaut (≈ 0):\")\n", + " print(f\" - Précision: {default_precision:.4f}\")\n", + " print(f\" - Rappel: {default_recall:.4f}\")\n", + " print(f\" - F1-score: {default_f1:.4f}\")\n", + "\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(\"La courbe précision-rappel montre le compromis entre précision et rappel:\")\n", + "print(\"• Seuil élevé → Précision élevée, Rappel faible (peu de faux positifs)\")\n", + "print(\"• Seuil faible → Précision faible, Rappel élevé (peu de faux négatifs)\")\n", + "print(f\"• Le seuil optimal pour F1 est: {optimal_threshold:.4f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "da6f1f8b", + "metadata": { + "id": "da6f1f8b" + }, + "source": [ + "# III- Apprentissage d'un classifieur multi-classes\n" + ] + }, + { + "cell_type": "markdown", + "id": "f4510e3b", + "metadata": { + "id": "f4510e3b" + }, + "source": [ + "## 1 - Apprentissage des données" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "7dc960b0", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7dc960b0", + "outputId": "82d39e25-e7f6-446c-a31a-96bbd7f15e2a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Apprentissage d'un classifieur SGD multi-classes ===\n", + "\n", + "Entraînement terminé\n", + "\n", + "=== Informations sur le modèle multi-classes ===\n", + "Classes apprises: [0 1 2 3 4 5 6 7 8 9]\n", + "Nombre de classes: 10\n", + "\n", + "=== Test de prédiction ===\n", + "Vraie classe de la première instance: 5\n", + "Prédiction du modèle multi-classes: 5\n", + "Prédiction correcte: True\n", + "\n", + "Scores de décision pour chaque classe:\n", + " Classe 0: -34247.2435\n", + " Classe 1: -34038.0553\n", + " Classe 2: -9402.6905\n", + " Classe 3: -1112.1161\n", + " Classe 4: -18693.7725\n", + " Classe 5: 2899.9443 ←\n", + " Classe 6: -26730.2695\n", + " Classe 7: -23111.9520\n", + " Classe 8: -6841.6073\n", + " Classe 9: -13138.4632\n", + "\n", + "=== Conclusion ===\n", + "Le modèle SGD a été entraîné avec succès pour classifier les 10 chiffres (0-9)\n", + "Il peut maintenant prédire n'importe quel chiffre manuscrit du dataset MNIST\n" + ] + } + ], + "source": [ + "from sklearn.linear_model import SGDClassifier\n", + "\n", + "print(\"=== Apprentissage d'un classifieur SGD multi-classes ===\\n\")\n", + "\n", + "# Création d'un objet SGDClassifier pour classification multi-classes\n", + "sgd_multiclass = SGDClassifier()\n", + "\n", + "# Entraîner le modèle avec toutes les classes (Y_train au lieu de y_train_5)\n", + "sgd_multiclass.fit(X_train, Y_train)\n", + "print(\"Entraînement terminé\")\n", + "\n", + "# Afficher les informations sur le modèle\n", + "print(f\"\\n=== Informations sur le modèle multi-classes ===\")\n", + "print(f\"Classes apprises: {sgd_multiclass.classes_}\")\n", + "print(f\"Nombre de classes: {len(sgd_multiclass.classes_)}\")\n", + "\n", + "# Test de prédiction sur la première instance\n", + "print(f\"\\n=== Test de prédiction ===\")\n", + "premiere_instance = X.iloc[0:1]\n", + "prediction_multiclass = sgd_multiclass.predict(premiere_instance)\n", + "vraie_classe = Y.iloc[0]\n", + "\n", + "print(f\"Vraie classe de la première instance: {vraie_classe}\")\n", + "print(f\"Prédiction du modèle multi-classes: {prediction_multiclass[0]}\")\n", + "print(f\"Prédiction correcte: {prediction_multiclass[0] == vraie_classe}\")\n", + "\n", + "# Obtenir les scores de décision pour toutes les classes\n", + "scores_decision = sgd_multiclass.decision_function(premiere_instance)\n", + "print(f\"\\nScores de décision pour chaque classe:\")\n", + "for i, (classe, score) in enumerate(zip(sgd_multiclass.classes_, scores_decision[0])):\n", + " marker = \" ←\" if classe == prediction_multiclass[0] else \"\"\n", + " print(f\" Classe {classe}: {score:.4f}{marker}\")\n", + "\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(\"Le modèle SGD a été entraîné avec succès pour classifier les 10 chiffres (0-9)\")\n", + "print(\"Il peut maintenant prédire n'importe quel chiffre manuscrit du dataset MNIST\")" + ] + }, + { + "cell_type": "markdown", + "id": "82287df9", + "metadata": { + "id": "82287df9" + }, + "source": [ + "## 2- Evaluation du modèle d'apprentissage sur les données d'apprentissage" + ] + }, + { + "cell_type": "markdown", + "id": "3f807144", + "metadata": { + "id": "3f807144" + }, + "source": [ + "### 2-1- Taux de classification" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "6f487ebf", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6f487ebf", + "outputId": "3c22b7e0-263f-4fb9-9310-26206a21c930" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Évaluation du classifieur SGD multi-classes avec validation croisée ===\n", + "\n", + " Fold 1: 0.8786 (87.86%)\n", + " Fold 2: 0.8512 (85.12%)\n", + " Fold 3: 0.8707 (87.07%)\n", + "\n", + "b. Moyenne des taux de classification: 0.8668 (86.68%)\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import cross_val_score\n", + "\n", + "print(\"=== Évaluation du classifieur SGD multi-classes avec validation croisée ===\\n\")\n", + "\n", + "# Utiliser cross_val_score avec 3-fold cross-validation sur le modèle multi-classes\n", + "# Évaluer avec toutes les classes (Y_train) au lieu de la classification binaire\n", + "scores_multiclass = cross_val_score(sgd_multiclass, X_train, Y_train, cv=3, scoring='accuracy')\n", + "\n", + "# a. Afficher le taux de classification (accuracy) de chaque fold\n", + "for i, score in enumerate(scores_multiclass, 1):\n", + " print(f\" Fold {i}: {score:.4f} ({score*100:.2f}%)\")\n", + "\n", + "# b. Afficher la moyenne des taux de classification\n", + "moyenne_accuracy = scores_multiclass.mean()\n", + "print(f\"\\nb. Moyenne des taux de classification: {moyenne_accuracy:.4f} ({moyenne_accuracy*100:.2f}%)\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "60f56dbb", + "metadata": { + "id": "60f56dbb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Amélioration avec StandardScaler ===\n", + "\n", + "Forme des données standardisées: (60000, 784)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\BreizhHardware\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\sklearn\\linear_model\\_stochastic_gradient.py:726: ConvergenceWarning: Maximum number of iteration reached before convergence. Consider increasing max_iter to improve the fit.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Évaluation avec données standardisées:\n", + "Fold 1: 0.8999 (89.98%)\n", + "Fold 2: 0.8994 (89.94%)\n", + "Fold 3: 0.8987 (89.87%)\n", + "Moyenne: 0.8993 (89.93%)\n", + "\n", + "=== Comparaison des résultats ===\n", + "Sans standardisation: 0.8668 (86.68%)\n", + "Avec standardisation: 0.8993 (89.93%)\n", + "Amélioration: +0.0325 (+3.25 points de %)\n", + "✅ Les résultats sont MEILLEURS avec la standardisation\n" + ] + } + ], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.model_selection import cross_val_score\n", + "\n", + "print(\"=== Amélioration avec StandardScaler ===\\n\")\n", + "\n", + "# a. Instancier un objet de la classe StandardScaler\n", + "scaler = StandardScaler()\n", + "\n", + "# b. Appliquer fit_transform sur les données d'apprentissage\n", + "X_train_scaled = scaler.fit_transform(X_train)\n", + "print(f\"Forme des données standardisées: {X_train_scaled.shape}\")\n", + "\n", + "# c. Évaluer le classifieur SGD avec les données standardisées\n", + "scores_scaled = cross_val_score(sgd_multiclass, X_train_scaled, Y_train, cv=3, scoring='accuracy')\n", + "\n", + "print(\"\\nÉvaluation avec données standardisées:\")\n", + "for i, score in enumerate(scores_scaled, 1):\n", + " print(f\"Fold {i}: {score:.4f} ({score*100:.2f}%)\")\n", + "\n", + "moyenne_scaled = scores_scaled.mean()\n", + "print(f\"Moyenne: {moyenne_scaled:.4f} ({moyenne_scaled*100:.2f}%)\")\n", + "\n", + "# Comparaison avec les résultats précédents\n", + "print(f\"\\n=== Comparaison des résultats ===\")\n", + "print(f\"Sans standardisation: {moyenne_accuracy:.4f} ({moyenne_accuracy*100:.2f}%)\")\n", + "print(f\"Avec standardisation: {moyenne_scaled:.4f} ({moyenne_scaled*100:.2f}%)\")\n", + "\n", + "amelioration = moyenne_scaled - moyenne_accuracy\n", + "print(f\"Amélioration: {amelioration:+.4f} ({amelioration*100:+.2f} points de %)\")\n", + "\n", + "if amelioration > 0:\n", + " print(\"✅ Les résultats sont MEILLEURS avec la standardisation\")\n", + "else:\n", + " print(\"❌ Les résultats ne sont PAS meilleurs avec la standardisation\")" + ] + }, + { + "cell_type": "markdown", + "id": "ab0c9c23", + "metadata": { + "id": "ab0c9c23" + }, + "source": [ + "### 2-2- Matrice de confusion" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "55bf66d7", + "metadata": { + "id": "55bf66d7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Matrice de confusion du classifieur SGD multi-classes ===\n", + "\n", + "Prédiction des classes avec cross_val_predict...\n", + "Forme des prédictions: (60000,)\n", + "Classes prédites uniques: [0 1 2 3 4 5 6 7 8 9]\n", + "\n", + "a. Matrice de confusion (valeurs absolues) 10x10:\n", + "[[5622 2 42 25 13 55 65 14 35 50]\n", + " [ 3 6484 42 26 10 30 11 47 62 27]\n", + " [ 37 94 4879 273 64 90 166 125 167 63]\n", + " [ 18 33 125 5299 22 163 27 82 151 211]\n", + " [ 10 25 31 13 5154 39 93 40 66 371]\n", + " [ 60 31 45 340 79 4339 142 30 174 181]\n", + " [ 36 24 54 10 51 92 5590 2 43 16]\n", + " [ 21 31 58 23 60 21 5 5595 24 427]\n", + " [ 31 188 148 542 81 493 74 54 3695 545]\n", + " [ 27 27 13 83 196 53 3 287 36 5224]]\n", + "\n", + "Matrice de confusion normalisée:\n", + "[[9.49181158e-01 3.37666723e-04 7.09100118e-03 4.22083404e-03\n", + " 2.19483370e-03 9.28583488e-03 1.09741685e-02 2.36366706e-03\n", + " 5.90916765e-03 8.44166807e-03]\n", + " [4.44971818e-04 9.61732424e-01 6.22960546e-03 3.85642243e-03\n", + " 1.48323939e-03 4.44971818e-03 1.63156333e-03 6.97122516e-03\n", + " 9.19608425e-03 4.00474637e-03]\n", + " [6.21013763e-03 1.57771064e-02 8.18898959e-01 4.58207452e-02\n", + " 1.07418597e-02 1.51057402e-02 2.78616986e-02 2.09801947e-02\n", + " 2.80295401e-02 1.05740181e-02]\n", + " [2.93589953e-03 5.38248247e-03 2.03881912e-02 8.64296200e-01\n", + " 3.58832164e-03 2.65862013e-02 4.40384929e-03 1.33746534e-02\n", + " 2.46289349e-02 3.44152667e-02]\n", + " [1.71174255e-03 4.27935638e-03 5.30640192e-03 2.22526532e-03\n", + " 8.82232112e-01 6.67579596e-03 1.59192058e-02 6.84697022e-03\n", + " 1.12975009e-02 6.35056488e-02]\n", + " [1.10680686e-02 5.71850212e-03 8.30105147e-03 6.27190555e-02\n", + " 1.45729570e-02 8.00405829e-01 2.61944291e-02 5.53403431e-03\n", + " 3.20973990e-02 3.33886737e-02]\n", + " [6.08313619e-03 4.05542413e-03 9.12470429e-03 1.68976005e-03\n", + " 8.61777628e-03 1.55457925e-02 9.44575870e-01 3.37952011e-04\n", + " 7.26596823e-03 2.70361609e-03]\n", + " [3.35195531e-03 4.94812450e-03 9.25778132e-03 3.67118915e-03\n", + " 9.57701516e-03 3.35195531e-03 7.98084597e-04 8.93056664e-01\n", + " 3.83080607e-03 6.81564246e-02]\n", + " [5.29823962e-03 3.21312596e-02 2.52948214e-02 9.26337378e-02\n", + " 1.38437874e-02 8.42591010e-02 1.26474107e-02 9.22919159e-03\n", + " 6.31515980e-01 9.31464707e-02]\n", + " [4.53857791e-03 4.53857791e-03 2.18524122e-03 1.39519247e-02\n", + " 3.29467137e-02 8.90906035e-03 5.04286435e-04 4.82434023e-02\n", + " 6.05143722e-03 8.78130778e-01]]\n" + ] + }, + { + "data": { + "image/png": "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6sqczunTp1XeSf89duzYqi96+PBho21CO14h7Wtw9SS4fZO06/JW9kH2RepJ4H3V9a8N+6+Bj8fKlSv9ypUrp/reUk5Sp07t16ZNG7/79+8b7cOrV69UXzpjxoxqO/nswoUL+40ePTrI94GZM2f65cmTR9VZ6f9K2evRo4ffvXv3gqSNrEMk+cfUAUoiot9NrkLLHDlyVSykSYSJQiNXV2X0wOnTp0OcC4mIiIiIiMjcMDhIRFZB5pyT22Tl6b0NGzY09e6QGdI9XU6ebk1ERERERGQpGBwkIiIiIiIiIiKyUnxaMRERERERERERkZVicJCIiIiIiIiIiMhKMThIRERERERERERkpRgcJCIiIiIiIiIislJRYcbkqZH37t1DvHjxEClSJFPvDhEREYUDeVbaq1evkDJlSkSOzOuYRBGB/WoiIiLr7VebdXBQOjD29vam3g0iIiL6Dby9vWFnZ2fq3SCyCuxXExERWW+/2qyDg3JlU8SoOAqRosWCObsztzEsKTJtCXjVXHu+frWMsmVJIke2jHpiKe2WpXj16iUypUutP88T0e+nq2/Rc7ZApCjRYc68do2EpeD5SVss6fsB+9Xaw361tvj5WU6/OnP67/ero1pC4yyBQXMPDtrY2MBSWEpjYEknf0vBToz2sBNDvxPbYSIT9KujREekKDFgztivpt/Fks5L7FdrD/vV2uJnGckIc/vFiXyIiIiIiIiIiIisFIODREREREREREREVorBQSIiIiIiIiIiIivF4CAREREREREREZGVYnCQiIiIiIiIiIjISjE4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeAgERERERERERGRlWJwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERWShPBwSlTpiBt2rSIGTMmChQogOPHj//Wz7s8tTbOTKiFo6OqqVetwunU8uhRI2Nsy4I4N6kWjo+pDvc/i6nlMaJFwbIepXF2Yi0cHV0dG/qWR/rk8fR/b0Z7F/26XUMqIU+GxNCS9+/fo3at6siZPTPy53ZCpQplcfPGDZibBfPnInb0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aEjEli+tpeNn+9VaK1u/0q/WUtvl52cZZUvO7ymSfL9fbfLg4LJly9C0aVPMmDFDdWjGjx+P5cuX48qVK9+9TfRngoNa9TuDgxHNxEUq3PBpu9oTnsFBCh8RPVn872Ip7ZalYHCQKGL71OERHNSSXwkOag3PT9piSd8P2K/WHvartcXPMpIR5uCgyR/tWrduXfXkw379+qnhlM7Ozti6datZzB9HRERERKQF7FMTERHRzzJ5cFDI7Q7yIiIiIiKin8M+NREREVn804qJiIiIiIiIiIgo/DA4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeAgERERERERERGRlWJwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERWisFBIiIiIiIiIiIiK8XgIBERERERERERkZVicJCIiIiIiIiIiMhKMThIRERERERERERkpaLCAtyZ2xg2NjYwZwnzdYSleHp8kql3gSxUpEiwCJEsJSEA/Pz8YAksJBmIHNkyypYl1REic+O1ayT71RpiKf1qtuvaYylZYklli/1qbYlsIf3qsKaDIweJiIiIiIiIiIisFIODREREREREREREVorBQSIiIiIiIiIiIivF4CAREREREREREZGVYnCQiIiIiIiIiIjISjE4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeAgERERERERERGRlWJwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERWisFBIiIiIiIiIiIiK8XgIBERERERERERkZUyaXBw//79qFKlClKmTIlIkSJh7dq10JrKFcshXy5HFMjjjNIliuKMhwe0Inq0qBj3v9o4v64fTizvjTlDmhitb1y1IN55TEaVEo76ZXlzpMG++d1wZMn/4LHqH3RtWibI382SLhmeHB6LUX/XgpYsmD8XsaNHxvp1/uWk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kWC/OzgYkX6lE6M1vzM4GJG08mRqCqCRaxm/zJLKlqXkiYUkI9yDg6bC4CDRj2O/OgD71dpjSX0fS2EpfThLKluWkicWkgxYW7/apHMOnjx5UnVadLp27ar+b9q0KebNm2fCPSMiIiIiMh/sVxMREdHPMmlwsESJEhYTHSciIiIiMhX2q4mIiMgqHkhCRERERERERERE4YfBQSIiIiIiIiIiIivF4CAREREREREREZGVYnCQiIiIiIiIiIjISjE4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeAgERERERERERGRlWJwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERWisFBIiIiIiIiIiIiK8XgIBERERERERERkZWKCgvg5+enXubs8bFJsBTZe2yGJfAYWgGWIHpUy7kGYN61PMDXL19hKSJFgkUw81OIxfn6lRlCZCqW0K9+dHQiLEXSxgtgCe64N4QliBktCiyFedfyAF8sqF8d2UL61ZbSjYsK6+pXW07UgIiIiIiIiIiIiH4Ig4NERERERERERERWisFBIiIiIiIiIiIiK8XgIBERERERERERkZVicJCIiIiIiIiIiMhKMThIRERERERERERkpRgcJCIiIiIiIiIislIMDhIREREREREREVkpBgeJiIiIiIiIiIisFIODREREREREREREVorBQSIiIiIiIiIiIivF4CAREREREREREZGVYnCQiIiIiIiIiIjISjE4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeBgIFVcyyN/bicUyJsLZUoWwxkPDzx58kS9170cs2dBvFjR8PTpU2jVjm1bUbRQPhTI44SSRQvh/LmzRuv37tkNm1hRMWXieGjFH/nt4DmuEso6JFPvnVLHx+rOhbGxmwt29CyONqXS67ed3jwPNv3ton/dHOOKMjmSqnWJ40bHtOZ5sKV7UfV7zYulNUl63r9/j4Z1ayKvUza4FMiNGpXL49bNG2pd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ml8JDmrJrwQHtcZSaokl1XcL6cZZTGwjqpX1q6Oa+paHxYsXq6ub8eLFU8MphXRMYsWKZcpdIyIiIiIyG+xXExER0c8y6ZCiadOmqehliRIlkCJFCv1r2bJlptwtIiIiIiKzwn41ERER/SyTjhzUwHSHRERERERmj/1qIiIi+lmWMxkZERERERERERER/RAGB4mIiIiIiIiIiKwUg4NERERERERERERWisFBIiIiIiIiIiIiK8XgIBERERERERERkZVicJCIiIiIiIiIiMhKMThIRERERERERERkpRgcJCIiIiIiIiIislIMDhIREREREREREVkpBgeJiIiIiIiIiIisFIODREREREREREREVorBQSIiIiIiIiIiIivF4CAREREREREREZGVYnCQiIiIiIiIiIjISjE4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrFRUWIBIkSKplzmLDD9YivMjKsISpG2zHJbAZ3Y9WIpPn7/CEkSLajnXZb58tZS2yzLS8emLZdQRS0kHkTmyhH51FMs5zeL+/MawBElqTIYleLb+T1gKi+lXRzHv9sqQpXSrI/lZRkI+Wkh/NKzpsKBTJxEREREREREREf0IBgeJiIiIiIiIiIisFIODREREREREREREVorBQSIiIiIiIiIiIivF4CAREREREREREZGVYnCQiIiIiIiIiIjISjE4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeAgERERERERERGRlWJwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERWisFBIiIiIiIiIiIiK2XS4OC0adPg6OgIGxsb9SpUqBC2bNlisv3p2uVPZMmYFrGiRcLZM2f0y7du2YxC+XKjQB5n5HF2wMIF82EOqriWR/7cTiiQNxfKlCyGMx4eoS7Xiu5dOyNH5vSIFzMKzp31z4f379+jXu0acHbIikL5cqGqazncvHlD/zsVy5aCQ5YMKJw/t3pNnjjeJPt+enQVHB3uij2DyqtX9fz2iBEtMhb86YJjI1yx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JTioNRZS3X8pOKglXyzkXBLW4KBJH/Uoj91u0qQJ7t+/rzojjo6OYe7AEBERERGRP/ariYiI6GeZNDjo7u5uyo8nIiIiIrII7FcTERHRz7Kce9uIiIiIiIiIiIjohzA4SEREREREREREZKUYHCQiIiIiIiIiIrJSDA4SERERERERERFZKQYHiYiIiIiIiIiIrBSDg0RERERERERERFaKwUEiIiIiIiIiIiIrxeAgERERERERERGRlWJwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERWisFBIiIiIiIiIiIiK8XgIBERERERERERkZVicJCIiIiIiIiIiMhKMThIRERERERERERkpRgcJCIiIiIiIiIislJRTb0D5C9SpEiwFFEsJOTsM7seLEHa9ithKTyn1IIl+PLVD5YisoU0XX6wjIR8+PIVluDLF8upI0REvyKKhZxon63/E5YgoetoWIqnm7rBElhSv9pS6nskC0nH+w9fYAk+fw7b9wMLCeMQERERERERERHRj2JwkIiIiIiIiIiIyEoxOEhERERERERERGSlGBwkIiIiIiIiIiKyUgwOEhERERERERERWSkGB4mIiIiIiIiIiKzUTwUHP3/+jJ07d2LGjBl49eqVWnbv3j28fv06vPePiIiIiMhisV9NREREphb1R3/hzp07qFChAry8vPDhwweULVsW8eLFw8iRI9X76dOn/549JSIiIiKyIOxXExERkVmOHOzcuTPy5s2LZ8+eIVasWPrlNWrUwK5du8J7/4iIiIiILBL71URERGSWIwcPHDiAw4cPI3r06EbL06ZNi7t374bnvhERERERWSz2q4mIiMgsRw5+/foVX758CbLcx8dH3QZBRERERETfx341ERERmWVwsFy5chg/frz+faRIkdSEyf3794erq2t47x8RERERkUViv5qIiIjM8rbiMWPGoHz58siePTvev3+PBg0a4Pr160icODGWLFnye/aSiIiIiMjCsF9NREREZhkctLOzw9mzZ7F06VKcO3dOXd1s2bIlGjZsaDSRMhERERERhYz9aiIiIjLL4KD6pahR0ahRo/DfGyIiIiIiK8J+NREREZlFcHD9+vVh/oNVq1b9lf0hIiIiIrJY7FcT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/Pvvv2Fra4vy5cur9+fPn0eNGjWCbCeTJz9+/Pj37CURERERkZljv5qIiIjMMjgYLVo0TJo0CW3atFHvEyRIgPv37wfZzsPDA6lSpfo9e0lEREREZObYryYiIiKzfiBJ7dq11f/16tXD//73P/j6+iJSpEj4+vUrDh06pK6ENmnS5HfuKxERERGR2WO/moiIiMz6acXDhg1D1qxZYW9vryZNzp49O4oVK4bChQurJ60REREREdH3sV9NREREWhD1Rzb28/NTVzYnTpyIfv36qXlSpCOTK1cuZMqU6fftJRERERGRBWG/moiIiMw2OJgxY0ZcvHhRdVrkKicREREREf0Y9quJiIjILG8rjhw5suq8PHny5PftERERERGRhWO/moiIiMx2zsERI0age/fuuHDhwu/ZIyIiIiIiK8B+NREREZndbcVCnpz29u1bODk5IXr06IgVK5bR+qdPn4bn/hERERERWST2q4mIiMgsg4Pjx4//PXtCRERERGRF2K8mIiIiswwONm3a9PfsCRERERGRFWG/moiIiMxyzkEiIiIiIiIiIiKyDAwOEhERERERERERWSkGB4mIiIiIiIiIiKwUg4NERERERERERERW6ocfSKJz48YN3Lx5E8WKFUOsWLHg5+eHSJEiwRS+fvVTL3NmokNHoXjz4TMswc3JtWApio/aB0uwu1sxWAwLaby+mPk5RCdaVMu45hfVQtJBFFbsV4cvCzk1KX7mnRV6r99/giV4vLEbLEWK5otgCbxnNzD1LlAgn758hSWIbiH90bB+P/jh1D558gRlypRB5syZ4erqivv376vlLVu2RLdultNYEhERERH9TuxXExERkRb8cHDwr7/+QtSoUeHl5YXYsWPrl9etWxdbt24N7/0jIiIiIrJI7FcTERGRWd5WvH37dmzbtg12dnZGyzNlyoQ7d+6E574REREREVks9quJiIjILEcOvnnzxujKps7Tp08RI0aM8NovIiIiIiKLxn41ERERmWVwsGjRoliwYIH+vUyW/PXrV/z7778oWbJkeO8fEREREZFFYr+aiIiIzPK2YumslC5dGidPnsTHjx/Ro0cPXLx4UV3hPHTo0O/ZSyIiIiIiC8N+NREREZnlyEEHBwdcu3YNLi4uqFatmrodombNmvDw8ECGDBl+z14SEREREVkY9quJiIjILEcOivjx46NP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mFcTERGRYouDmzZt+ulfWK1atT9ZHyIiIiIig8W8moiIiPSyOFijRo2f+mXiiZLh4YbxEAciIiIior+NeTURERHpZXHw82c+bYaIiIiI6E8xryYiIiKl+eWnFRMREREREREREZGRFQf37duH3LlzIzQ0NMayly9fIk+ePDh06NDfXj8iIiIiIoPCvJqIiIj0sjg4efJkeHh4wNzcPMayZMmSwdPTE5MmTfrb60dEREREZFCYVxMREZFeFgcvXLiAChUqfHN5uXLl4Ovr+7fWi4iIiIjIIDGvJiIiIr0sDgYHByNevHjfXG5qaorHjx//rfUiIiIiIjJIzKuJiIhIL4uDFhYWuHz58jeXX7x4ERkyZPhb60VEREREZJCYVxMREZFeFgcrVaqEgQMH4v379zGWvXv3DoMHD0aVKlX+9voRERERERkU5tVERESkJKY/+8YBAwZg3bp1yJEjBzp27AhbW1s5//r165gxYwbCw8PRv3//f7muRERERER6j3k1ERER6WVxMF26dDh27BjatWuHfv36QaVSyflx4sRB+fLlZSIj3kNERERERN/GvJqIiIj0sjgoZM6cGdu2bcPz589x+/ZtmcjY2NggRYoU/24NiYiIiIgMDPNqIiIi0sviYASRtBQsWPDvrw0RERERkRFhXk1ERER680ASIiIiIiIiIiIiMiwsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERuq3HkiiNHHiqCd9FkffA4ji82cVDEGSBAaxe8inHxqK/T3dYAjSNl4AQ/F0eUsYAlMTGARDOZfEN+W1SyJdYV6tLIaSx5kligdDYCjfc4RA70YwBGlcesJQPD8+EYbAUPK4OAZyLkkQ7+e+6BhGqxEREREREREREdEvY3GQiIiIiIiIiIjISLE4SEREREREREREZKRYHCQiIiIiIiIiIjJSLA4SEREREREREREZKRYHiYiIiIiIiIiIjBSLg0REREREREREREaKxUEiIiIiIiIiIiIjxeIgERERERERERGRkWJxkIiIiIiIiIiIyEixOEhERERERERERGSkWBwkIiIiIiIiIiIyUiwOEhERERERERERGSkWB4mIiIiIiIiIiIwUi4NERERERERERERGisVBIiIiIiIiIiIiI8XiIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlI6Lw4+ePAATZo0QapUqZAoUSLY29vjzJkzOlufqpXKo1A+BzgXcEKZkq44f+6cnP/hwwd069IR9rlzoKBTXrRq3hRK9f79e9StXUOuq4ilcoWyuHP7drT3HNi/D0kSmGDalMlQulw5rOFolxOFCzrJac3qldGWL1rogyQJ4mLzxg1Qsu5dO8M2exYkihcHF86f18y/fesWShQvKturWOGCuHrlCpQup401HPLklPuJmNasUrdJj26d5bLE8eNGi1Gpdu/cgeJFCsI5vwNKFi+CSxcvyPltPVrBMY8tChdwRJkSLvA9c1rXq4qrM+rg3JRaOD6umpxqF7X+7nyhrKMFDo+uipPjq2P/iMqwz5xCsyyNeUJs6F8WF6bWxukJNVAsVzro23FMH4jjk9gfNn05PrVxb6U5x7gUKYT9+/ZCH49b4pzYtXNH2OWyQQFHe7Rs1kSn60lEus+pRT5T0rUY8ua2lce3b+UzC3y85bE9T87saN/WA58+fZLz7/v5oXyZkkifOrk8RurKz+ZlC+Z7y2NgbttsaO8ZGcePlmkzjlJuxeCQxxbFixbC1auxx7HQxxt5c+eAXc7s6NAuentUKFsSGdIkl/m3LrFNFNgmt2+hdAkX+R3NrZgzrn0nFpFT581lg47t2mhiObh/H0q4FEYBRzsUdLLHgP/64PPnz1qOAshmlRr7vTvh4pq+OLKwK3JljZkPx4kTB6O6VMWZFb1wfnUfzBpQH/FMTWK8b87gBnh3eiKSJU0IbTOUfUTgueTTTy0zuOLg8+fPUaxYMcSLFw/bt2/H1atXMWHCBKRIEfkFVtsWL1uJU2cv4OSZc+jUpRs83VvK+QP795UHhotXbuD0uYsYOWYclKy1exu5riKWKlWro52nu2bZy5cvMeC/vqhQsRL0xcKlK3Di9Dk51albXzNf7PwL5s9DIefCULpatetg74EjyJQ5c7T5Hdt7yva6dPUmevTqA4/WLaAPFi1dIfcTMdWpp26TmrXqYM/+wzFiVCJx/Gndogm8vBfgpO8F/G/UWLRqri5wVK1WA2cuXMGJM+fRo1dfNG1UD0rQfNIBFOm1SU5rj9377vzkSeLDu7Mr2kw/DOeeG9F/8RnM7+ym+cywxvlx6uZjOHReC8+ZR+DTxQ2mJnGgL8cxfSCOTz7e0Y9PY8dP1Jxjps/0QpOG9XSSDP/pcUucQ8Q5URy3zpy/hFFjx+tsHYlIGTl1pw5t0crdAxev3kCPnr3R5ksOHZXfvXsYNmQQdu87hMvXbiEkOATe8+bIZWbm5hg8dDh8Fi2FLv1MXibiGDpkoMx5rly/jZCQYHjPnfPDZdrUqWNbtGztgQtXbqB7z96a7zRfxzFs6CDs2ncIl760x/wo7TFoyHD4LNRtewhsE+W1SZcO7dCylTvOX76Obj16yQvrscXyv2GDsXPvQVy4elP+3X2858plyVOkgM+iZThz/jIOHz+Nk8ePY9mSRVqPY3q/uvBefwJ564zGhIX7MHdwwxjvaVHdGY62lijSZCIc647BZ5UKHRu4RntP9ZL2+BSmu3zOUPYRgeeSOT9cZpDFwTFjxsDKygo+Pj4oVKgQrK2tUa5cOWTLlk1n65Q8eXLNz6GhL8WlArx58wYLfeZjyLAR8suQkD59eihVwoQJZeEvYl3FF9P79/00y7t17oi+/w1AypSpoM/EF2pxlWDCpKlIkCABlM6luCssLS2jzQsJCcFZ3zNo2FhdlKpZqzYeBAbo5R1S34pRqe7dvSP3gdy588jXxVyKIzDAH+fPnUXlqtVgamoq5xd0LoyHDx4gLCwM+sQ6nRmevfqAa4Ev5Otj14NhmToJHK3V+32totbw3n1D/nz2zhM8ev4WxXOn15vjmN4cnyZHPz7FOMfo4T6tPid6Y+hw/TgnEhkDXefUmnymkTqfqVGrNgJjyWfWr1uDylWqymOGOH64t/HE6pUr5LKUKVOiaDEXJEmSBLrys3nZunVrUKVKtShxtMWqlct/uEybcZyL2h41v9MelSPbo7WHJ1avit4eiXXYHgLbRHlt8ljEcvYMGnyJpXpELHeix7Jh/VpUqlwV6aLG8mV/d3B0gnXWrJqcL6+DA/zv39dqHGlSJEW+XFZYvt1Xvl6/7yIs0iVHVsvU0d6X1yYj9p26iU9h4fL1rmPX0LBSfs3ytCmToleLMugzaSN0wVD2kWix8FwCXbSJTouDmzZtQoECBVC3bl2kTZsWTk5OmDtXfTVBl9xbNodN1kyyGu3tswh379xBipQpMW70SHlLqOhurPSuYFHNmDZF3nUjrFu7BnHjxkWVqtWgTzxaNUfBfHnRzrM1Hj9+LOdNnTwRRYoWhVO+yIOzvgkMCED6DBk0hSix41taZUJAgD/0ok2c8qJtm8g20SfZstvg2bOnOHH8mHy9dfMmvHr1KkYBaub0KShfoZKmjXRpbsfiODWhBma2K4bU5gm+O/9OUChSmiWAc4608nWlAlYwTxwfmdIkRcqkCRDPJC6CX7zT/I77Ia9hlTop9OE4pg/E8alwkaLIF8vxaeB/fWUXiIb1amPZSvUxWZ9EnBPHinOicwGULlFcr86JRIZI1zm1+PKWPn30fMYqlnxGvM6UKfJO5MyZsygq5/nZvCzA3z/aHdVR4/jeMm158JPtIS6KWkVd1yzKag+BbaLANgkMQLpYYgn0jyWWr/Z3Me9rwUFBspBYoVJlaJNluuQIehqK8PDIO/4Cg17AKn3khVzh7PUAVHG1g1mSBDA1iYvaZRyROUNKzfIZ/euh/7TNeP32A3TBUPYRgeeSLDptE51+I7l79y5mzZoFGxsb7Ny5E+3atUPnzp2xcOHCWN8vxjgKDQ2NNv0L83wW4tZdfwweMhwD+vdFWHiYvJKRM1duHD1xGuMnTkGzxg0QHBwMpRNf3sRVnOEjRiEoKAhjRv4P4ydNgT7ZtecgTvlewLGTvkiVKjXatG6BK1cuY+OGdejTb4CuV88o7d57UHaNPHbKF6lTpdabrtBRJUuWDIuXr8bggf/BpXAB7N2zS+7jpiaRRcAVy5Zg/ZrVmDrTC7pWbvB22T24aO+NeBr6AXM6uH53fujbT2gyYT+GNs6PI2OqorSDBa4GPEeYgruw/sxxTB9cuXwZG9avk3dox2b4yNGye4AYxqJ/vz74+PEj9Im4i1acE3OJc+LJM/Lu7aaN6uvFOZHIUP1qTq3NvJqI6FvEcade7ero2r0X8uUvACVavPk0dh2/jl1eHbB7Tgfc8n+MsC8FRdHlWBQUD57Rz15fRFGZ6rrblbjKOXLkSPlaXOW8fPkyZs+ejebNm8d4/6hRozB06FCtrV+TZs3RuWM7ZMxoIe/saNCosZzv6OSEzFmsceXyJaRLp6xB/KOaNHE8Nq5fh6079yBx4sQ4eGA/goIewbmAo1z+9MkTbN2yCU+ePJbdw5TKKlMm+X8xjk6HTl3haGeLY0cOyzu88ubJobnidO3aVRmfh2c76AtLKysEPXokv2yLKwsqlUp9lc1KHbNetEnnrnJgZX3kVqKknCK+JGXLlEEWCAXx4JtR/xuGLTv2KGI/D3zyRv4/LFyFGVuv4PzU2t+dLxy6EoRDg7fLn+ObxsXduQ1wPfAFnr3+IJOadMkTae4ezJw2KQKevIbSj2P64OhR9fFJDEAccXy6/uX41CbK8alU6TLo3qUTLl++FOsdhkre//XxnEhkyH41p/7bebWlpZU8xkXNZwJiyWfE67t372hei2OlknKen83LxHHw3p3Y4/jeMm2x+Mn2EHeyiGFWNOvqp6z2ENgmCmwTSysExxKL5ZfvB9+M5b6fnBdB9NipWa0SKlepJsf617bA4BdIn8ocJiZxNXcPWqZPjoAg9ZA8UY2Yu1NOQt2yjrh2N0j+7FYgO1ycsqKii/r7g3B6eS/U7TEfF24+0EochrKPCDyX+Om0TXR652CGDBmQO3fkjiTkypUL/l/dkhyhX79+8mEaEVNAQMBfXZ8XL17g4cOHmtfi6ZIpU6WS3TNKliqN3bt2agaHvO93D7Y5c0GppkyaiNUrlmPLjt2aMa4qVqqM+w+CceO2n5zEwyP69R+k6MKgGNtKtEuE1SuXyzEqRAHw7v2HuHbznpzEeGTTZ3jpVWFQENuWo1M+LF+6RL5ev24tLCwskS17duhbm+gjcdCOMHrkcLiWKCX/9mvXrMLwwQOxeftuTSFUlxInMEWyxPE1r+u6ZMWFe0+/OT9C+uSJND/3reOIg5cf4W7QK/l6/Qk/tC6rLurmy5YaGVMmxuGr6kRHyccxfSAKgPf8H+L6rXtyEsenaTO95MDdUccaOX36FB4/DoG1tXrMHX2ROnVqvTsnEhm6X82p/3ZerclnlqnzmQ3fyGfEOGtbt2yWvVnEl6V5c7w0DzXTp7ysZs3a2LJlU5Q4ZqNuvQY/XKb1OCLaY/132mNrZHt4z/WK9uA/JWCbKK9N0qRNCwfHfLKHjbAxIpZs0WOpXqMWtm3dLC+SamL5sr+/fv0aNatWQtmy5dG7X3+dxPH4+WucvxGIhhXVF2hrlsqLB8EvcTfwSbT3JYhviuRm6pw6VbIk6NGiNCYu3idftxy4FDZVhiNn9f/JSSjYcJzWCoOGtI9Ei4XnEuiiTXR656B4qtqNG+oB8SPcvHkTmb/xpFMxqPu/fPBE6MuXaNywHt6/eyfvikidJg3Wrt8s+4hPnT5LPilTjBUllk2bMRsWFhZQosDAQPTt3UMO8lqhjPquqPgJEuDwsZPQNyHBwWjUoA7Cw8PlTiG+RM/1/nYXGSXr2M4T27dvlSfIapXLI6mZmexaKJ5YKrrljh0zEuZm5vCa5wOlt0nD+nXw+UubZLHOinnzF2qeyrRj+zYZY/UqFWSM4glSSvW/YYNw9MgRhIeFoVDhIpjpNU/Ob928iRw8uX6dGpr3ijsIU6XSzUN80iZLiGU9S8Ekbhx5PLoX/Aoe0w9/c36EAfWdUDRXOjk+yqmbIWg/66hm2cAlZzCvkysuTK2Nj2HhaD31kLz7UCkM6TgW4dOnT3JfF+caE1NTOVDy0hWrtfo00b913BLnQDHe6IB+fdTnxJleij0nEhmDX82p/0VeLY4L4qmS40aPkk+L9Jo7X84X+bO4M0iMdy2O6QMGDUHpEi5yWXFXN7h7eMqf3759i7x5bPHxwwdZrMxubYVGjZpgmJaHlPhWXtaujbt8YFlEHAMHDUUpt2JymatrCTkgvvC9Zdo0dfpseHq0xPgx6vaY/aU92rd1R+XK1WQssj0GDkGZiPZwc5MPjYhoDwe7yPawyWolHxIw7H/aH+KDbaK8NpkyY5Z8QvH4saNhbm6OWXO85fwObT1QqUpVuc+LWP4bMBhlSxaXy1xc3dDKvY38edb0qfA9cwpv377Bpo3r5Txx40qvvv9pNY6Oo1Zj7qCG6N2iNELffIDnMPVDLWb2r4eth69g66ErSJY0IXbO7iCfUhw3ThzMWHEI2w5fhZIYyj4i8FziqbM2iaMS3+515PTp0yhatKjs0lCvXj2cOnUKHh4emDNnDho3VndX+tEYBWLcsKAnL+RBSZ9FPPHREHz+rJwCw5+IG9cw2kSHu/hfZyCbFtI2XgBD8XR5SxgCQ9lPDOVcIs7v6VIlk0mdvp/fifQhpxaYVysT82plMZT2EESxyxCkcekJQ/H8+EQYAubV+plX67RbccGCBbF+/XosX74cdnZ2GD58OCZPnvzTSQwRERERkbFjTk1ERER6261YqFKlipyIiIiIiOj3MKcmIiKi36XTOweJiIiIiIiIiIhId1gcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iAREREREREREZGRYnGQiIiIiIiIiIjISLE4SEREREREREREZKRYHCQiIiIiIiIiIjJSLA4SEREREREREREZKRYHiYiIiIiIiIiIjBSLg0REREREREREREaKxUEiIiIiIiIiIiIjxeIgERERERERERGRkWJxkIiIiIiIiIiIyEixOEhERERERERERGSkWBwkIiIiIiIiIiIyUiwOEhERERERERERGSlTGIA4ceLISZ+pVCpdrwIZKEPatEzi6vd+HuHp8pYwFKka+sAQGEqbvHr3CYbgtYHEQaSPmFcTGQdTE8O4T+j58YkwFCnKj4IheL6zHwzBy7eGkY+++sk4DOOIQERERERERERERL+MxUEiIiIiIiIiIiIjxeIgERERERERERGRkWJxkIiIiIiIiIiIyEixOEhERERERERERGSkWBwkIiIiIiIiIiIyUiwOEhERERERERERGSkWB4mIiIiIiIiIiIwUi4NERERERERERERGisVBIiIiIiIiIiIiI8XiIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iAREREREREREZGRYnEwivfv36Nu7Rqwz50DhfI5oHKFsrhz+7ZcNnb0SOTNY4vE8eNi08YN0AdVK5WXcTgXcEKZkq44f+6cnN+jW2fktLGWsVw4fx5KlyuHNRztcqJwQSc5rVm9Us7fsX0bijrnl/MKONljyeKF0Mdtq03rlijolBfO+R1RrHBB7N+3F0r34cMHdO/SEXlz50DBfHnRqkVTOf/2rVso5VYMDnlsUbxoIVy9egVKZWj7e1S22bPI9RfblJhWr1LvM0pxdUYdnJtSC8fHVZNT7aLW350vlHW0wOHRVXFyfHXsH1EZ9plTaJZtH1IBV2bU0XyuY+XcUOq2FeHA/n1IksAE06ZMhlItX7IQac3jY9uWjfJ153buKOyUGyWK5kflsm4453tG897mjeqgZLECmildsgTYsW2zDteeiLRN5AAliheVxz6Rz1y9EnsOsGC+N+xy2SC3bTa09/TAp0+ffmqZNuMo6VoMeXPbwqVIoW/H4eMtY82TMzvat/0qju8s05afzckW+njLfM4uZ3Z0aBe5rvf9/FChbElkSJNc5tq6ZEjbFttEeW1iCHFks0iB/VOb4uJCTxyZ0QK5MqeO8Z44cYAx7Urj7HwPnJrbGjsmNELWjJH5dLd6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CH72Bp0m74DfI/VNBafntpbbRHxTEyzbcxmjFh/F73q+s59BtMnLt7+fV9++dUM+ofj5s6dIamaOKTPnIlcee3Tv6InylapoHi6yZIE3pk0aJ38u6uKKsZNnaNZV/I7yJYrgwvX7smbzJ3m1jdWP82pFFAeHDBkCLy8vecXy68HNtVkc1CUFNMNfYyih/M1ClC79i+KgrhhKmxiSXy0OKtXfLA7q0t8uDuoKi4NEv495NfNqJTKUHI55Nf1Lv1scVJo/KQ4qycs/KA4qyc8WB3XerVjc8eXj44PmzZv/UgJDRERERESRmFcTERHR79B5cXDPnj3w9/eXT1MjIiIiIqLfw7yaiIiIfofOLymWK1fOoG79JyIiIiLSBebVREREpJd3DhIREREREREREZFusDhIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iAREREREREREZGRYnGQiIiIiIiIiIjISLE4SEREREREREREZKRYHCQiIiIiIiIiIjJSLA4SEREREREREREZKRYHiYiIiIiIiIiIjBSLg0REREREREREREaKxUEiIiIiIiIiIiIjxeIgERERERERERGRkWJxkIiIiIiIiIiIyEixOEhERERERERERGSkTGEAVCqVnPTZp3D9Xv+o4saBQXj55hMMgXmieDAUL98aRpuYJTSIQ6/0eGkLGIKmi8/CEEytZQdDEPbZcM6JRPrGEPLqj2GfYSjixjGMxPr5m48wBMmTxIehePbaMNokWWLD+a7zdHtfGIJSEw/BEKz2cIYhCAv/uXMi7xwkIiIiIiIiIiIyUiwOEhERERERERERGSkWB4mIiIiIiIiIiIwUi4NERERERERERERGisVBIiIiIiIiIiIiI8XiIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iAREREREREREZGRYnGQiIiIiIiIiIjISLE4SEREREREREREZKRYHCQiIiIiIiIiIjJSLA4SEREREREREREZKRYHiYiIiIiIiIiIjBSLg0RERERERERE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dlFU38TVwROC/4XQL08S1XfJkyj/AXw/6+WXC5f6Llliw/muYxh7O1Bm8mEYgtUezjCUvDpXlrQ/zKt1+g112rRpGDhwINq3by8fwZ0xY0Z4enpi0KBBulwtIiIiIiK9wZyaiIiI/oROi4NmZmaYPHmynIiIiIiI6NcxpyYiIiK9fSAJERERERERERER6Q6Lg0REREREREREREaKxUEiIiIiIiIiIiIjxeIgERERERERERGRkWJxkIiIiIiIiIiIyEixOEhERERERERERGSkWBwkIiIiIiIiIiIyUiwOEhERERERERERGSkWB4mIiIiIiIiIiIwUi4NERERERERERERGisVBIiIiIiIiIiIiI8XiIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiNlCgMQJ04cOemz+Kb6vf6GyDxRPBiC8M8qGAqzhAZxyELcuIazv7/7GA5DsKiJEwxB5rarYQg+f3yr61UgIj0W39Rw7n/Q9+84EZIniQ9DEBb+GYYieWLD+K5jSHn12w9hMAR7uxWHIUhZZSIMgSrs/U+9z3DOnERERERERERERPRLWBwkIiIiIiIiIiIyUiwOEhERERERERERGSkWB4mIiIiIiIiIiIwUi4NERERERERERERGisVBIiIiIiIiIiIiI8XiIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iAREREREREREZGRYnGQiIiIiIiIiIjISLE4SEREREREREREZKRYHCQiIiIiIiIiIjJSLA4SEREREREREREZKRYHo3j//j3q1q4B+9w5UCifAypXKIs7t2/LZcWLOsM5v6Oc8jvaIVG8OLh08SKUyFDiELp37Qzb7Fnkel44f14zv0rFcijolFfGUbpEcZw/dw5Kt3P7NhRzzo8iBZ1Q0MkeSxcvlPPberSCc34HOd+1aCHs37cXStO7exfY22ZFskQmuHghsh327NoBt2KFULSgI0q7FsWlixc0y1QqFUb9byjy2edEkQIOqFK+NJQmVw5rONrlROGCTnJas3qlnF+1UnkUyu8g55Ut5Yrz55W/fUWlL/uHOFY1qV8LBR1yobhzPtSsUh5376iPVb5nTqFsiaJwK1IAzvnsMHXiOM3nmjaoDdfC+TVTqqTxsH3rZiiJ3IbyOcC5gBPKlHTVtMGHDx/QrUtHeXwWbdSqeVNdryrOjKmMYyMqYN/gsnKqXtAKCUzjYmHHYjg+oiL2DymH1d1dYZ02qeYzU1oW1Czb0rcUHLOkiPF7bTKYwW9mLQxv4KjliIhIF27fuoWSrsWQN7ctXIoUwtUrV2J93wIfb3kMzJMzO9q39cCnT59+apm2GFIcJYoXletRrHDBb8cx3xt2uWyQ2zYb2nt+Fcd3lmk7ltJuxeCYx1bmylevxh7LQh9vOOTOAfuc2dGxXeT63vfzQ4WyJZExTXKZb+vKndu3UK6kC/LnzYWSLoVx7RtxLFowX+bPjnlyoHN7T00cp04eh4tzfjkVzp8XXTu2k3mFrtqklFsxOOSxRfEftEne3DlglzM7OsTSJhnSJJf5tq4Yyn4itq0KpYujkGNulHYtjOvfaI8lC+fLvDu/vS26dozcto4cOgiL1GZwK5JfM7179w668KfHYLFtlS9TEulTJ5d5uK5ky5gc+yc1xEXvljgytTFyZU4V4z1x4gBj2rjh7JwWODWrGXaMrYusGZNrlveoV1AuOzGzKQ5ObogCtukNtzj46tUrdO3aFZkzZ0aiRIlQtGhRnD59WperhNbubXDxyg2cOnsBVapWRztPdzn/8LGTOOl7Xk79Bw5Bnjx2sM+bF0plKHHUql0Hew8cQabMmaPNX7J8FU6fuyjj6Ny1O9q0bgElE4Uy95ZN4TXPB8dPn8Oa9ZvRuUNbuQ+MHjcRJ30vyPnTZnqhWaN6+Pz5M5Skeq3a2LH3EDJlimyH58+fy5hmz/XBsdPnMXzkGHi0jCx0zJ4xDZcvXcQJ34s4fuYCvBcuhRItXLoCJ06fk1OduvXlvMXLVuKU7wU5r1OXbvB0bwl9ok/7R/OW7jh1/ioOnzyLSlWqoUuHNnJ+t47t0L1XPxw8fgY79hzC9CkTcf3aVbls8Yq1OHTCV05TZnohRcqUKF22PJREbkNnL+Dkmejb0MD+fREnThx5fBZtNHJMZNFTl9rMPoFSQ3fLaePpADlv0cE7KNJ/O0oO2YXt5x9iYvMCmvdvO/sALgN3yGVTtl3DvHZFo/0+U5M4mNCsALafe6D1WIiMla7z6k4d2qKVuwcuXr2BHj17o00s506/e/cwbMgg7N53CJev3UJIcAi858354TJtMpQ4Orb3lN8HLl29iR69+sAjllxArOvQIQOxZ/9hXLl+GyEhwfCeO+eHy7Stc8e2aNnaA+ev3EC3nr3R9httMnzoIOzadwgXv/zd53/5u5uZm2PQkOGYr+NctGundmjeygO+F6+ha49eaN+mdYz3+Pndw8hhg7F99wGcu3xD/t0XeM+Vy+zsHbD/yAkcOekrc+/Hj0Mwb84sHUQCdPrSJheu3ED3nr1jzZXlvvClTS59o018dNwmhrKfdO/cHs2+5NRduvVCh7Yxt637YtsaPgRbdu3HmYvX8TgkBAvnq7ctIbtNDhw87quZxHlEF/70GCy2rcFDh8NnkW63reldysJ720Xkbe2DCatOYW6PCjHeU6VwdhTJY4FC7RbJaf85fwxr6SKX5c2aBm2qOKJ456Uo3H4xZm06j0kdShlucdDd3R27d+/G4sWLcenSJZQrVw5lypTBgwe6+TKRMGFCVKhYSX5xEwo5F8b9+36xXgFp3jLmDqcUhhKH4FLcFZaWljHmJ08eWVEPfflSE6uSiXV88eKF/PnVq1CkTJUKCRIkiBbLy5cvoUTFXFxh8VU73Lt7BylTpkKu3Hnk66IuxREY4I/z587K11MnjcfQ/41C/Pjx5et06f/tlY6/6es20YftSx/3D3GsKlsh8lhVoJAz/O/flz+LeS+/7C9v3r5BvPjxkSJFyhi/Y8lCH9St31iznSmyDUJfykuDb968wUKf+RgybIQm5vQK3S8+hH3G3ktBmte+d57CKnUSzeudFx4i/LNKvezuU2RInggmcSO3sx5V82DTmUDcDX6t5TUnMl66zKtDQkJw1vcMGjZqIl/XqFUbgYEBmp4rEdavW4PKVarKY584Drq38cTqlSt+uExbDC6Oxuo4ataqjQexxLFu3RpUqVItyrq2xaqVy3+4TNuxnPM9gwYRbVIz9jbZsG4NKlWuKvNNsb6tPTyxZpX6754yZUoULeaCxEkiz2PaJgox58/6on7DxvJ1tRq1ZJtE9JiIsGn9WlSMEkcr9zZYu1odR+LEiREvXjz588ePH/H+3Tud5HgRbdLwB20i94XKkfuCaJPVCmoTQ9lP5LZ1zhf1Gqi3rao1auFhYGDMbWvDOlSsVAXp0qnXtUXrNli3Rt1rSin+xjE4YttKosNtK02yRMhnkw7L96pvbFh/5BYs0phFuytQUEGF+PFMkDC+iXxtnjgBHjx+9WUZEM80LpIkVO/zyZOKZa8NszgoblNdu3Ytxo4dC1dXV2TPnh1DhgyR/581SzdXQL42Y9oUedddVAEBATh86KDmIKIPDCWOr7Vu0QzZra3k1RrvBYuhZOKgtXDJCjSqXxu5bLKgbMnimDNvgaagMah/X9kFonH92liyYg3ixlV+j/9s2W3w7NlTnDx+TL7etmWTvGvB/74fQkND5ZWzrZs3oVTxInJa+6XLrtJ4tGqOgvnyop1nazx+/Fgz371Vc+TIlklehZ7nswj6Rp/2jwheM6ahYuVq8ufps+dh5PDBsLe1RiGHXBg45H8xCszyPLJ6BZo2V+adne4tm8MmayZ5ZdPbZxHu3rkj73IcN3qk7LoiuhsrZRiB6a0L4cDQcpjUogBSJU0QY3mbsjbYcT72AoNHmRzYc+mRpliYzzolCmZLhXl7b/3z9SYiZeTV4stb+vQZYGpqqsl7rKwyISDAP9r7xOuovRAyZ86iec/3lmmLwcQREID0GaLHYRlbHP7+0XrnRIvjO8u06cGvtEmU9c2URTfr+7040n0Vh6WVVSxxBMAqUybN60yZs8j2jCBu+CjmnA/ZrNLBPFkyuLdpB6W2ibhpwCrqNqSwNjGU/eTBg5jbloWVVbTtJqI9LKMcm6wyZY72nnv37qJksYKyW7K3ju5I/RvHYCWwTGOGoGdvNLmxEPg4FFZpzKK9b+uJOzh8MQB+K9rh3vK2KOGUCcMWqb9bX7r7GNPW+eLaQnfcXtIGnWrmR/eZ+/7peuusAhEWFobw8HB550hU4vbVI0eOxPoZMaaCKDpEnf6VsaNH4s6d2xg+YlS0+UsWLUDFylWQOnVq6ANDiSM23gsW4fa9AAwZ+j8M+K8PlExs72NGj8CylWtx7ZYftuzYA/dWzfDkyRO5fNiI0bh0/TYWLV2Jgf/1kVcDlS5ZsmRYtGwVhg7qD9eiBbFvz27kzJVbHsxFvGJ6//4d9h0+Dp8ly/Ff7x7RxiRUgl17Dsruw8dO+iJVqtTRut/Om78QN+/4yy4PA//rC32jT/uHMHHcKNy9exuDho2QrydPHItBQ0fg0o17OHbmIv43dKCmW3HUq+vZs9sgt509lGiez0LcuuuPwUOGY0D/vggLD5N3Ror95OiJ0xg/cQqaNW6A4OBgna5n9TH7UWLILpQZthvPXn/EtNaFoi3vUimXHG9wxNpLMT5bp3AmVC9oiZ6LzsjXieKbYEyTfOi+UP2aiLRD6Xk1Ef0doghy9ORZ3Lj3QO7Dmzeu1/UqkQHI6+iEyzf8sP/oaSxavgYLvOdgw9rVul4tg5c/R3rkzpIa2Rp5IWuj2Thwzh/TOpeRyzKnM0f1Yjawa+mN7E3mYNp6Xyz+r4phFgfNzMxQpEgRDB8+HA8fPpQJzZIlS3D8+HE8evQo1s+MGjVKFiQiJisrq3+ybpMmjsfG9euwcct2eft21HHjFi30QQuFd8U1tDh+pEmz5jh4YD+ePn0KpRIP8Qh6+FB2kxbyFygICwtLXPzqQRclS5eRd99duRzzS7gSubqVxLbd+3Ho2GmMGDMejx49hG2u3PJ27qRJk6Lel64TIpFxLlIUZ311O6bo1yKuzIouGh06dcWxo4djvKdJ0+Y4dFDZ25e+7x/TJk/Alo0bsHr9VnmsevrkCbZu2oA69RvK5Vmss8ouxydPqK+kRViyaD6aNG8FfWiDQwf2I2NGC3lXcING6v3C0ckJmbNY63x/f/Dsrfx/WLgKXrtvonCOyItG7cvbonJ+CzSYdBjvPoZH+5x4cEnPanlQd8IhPA5VD4ieJU1SWKRKjHW9SsgHnYg7Dhu5WGNaq+gFRyIyrLza0tIKQUGPZJEyItcUd3GIOz6iEq/9/dXDR0TcBRXxnu8t0xaDicPKCkGPoscRGFscmTJphvP4el2/t0ybLH6lTaKsr7+fbtb3e3EEfxWHuGsrZhxW8m60CKJHjmjPr4k8u3bd+li1YhmU2ibyLryo25DC2sRQ9hMLi5jb1oOAgBjbjWiPwCjHpgD/+5r3mJubyztR1b/PErXqNsDxY7FfWFL6MVgJAh+/QvqUSaINuWOZxhwBX7oMR2hcJjcOnPfHyzcfoFIBS/ZcgZuDuk1qFs+By35P8OjZG/l60c7LKGpnIbsa/ys67bsoxkQRDW5hYSHHXps6dSoaNmz4zS6V/fr1k+N/RUzituu/bcqkiVi9Yjm27Ngdbcwo4cD+fXJDLV2mLJTOUOKIjRi3TyS+ETZt3CDH7xMFKaWKOIlev3ZNvhbjJogx+8TAr1HHUDhz+hSePA6RxRB9IE6oEcaO+p8sFmbLll2+rl2vAfbs2il/fvbsGc6eOQ07O+U8/EaM/xYxBqSweuVyODg6yXmPomxfm/Vg+9Ln/WPG1ElYt3ol1m3egWRfjlXJU6SQY9AcOqC+dV4UC31Pn9KMbymIcVTE2D0iedGXNkibNi1KliqN3V/2CzGYshgc2jZnLp2ta+L4JjBPpB7LRKjlnAmX/NX7RdtyOVCzkBXqTjiI0HfRn7xXrYAl+tW0Q50JBzXFReHag5fI3XUTCvTZKqc5u29h2ZF76DT/lBajIjJOusyrxfHN0Skfli9bIl9vWLdWfsHMll2dE0QQY5Nt3bIZQUFBcl3nzfFCnXr1f7hMWwwujqXqONZ/I46aNWtjy5ZNUdZ1NurWa/DDZdqOxcEpH1ZEtMn62GOpXrM2tm3djOAv6+s910sWz5QiTdq08u6slcuXasZ/y2hhiaxf8uYIYizC7VHiEA/wqFWnvib3iXgiq+hltGXTBtjpoPdEjP3kG20i94WtkfuCaJOIh/8pgaHsJ2LbcnBwwqoV6m1r84Z1yGBhEWPbqlq9JrZv24LgYPW6irsDa9auJ5eJ76kRD8QUN6rs2rEVeR0coW1/4xisBI9fvsP52yFoWDq3fF3TxQYPnrzC3YeR3z2Fe49eooRjJk3Br5JzVlzxU9/Qce/RCxTJk1Ez5mClwtlwM+AZPoX9uweXxlGJv6YCvqSLrgwZMmRA/fr18fr1a2zduvWHnxOfEVc6g5++lNXuPxUYGAgbaytYZ80Ks6Tq/uDxEySQT/gVmjdtJLuwDRw8FEpmKHEIHdt5Yvv2rfIEmSpVKiQ1M8O2nXvRuEFdOQivSHhTp0mDUWPGw8Hx7x7Aoo4R8DeIwWnHjxkl11kcfHv27osq1WqgWqVyMikX3XHFwKkDBg9DiZKlFBVHl45tsWv7NnkyEUWOpEnNcP7KTXRq3wbHjx6RxWbx4JuxE6dqitHPnj5Fe89WsgAitG7TDh6efzYuimmUqy9/6t7du2jUoI68u0IcBq2ts2LchMlybIsmjerJ8Zvk9pU6jXyirMNfPEHG/YtxfO3+/fta2T8ifH032a948CAQ9jmyyGK42KaE+AniY8/B4ziwbw+GDvxPblufwj6hafNW6NC5m+azwwf3l0XcmXN9/kocCeP9vWtl4gpy44b1orXByNHjZBuI7U48PV4UPMWyfv0HysGW/5bMbX+tC0jm1Ekwv0NRmIgBzeMA9x+/wYDl5xD2WYUL46vCL+Q1Xr//pHlIScUR6jESH3jVQUjoezx/rb5jUKg9/iCev4k+JEKvanlgnjgeBq44/0vr9fnjWzxd0lIeG//G+Z3ImPxpXh305MVv7Xc3b9yQT5UU53/xtEivufNhZ28vj3mVq1RDlarqMWXne8/FhHFj5M/FXd0wbcZszUMWvrdMW5QWx+8+cELEIZ68KsaHNjczh9c8H3UcbdxRuWqUOObNxfhxo+XPrq4lMG1mlDi+s0yb+aiIpa1HZJvMEm1iZ48Obd1RqXI1GY/g4z0XEyP+7m5umDJdvb5v376Fo52tuiv9y5eymCIeeCAenPerwsJ//4v5rZs30L5NK3nR3MzMHDO85iGPnT06tWsjH0JSqUpV+b6F8+dh0oSx8meX4m6YNG2mjEM8tdhr1nTENTFBeFgYXEuUkkMTfT2cwM+KZxL3j9rEM0qbzP7SJu3buqPyV22i2Rfc3DA1Sps42Nni44cP8lwf0SbDfqNN/iSvVtp+8vaD+o6539m2OrZtjefPnsnvy9NnzZPD7nTp0AYVKlWV25ewyGcepkwcJ38uVtwVE6aot625s2fAZ94cmJqaICwsXBbbe/838LePP2KYGV0dg8W2lTdP9G2rkdi2vhpm7WekrDLxt+OwsUwhn1Cc0jwhQt9+hOeEnbji9wQzu5aTYw2KSTyMRDyBuGgeC1n0C37+Bp2m7oFfkPohpeLJxdWK2eDDpzC8fR+G7jP24tztkF9eF1XYe3zYN+CHebUiioMRnj9/DmtrazmYcps2bbReHCT6l8VBXTGUOP52cVCX/mVxUNv+pDioJH+zOKhLv1ocVCoWB4l0l1f/bnGQ/g1dPI32XzCUfPRPioNK8yfFQSUxpLz6d4uDSvMnxUElSfkHxUEl+dnioPoxMDqyc+dOeceOra0tbt++jV69eiFnzpxo2VKZT54kIiIiIlIi5tVERET0u3R6uUBULjt06CATl2bNmsHFxUUmNtruPkBEREREpM+YVxMREdHv0umdg/Xq1ZMTERERERH9PubVRERE9LsMY6ABIiIiIiIiIiIi+mUsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iAREREREREREZGRYnGQiIiIiIiIiIjISLE4SEREREREREREZKRYHCQiIiIiIiIiIjJSLA4SEREREREREREZKRYHiYiIiIiIiIiIjBSLg0REREREREREREaKxUEiIiIiIiIiIiIjxeIgERERERERERGRkWJxkIiIiIiIiIiIyEixOEhERERERERERGSkWBwkIiIiIiIiIiIyUqbQYyqVSv7/VWiorleFDFD4Z/X2pe8MJQ7BNG4cGIK4BhKH8O5jOAzBx3iGca3s88e3MASfP76Ldp4nIi3m1a+YVytJnDiGkTMYSj4aFv4ZhiKeiWHkPoaUV7/9EAZD8Cm+CQyBKuw9DCmOH+XVel0cfPXqlfx/dmsrXa8KERER/YPzfLJkyXS9GkRGISKvtrHOpOtVISIiIi3n1XFUenxZ/vPnz3j48CHMzMz+2VW10NBQWFlZISAgAObm5tBnhhKLocRhSLEwDuUxlFgYh3HGIlITkcBkzJgRceMaxp0NRErHvNo4Y2EcymMosRhKHIYUC+NQnlAF5dV6feegCMzS0lIr/5ZoKH3f8AwtFkOJw5BiYRzKYyixMA7ji4V3DBJpF/Nq446FcSiPocRiKHEYUiyMQ3nMFZBX83I8ERERERERERGRkWJxkIiIiIiIiIiIyEixOPgDCRIkwODBg+X/9Z2hxGIocRhSLIxDeQwlFsahPIYUCxFplyEdPwwlFsahPIYSi6HEYUixMA7lSaCgWPT6gSRERERERERERET0+3jnIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDv7AjBkzkCVLFiRMmBDOzs44deoU9M2hQ4dQtWpVZMyYEXHixMGGDRugj0aNGoWCBQvCzMwMadOmRY0aNXDjxg3om1mzZiFv3rwwNzeXU5EiRbB9+3bou9GjR8vtq2vXrtA3Q4YMkesedcqZMyf00YMHD9CkSROkSpUKiRIlgr29Pc6cOQN9I467X7eJmDp06AB9Eh4ejoEDB8La2lq2R7Zs2TB8+HDo47PAXr16JffvzJkzy1iKFi2K06dP63q1iEiPMK9WDubVysa8WhmYVysL8+p/i8XB71i5ciW6d+8uHy199uxZODg4oHz58uTZbOEAAA6WSURBVAgJCYE+efPmjVx3kZDps4MHD8oD2IkTJ7B79258+vQJ5cqVk/HpE0tLS3nC9/X1lSeXUqVKoXr16rhy5Qr0lTiQeXl5yeRMX+XJkwePHj3STEeOHIG+ef78OYoVK4Z48eLJxPjq1auYMGECUqRIAX3cpqK2h9jnhbp160KfjBkzRn5xmT59Oq5duyZfjx07FtOmTYO+cXd3l+2wePFiXLp0SR5/y5QpIxNnIqIfYV6tLMyrlYt5tTIwr1Ye5tX/mIq+qVChQqoOHTpoXoeHh6syZsyoGjVqlEpfiSZfv369yhCEhITIeA4ePKjSdylSpFDNmzdPpY9evXqlsrGxUe3evVvl5uam6tKli0rfDB48WOXg4KDSd3369FG5uLioDJHYrrJly6b6/PmzSp9UrlxZ1apVq2jzatWqpWrcuLFKn7x9+1ZlYmKi2rJlS7T5+fLlU/Xv319n60VE+oN5tbIxr1YG5tXKwbxaeZhX/1u8c/AbPn78KK9AiepthLhx48rXx48f1+m6kdrLly/l/1OmTAl9JW6NXrFihbxKK7pB6CNx1bly5crR9hV9dOvWLdlFKGvWrGjcuDH8/f2hbzZt2oQCBQrIq4Cii5CTkxPmzp0LQzgeL1myBK1atZJdIPSJ6CKwd+9e3Lx5U76+cOGCvHpesWJF6JOwsDB5vBJdAaMS3SD08W4AItIu5tXKx7xaGZhXKwfzauVhXv1vmf7j36+3njx5IhssXbp00eaL19evX9fZepHa58+fZR99cau3nZ0d9I24dVgkLe/fv0fSpEmxfv165M6dG/pGJGCia5Cux0f4U2LcowULFsDW1lbeaj906FAUL14cly9flmPx6Iu7d+/KW+1Ft63//vtPtkvnzp0RP358NG/eHPpKjOf04sULtGjRAvqmb9++CA0NlWPtmJiYyPPKiBEjZKKsT8R+II5ZYlyXXLlyyXPh8uXL5Zf67Nmz63r1iEjhmFcrG/NqZWBerSzMq5WHefW/xeIg6e1VNXGC0dc7VsTJ8vz58/Iq7Zo1a+QJRoz9ok+JTEBAALp06SLHSvj6qoe+iXq1SYzvIpIaMTjsqlWr0Lp1a+hTci+ucI4cOVK+Flc4xX4ye/ZsvU5ivL29ZRuJK9D6RmxDS5cuxbJly+T4O2K/F1/ARCz61iZiTBRxldnCwkImZPny5UPDhg3l3UBERKS/mFfrHvNq5WFerTzMq/8tFge/IXXq1LKRgoODo80Xr9OnT6+z9SKgY8eO2LJli3xanBiEWB+JK04RVwXy588vr0RNmTJFDj6sL8SBSwwiLg5kEcTVG9EuYpDYDx8+yH1IHyVPnhw5cuTA7du3oU8yZMgQIxEWV6PWrl0LfXX//n3s2bMH69atgz7q1auXvMrZoEED+Vo85U7EJJ4SqW9JjHginPiyJbpriau2YnurX7++7DJERPQ9zKuVi3m1MjCvVh7m1crDvPrf4piD3znJiJOL6NMe9eqBeK2vY1joOzHus0hgRFeBffv2yUeYGwqxbYmTvj4pXbq07MYhrthETOLqmritW/ysrwmM8Pr1a9y5c0cepPWJ6A5048aNaPPEmBziaq2+8vHxkeO8iPF39NHbt2/luFpRiX1D7PP6KkmSJHLfEE/x27lzp3wqJBHR9zCvVh7m1crCvFp5mFcrD/Pqf4t3Dn6HGF9AVKDFgblQoUKYPHmyrOy2bNkS+nZAjnql5t69e/IkIwYczpQpE/Spy4O4hXjjxo2yn35QUJCcnyxZMjl4p77o16+fvJVb/O1fvXolYzpw4IA8GOgT0QZfj0sjDm6pUqXSu/FqevbsiapVq8qT/cOHDzF48GB5ohG3duuTbt26yYF6RfeHevXq4dSpU5gzZ46c9JE40YskRhyHTU3183QltisxForY30X3h3PnzmHixImyG4G+Ecco8WVSdN8S5xRx9VaM+aJv50Qi0g3m1crCvFpZmFcrD/Nq5WFe/Y/946ch671p06apMmXKpIofP76qUKFCqhMnTqj0zf79+1Wiqb+emjdvrtInscUgJh8fH5U+EY9fz5w5s9ym0qRJoypdurRq165dKkPg5uam6tKli0rf1K9fX5UhQwbZJhYWFvL17du3Vfpo8+bNKjs7O1WCBAlUOXPmVM2ZM0elr3bu3Cn38Rs3bqj0VWhoqNwnxHkkYcKEqqxZs6r69++v+vDhg0rfrFy5Uq6/2E/Sp0+v6tChg+rFixe6Xi0i0iPMq5WDebXyMa/WPebVysK8+t+KI/7zrwuQREREREREREREpDwcc5CIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIRERERERERERkpFgeJiIiIiIiIiIiMFIuDRERERERERERERorFQSIiIiIiIiIiIiPF4iARGaXnz59j6NChePToka5XhYiIiIhILzGnJjIMLA4SUQxx4sTBhg0bYKhUKhWaN2+Od+/eIUOGDN9975AhQ+Do6Kh53aJFC9SoUUMLa0lERERE+ow5dSTm1ETKxuIgkZEJCgpCp06dkDVrViRIkABWVlaoWrUq9u7dC2Mxbtw4mJubY9SoUb/82SlTpmDBggWa1yVKlEDXrl3/8hoSERERkZIxp2ZOTWRITHW9AkSkPX5+fihWrBiSJ08uT+b29vb49OkTdu7ciQ4dOuD69eswRB8/fkT8+PE1r3v37v3bvytZsmR/aa2IiIiISB8xp1ZjTk1kOHjnIJERad++vezecOrUKdSuXRs5cuRAnjx50L17d5w4ceKbn+vTp498b+LEieXV0YEDB8oEKMKFCxdQsmRJmJmZyauH+fPnx5kzZ+Sy+/fvy6uoKVKkQJIkSeS/t23bNs1nL1++jIoVKyJp0qRIly4dmjZtiidPnnxzXcQVRpGIiS4aNjY2SJgwIcqXL4+AgIAY3RbmzZsHa2tr+R7hxYsXcHd3R5o0aeR6lipVSq57VKNHj5brIWJp3bo13r9/H2151C4Q4ueDBw/KK5/i7yomkSz+TlxEREREpB+YUzOnJjI0LA4SGYlnz55hx44d8mqmSCi+JpKDbxEndZFAXL16VZ60586di0mTJmmWN27cGJaWljh9+jR8fX3Rt29fxIsXTy4T/96HDx9w6NAhXLp0CWPGjJEn94jEQiQTTk5OMvER6xccHIx69ep9N5a3b99ixIgRWLRoEY4ePSp/T4MGDaK95/bt21i7di3WrVuH8+fPy3l169ZFSEgItm/fLtczX758KF26tPzbCKtWrZJJ0MiRI+X6iLFTZs6c+c31EH+LIkWKwMPDQw7CLCbRpeR34yIiIiIiZWNOzZyayCCpiMgonDx5UiV2+XXr1v3wveJ969ev/+bycePGqfLnz695bWZmplqwYEGs77W3t1cNGTIk1mXDhw9XlStXLtq8gIAA+e/fuHEj1s/4+PjI5SdOnNDMu3btmpwnYhQGDx6sihcvniokJETznsOHD6vMzc1V79+/j/b7smXLpvLy8pI/FylSRNW+fftoy52dnVUODg6a182bN1dVr15d89rNzU3VpUuXP46LiIiIiJSPOTVzaiJDxDsHiYyEOj/5PStXrpTjqqRPn15eoRwwYAD8/f01y0UXCtG1oEyZMrILwZ07dzTLOnfujP/973/y84MHD8bFixc1y0T3g/3798vfGTHlzJlTLov6O75mamqKggULal6Lz4irtNeuXdPMy5w5s+zqEPXfev36NVKlShXt37t3757m3xKfd3Z2jvZviauYv+p34yIiIiIiZWNOzZyayBDxgSRERkKMJSLG7/jVAZKPHz8uuzgMHTpUjkMiBg9esWIFJkyYoHmP6DbQqFEjbN26VXYvEAmLeE/NmjVlgiM+J5bt2rVLPs1MfFY83U0kFmLsFNEt4mui+8Gf+Lqbh/i3xO88cODAL3X/+B3/Mi4iIiIi0h3m1MypiQwR7xwkMhIpU6aUCcWMGTPw5s2bGMvFmB6xOXbsmLxi2L9/fxQoUEAmRGJA5K+JwZW7desmk5VatWrBx8dHs0yMGdK2bVs5VkmPHj3k+CqCGJ/kypUryJIlC7Jnzx5tim0MlwhhYWGawZmFGzduyPXPlSvXNz8j/q2goCB5hfTrfyt16tTyPeLzJ0+ejPa57w0qLYgntoWHh8f4t34nLiIiIiJSNubUzKmJDBGLg0RGRCQx4qRbqFAhObDwrVu35G3/U6dO/eat/iJxEd0dxFVLcfu+eO/69es1y9+9e4eOHTvKq4ciwRGDGYtBlCOSiq5du2Lnzp2yq8HZs2dl14CIZWJgZTFwccOGDeVnxO8X723ZsmWM5CAqMTCzuEoqkg4xCLJ4wlnhwoVlXN8iumeIGMVT0USyJZ6AJpI0kaBFJEVdunTB/PnzZRJ28+ZNebVWJCTfI5IVsR7i94knp33+/Pm34yIiIiIi5WNOzZyayNCwOEhkRLJmzSqTiZIlS8qrjXZ2dihbtiz27t2LWbNmxfqZatWqyauXIllxdHSUJ/+BAwdqlpuYmODp06do1qyZvNIpnh5WsWJF2WVCECducWIXyUuFChXkeyKeVpYxY0aZ+Ij3lCtXDvb29jLxEV0S4sb99uEpceLE6NOnj+x2IcZdEeOPiDFcvkd0/9i2bRtcXV1lQiHWQzyNTSRf6dKlk++pX7++jK13797Inz+/XNauXbvv/t6ePXvKv0Hu3LnleCwi6fvduIiIiIhI+ZhTM6cmMjRxxFNJdL0SREQ/a8GCBTIp+FaXDSIiIiIi+j7m1EQUFcvtRERERERERERERorFQSIiIiIiIiIiIiPFbsVERERERERERERGincOEhERERERERERGSkWB4mIiIiIiIiIiIwUi4NERERERERERERGisVBIiIiIiIiIiIiI8XiIBERERERERERkZFicZCIiIiIiIiIiMhIsThIRERERERERERkpFgcJCIiIiIiIiIiMlIsDhIREREREREREcE4/R+JIXBKzm7RWwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Interprétation des résultats ===\n", + "\n", + "Accuracy globale: 0.8647 (86.47%)\n", + "\n", + "Performances par classe (rappel):\n", + " Classe 0: 0.949 (94.9%) - 5923 instances\n", + " Classe 1: 0.962 (96.2%) - 6742 instances\n", + " Classe 2: 0.819 (81.9%) - 5958 instances\n", + " Classe 3: 0.864 (86.4%) - 6131 instances\n", + " Classe 4: 0.882 (88.2%) - 5842 instances\n", + " Classe 5: 0.800 (80.0%) - 5421 instances\n", + " Classe 6: 0.945 (94.5%) - 5918 instances\n", + " Classe 7: 0.893 (89.3%) - 6265 instances\n", + " Classe 8: 0.632 (63.2%) - 5851 instances\n", + " Classe 9: 0.878 (87.8%) - 5949 instances\n", + "\n", + "Meilleures classes (rappel élevé):\n", + " Classe 1: 0.962 (96.2%)\n", + " Classe 0: 0.949 (94.9%)\n", + " Classe 6: 0.945 (94.5%)\n", + "\n", + "Pires classes (rappel faible):\n", + " Classe 8: 0.632 (63.2%)\n", + " Classe 5: 0.800 (80.0%)\n", + " Classe 2: 0.819 (81.9%)\n", + "\n", + "Erreurs les plus fréquentes:\n", + " Classe 8 prédite comme 9: 545 fois\n", + " Classe 8 prédite comme 3: 542 fois\n", + " Classe 8 prédite comme 5: 493 fois\n", + " Classe 7 prédite comme 9: 427 fois\n", + " Classe 4 prédite comme 9: 371 fois\n", + "\n", + "=== Conclusion ===\n", + "La matrice 10x10 montre les performances du classifieur sur les 10 chiffres.\n", + "La diagonale représente les bonnes classifications.\n", + "Les valeurs hors diagonale montrent les confusions entre classes.\n" + ] + } + ], + "source": [ + "from sklearn.model_selection import cross_val_predict\n", + "from sklearn.metrics import confusion_matrix\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "print(\"=== Matrice de confusion du classifieur SGD multi-classes ===\\n\")\n", + "\n", + "# Prédire les classes avec cross_val_predict pour le modèle multi-classes\n", + "print(\"Prédiction des classes avec cross_val_predict...\")\n", + "y_train_pred_multiclass = cross_val_predict(sgd_multiclass, X_train, Y_train, cv=3)\n", + "\n", + "print(f\"Forme des prédictions: {y_train_pred_multiclass.shape}\")\n", + "print(f\"Classes prédites uniques: {np.unique(y_train_pred_multiclass)}\")\n", + "\n", + "# a. Calculer la matrice de confusion normale et normalisée\n", + "cm_multiclass = confusion_matrix(Y_train, y_train_pred_multiclass)\n", + "print(\"\\na. Matrice de confusion (valeurs absolues) 10x10:\")\n", + "print(cm_multiclass)\n", + "\n", + "cm_normalized_multiclass = confusion_matrix(Y_train, y_train_pred_multiclass, normalize='true')\n", + "print(\"\\nMatrice de confusion normalisée:\")\n", + "print(cm_normalized_multiclass)\n", + "\n", + "# Visualisation des matrices de confusion\n", + "fig, axes = plt.subplots(1, 2, figsize=(15, 6))\n", + "\n", + "# Matrice de confusion absolue\n", + "im1 = axes[0].imshow(cm_multiclass, interpolation='nearest', cmap=plt.cm.Blues)\n", + "axes[0].set_title('Matrice de confusion (valeurs absolues)')\n", + "axes[0].set_xlabel('Classe prédite')\n", + "axes[0].set_ylabel('Classe réelle')\n", + "\n", + "# Ajouter les annotations pour les valeurs\n", + "for i in range(10):\n", + " for j in range(10):\n", + " axes[0].text(j, i, format(cm_multiclass[i, j], 'd'),\n", + " ha=\"center\", va=\"center\",\n", + " color=\"white\" if cm_multiclass[i, j] > cm_multiclass.max()/2 else \"black\",\n", + " fontsize=8)\n", + "\n", + "axes[0].set_xticks(range(10))\n", + "axes[0].set_yticks(range(10))\n", + "axes[0].set_xticklabels(range(10))\n", + "axes[0].set_yticklabels(range(10))\n", + "\n", + "# Matrice de confusion normalisée\n", + "im2 = axes[1].imshow(cm_normalized_multiclass, interpolation='nearest', cmap=plt.cm.Blues)\n", + "axes[1].set_title('Matrice de confusion normalisée')\n", + "axes[1].set_xlabel('Classe prédite')\n", + "axes[1].set_ylabel('Classe réelle')\n", + "\n", + "# Ajouter les annotations pour les pourcentages\n", + "for i in range(10):\n", + " for j in range(10):\n", + " axes[1].text(j, i, format(cm_normalized_multiclass[i, j], '.2f'),\n", + " ha=\"center\", va=\"center\",\n", + " color=\"white\" if cm_normalized_multiclass[i, j] > 0.5 else \"black\",\n", + " fontsize=8)\n", + "\n", + "axes[1].set_xticks(range(10))\n", + "axes[1].set_yticks(range(10))\n", + "axes[1].set_xticklabels(range(10))\n", + "axes[1].set_yticklabels(range(10))\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# b. Interprétation des résultats\n", + "print(\"\\n=== Interprétation des résultats ===\")\n", + "\n", + "# Calcul des métriques globales\n", + "accuracy_total = np.trace(cm_multiclass) / np.sum(cm_multiclass)\n", + "print(f\"\\nAccuracy globale: {accuracy_total:.4f} ({accuracy_total*100:.2f}%)\")\n", + "\n", + "# Analyse par classe (diagonale = bonnes prédictions)\n", + "print(f\"\\nPerformances par classe (rappel):\")\n", + "for i in range(10):\n", + " rappel_classe = cm_normalized_multiclass[i, i]\n", + " nb_instances = cm_multiclass[i, :].sum()\n", + " print(f\" Classe {i}: {rappel_classe:.3f} ({rappel_classe*100:.1f}%) - {nb_instances} instances\")\n", + "\n", + "# Classes les mieux classées\n", + "diagonale = np.diag(cm_normalized_multiclass)\n", + "meilleures_classes = np.argsort(diagonale)[::-1][:3]\n", + "pires_classes = np.argsort(diagonale)[:3]\n", + "\n", + "print(f\"\\nMeilleures classes (rappel élevé):\")\n", + "for classe in meilleures_classes:\n", + " print(f\" Classe {classe}: {diagonale[classe]:.3f} ({diagonale[classe]*100:.1f}%)\")\n", + "\n", + "print(f\"\\nPires classes (rappel faible):\")\n", + "for classe in pires_classes:\n", + " print(f\" Classe {classe}: {diagonale[classe]:.3f} ({diagonale[classe]*100:.1f}%)\")\n", + "\n", + "# Erreurs les plus fréquentes (hors diagonale)\n", + "cm_erreurs = cm_multiclass.copy()\n", + "np.fill_diagonal(cm_erreurs, 0) # Enlever la diagonale\n", + "erreurs_max = np.unravel_index(np.argmax(cm_erreurs), cm_erreurs.shape)\n", + "\n", + "print(f\"\\nErreurs les plus fréquentes:\")\n", + "indices_erreurs = np.argsort(cm_erreurs.ravel())[::-1][:5]\n", + "for idx in indices_erreurs:\n", + " i, j = np.unravel_index(idx, cm_erreurs.shape)\n", + " if cm_erreurs[i, j] > 0:\n", + " print(f\" Classe {i} prédite comme {j}: {cm_erreurs[i, j]} fois\")\n", + "\n", + "print(f\"\\n=== Conclusion ===\")\n", + "print(\"La matrice 10x10 montre les performances du classifieur sur les 10 chiffres.\")\n", + "print(\"La diagonale représente les bonnes classifications.\")\n", + "print(\"Les valeurs hors diagonale montrent les confusions entre classes.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "16a1368f", + "metadata": { + "id": "16a1368f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Affichage de la matrice de confusion avec matshow ===\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Analyse de la confusion entre les classes 5 et 8 ===\n", + "\n", + "Confusion 5 → 8: 174 instances (3.2% des vrais 5)\n", + "Confusion 8 → 5: 493 instances (8.4% des vrais 8)\n", + "Performance de la classe 5: 0.800 (80.0%)\n", + "Performance de la classe 8: 0.632 (63.2%)\n", + "\n", + "=== Pourquoi la classe 5 est-elle confondue avec le chiffre 8 ? ===\n", + "\n", + "RAISONS PROBABLES DE LA CONFUSION 5 ↔ 8:\n", + "\n", + "1. SIMILARITÉ MORPHOLOGIQUE:\n", + " • Les chiffres 5 et 8 partagent des caractéristiques visuelles similaires\n", + " • Courbes fermées et boucles dans les deux chiffres\n", + " • Segments horizontaux en haut et au milieu\n", + "\n", + "2. VARIABILITÉ DE L'ÉCRITURE MANUSCRITE:\n", + " • Un '5' mal écrit peut ressembler à un '8' ouvert\n", + " • Un '8' déformé peut être confondu avec un '5'\n", + " • Variations dans l'épaisseur des traits et la fermeture des boucles\n", + "\n", + "3. QUALITÉ DES DONNÉES:\n", + " • Images de résolution 28x28 pixels (faible résolution)\n", + " • Possibles artéfacts de numérisation\n", + " • Variations de contraste et de netteté\n", + "\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib as mpl\n", + "import numpy as np\n", + "\n", + "print(\"=== Affichage de la matrice de confusion avec matshow ===\\n\")\n", + "\n", + "# Affichage de la matrice de confusion avec matshow\n", + "plt.figure(figsize=(10, 8))\n", + "plt.matshow(cm_multiclass, cmap=mpl.cm.gray)\n", + "plt.title('Matrice de confusion avec matshow (en niveaux de gris)', pad=20)\n", + "plt.xlabel('Classe prédite')\n", + "plt.ylabel('Classe réelle')\n", + "\n", + "# Ajouter les annotations des valeurs\n", + "for i in range(10):\n", + " for j in range(10):\n", + " plt.text(j, i, str(cm_multiclass[i, j]),\n", + " ha=\"center\", va=\"center\",\n", + " color=\"white\" if cm_multiclass[i, j] > cm_multiclass.max()/2 else \"black\",\n", + " fontsize=10)\n", + "\n", + "# Ajouter les ticks et labels\n", + "plt.xticks(range(10))\n", + "plt.yticks(range(10))\n", + "plt.colorbar()\n", + "plt.show()\n", + "\n", + "print(\"=== Analyse de la confusion entre les classes 5 et 8 ===\\n\")\n", + "\n", + "# Analyser spécifiquement la confusion entre 5 et 8\n", + "confusion_5_vers_8 = cm_multiclass[5, 8] # Classe 5 prédite comme 8\n", + "confusion_8_vers_5 = cm_multiclass[8, 5] # Classe 8 prédite comme 5\n", + "total_5 = np.sum(cm_multiclass[5, :]) # Total d'instances de classe 5\n", + "total_8 = np.sum(cm_multiclass[8, :]) # Total d'instances de classe 8\n", + "\n", + "print(f\"Confusion 5 → 8: {confusion_5_vers_8} instances ({confusion_5_vers_8/total_5*100:.1f}% des vrais 5)\")\n", + "print(f\"Confusion 8 → 5: {confusion_8_vers_5} instances ({confusion_8_vers_5/total_8*100:.1f}% des vrais 8)\")\n", + "\n", + "# Performance de la classe 5\n", + "performance_5 = cm_multiclass[5, 5] / total_5\n", + "print(f\"Performance de la classe 5: {performance_5:.3f} ({performance_5*100:.1f}%)\")\n", + "\n", + "# Performance de la classe 8\n", + "performance_8 = cm_multiclass[8, 8] / total_8\n", + "print(f\"Performance de la classe 8: {performance_8:.3f} ({performance_8*100:.1f}%)\")\n", + "\n", + "print(f\"\\n=== Pourquoi la classe 5 est-elle confondue avec le chiffre 8 ? ===\")\n", + "print(f\"\"\"\n", + "RAISONS PROBABLES DE LA CONFUSION 5 ↔ 8:\n", + "\n", + "1. SIMILARITÉ MORPHOLOGIQUE:\n", + " • Les chiffres 5 et 8 partagent des caractéristiques visuelles similaires\n", + " • Courbes fermées et boucles dans les deux chiffres\n", + " • Segments horizontaux en haut et au milieu\n", + "\n", + "2. VARIABILITÉ DE L'ÉCRITURE MANUSCRITE:\n", + " • Un '5' mal écrit peut ressembler à un '8' ouvert\n", + " • Un '8' déformé peut être confondu avec un '5'\n", + " • Variations dans l'épaisseur des traits et la fermeture des boucles\n", + "\n", + "3. QUALITÉ DES DONNÉES:\n", + " • Images de résolution 28x28 pixels (faible résolution)\n", + " • Possibles artéfacts de numérisation\n", + " • Variations de contraste et de netteté\n", + "\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "cf44022c", + "metadata": { + "id": "cf44022c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Analyse des erreurs de classification ===\n", + "\n", + "Matrice de confusion avec diagonale remplacée par des 0:\n", + "[[ 0 2 42 25 13 55 65 14 35 50]\n", + " [ 3 0 42 26 10 30 11 47 62 27]\n", + " [ 37 94 0 273 64 90 166 125 167 63]\n", + " [ 18 33 125 0 22 163 27 82 151 211]\n", + " [ 10 25 31 13 0 39 93 40 66 371]\n", + " [ 60 31 45 340 79 0 142 30 174 181]\n", + " [ 36 24 54 10 51 92 0 2 43 16]\n", + " [ 21 31 58 23 60 21 5 0 24 427]\n", + " [ 31 188 148 542 81 493 74 54 0 545]\n", + " [ 27 27 13 83 196 53 3 287 36 0]]\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdEAAAGpCAYAAADSsgw3AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAoRdJREFUeJztnQe4FMXSsNucAMmCAhIMICAoiGJEEQwY8SoqKsZrjlcUDJjFcM1iuAYwZ8AMKiqoBBEERARFSUoWJIhg2v95+7f26zNndnd2dnb37Jl6n2fg7Ozs9HSYqq7q6u71EolEwiiKoiiKkjXrZ/8TRVEURVFAlaiiKIqihESVqKIoiqKERJWooiiKooRElaiiKIqihESVqKIoiqKERJWooiiKooRElaiiKIqihESVqKIoiqLEWYlef/31Zr311jMVgUGDBtlnmT17til1hg0bZtq2bWs23XRTm6dffvml0pcVz0N7UgrP559/bjbeeGMzZ86cjNc2btzYnHrqqcnPH3/8sa07/q+MVJb89enTx+y+++4mtkpUhB7Hp59+Wu57VhBs2LCh/f6www4L9UC33nqrGTp0aKjfKtHx888/m+OOO85sttlmZsCAAeaZZ54xW2yxRbEfS6nEXH311eaEE04w2267bbEfRQnBG2+8YXbddVfb6W7UqJG57rrrzJ9//lnmmksuucRMnjzZXhtrS5RCev7558udHzlypPnxxx/NJptsEvqBwijRa665xvz222+h01TKM378eLNq1Spz0003mTPOOMOcdNJJZqONNoo0jZNPPtnWmwpNZdKkSeaDDz4w55xzTqjf77vvvrYt8b9SeN59911z1FFHmerVq5sHHnjA/n3zzTebCy+8sMx19erVM0ceeaT573//ayoLG4b50aGHHmpeeeUVc//995sNN/y/W6BY27VrZ5YuXWoKwa+//mqtI57BfQ4ldxYvXmz/56XIFxtssIE9lHD8/fff5vfff7ed2lTvRiHA2uBZcMWGZeDAgdZ62WOPPUL9fv311/ctB6UwXH755WbnnXc27733XlIWV6tWzRpFF198sWnevHnyWjxcxx57rPnhhx9M06ZNTSwtUVwuuPvef//95Dle5ldffdWceOKJvr+h57HnnnuaWrVqWRchypbrXXAD8/I/9dRTSbexjHvIuOe0adNsGjVq1DB77713me+8PPvss6ZDhw5m8803t9fTS6WSvT2offbZxwqcqlWrmm7dupmvv/46UDlw3QEHHGDz06BBA9vzQpj4kUs6jEVeeumldhwIK5+0TjnllDKdFZQeFuNWW21lhUmbNm1sObow9kg5URf/+9//TLNmzez9dtttN2t5Cp06dTK9evWyf/OdWw/esSj3Nxwu9EhbtmyZLP/27duX8WCkGhN96KGH7O94tq233tqcf/755cZjSatVq1a2Pey///42jW222cbccccdgcp03bp1tkzr1Klj6+OII46wXhQ/fvrpJ3P66afbsuWZeLYnn3yy3HWZ8pvuWXB9bbfddvb+DIlcccUV9rwLZXXBBReY5557Llk+jFtLOeIJOu+880zdunVtG8mm7fnVH1DX1LlfG7r33nuTbYh6yKUM8D7xLnnfY4aIeK/ID/ekrv3eG78xw08++cQKa5SzlCt17ue1wijYaaed7LtDuxoyZEi5vAPy6T//+Y+9F/fccccdbVl4N8OSuiJf3E/aDfXlwvgvdcZ9kCPIR545aJzAuHHjzMEHH2y23HJLWz777bef+eyzz0whmTZtmj3+/e9/lzFmyBfl4pXzBx54oP3/9ddfN5WBUOYbDatjx47mhRdeMIccckjyRV2xYoU5/vjjrYXq5b777rOCqmfPnlbhvvjii7axvPXWW/alBsbdzjzzTKv4qBDgJXXhN9tvv73t4aTbxe2GG26wyhXFfeONN9peMg3uww8/NF27dk2mh7I46KCDzO23327WrFljHn74Yaucv/zyy3IvkMvChQvtC00vnMFyBBSKiRfBSy7prF692grAb775xgpyxhxQnowpIPRr165thQICcObMmfbFbdKkiRUKCAGUDz1BF4Qartqzzz7bvuwonu7du9ueIS5bxqZ4qckPZcf9vPWQiccee8xcdNFF5l//+pdNf+3atWbKlCm2DlJ1tIA6o+540c4991wzY8YMW1YoeYSD61Jevny5FSA8O71bXtYrr7zStG7dOtkuU0E7o5PFs9BGaBfSDl0WLVpkrSMRiihd2jodlpUrV9oxnlzyS6eL94IYA9p8ixYtzFdffWXuuece8+2335Yb2uA5X375Zfss1D1tB1eoCC2er1+/flbY59r2MlmO5JFnRkHUrFkzdBnQSZk7d65t217IC0oU7xfHxIkT7fuLDMkE7wD5pR2hnAhcQsnz3vCd8Pbbb5sePXrYdtO/f3/brqhfOmUuyBvq6qOPPrLfE3Q3fPhw07t3b5sH6syFOh08eLCtFzovyMVjjjnG5pXnAdr16NGjrdyko4DypH54n1FMKMZU0BZo5xgkdMKwxqkXOiN0IJCj6QjqMeTZ0w3Rffnll/Z/OkwudIDJk3wvoPCRJ7zPdGpKnkQWDBw4EK2VGD9+fOLBBx9MVK1aNbFmzRr73bHHHpvYf//97d/bbrttolu3bmV+K9cJv//+e6JVq1aJAw44oMz5LbbYItGrV69yaV933XU27RNOOCHld8J3332XWH/99RNHH3104q+//ipz7d9//23/X7VqVaJ69eqJs846q8z3CxcuTGy55Zblznu55JJLbJrjxo1Lnlu8eLH9LednzZoVSTr9+vWz9xs8eHC57yQv9957r73m2WefLVO+HTt2TFSpUiWxcuVKe45n4rpatWolli1blrz29ddft+fffPNN37p2oW796me//fazh3DkkUcmWrZsmTZvkoaUFeW38cYbJ7p27Vqm3mhrXPfkk0+WSY9zTz/9dPLcunXrEvXq1Uscc8wxadOdNGmS/e15551X5vyJJ55oz9OehDPOOCNRv379xNKlS8tce/zxx9v6k3YdJL9+PPPMM7atfvLJJ2XOP/LII/ZZPvvss+Q5PnPt119/7VuOe++9d+LPP/9Mns+m7XnrT6CuqXNB2lC1atVsfbmELYMPPvigXPtz2wOyRNo6XHXVVfZ6tx1+9NFH9hz/p5I50L9//8R6662XmDNnTvJc69atEw0aNLDlJXz88cf2fm7ehw4das/dfPPNZe75r3/9y95z5syZyXNcx7O75yZPnmzPP/DAA2mfccyYMeXatjd/lMf222+fOOigg8qUDfdr0qRJokuXLolMcL8gB+0rHXfeeae9bu7cueW+22233RJ77LFHufO84y1atEhUBkJPcaHnjwWEJYlVw//pepuuhUZPD6sVC4ueZTYECTyg904Pn14svTMXcRfhisZKwzVNj0wOxugIwaa3mY533nnHWihubw8rAEvbJdd0XnvtNeuaPfroo8t9J3nhWRiwJw0Biw2rAEsWN58LvW5cbQL1AFiiUcFYKj1+102cCQJLsDCw7tx6O+uss+z4ChaDS5UqVWzAk4C3gfrIlA/KCygfF7EqBeQM5X/44Yfbv936w7KjDUv7DZNfwCLC+mTMyL0/1gR42wfuOtyOflBO7hhzrm0vHVhUtHeXsGXA0BC4bdJtDwSnuG5ebz0FkTlY5uQdrwN1KdbR/PnzreXP8AjtyS1nLFNvu6HsvO0G9y73xEPhgjfF9eAwZkg7dtun+4x//PGHLQvc+pRlOtmI9+G7776zMpffSN2Sz86dO5tRo0alHFpy20eQg7aejt/+cY/7Wau4x/3c59R1oWJn8k3oaBxeIBoJrkFcJn/99Zd146QCJYtbhsp3x3qynd+JazET33//vRXCqYQN0ABBhJUXGns6GMvwm++EGzTKdMgLAivTs+Di9nYYEM7yvQtjRC4ivOjcRAVuVYQgSg2hgAuOF36vvfZKmw+/MkQ5EoDgzQeuIm/7IS+4ENPBfSgrr4vam+6SJUusEsKtzZEuACtMfqV94Kr3KiTv/YO0f+93uba9dPg9R9gyELzDM1LftG0XysqrcP3AbUpHmqEPb9umA+SmwfN64ZyryLgWFyXuzTDvGfDc7rOgYHAh44bFJeyWgTyjH1K3ErvgB79PV04yNpkrm/3TEfCO4QMufb8hLvJZUeb250pOIa28IPR+GR/EN58qkhP/PGMJBPYQNFK/fn1rKdFwggQduPhVSBikl8aYEVacl6iifQuVTjakiohNN8YspGr4dKLc+yJYGM+k80QwBRYddY9QY8yz2PnIpu6wdlMJK6yLXPJLGlg8d999t+/3BLAEbf/e77Jpe9SrX7lRr0HSyqUMZHwwyk4cz92lSxezbNkyq9yx9IlbQFERK5DJSitU+8TKRg5iXRNnwnghdcEYabpnlO/uvPNOOzbrh2tZ+4HcDgLPlK7d1a9f3/6/YMGCcu2Vc35js9Q1Y/qVgZwkOC5GglPGjh1rXnrppZTX8TJh1jMI75r8NB4vUfROsDBoZAzMp2pgYoUQyRimR8bcRukNuiBEokyH30+dOjXjs2B9kWfXGp0+fXry+6igZ+u3chG9cG+4OkIL1zEHbjkCgG655RbTt29f3+kI8pyUoXsvfjtr1qzIes6kQ1lh5bvWp7fuJHIXgRwk7WzzK/XL5HNccFH3zLNpe9Srnxs8yOpBuZaBTH+gjv3aA++Z2x7wEGRSuLhoCcwiQh1XreDOKHDTICjPi/cc12JpM3zlWqO5vGcEw9FBu+uuu8pYb5lWB5O6xZsQ9r0Q5ZcJ5LRfRL7Q9h8Z+8UXX5RRmLjKce9LkKgLdc0wlYn7sn/0dIgkI6KScaN0PTIEhNurJQrNb1EFXsJcl5djoi/KhMhSb29OeoH4+WUeE2MRXnhR00GkIJ0HIv7c3zD9wCXXdHDlImQJufcieeFZ6FW6HRmiholEpI4Y34kKXl7y7UZHYnnMmzfPd5zLdcniXueZ/coBEAZcRxSj21t/4oknrGvKL3o2DBK5640iZ8qGt91S/nQC/Toybt2Fya/EFmAdEdnqBVefRNmGIZu2R72iDNxztLtspkuELQOiYLFgEMLe9oDHinbstgdvPaWzAt3f8TezBFxwzzIF5emnn7bxAwJxBChiF94zZNiDDz5Y5jxRuci3TBHhqZ7T6wEgv6k8AAIRudQZ02vc5w4qV6IcE23ZsqXtCDHk4T43uoFy8Q7z8S7TgWV8ujKQsy8xnU9eQPjhrmI6Ai5gxnlYSo4xB+/4FY2D3h7X08AZe8l2rUXuyzQNVtshaIbeMBYwAQ/ckzEIhAuVzKo5hNbjPsHyYByFABbGcbwviwvz+HCTkSfC+WWKi1iFQq7pED5Pb5WpPUxxoXxwUTHO88gjj9jeHD29Rx991PYWJ0yYYKct8BsEIALHO4aTC0wN4d7kGwXAy8BUEe/4IuNhuBDJH/MrGfcjn7SFVM9DuWCx4Prj/gwBYB3iEmS+qhtElAv0nAm24b680LzMI0aM8LVGbrvtNhuAQxtk6AKlQPkzVkY75e+w+QXaBVNWCJgjHX6PIEKhcR7vjXfqQFCyaXu0Ld45BCbTN3hHaV8ISKbyBCFsGQCr2NBRdMfKeFYm8fO+sowoSoyAIAJ4MrkCEeq0SX5PJ4WyoDPkZ8HSySB9nvu0006z1/DcKFdXQWEoMK0N2YIRwLvHvHPmO+KOzXYaGJAv5AguU9rWmDFjbLsSF3cqMBIef/xxq7ipI56bzgh5pR2R3zfffDPtPaLy7IhbmfeVNkA7o9NJGSIvZMxYIH/UM2VeKcgmlDfVtAcvflNcnnjiCRuSvckmmySaN29u7+WdmgLTp09P7LvvvonNNtusTBi7XLtkyZJy6fndB5gSscsuu9g0a9SoYUP433///TLXEDJOmDgh/5tuummiWbNmiVNPPTXxxRdfZCyPKVOm2Hvyu2222SZx00032Xy60zaiSOfnn39OXHDBBTYNwuYJx6dc3GkXixYtSpx22mmJ2rVr22sI2/eGpsv0BELSvXindqSr67vuuss+C+W611572Tx4p0g8+uijth6ZTsN15Ld3796JFStWlEvDW1ZMaaGNbLTRRomtttoqce655yaWL19e5hrS8ptO4Z2SkYrffvstcdFFF9nnY1rV4Ycfnpg3b165cpCyPf/88xMNGza0z8Q0ms6dOyf+97//ZZXfVDAd6fbbb7f5kbbarl27xA033FDm9zwbz5Htexm07TFFqmnTprb9tG3bNjF8+PCUU1z82lAuZTBx4kR7X+9UH6Y6UQ5MM0ImdOrUKTF16tRyU638prhMmzYtceCBB9ppXrwXTOmRaSbed+PFF1+0bY7nZurdG2+8YadKcc6FaTCXXnppYuutt7ZtAZlGWbjTTNLVlfe5adfy3vKc1BMyMEj+4Msvv0x07949Web87rjjjkuMGDEiUWiGDBli2w3PgYy65pprbNv20qNHDzsdq7KwHv8UW5EriqIwLoynCMusIoDHAmvYO46qhGfhwoXWu8hiO5XFEq0UW6EpilL64FZlXD/bYKZcYbzWu9sIywcyJuy3FKISnnvvvddGo1cWBQpqiSqKEmsY32R8kDF3LGHGoxkPZpySsb1M45NKvNGtTxRFiTVM7yFgj0AdoloJEiQYiqAyVaBKJtQSVRRFUZSQ6JiooiiKooRElaiiKIqihESVaAngt+FwnHA3go4TFaneK9KzKEpFQpVoERk0aJAVTHKwtugOO+xgN1xmM2ilclJZ6p0VsnQKiBJ3NDq3AsAav0xAZuHpTz/91C7Vxt6FhNen29leKW203hWl9FElWgFg/UtZH5W1JgmrZx1T1uR0N9pWwsEi7kxbqGhovf8fTBJItfck51nM3rtfbj5gwwo2V0i140xlgPJ0N5DIhY033rhSl1UQ1J1bAZFNlL1bQ7mwyLzf9kS417wuNnaFYJFqrBvmxCG4g+zjGuR3LAiOMmDBa3aMYek2dnnxc19ibV100UV2KTX2nmUbPV5mdu1huyrS4GBx/1Qzr9gxg0X+EbbsTuPdXYUy4TlYGJ8Fy1n4vGfPnkkByYop5IkXn0XSeYYo97GsDPXO9lXshETHg23ULr30Ut8Nl/0IWsbkg8XXZYF96pNNFGTslWXhrrnmGruoOs8vi+CPGzfObk7AQgicpw14d5qhfLi/F3ab8m43x2fc6Oy+xDOzUQV7oQLPwPxR2hDtm5V2vLvAlKICxftB+UVxNPnHkxJn1BKtgKAAIIqJ3myxheJiOyJ2m6HBs8sMwogddXL53ddff213yUHAoPjYtgpBiDBnKynv7jtsQMwuH+zSgqJl1xuU6ejRo02jRo3ssm+4M9kRgh003H0gge2q2Mvx/PPPt8+DQEPxsGUVwlpgCTd2I9l7771tMJK4RhHmKHR2vCBvKCt2mqAjgCDm+VOBEiHtIITdbLgi1Dvbr9ERYqcXfi9r2X744YeB0s2mjNmhB4ub37BDjru3KzswYeWwCwtlz988Ax02FNt1111nLVP2uqQNfPLJJ76bPweB+7JjDsqUukMBs14uz0ZZ3H777fY6dqUhD5RnqUKnlfVrqV/e21ygY9OoUaNKb7lnpNgr4McZ2X3jgw8+sLvTsIsIu0mwIwM7Vvz4448pd3Dw7vIgeHdTOfLII313O8lEkN8dddRRdseP77//Pnlu/vz5iapVq9rdPLz5ZIcKd7eLjh07JtZbb73EOeeckzz3559/2h0g3DzIziFumcC4cePseXbVECgTzvXp06fMs7I7COefe+65MueHDRvme96L5CHIUcr1fu+999o0X3755eS5X3/9NbHddtv57iIStozJB+f4zkXyzG4ya9asSZ6n3bBjircNcU2TJk0SXbp0ybiTj99uT3xef/31E19//XWZ8xdffHGiWrVqtj1WJthRhzyzeww75ORycA9jTKBdeiozaolWALz7+uGuxL2EKytXsPRwz7GXKntyRvU79rxkL0Xcfk2bNk2er1+/vrV0sIToqbq9XfapdN1pWKrsn8h5d5Ni3Hvsi+qFtNwywfLgHlivjCW6nHvuuWU+v/LKK9b91KVLF7N06dLkeawa3L/swZjOQsOyjXo3j4pY75QldehupIwlz561eBvSkW0Z4wpMteEz+xS746OTJk0y3333nXXxejf/xlrEWsaVHGbcFJcwe3l6y4+xdOoc97GipEKVaAWADcqZ4rDhhhtatyRuraiCKK688kq7CS4Kh83K2TQXQcYGxLn8jjVG16xZU8YFJ7AJLwJt3rx5dpxJwPXjgsCFhg0bljvvN065/fbblztHueGKc6EcGzRoUOYcApgNuBnj84NNqNOBYuGo7PXODipc7x079KtnL9mWMUo0Fd7vuLco11SQNmO/2eL3HOedd55tV7iP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mtdff70pBk8++WRR0i3WqjTNmjUrSrrs8lNo/FY/KgRffvllQdNjyUDWjK4sjB8/3jz66KNm5513LnP+0ksvNW+//bZ55ZVX7PKMF1xwgenevbv57LPP7PcslYkCrVevnhk9erRdE/mUU06xK5ndeuutRcqNWqKKoiixI0pLdOXKlWUO2Y0n1TKtPXv2NI899liZZUVZ4/iJJ54wd999tznggANMu3btzMCBA62yHDt2rL3mvffeM9OmTTPPPvusadu2rTnkkEPMTTfdZAYMGFBu7eNCokpUURQlZkSpRBs2bGgtRzn69++fMl3ctViTBx54YJnzEyZMsAvzu+ebN29u1z8fM2aM/cz/rVu3NltttVXymoMOOsgq7q+//toUC3XnKoqiKKGZN29emb1YU+009OKLL5qJEydad66XhQsX2l2MGFZzQWHynVzjKlD5Xr4rFqpEFUVRYkaU0bnVqlXLuKE5ivbiiy8277//fmQ7E1UU1J2rKIoSMwodnTthwgS7Sfquu+5qt9zjGDlypLn//vvt31iUjGvKRukC0bkEEgH/e6N15bNcUwxUiSqKoih5pXPnzuarr76yez3L0b59extkJH8TZTtixIjkb2bMmGGntHTs2NF+5n/ugTIWsGyxgnfaaSdTLNSdqyiKEjMKvdhC1apVTatWrcptYs6cUDl/xhln2P2ga9asaRXjhRdeaBXnHnvsYb/v2rWrVZYnn3yyueOOO+w46DXXXGODlVKNwxYCVaKKoigxoyKuWHTPPfeY9ddf3y6ywDQZIm8feuih5PcbbLCBeeutt8y5555rlStKuFevXubGG280xUSVqKIoilJwPv744zKfCThizidHKrbddlvzzjvvmIqEKtEMsBQVA96sGkLPi1WNGAinwvn/t99+s9ekArcEPahsYXWOpUuX2knIzJ/iHtyLAXjv/fh+5syZ9jfS0HCfVEToQe6www7WjbPZZpvZydezZ8+2QQYEFbRp08YcddRRKX8/aNAgM2fOHFsOuHdY+ov70HNdtmyZDWBgjCUolBllTD3yNz1hxmZ4PupXYGkx3Ee0A6hTp45NNwzcg7plfpvULfVFXqRueZYlS5bYa0ibZyL8n1V6Mq2sxepBtAfKUyahM7+ucePGZa7j3t9++629nufAJcYEeManXChvDuoKWHKtadOmdg6fC+X4008/2etkKTauk6AP8sQYl0zIpxyY1kCeWL3LbddMsvejQYMG5dLl+b/77rsy+WU1HG9+eT5vfnEdSn5ZGSfVykvUdZcuXUwQyCd59LYp0uJv2qnIFCDf3J85loVcNU3Xvo2GklaiTOodPHiwmT59um2Ee+65p7n99tvNjjvuGFkaCAMaO42fRsffvICcp9Fz3qvUROEC34cBYSNLfaGweel4+XgxEUzyspHOjz/+mFSgFZ0OHTrYckOJUIYIbVYfYbm6Bx980KxZs8bmx4XrpVMgghyFguCVVVJQeghYDuonyORrygzFKGWHoAPKmmcTJUoZI1xF6OUKdUs+AUHO8y5fvtzWbZMmTZJTAuQaFA3PhFLl/0zLRaIsuBZll2olF/LDSjDkiXxSvpSDN/qRQA46OcA7RhnRaeR5vcqM9kra5MlvPVPyyVJttF3uxbNxL+qbevULDsFl5yoWysILyjNofsmjm193fiHnvHXMvan/oNMypAylTUkb4rlEoVLPEqHKOcpq1apVNm0UbVzduaVKSStRrBcGlXfbbTfbEK+66iprnbA0FC9fFHhfYrE8aUC8AAgMd1Cb8wgSeeHD9Cy5vyjQ+vXrWwVBz53eNulzf5mUjDLiWqxUlElFh8nWkydPTj4r4x4EDiDA6BzQISKfLuecc479/vvvv09aClzDItYCyviiiy6yfyPcgyhRypE6RDG4VqBXOCDgKH+EtCi2sHAfuQcWGkKTc+SNeqdcaDNyDavBkHc6UAh8npnfpLOC6UjgjeC+brSjQP6mTJlihTYKGetf8u4qP9IUBYq1RlsU/JQk1jkeApQl9ewFBcJzkW/SI33WcUaBoqzcjotAhziTAqOMsDrJ7wcffOCbX9ocdU1+6bT55de7livPNGrUKPu3dG6CtinykapNUT9uJ5iOBb8p5NJ1qkSjo6SV6LBhw8q5+mi4uPT23XffSNKgsUuPWSxROe/npuVaaVz5mlSM0EGJolAJ90bIIlhLQYl+8sknZT7jJpToOz/BjIUqq5K4Lj7xDpx22mn2f7cHj6WXCepIFBX1SDmKEKdDIp0wmbuG0mJR+VyVqJ/gcTta7kLjnJeF7KljsZqo/3RK1M9ac6GdiEUPH374YdKzgjUoHbT58+cn2zGWMW5yygclyJJsXsSSR4mmei7XiqbeyJ88i1+HU5Q9HVWU9NZbb13OuxMmvzwj+XTz6wWXOFDWpBukbnkngXKiTbmWr9uxp4MiHi2RKZnyoVRMKtU8UbEA07lE6K16F0zOhFid0th5iXkpvC8918n4KAIlzFgouNYtvVReZiwVQV48BBtpFGs3mFyh/FhoWoTKrFmzyl2Dix5QID/88EO539OrR8Ah6Kmjd999N5AVSvlJfVJnopQRbFi7KEsZu6SMo3KzuXVLnqhXN1+kL0qIdKWNecdnc8FV1IxhSjsl33RUpKMg11E+uEtRJvyNdRrFTiYyRAGMi3rfF/Is3hyUE50jr5ciCK4CdfNL3TIO6tcx4hzvHuAhCTIs421TyANpU+TTTYdz7rgoXg53QfbKvhVaZaKkLVEXGiN70+21117l5iN5x1FvuOGGrO6N0ONlFuuFF4AXE4vFVaScl5ciFyuUe+KeQsgiyHjZ6MXSAeBvvmfchb+5zusCKwXoZBDKvt1221l36QsvvFBuXBeLBwGWKtCE66lL6qZFixbmiCOOsMEfCCyxIlLhCgDKT1yVCE4Zo0IQ8jcWEII3lYWVDdQd7masFNoQ90SAUrf8zff5Fk7uuB/PgjuXdo2FRpnSOcON6l6Ht4BhBQJzmARP++M3PHsYKFuGXWjDdEi9W7gRCIWVSnnwTLj56SSj6CmrbOYFuuVJfnHn8l5JflHOXsuazg2/o53igs4WsdiB95h2RH1LeeFd4f7kXzpt/AZvQCFQd250lJ70TQFjo4yvfPrpp2mv69u3r53QK2CJMqaSCV5mDpSjKEteAPdlFisUgZurYuPldZ+L9BAkgNIQt5G4Lt0GzTkUfJB8FQM6BCeeeKK1ILEGnnvuuXLLfYGsVILwTLcHKvXAmNfuu+9ulSGu/ExK1LV63LFr/kbgudYez+iFc1hmYfa0pG5d7wF1i3KS9EV5IeAlItx9nlzbltvBE6GNcCdtd8zWdRmLy9N1fYZVonRQvvnmm2RgGdHaXivUjS4XT4B4mrJVom5+5flpg5JfeZfc9iTvFQo0aHm71mqmNgV8Tz4kSh1ZRL7DBiMqxaFS1BabtzIJ96OPPrLuvXTQaGXB5EwLJyPMeMlcBeW+CN7zYklFMRYq4fGSjju9wu2tigvJfRZ37LaigVV35plnWgXKeCh7CPopUOqlZcuW9u9x48aV6/ViqbiCFiErLlcZm0uHdIiATpH0zMXadO/h577KpSfvrVusOrduZRyU78QVieKJasNvFJcoBlFMKEQJbJGxO7eDINe5dRUmeI/OB+523hcZW/UqUNLiOilfykbcvpDt6jRufuX5/fIr4K6WSFrxhARBlKIbGyHWJvAMMmXOK2MKbd2pOzc6StoSpRJZGmrIkCF24m7QCLps7s/LxiFTXNyG4wpasUJlTliuMIWAQ3qxInRxqck8PReZb1nR54ked9xxSWuAvLF2pkBEp4y14T5EuFKuBIp5YSHro48+2gpchJA7poZVGgQUFvdHcUoQjZQzStzbGeJaWbczl3miCHIOb91K1C3p8j/KFteqTHGR58qULi5pXKVuW8UFi4sShULZYf1xDRYXCkrG8FAC4r6ko8N4LWXM9BDanShzvBze58AtScfI6xWhbFH8DD2IxY3Coc0yhUagTXMd9YkngXdJpndJ55XAQa8S5f7e/OK14R7kl3F33NMo73T5FaUmY/N4C7LtENOmJEhNxlTdNiUxGTI1juvcQMSwcRTZou7c6Niw1F24zz//vHn99dfLRC/SkMMKOBdedBSiG1Qk59wGz/diwfBSRjFhmueX8VAgPdfaKlVc15g7ZQLEBUsZIugBBeoX+o9SQLkgJEU4YdGhiF3BnA7SYWxKJumLdUr7yedanNStawmRJvmQwBIZN5XFFriONsdzobwzgWD2BstwDw55LxiD5J4oSdoZZSmWoeQdQU9nBoUk4/O8ZzybX4eV98C78Ii4MV03tdfKdn8PpEG9kHfuR3lgLXLOu59kNvml7Xnzy1i6W9fMWRXL0DtOGwTuhaKn4yFtinPSpkRxSwCZyBOeM9N2YkrFZL1ECXcnUimrgQMHmlNPPTXQPXhRaeBYR4VcLQSKFVWbbnwxX1x//fWmGDz55JNFSTdXl2tYwgj+KEi10k8+CTMeHQVRRCZnA50PoopRzLkqWpF3xCGEDQoT6Lj07NkzkucqZUraEi1h/a8oilI01J0bHZUisEhRFEVRikFJW6KKoihK9qglGh2qRBVFUWKGKtHoUHeuoiiKooRELVFFUZSYoZZodKgSVRRFiRmqRKNDlaiiKErMUCUaHapE/4El1gq92AJLuhUD2V4sDost5DqhPCzeDZ4LBetHF4Mg+21WlgUtunbtWtD0WPnomWeeKWiaSnA0sEhRFCVm6AL0xq7eNHToULujUC6oElUURYkZcVSixx13nHnwwQeTnsf27dvbc3iNXnvttdD3VSWqKIqiVHpGjRpl9tlnH/s3O3/RCWDzifvvv9/cfPPNoe+rSlRRFCVmxNESXbFiRXIXrGHDhpljjjnGxkx069bNfPfdd6Hvq0pUURQlZsRRiTZs2NCMGTPGboWHEpUAMfZtznbfWBeNzlUURVEqPZdcconduo2objZi79SpU9LN27p169D3VSWqKIoSM+I4T/S8884zu+++u5k7d67p0qWL3XQemjZtmtOYqCpRRVGUGFJqSjAX/vjjD9O8eXPz1ltvmaOPPrrMd4yJ5oKOiSqKoiiVmo022sisXbs2L/dWJaooihIz4hhYdP7555vbb7/d/Pnnn5HeV925Gfj7779tofO/sOGGG9oj3TUsIcg1G2ywQah0161bZ5f7kntyP+5FFBn/04DXrFlj/vrrr2RjxsdPj2uTTTbJuIQh4d4//fSTWb16dbJRMTZQr149+zf3ZeyAVT14Fp5j4403NrVr1zbbbLNNmXyNHj3aN40GDRqYRo0amYrq3iGPbvlK+fE/ZSrXSPlKHXBN0CUif/75ZzNz5kxb3tQnEMRAYIML5fztt9+aZcuW2XQpa8Lx27Vrl/x+2rRpZtWqVfY+tK0tttjClm+QMuY31LMIP8kL6ZBf0qSeU0G7c9t8UChfykDambwX1apVMzVq1LDPsnTpUjv5nWfgM99XrVrV5l/GrdJBmc2ePduWMfeAFi1a2GhMYfz48TYK00v16tVNhw4dkp9ZvYbriODkWSgfCUBx+eqrr+z7QZqU22abbWbq169vdtllF5s3mD59uq178i/vGNMqSNOFiNGFCxfadEmTe5144okmn8RxTHT8+PFmxIgR5r333rPvIO+Py+DBg+OnRB9++GF78AJBy5YtTb9+/cwhhxwSWRoIAQ5efr9GI8LWFbTSQDkvwjlbRODJb0VR83IjYOQavueQ5+SFlhcxHdwHAYDC9euZ8ewLFiywz8+9EMK4Q3788UcrEHfaaadyv6FRusoFAVRRkfKV5+VvFCYH+eWzlItbp9JZChoSTxmjJJiPJkrUTwmMHTvW3lcUCOksWrQoeQ0dJoQsz0ba1B8TxTlQhnRs0iFtlLxIfkiD/PJsfu3UFbRh2jAsXrzYdgCkPZBHyoEyIU0iJckDf/M9z8SzUia0NzpimaBjgaKibESJpoJr3HbpXX9X2jz1kO5edGh4D7bcckt7Lc+AwqRj+q9//cumwdrYPBf1xbWp4HeUL+9ivlyOirGdFzoxUVPSSpQX7LbbbjPbb7+9fdmfeuopc+SRR5ovv/zSKtQoQECJ5ZdKCIqgEQsVwSQvYNjemgg2gZdLFCSCiJeOHq+rBHiRXeGfjjp16pitttrKPufEiRPLfc/9sZawTMk/aU6dOtUKA4QeaXgtkx133DGn+VaFRLwEohzEUgPqTyxOrhOlIxY5h6uAM7VRypHffvjhh+W+5z5Tpkyx90QRsgSZWPluPdatW9ccfPDByTRRorLYPMo1kxKVvEh+eR5RrOTX610Rxc33bjllCxamtGfKgnx+//33yXZKfvBuIOCkM0hHjfYunpZM3hwsQO5NHX7yySdpr8Xbkq6s2JyBNkxbnz9/fsrraOvbbbddUgnTCfr6669tfvld48aN7b1Q2ijJdM9FoAv3YapFLpP+syGOlujAgQPzct+SVqKHH354mc+33HKLtUxp0FEpUVdJpbvG7dnLtbkIH+4pLjZRnHKee8pzIWhcwQ5B3G4IVUjV26Yn7Qob0uRFlx61nwIRZUCPGiXNzh5h859vJP+C+5xirbnXSLm7LvsgZLLG6fhImVJ/KEbaERYOLklx/Unan332mb0OJSrIKizZPAdtM52l5Q5PeMsqG8Q6pJ3iMZJ2ynncubRV9/nJJ0pMLLIgHZVsPB4zZsywViRpkC6KkPYqBO0Etm3btsxnOpsoURCl73UXVqTdaOKoRPNFSStRF5TXK6+8YoVLx44dU16HUnLHfsTVFBZxQ9EL9jZMEcZhQeCQL/d+Xpep9xoEXj6sQfKHiw2wHLzWAcKQcqBs6Y0zXkRd0GOv6Hhdt36KXyw2IO9RbZvnuvmwYKhfngU3IGNl++23X3I7N1nrU+AZcKtnuw2ZDDXIPfwsPfmesggzFupa0PKeiSdH3hm/cibvdCwAt3aUnTBxmZIGbRTXK20aeZFLHnkHGf+UZy7GtnDZEkcl2qRJk7Tv7Q8//BBPJcoAPy8BPVd6dCws7DdeJ/Tv39/ccMMNkaXvjokijHgZeak4J+6qsMFFvPAIG+5N/qRHTz6lMfC3CHi+4xrpzUcFadN7RwgiJJo1a1bmewbp5Zl4DgQKY4EoApSq29OvaIibXqwuv6Asd6yZso1yrNcVRATCtGnTxtYjFillybiadERoR4cddphtV4zdYfkTCIOSxTUfND3qk3vLeLc3vzI2nKsVCih9FCjtEeUieaJ9kK4oWaAeUGxcw/VB8xQEypA2KkFjuFhnzZpllSnjtmEVH+/bxx9/bJ+bsmQSf9j3Xcn/ikXeumPojyUAe/fuHfq+Ja9EeTkmTZpkX8pXX33V9OrVy4wcOTKlIu3bt6+57LLLkp95wd0ovmwRV6probiuMr7L5aUSaxbhzj1FQbuCXAIhEHgIIlFcUVhLWAUIagQ37rcddtihXH4k0An4DjcZ9QEVWYm645xSxl7LB4EunguJZo1y83a3syOuW5Sia9V7oa5psygB2i8KIYjCIZ8oUMkvQt/P0nOt1FwtNAKIpI3IuCvp4qWgsyDIWCLljTXOOGeUVqhEzEq+cL9SfhA2mIfnf//9920ecb+zFqubTkUmjpboxRdf7Ht+wIAB5osvvgh936INWCGUP/jgA/Poo48m3Te8ROmi2PxA2DCuwVQArEx68vfdd1/K6xGUNHT3iApRptmOm/ndR9zDgjt+5Y6/uue8n3MF4cA4D2khdFjxw6tAJfpU0uPZxe0LFVWBugpFLHev0CbfokDFXR2lAhXFKYpKXLUIZ3F9yrgawTauQuU9ESUUJJCMtsHvJb8oaj8lJVHgkM1UHj/cNiiKSto2SPq8/+SPZ6Q8oh5Lpw4Zj3XLyY18zhTJ7gfBXG+++aZt+3RgiM8oFQUa13miqWA2Ry77iRbFEp0zZ46NNGTcjAaOC4SeKhNh+fzII4+Evre43qKCF1umQwiiwLzBJ97AIghrhSLwOMT95N7TtTjFUvXOY80khHC1Ug/ufakPOjK4vYguZN4icH+ENq5zN8qR63gGCdFHEblRrrjqKqoSFfesa5F6y8+Nxva2q1Rjel5wu2LJu+VMcAsRqiiMXXfd1Vr3uMtxcyKcUThcT9nJHFDqBo8LAp/noz7knkE8KXJPwVXItCe/QLNcXbm0fZ6XtFCU0mmRzh5KR1zT0s64hnIQaEOZhiZQiLRVN3+0SRQnFiLR+3xP5CudB9IXpU4nxXUpM5eQ76TuKQ+JrGXYQrwFzDeUDj/XDB8+vIx3jOPzzz+3z+CWKa5D2g2BjxL8+Pbbb1vLXJ6J/19++WX7N3NUvfNKlWjBgxkkOK9CKVHManYVnzx5sqlVq1aZUO+zzjor8H1wzdKLQNDwkj7//PN2fMJt0LmSqsflzguV+W1uhKwEZITpUYty9i4GwP3E5ShTb1x3srvYQiZcQSKQBw6Zz+fm1eshEEFI54eeOG5F7ifBT5yLckwrn3jrWKw1F693IWgvnPJ03ZaAgOYQ5UCHhLrFvYgwpfwpOyx/qUs8ATLuTTnLfFLafpC5lOnyIp/dgKNcrVABqxLPhLvYAvlGMaBEvR4Wb5sM4tWRQCEX7stBWuSFoBI6jlwnLmOUJ51Ft6PL9+4z8Exyb/dZXK+P63kBqQ/uI142QaKq3Q4ZZeO+XzJdzZtOlMTRnbvLLruUadM8P4tcLFmyxDz00EOlpUTp2bHKjTdAgwbNAH1QCAg45ZRTbE+WHidz7FCgWLZR4Td/zkvUwSZUtERkpsK1HsKAAHF74H4wzy0TWBreQKNSIIgLL1MdBAErMYilmGnlIRQtR1iCTLeQxQ+iBAXFdCcOP2jDWOK5wFSsTPNksUY5MrHvvvsGSrNHjx6B7hXkfpnulWp+ei7EUYkeddRR5eQ27RJrnw5rSSlR77QMgXERN0glE0888UTET6YoiqJURq677rq83LcogUVEsd17773JzzLmRiYPPfTQYjySoihKbIhrYNH3339vrrnmGnPCCSdYTya8++67yYUySkaJ3nXXXXblFaahMG7AYsviyiW4SFEURckfcVSiI0eOtMFh48aNs4vNyzg0sTm5WKlFcecy8M6Dv/jii3bCOJk544wzTM+ePUOFmyuKoihKOvr06WNuvvlmu06AO2x4wAEHmAcffNCU3GILBOucdNJJxUpeURQltsQxsOirr76yMzi8EGApi4JUaCX6xhtvBL72iCOOyOuzKIqixJk4KtHq1avbmRxMd3Jh6b9M0d0VQol6w4tTIeuvKoqiKEpUHH/88ebKK6+0G5XIAjXE5lx++eV2qmSFV6K5LoWnKIqiREMcLdFbb73VnH/++XbeNoYaga38T2ArEbuxXYA+KmQ1lUJSrN0e2HC40ES5GEU2eFcLKhQsJlIM2AC8GMjSfYUkyuU9s+Gcc84peBt+5plnIr1n3JRo4p/Vie6//37Tr18/Oz5KQCurGAVZhKNCKFEePigXXXRRXp9FURQlzsRRiW633XZ2PihKM5edu4qmRO+5555A12ENqhJVFEVRooIl/lCerJ+cq+VZNCUqe/cpiqIoxSVulijcdtttdvPthx9+2LRq1cpERVHHRFlYGeXKAua5bP6rKIqiVG4lmCtE4DK+zJ7TxGh4F/bx7sYTlKJoLjJy4YUXmqeeesp+Zq8/dqjgHPN1WFlCURRFUaLCXa89SoqiRNkHlGX/2PuTzbmFAw880Fx//fWqRBVFUfJI3Ny5f/zxh10799prry232EKuFGUB+qFDh9q1Cvfee+8y00rY6Z1V9hVFUZT8EbcF6DfaaCPz2muv5eXeRVGi7CTutyE0u74Xeq6moiiKkl8efvhhs/POO5tq1arZo2PHjnYLMoHdvFgIoVatWnZj+GOOOcYsWrSozD3mzp1runXrZjbffHOrPwgSYn5/NqvmYcBVCndu+/btzdtvv23HQEEU5+OPP24LV1EURak87twGDRrY6Fiml/A74mGOPPJIu24tHshLL73U6gSW5Ntyyy3NBRdcYLp3726X5QNWFkKB1qtXzy5kwuIeBAphYbISURBI+8Ybb7T3bNeundliiy3KfB92amVRlCiZPuSQQ8y0adNsT+K+++6zf1M4+K0rElQ4Sxa6DYY5RxzuNXKdQMeAa8JY1vjvKRe5L/dgdSMajKT722+/+S6lyPdht5NjBRjSlvtKuptssklydSXvNcC2Qrl4ELgXL4lbxqTnruhEeXjzS165ptS8F/S6KUdpVzw/0enUm0Spk1/qWNoB+dx0001tXYSB8mWO3IoVK2z9cT/qbauttrJ/8yw//vijfTZZvYtnwWqoU6dOmfYeFlaIISIfiI7E4gDy55YJafM9FkcudUueyS+BjPxNHrhvzZo17bsEpMs1PBdpUxakyzWZYFYBK0RRb/yOexDhOX36dLNy5UrTvHlz06JFi5S/Hz58uH026hQrrUaNGknBTl2MHz/eVBYlevjhh5f5fMstt1jrdOzYsVbBPvHEE3aHFbYlg4EDB9qy4/s99tjDvPfee1ZHfPDBB7bNtm3b1tx00012LVziaIKsiEYaLEI/YcIEe0S1PkFR3LmMhU6aNMm+rGySSgFhno8ZM8b2ECoSQRobL59XwPMbr2IIileRcQ/Kihfeez9R1u4RFnleuY+ky4su6cqzRam4gpYxuOlyLht3TkVBytAtZ86tWrUq2W4QwpwDBDTnGO6gDYQBVxjDKAh6BA7pL1++3MyePTtZ/ig5yhehznNxLVtEsVxarqAgRYF6IV/SKZTy4HopjzBQXjw39+BvlCblyH1lgwvSxWUoZSqCmGcJQu3ate1veD+4F50cZhfss88+Ni3ug1J1DykDnkH+5ncoEjlfaqxcubLMkWk5RvLIXtKUGZ5HFBptncBSgQ5Io0aNrE4A/kdXoECFgw46yKbHKkRBYDplquOHH34Inf+iTc6kF/fYY49Fek/cBUT+XnzxxZGFM4s1lqqBuwpAhKKrVMXSyAYsAA5RiDRKsUh4BndOLcJBetW5Qo/afVaECy+6WNnSS+eadEIxW1zlL4rDC3l0n43rSi24QcACc/OCEJYOkitcAdcWZSPXIJhRctm0KX4nawjjDmPcifqbOXNm0hIjHQSX1APP8t1339lyznX9YekA0G69HU7ateSXtoVC4TMKne9IP8y6y7/88ov9Pe2GDrq8M9JeeAaZF4i1jYUiZRp0swwsRfdaLCfKkOfFyp8zZ449BMpWZiPQqZEOIIoeVyb57tq1azk3Y0W3RBt6ltC77rrrrHXohfVqUZq0Od6BIUOG2EXgMagoM+rABYUpHTj+dxWofC/fFXN9gqIpUaJwMdnpAaDwaOgMNNP7wEeeLTToRx991LpFokRerFQNzj0v17oCLkxD9QoNlFcqi4sGwSHKnt+GtRL5HULLVZxyXoRrFG49v3QzlRXXuC5u729LCZ6ZMkYhess506YE4h0I23Hya6MoOFEiP/30U9JlL20O5RYW18JFOaAwXNxOk7R7N29hlChpiuKnPBcvXmzzgsCks8BzuMMhKHncp0AHBbdqkHbO7+vXr2922GEHe28UJ1B+5NkLso3783x0YNz7RNUhLYYSnTdvnu2ICKmGHHbccUerMOm0vfrqq6ZXr14FHb7L1/oEBXHnzpgxo8xnCg7TfNy4cTbsWBocc0fpxWQLv+/Zs6e1bHkBColfQ4za1SnCzE/Ics51+YpgziU9hIor2HMdm4oSrwIt1ZWuZBxYyhmhLWPLrhJB4HC4btxstxVEqIlgIyCDDqw7lcxVZCgAGYsFlA6KIizci3zShvw6CN44AvlflFiYLRRda5dyEzcx+cQ9TafBzTOfeTbaFs+LizdoupQr46coEZ6be33yySe+nV5ZsxXLyU/JlirV/om4lSOVEqUzxCLwDNn179/frhxEPAzeEToReA9cqAe+A/73RuvKZ7kmm/UJ8HgIuJFfeuklE5aCKNHBgwdbJSfuUDT+zTffbN5///0yvUwGlRlIzhZCo4nccn3qqUBIeH34FRUZG6LceEGpeBE0EnjBgRtWBK8owbCIKwp3CwpKBEtFcJvyPOST/6XjUIpjSEBd0uFDSckYJQKYPJFHyl8EO9+570m2HRquJwCGtCg7BBbtRu7p3o+eOW7Jxo0b22tR4IylhkHG8Ukn24CoXDuCAmWJlcEh74jXGqZctt566+S0O9pUUBc2Y8q4JYcNG2atWazcDh06lOvc0RGRQCrc5MWmIswT/fvvv618Q6lSNyNGjChjeOHyltka/I87GK+CgP5AaeMSLub6BAVRouwcTm+NgWCgMI4++uhy19GI6SlmAwPUEydOtD2bIHAdL40cuW6J4yfQolA4NDAEkKtAXReTG5Xqtchy3QBdLAERfNwv1VhloXEDqcAbOV1KiGdBesXUtQRloHhon7w3KFvXOg3jUuf3BK/gUkNJ0u7FWvIqOO6PMiB9QImGaVPSwZGoVQ65j5zzRrn7xRlki2vxyli6a+GTb/cayb9bDtkGrNHRFI8bgl0ChbxWKHkmSjpuSrRv375m1KhRttOB/OczFiHGFe3sjDPOMJdddpn56KOPbKDRaaedZhUnkbnAWDHK8uSTT7bWJJHNbKSNARW0g5av9QkKokRpvA888IA5++yz7WfGX/w28WXOED3GoOCLJ4joueeeK2Oep4PKExcZB/fIBb/xz1zH7ESBihsKS9MrbCSwxu+lDyN4ZFwm1T2Lqai8wSje6USlpESl9+0+s18HxT2HMpKIUe/Un2zdqkDarssSoY970Y1KlWAg97nzgds5kHFBN+9hxn6l0yn3EoEv9+WerryQdN2o0kzDBHRy6Ii477frVnTriI4QwVwVxQothhJdvHixnddJJ65z5842hgVF2KVLl+RWmYcddphdZGHfffe1ZYkH0y3Pt956y/6Pcj3ppJPs/Zj3me36BEJU6xMUdEDp2GOPtf8ff/zxdn4PE2vJCC8oE2CxWCmYoNBjoXJ23XXXMi8/PR7Mdl4Kr8Bxx4eC4Dd9Rc6JJSGuRb9rwyhR71QWdzyMl1vmpElAEcj1QYJT0qXLIdMMvG4xkDl37ncyvoNgCiP0/KaqkIbM7SNPogCkrAWxMkoFnh3l5I7DuW3GdTmK1e26rMOOTzPexJQWhD9lLfdEwHNP3iN66jIfWeZNAu79MGPPfu8azyHuaXFv8jfpSQSypCvu+zDQUWfsEcVJsBRInsWtTb4oZ56J+pA2SJqZImT5PUKZ+Yr8lt9IABZpzp8/v5wVynvinhd4b5gWAzLHm8hTUTCvv/66KXWeeOKJtN9TBgMGDLBHKhiSeOeddyrc+gRFW2wBM5yeHA0bM53/TzzxRGuiB4UeDa4BF9wAhJmjpMMqk6C4LievWzGXxRZc/OafimvKGwAkQidMmvJ7v3si5NypD94eqJ8Fng3pfsd3Uo7etEtxsQVZSMBVZORDFluQNss1CGNx53sXY8gWfouwd+cmiqtYvkcJ0PGUjhTXoGiYD5lPUFgyLzWqxRZQ3igiFKR0NskPylUUO3mnvFFulLWUMUo2U7pcjxdLFkiQaUgMRxH1KVY930lglhuR68JvpUORj6lr+Y7OLRVkfQKmQsr6BBhgMgc1LOslClwSJEfjYxUUGhxKkEa8yy67RLLjeKdOnWzvMOg8UQKLeGmKIYzDrj6TK/nuXPgRdpGAXCn01AEhyIo3+SCXSNpc8BueyTcypaTQ4HosJChn3JcMP7lTScIg8q5fv36Bh8DSvdM33nhjJM9VyhTcEkWJEubMKhMozVwDexRFURQlNkoU1wXKkwi1KCxPL0R8KYqiKKmJkzt3fWc4yI2viIqijInik2YbGxYgbtWqVTEeQVEUJbbESYnOmjUrr/cvihIlAhc/PytWEEDg3XVE1rRUFEVRlFwgqrfSKdGoFodXFEVRsiculuiUKVMCXxt23fWiKFEWHlYURVGKQ1yUaNu2bcuMh6Yj7FhpUfYTVRRFUZR8I3uF8j+bnTRp0sQ89NBDdnU8Dv5mSzS+C0tpboGhKIqihCYului2zngoK+bdf//95tBDDy3jwmWa5bXXXmuOOuqoUGmoElUURYkZcVGiLizsgyXqhXMs/xcWVaL/wBJh+dhwOh3F2oatGCslsU5lMbjllluKkm6qJd7yDRHvxSDXjRzC4K6ZXUheeOGFgqZXUXZQKnVatGhhd/FiwXnZBpAVzTjHdyWpRBE07OPGqv1Mcwky+KsoiqLkTqlZkrnyyCOPmMMPP9xuUyeRuETvonPefPPN0lKirFbUo0cP8+GHH9oMsD0QmwGzpxwLOt91113FeCxFUZRYEEd3bocOHWyQEVtnTp8+3Z5DD7HxSaZdeyqcEr300kvtjgnsXO6a0WSIjVlViSqKouSPOCpRQFn++9//NlFSFCXKFjRsyOq3+/ucOXOK8UiKoiiKUhpKlH0NZQNb73J/xdoeTFEUJS7E1RLNB0VZbIFd3J9++unkZ8ZF2Yz3jjvuMPvvv38xHklRFCV2SjTXQymSJYqy7Ny5s/niiy9siPEVV1xh9xfFEv3ss8+K8UiKoiiKUhqWKNufffvtt2bvvfc2Rx55pHXvdu/e3S7DxBJMiqIoSv6IqyX6yy+/2Hmiffv2Te4WNnHiRPPTTz+V3jzRLbfc0lx99dXFSl5RFCW2xHFMdMqUKebAAw+0umf27NnmrLPOMjVr1jSDBw+2M0XcIcYKr0SHDRtmqlSpYi1RGDBggHnsscfMTjvtZP9mrmhFgtX9WV1o7dq19m9WNtpoo43sczJVh/Fcvv/tt9/s9xtssIENpa5atWroxSPWrVtnVyrh3sB9uC+BV/zvdw3kkqYL+eH+QJrVqlVL9uT8VlChHILUG3W83Xbb2fonH5TpkiVLzOTJk83y5cvLXEvwGZ4KCTZ7//33y/QYyWu7du1M/fr17b1WrFhhl/biBckEeUgXCU5eatWqZcuAHivPSTlT79WrV0+WR0WEOiJvq1atStbVDjvsYLbZZpty1/75559m/PjxNn/e6/AQIVzctsCiKHy/9dZbR9aOEcZynSy4wooyHEHa8tKlS61ni/bD8JDs3iFLvFEWWBupQA7VqVOnzLmFCxeaMWPGJD8fccQRyecVaLfMN6R9SLqsouR601avXm2HqriWPPKe0Ha89YEXjmsoa8qA8iFNJTqYPnnqqafa4URkh8BauswVLSl3bu/evZNL3iH0yBwZYaV9/q5IoBQXL15sBQp/8xKgRHlp+EyD5yXmZZHvRel6lUK26XJv0uLgbwQem5lLD1CEU9SrPCFQRWimgmcir+4RdHnFTTfd1Ap4DoRy48aNzUEHHVTuHgSgpYrW5ne0GX5L/unAoPQ6depklXQm+A33dg8UpMCzUMc//vijrXvKnO85R3tAUVVUKFfaXpA6YaETUaBeaMMoE/IsK4rRzmfMmBF4KlqmdszB36Qh14lSTfVcXqgL6kSWcvNC3dIpcg/aoOD+DaSbTukKlPGiRYtSpks+Ro0aZTsi5AflKfJi9OjRZdoQ5Um6qe4VNXF0544fP96cffbZ5c7TmaGdh6UolijKEosE2IKGpZhuvfVW23DdFfYzcf3115sbbrihzLkdd9wxuRpFFCBIRDnSW3V7z0DDl14oQhxhg6DhBUE4YHGFeTG4j6scJR3SRXHyHFhqXMMLKs+QK+SV5xcL27VyXbC0vcInCAgVd9++XXbZxa73yr1ws7CalYybY2HSVvwWjW7durUtI/I9ZMgQq0RRoCjV9u3b25VJUj07kD92b3DBEsCaRZDTU8XCEMuI3SAoc54P4cl3CMVCr7cchHr16llLkbIZO3ZsyutQPAiPunXr2r+9UCctW7a07Z4yoA0iiFCCKA93h4yw7ZhD2gPXSkeF6+gk8u54LUAv1CNthN8wB92vPDhcRowYYa8n765VAsgh0qb9LViwIGW65B+rk/u888475b6nTdIBA8qxefPmtpxHjhxpzyEf8GpA165d7fv8+eefF2SufBzduZtssonveuV4MbyeiAqvRHkxaEDwwQcfmFNOOcX+jX8620XZaZzcQwhqEQVBesnAi4yQFYXKi0ejl94ygkKUCsJAeplhe5fcjxfZFThyXgR31AKcdKT8URDprC0ULRYPz0D+UKpBnofya9SokVWCCExxiyJwUGDSDlCu9OCxevyUqCzUQZ3wW0D4oESpBzo0fBcU8R4AyjxTXqgPOi/UdUXDtahTQbukbGnHlK+fEvW65ylXBBFKNGjby9SO3c6fvLvuO0xamZRotnPL6QBIXbPAiwtrefM9nTjSTqdEM6VL26ATLS5dFulHqUqnDCUt+M2bV6IF9/iNN95oXn75ZfuZekDGXHnlleaYY44Jfd+idKMZg8Bte9NNN9meV7du3ZI9Au8qRpnghZOeJkft2rXTXo/g4wVyj1Twwktvi9+JuwmhgCWCguVFA85Lj9sVMGF3SwfS4/eu4BHrMx9IfmS8Mh3inuPZEMhYZ0F7pggXen70wrkHypgVrERg7rfffra80013krUuXZef+zd5yAYUuFidKFE3Dc6joHnhXBe91H2pQX6++eYb+z8eoaDtiU6VWFZBx0QztWP3nDyH+zzpvAlhwYUN1DOWqIAsmDp1qj0XZEggE+SDtkxnhHxIPAGdTs4Vc7ONOLpz77rrLtuhoX7peFM31DMdyVx2eyqKJfrggw+a8847z7z66qvm4YcfTg6wv/vuu+bggw/O+oXghaaX3LFjR7utDZZOKvje6wIOqqwZzwN6qghQKiSfLwIvG1YFjRUFQZpUPsI96nR5uVGi9K7TuWlJm7IgfZ4LocoziWUWxMWLBcTBvXC9YgnRoN9++20bmIF1ilsu07hsVJAPsYJ5ocQSQtljLUjgCIqA71H6UKo7DjHOi0Bn6ANlJpZ8OnBjY00BHd1slGi6dlxoyLd4KLyKElc1dU+wWhR1S35xDdPxwuLFupWAJQKJJEirGMTRnbvlllva4EQ65wQyIr+RN0Ts5kJRlChK7q233ip3/p577snqPrvvvrsZNGiQFQa4XVCOBKPQm/SOcwjMD3KDl+h9esfGBNeiRAjIi8XfCAKEKkoDYS8BFG7vGjJZdJmQHrq40bi39GajRCxm8uJ1g8o5XKSuq1Bc2CKEs7W6UcCEnaNE6Zmzkw+uXDjggAOSaQisZoU1yLgqv0XZpgoQ4QXJdtwbZIxKQNC7wl4CooK6TSsiUjZsRcjhCkM+I+hRJAIR0XRWuY66wmUeVTvmHZN3R94f93miHrIQKxQF5vV60Q5IX8ZV3feYDh5KkDYaFFzk4g7GfYuCJk3+l3FlVaKFgbZGnU+aNMnstdde9oiKorhz6Z0RlSu8/vrr5qijjjJXXXVVVgEyhxxyiDn22GPt3nBEdzK4T09TfN5+yHQN90iFvPQg4fccMm2Al8EdFxF3otuzDxN8w8srY0h+rsNiNl6sVVe4uNZikLErhJArGF1BJhYg5Y6C4nDHx9woYJnqgltYxiUl0IV6kAClTFCWMvaLVebtnLh1KW58kCkYpQzKy3WzghtMRtmgVBlmAXZcykaBBmnH3vFP7zVRxjjQdqXdEBDkp6DF9SyKXfCWUxDcqWAyDEAHTPIXZd6U9CBLMN5yGV5LRVFqkTDjPn362OASoiiPP/54c/TRR5tXXnnFNvR777031H2xIph/xYsfFShZrDAavoRBS0Vg7aIYEKYICwS39DIB4R5W0KIIOCTk332hxQKirLwvu1gZKO9sLCWu9yp88oPgkI4HaWEBcqAw3UARmfuXDp6HDdj33HNPK0z4LGOXlB/jjtOmTSvzG8a5xcXvzhOlE4ZVxDPTdlDm4n2YMGFCYIFHGYqw85vnOn/+fFsH5M+dxygRqxUR2isBMm67IMqZwBbqkXFQdwtCOgoSxevOX8SS4jdAu6bs3Xm6rrUath3LvFHaFs9BPUrdkWYQTw7PhKvZvTftCKuTOt1tt93sObG6SdevM0A7cmHcWCL9/eaJ4hbHk+Kmy3MwVIHXhrmqpEW7oU3yPDKmTL7dYaePP/7YtkXplPI+SMRvkHLOlrhZosDiPhhqzzzzTNLjVbJKlJ4tDQxQnAjW559/3vqqUahhlSgKBOFx8sknR/asKAYEJq4eGrZYpwgjURoEM/G9BObI9JOwE/JJQ+abuoEXnEMpSw/a70WQz/lo4KRLvtw5suSVZwoS8MTv6DRRXig87ked4dZCGImACQJljZBhTIPxOdJH6ePKR2EERSwE6tIv0hZXLsKdZ5f88wJW5N2GZMzRBUHOkc1zux0R+X0+2jFl6i7KIJ6IoM9Kfr1th/qS+a3y/DJ1BAUahSveL13ywUGeeH6GJVDGMpecfCNP6Mi4QwfcR2YCgMQbQD6spzgq0QcffNB2pJAXeK28Y/JB5gZXGCXqWjBMTznssMPs34xN0tiCcvnll9s5phQIFsN1111nhfoJJ5wQ6fOKIk0FwoAXwjueFhaJXsxEvgMz6E17n8s7RpgNCDXGM7MB659xbz/ouNCDz4VM0eDe+YWlAMFQ7vSJTKBo/HZPyvY+YduxjK2HGfoA3v9Mc1ZRmsiKbMBady12LyjjTO5tOtLEbmRCZij4kW3nRfGHIcN8UBQlSkTmzTffbKOimHhMhC5gQUgEbBBwp6AwsUBQckydwS2Vy8RZRVGUyk4cLdHrrruu8ihR3LU9e/Y0Q4cOtX5qCTVnygvjZUF58cUX8/iUiqIolZM4KlGBLThxsQNu9VzHnIuiRImmdaNzhTvvvDPnKSGKoiiKkspzSeyNDL0RmY/hhkGW7UI/QoVa+DPbiFJFURQle+K4YtGZZ55px5exQpmqxsHfxOfwXUlZokSbsbAC8zmZPO+dGypz8RRFUZToiaM7d+TIkXb3HBbnEfj7gQcesIv0hKUoligrC919992mR48edrk1VhDq3r27jXJlZxZFURRFiRJmf/hFOmPUZbOMZYVQos8995zdhPs///mPnTOGn/rxxx83/fr1S7t1k6IoihINcXLlSszNhRdeaAOLBP6++OKLzX//+19TUu5c5v6xWhGwYo0s/s180WuvvbYYj6QoihIb4uLOreHZLYcFLJi3K0susmAGf59++umh55EWRYkSBcXCzCx7xRqWLPjMyjPsolCRV4JRFEVRSod7Q65+V+GVKGtUsrM8PQLM65NOOsk88cQTNsjo0ksvLcYjJTfLLST5WM4rCMVY+Pqss84yxcC76lKhYLP4YvDRRx+ZuPDJJ58UJV3ZxadQ5MPii4sl2qtXr8qpRG+77bbk3wQXYZGyxx577mW7NJeiKIqSHXFRon6wsQKHd5MK1i8IQ4XYi4fNtDkURVGU/BNHJTphwgRrmTI31PvseCHDegYLpkTfeOONwNey7ZCiKIqiRAXBQ2z1x9Aha7RHNXxXMCUaNPIplx6BoiiKkpk4WqI//PCDee2115JrtUdFweaJ4n8OcqgCVRRFyS9xXPavc+fOZvLkyZHft0KMiSqKoihKPmFBH8ZEp06dalq1alVunfaww4gFVaIffvihueCCC+yqRGxW68KCC6ymz96i++67byEfS1EUJVbE0Z07ZswYu4PLu+++G+kw4vqFnvjKfEGvAoUtt9zSnH322XZhekVRFCV/xNGde+E/axKw0E+Uw4gFVaL4ow8++OCU33ft2tWGISuKoihKlPz88892MR8ic6OkoO7cRYsWpd0vlJV0lixZYioSa9eutVu1ycRczH6ek71P3Q3EWYORa+nR0EPjOvK6+eabZ50mOw1wP+ntcS/S4n7sdOPC97/99luyV7jxxhuH3pOVlVhWr17t+129evXKhIRTHkuXLk324PAubLHFFqHS5dm5n9uzJZ9uXqUs3AnSPA/XhAlVJ5+sUuVH3bp17T3XrFlj69Td+YEVkHJd8Yky44VmCIN7U7dVq1a1Lzd/c473gPQlbeqUjYRJv9Ara5U6K1euTG63yPshnjDaO+UrbYq2xPe8s973LAhi0bhtlLbiyolU15FeIfdSjqM7t3v37nZFL5aaLVklus0229hB3VQhxlOmTDH169c3FQlRirwIIsRFyfEyItB4QRF4IAqPa7kmDNxflCeH3Itn2WyzzcoIUdKOujHzQntffD/BFFUkdZAX2qtk5Xc8A88aVrFIp8gPypa6oDy8q5vkAstbSnthrWjSWL58ue0MNW3a1KbLZxHqfF63bp3thPJ3Lts2xQ3pBPvBeal/cetJR5jhpWyRe8g7m+oat1Mm7TbK9hWEOCrRHXbYwfTt29d8+umndgMUb6floosuqvhK9NBDD7W7tODSxZJzQYBcd911dieXbPjpp5/MlVdeaQeLEUwo6IEDB5r27dtH8sz0Sl0BzXMi0FwBzjkRiORLrg/byHipOaQ3THpimZKmuwMBB88Q5dQg8oHVkwryy0FeETq5Ih0P8MuH+8KLhSoCS74Pq0TJayqBiXXId+SRTkMUcC9RoFj3WJbU78yZM+13WKeUK4qStMkrZfL9999b4cv3qkSDQbnhbXCVpEvNmjXLtJtffvkl+U6FgTYs7dhPcbsda+qV58pVVijZReeyaxibc3O4UA8loUSvueYaM3jwYNsjIEpXdhifPn26GTBggG30V199deD70Vvfa6+9zP7772+VaJ06dcx3331nt7+JCrE0RXG6bl1eGFFuwHcibPkOqzGTRecH1oeLpONCWjyTWCuiyKMAYc7gu7iYUCbSa6OOEORyPiolmk6QuOflWlf45SKAxMITocZLJnkNU3fZ4JcXhD4dGLeTyXPwGSWqrtxg0CZkoXjaqWy36EJZUt7i0pV3O6y7Pkg7dr8TRSvWcCHrNo6W6KxZs/Jy34IqUcZ8Ro8ebc4991xrVksl0HgOOuggq0izGfS9/fbb7W7lWJ5CkyZNMgpNDiGIhSEWoIDAZfzPGxbtuv5Qeoy7ics3LG7v1XUVSx7ysXWcWMDkTcqrdu3a9kWntw7pLNWo8XtZoxI4bl4RasuWLbMWSr7Gp6gvDsqUjgodQddqcV19AtfK+G2UHcTKDNY+7w2donSdIerd7aBK5zDf7dh1+3JQ73SGC6VI46hEXVzdkysFjc6Fbbfd1rzzzjs2KGXcuHF2zih/cy6TAvRbjxe37bHHHmuDQXbZZRfz2GOPpf1N//79rZtMDpRwUJcfCpGXjBcAoeZtRFgLXMOLC3yfajwmCKIsedGpbHEV89LzDLx0YQIgUoHlTCeGsuRAmQjkl4P8kMdibKcWJZQlngs6Bxxup0DcrfmA+uMdoD1RhpQnQwbiffC+1HgYZs+ebesb4U69KOlBIVFuMrySDtoyLnXqX4K6UgWcRYl4kFyvk67Wln+efvppOx6KrONg55ZnnnmmtJSoQI96t912Mx06dAjdu2YtRBZnYAu14cOHWwsXv/ZTTz2V8jdYwLh25Jg3b16gtCQSVF5KhBoC0FVi0uN1e75hAwbcIAdRoJKW3JP0eeFdVy7nwrp23XFYQAiJUHd77FjvCxcutJ0fgXPu5yjx6y3m2gtOl9d8B3nQEWNjeoYzWrRoYTtyUrauZ4EyxQXFd7wjXKfu3MyIMqIDSpvkcN8Z97M34l5+lw+F5tadtL2ohiUq+jzR/v37W3kvHUHWUp8xY0aZa5B3559/vu3UYIgcc8wxdqjFG5TXrVs32/HkPr179w48hn333XdbHUFszssvv2wP4nPOOeecnNYnKGlzghcBS/TWW2+1n7FEif595JFHUm7GKu60oPenghB60thddxuNyHX78eLx2X0Bw4yriQLl/qK4CyE8cT+747gyDuzNR5CxyyjxChpv9GOYsqHz4U5TSpXXfEAnBwtEXPMIChHqMv2CKTB0VADvANayEh3SKRRLUFyq+WzL3iliUv9+31c2d+7IkSOtgkSRUu5XXXWVXRdg2rRpyalxzOF8++23zSuvvGI9NcTNMC2FVYakzlCgBOQxLMhwyCmnnGJlruiAdDzwwAPW6OI37lJ/LVu2NNdff71NP3ZKlOkwO+20U5lz9OxZqT8KaCTi2kOZeRueuFNlGoJ3TmnYuV+iQN3PAj1mb0eA9MT6zGWeKHklGEOmjLhjsTR0maso8L3M681lnqhf5KQ7XUCeRwK7vNeGET7klU6DzDOVjg9/y9xeysJVrsAYplwTZg4wMK7MfagrmboEuM+5J88mCpTnwyJ1x+6xSAs5p7DUoHPkdeMy1i1DILRV3inqXzxM7hQqN8o2G7zjqyDRviILJJJePFlu28p3562YSnTYsGFlPg8aNMhakiyuwzKveAXZouz55583BxxwgL2GWBfkOUN+e+yxh3nvvfes0v3ggw9sx7Jt27bmpptusrMzUILegEwvKF2WlvXCOb4rOXduFBCZ63UJfPvtt3bMKQpkwQT3JZNzuCXEJYP1Ju5WrhPFiksi196ld3mqfLp8eF6eW4KZJCoUK6hYQtud2uItS1fBZgsKX146hJpY/CgyGe/1mxTvdy5baC/S8eI+MqVF5kh7A1BkSpEcpRzQUVGQxUukAyVzwakbrKCorUKpM5m+5npTRF6Uqqt+5T+dPDncwM1USLS0xF2gTPEEHHjggclrmjdvbho1amTXvAX+ZzzTDT4lIJU0v/7664xpMv0RF66Xl156yQ4JhqWkLVHMb3oRmPLHHXec+fzzz83//vc/e0SBROFmQsYsMwUxBCVbCyfocwZJN5u0EQZRLI7hXZ0o01zSKAiSVwk+ixqs+XTRzdQlLiYlOtwgOUCBRl23QS3YsJZuRbVEG3qCM5nvj2WYCjqGl1xyiTWC2E0F8LzQkfC+FyhM8crwv3f2hnyWa9Jxww03mB49ephRo0bZtAFX8YgRI3yVayyUKP71IUOG2GChG2+80Ub3ssh9z549i/1oiqIoFZYolei8efPKbCqSKeaEsVFiV1g5qJAQqMSMEIKIhg4das/hLsb4Ip4mlkoUWOEo21WOFEVRlGioVq2a785cfhAs9NZbb1lrkAh1gWAhhjeIF3CtUYLu+E6uQeG5SPSuXJOJdu3amWeffdZESUmPiSqKoijhKNT0FuB6FCieQ/aV9q4JgHLDxY5rVSDehSktHTt2tJ/5/6uvvjKLFy9OXvP+++9bBe4NMC0kJW+JKoqiKBU7Ovf888+3kbevv/66DcqUMUzGpSWY64wzzjCXXXaZHb9GMbL/J4qTyFxgSgzK8uSTTzZ33HGHvQdLyXLvdC7kIDs9ubMRskWVqKIoipJXHn74Yft/p06dypxnGsupp55q/2asEoXH2CURvkTePvTQQ8lrCcbCFcyCCShXAvBYD4B4mHRg/aaCiN/7778/p2h7VaKKoigxo9CWaCLAtcxuYP10jkzLxmbDkUceWe4cruI+ffqYN9980waiZlLE6dAxUUVRlJhR6GX/Kgrz5883Z511lp1vivt20qRJdpnYXNYWUCWqKIqiVGpWrFhhVzZiwQUWZiCACStU5qnmgrpzFUVRYkactkK744477LaZTIN54YUXfN27uaBK9B8YyC70slvFWkovyu3TgpJpXcvKVsbsMBSXui3Ezjd+RLkRfTawmHohYbOEzp07R3rPOCnRPn362AhgrFBct6l2+Ro8eHCo+6sSVRRFUSotp5xySl4NJFWiiqIoMSNOluigQYPyen9VooqiKDEjTko036gSVRRFiRmqRKNDp7goiqIoSkjUElUURYkZaolGhypRRVGUmKFKNDrUnasoiqIoIVFLVFEUJWaoJRodqkTT8Mcff9iVWGQ1FibssiLMhhtumFwZhkWM//rrrzIrtrC3Xa4rx7CCkqQvabMVEPfmf79rgL36wk4sXrlypVm1apXvd1tvvXVyzz2uYxd68i3lUaVKFbsqSLbw/G75SRmz0pCUIfnke3lppSy4JtdJ1KRNftauXZvMD/etUaOGzRfp8j2r4/A96bIFU9hyLmabymaVIZ6r0Ct4RQVlTBmKonDbi7cM+Z66lbbFylphVrliNZxatWrZv3/++Wczc+ZMm1aDBg3s3pjcl2egLS9btswuhC7l37RpU1OnTp2U92YT66hRJRodJe/Obdy4cfKFdw82as0VEWRyTxoN53gRpAG510QJ9yUNXkQO/kYwrFmzJpm2COSo0xZF4h5AukuXLk0qFM5zDoWKYOB5soU8+ZUxSs0tY3kuuYbfUQ+5wH0XL15sl1Xjb1Fk0kGQ/K5evTr5vSjd5cuXl1ybigvezqe0F7dNCdR1rsqgdu3aSQXqQnupX7++7VxKm+LvbbbZxmy//fbJ63gu2ph7SJvnd0rFpuQt0fHjxycbHEydOtV06dLFHHvssTnfm54jh/Re5UUAXlK+kzVhOR9GiaSCl80Vorxo8sJL2ptvvrm9BgEc5cvGvn5YYl7Io+Sf3jXWGGmjaABBlW0vHkHjLWPZYV4Ul19ZuNZcWFCGkgaWgFj4IlSlzAEhyXMg4H755RfbmcH6znZN4GK2qYq45m4+oD5dy542Kpap1DdwjoMyd2VINuAhoCOPB4d647NAenPmzLEdNekUtWjRwr431atXT6aLVcoh8A61bdvW/r1o0SKTD9QSjY6SV6JeN8htt91mmjVrZvbbb7+c7+1VCH4vW76sBe6LAHUVp5wX4ZAvwYeliZIQi1TcUXyWMhAXpyg8FDrKN9cydvMkZcv/lIO4dOXlzSX/3IM8AnlasmRJUsAi5MgPSlTSl7yhSFGiwPfZKtFitql0uB2SUnblgrdOKGNpp25+Ua60Ia4Pu5g9blza0vfff28VpAvv78KFC5OfuQ6vB+0rnRJjtxGei3ahSrTiU3m6n//06p999llz+umnpxQCvDgoAPcIgriEvIosn0jP2VWgYn3mExmjE0GDgqFsSZdOiygChIS4nKMYn0xXxtKRcBWo2+vPFvde4krlnuQJ1zQKVp5F3MjytxDWeilmmwpCqW64HKSMUaick6GAXNoQ4514I2bPnh1oaIF3qmbNmslxUz9PCm2gbt269m/p2CkVm5K3RF2GDh1qrYRTTz015TX9+/c3N9xwQ1b3lXE/afT0XAvRU5cgB9LH6kEY0GMmsCUf6WNlIRREmJMmLzvQg+ZZGAtE0fAMWKgSKMGmtwioMMFFfmWMcHPzyGcR7hJoxPW5CEFXuG211Vb2b3r+lDNu23zWcbHalItr6XuVpwTklDLSVlBE4k0QDw/lLt6VMMMCtH+C7RjKkHckHbTTHXfc0aaJ63fWrFm+16FAaY88+4IFC0y+UEs0OipG1zcinnjiCXPIIYfYxp2Kvn37WoEvx7x589Le0xXavID5iJJMh1gooix4jnyNk3mjFxE68lmCX6THjUXMd+54ZdhAHz9h51fGbiSrPFPYcVH3/mJFc4iV7Y6dSZARuOnJGGq2FLtNpXLblrrSdKFsJeLa26akDunE0Dl0XbmcC+LalXaPZdm+fXt7iBuZeAI+S/ugY9qyZUv7Gzqh06dPT9luceUCijmfQUWiRHM9lEpkiTKA/8EHH2TcWBWBFdR6EWtHXH2FshZIV4J0JD13TCdfjZceMi+6KA8J4AFxgwkocspDXLoQpmzEZSxl7LVAxZ3lBv1EsQG0KC+ZJuRGPANlwPcIWSkLysYVsGHGgIvVpvzw1ltlEYqiQKWMxQLNB36dH/ccSpYYDc4xPoqcSgXBayKb8mmFKtFSaZTowIEDrSukW7dukd3TDX93x1HcCECZ5+gKIOlByjVhQAhwyPQW9/5iLTFu500bNyQgOLKNlEVhMEaMwpI5ocDf4uaVKQO4zd1QfAjjynWndviVsVueknZUATC4oxl3Ip8SACL5IfgDgYaSI30sA55HyoS8ZhtUVOw25cV9Di+lbJV6p7JIgJjbOXI70ihd6RwFnSeKG1ei0gUiarmvzBPlPgQeUZakIRapgEtXgtuA6TCAh8w9nw/UnRsdlUKJ0kBRor169YpMwGRqMK4ASvddGHjpZE6iG1TEORnHyUfaKA6EiQhxmW7BeREsBBZhscpUFLGmuCaMUvE+u/vcMuXD25GIarEFBB75kcUjxDpFuYqQZQ4g30ugkUwt4ppSalN+eDsl7vlSVqIuXq9FIQW/W460YZSoizscQHtinLVQVqgq0eioFEoUN+7cuXNtVG6UBHHX5ao4/JAo3EzISxcV3C/TPVFcEmEYBUGs17Bjj9ko0lQg/JjTx1HKbcqPyqQsXYK8O946juJdmjRpUpnPdMzGjRsX6Ld01IJeGxWqBKOhUijRrl27aoNQFEVRCk6lUKKKoihKcNSdGx2qRBVFUWKGKtHoqFTzRBVFURSlkKglqiiKEjPUEo0OVaKKoigxQ5VodKg7V1EURVFCopaooihKzFBLNDpUif5DFFt5Zcuhhx5qikGQXSeiZvjw4aYYNG/evGibxReDvfbaqyjpht2PMxeKtb7s7rvvXtD0gm7XmA2qRKND3bmKoiiKEhK1RBVFUWKGWqLRoUpUURQlZqgSjQ515yqKoihKSNQSVRRFiRlqiUaHKlFFUZSYoUo0OlSJKoqixAxVotGhY6KKoiiKEhK1RBVFUWKGWqLRoUpUURQlZqgSjQ5VohlYt26d+eOPP8zff/9tP7M04AYbbGA22WQT+7/fNVC1atVAywguXrzYfPPNN2bZsmX2PtC+fXuz3XbbJa9ZtWqVmTp1qr2WazbccEOz5ZZbmh133NE0aNAged3ChQvtvX755Rf7PBtvvLGpXbu2adWqlalevXqZdJcvX25mzZpllxTjWlkir2HDhuWe8c8//zRjx45NLu3mvY5lBGfPnm1Wr15t78USiqTXtGlTWw5eUpVLqpdSro/6paW+eHaemzySDmVbrVo1U6NGDfuZsqSMKHdJv3HjxrZss4U622yzzezflOWKFSvs37SjKlWq2Huuv/765q+//rLf//rrr8nf8lyUJf9zDc/CM3NdkCX3qJd58+al/J76qlmzZvIz9/7xxx+TbbpevXpm8803zyq/tFvK1g/u57YD0lm6dKnNO1AHW2yxRVbpKUoxKOkxUV64a6+91jRp0sQKp2bNmpmbbropUmFLGtwPweUKrzVr1iTTEQUaZu1dlBnKL5VQJo2PP/7YzJkzxwpyBDHnlixZYj799FP7e0DQjxo1yixatMh+z3W///67FYQfffRRGQUv16O4UXiZmD59ekpBjaD/8ssv7b1IA2VAeaDwJ0yYkLIupCfsHn6kK1Pvd3zOpg54RspPFD/1S5khzFGekj/KXTpMYaF9igL1PjPKi+/4m7ZFWihMtwPCOZ5R2h/QZqjnTTfdNGP63JuOn3u4dY9yFkiDsvG2mbBQrqTlHl5oj6JAlfzj9/6FOZQSt0Rvv/128/DDD5unnnrKtGzZ0nzxxRfmtNNOs4LloosuiiQNEW7C2rVrraClASFkEG700LkGYct32YBVg/Lnvm+99Va5712LpHXr1qZFixZWUaIYAWWO1SRKDPbbbz9Tq1Yt89VXX5mvv/7aPhOC11XU9evXt1Ys36GMU4GCZ6HvrbbayqbrJ/zkZdp1111t2X///ffmhx9+sMqJdIMoaj8oU+4d1HJNd60f0jGg/igLyo9ndxUV+aaOyadf/oMgSpGy5m9XIaMA5TN1SLq0OcqR56LueS7aForNvWedOnXs35Qv7ScdKMltttmmzDk6C9QRSo7Oj4CFzP2wBF1rOCwobK8nxEWsacoiUz6UaFB3bnSUtBIdPXq0OfLII023bt2SCumFF14wn3/+eWRpIJQRNK7ilPMIH5D/wwqYdCBYEHC4xVCKc+fOtX+TPvlFGQJKk+fg+UaOHGkFIMIQAbvzzjuXs3SDuCMRaLiHUQAoej8lgttNlB0WKc/L8yG0cUmHVaCFeElRVtQtHRHc0ZQdaXKejonXQgsLChGoD9dlCq7S9+aX76gnV7HwexlSEMQdnw1YfbhbpQ6lDaOsUeYocM5HoUR5fjpiYpHSnqRd8BzSTjmvSlQpNUranbvnnnuaESNGmG+//dZ+njx5srWqDjnkkJS/QUhgVbhHJhBuvOyuAhXrM98geA444AAr1Elf3I8IVwSqCD8EUKdOnaxSRuFzHdeLMMwW8sw4LP9jAafqKKCs27VrZ4Ugz4Vg5jc8R6oxLVdZZOuCjZK6desmy0asdVFcuXSMXGSsM5W7kvYo7YqOEIdbX143MvcS1zPlyH3DKB7xIJBfSY/nwNp1rdwo4Fm5p1jUYgGTvrjN01mqSvSoOzc6StoS7dOnjxUGBLrwkiKkbrnlFtOzZ8+Uv+nfv7+54YYbskpHBBeNBoElAR0oiXwrANLETY1S3GGHHaxVSa/+s88+s2OOWE24IrGmsMARUnQutt56azNlyhTbwWCs9LDDDvMdk0sFFi9p7rTTTjafqcZEKQ9cxghFlC3Cd+bMmfb3WKZ77713WmtbBLlYs+D+nQ3Z/k6ChrCeKS/aD8E3WEbcCyWbC1ixUnapFB1pUs4oW1GOXC/DCN784F6X8U0sXDpPtMdshhFE+QLpirWNBUo9EvST6xgwkAfy77VyAQuXdHlu8sHf4kJX8o+6c6OjpC3Rl19+2Tz33HPm+eefNxMnTrRjo//973/t/6no27evFZJypItYdBH3rSgEetVh3GjZggt1/vz59m8CqBA2RMaKO0xcrCgu3Kicb9Sokb2O60VQ0/vPBnH1zZgxw3z44YfWdS6gmMVlTuASQp/0RPiKi5kyEksj3y9jtgpUokFBol6pW+lo0CmJaqN3lDQKmUMUipyT4QIUKVYgAWOUp3TO/KxXtzPHde54ZtC6lfuKqxlEEdOmiNxGYQucy3ZMWCKJBcrXzZcoTRQ6abltlHPZtllFKQYlbYn27t3bWqPHH3+8/YwlRBQr1mavXr18fyORiUEFrQTGyMvv9pYL0RNzFTW9eNy67rQUsRjkM//zPS466fW712WLnxCnXOS8lAf/Y11gebgu8iDp+lnzqc75jRuG6VW714uVSL5EkUTlzpVnTBdJLK5wOS9RueL+FKXLs4nr1w1QysYbQr5lag2ufr+xcb+yDFPGdOrolLhTweQebrtIF8Gt5A8t32goaSWKteAVdjL2EhUIWA4Zg3IbnliDPIdMhRFkfhzCL11wDZYwY7nuMxNAxLQSxseIeEXQIUBx62IFSrAHz7Ttttvav4m8/O677+zf7733XhllhrD0uiaxKrjefWYiU+mEYJ3QIWF+qYB1JFG87jxR3LdizY8bN84KTTfvEqCTCfc5/BRluvNhoJ3wrOQLy4w6djsHMk6IZUh+3PrB+iZtxvHS5c9vDiflRdruPFHSkuEI/pe2Jt9LHVIvPAcHVp7kP8g8UYG2KgrbtUIBl7b3+Rk6CDtPlLQoW/Ik03eAv2mfMp9Y4HvKW8pE54nmD3XnRkdJK9HDDz/cjoHivmSKC2Nwd999tzn99NMjub9MvvcGFXHODT7xa5DyOVNDQ6B5J6TTY+dAyGM1d+7c2UybNs0KGIQSaSOMybMIcYQcU1tQvghfrkPoMUWD67xRpjKh3/ssHEHmHQoo+l122cUqX/KB4OT3nGexhSCWaK4vo58yDXJPlAbWurvYAs+OYBclSjl53faiDKKa10gHiXSpI56bupeFKwSUvETlynUSWZxNYJEoZtd1nS9wM9PGKC/pIEgUbhRRz0p4VIlGx3qJEi4JFAWLLQwZMsSOJyEUTzjhBNOvX7/AK8pgrUmARqGjRA899FBTDFilp9AMHz7cFAOs5mLANKNisNdeexUl3Wys4agQK7nQFFpkioyiAxQm0t7vXnR8cw0eo2P05ZdfRvJcpUxJdwdRfPfee689FEVRlGCoJRodJa1EFUVRlOxRJRodJT3FRVEURVGKiVqiiqIoMUMt0ehQS1RRFCVmFGPZv1GjRtkZFQSAEsQ5dOjQcs9EUCiLtRA5fuCBByan7QlE07MiHYFMRNGfccYZKbfbKxSqRBVFUZS88+uvv5o2bdqYAQMG+H5/xx13mPvvv9888sgjds4584QPOuigMlO4UKAsM/r+++/bXa9QzP/+979NMVF3rqIoSsyI0p270rOJR6pV4dgYJNXmINyLWRbXXHON3ZkLnn76aTvPHYuVVenYUWrYsGFm/Pjxpn379vaaBx54wE4VZLlX72IhhUItUUVRlJgRpTu3YcOGdu6pHCy7mi2yVjMuXIF77b777mbMmDH2M//jwhUFClzPojdYrsVCLdF/YEWiQi+24LcJdyHIduPwUl70oEOHDkVJl8nsxSDsxuG5wsphhQb3XzEo1tZ9FZV58+aVWWwh6NrkLrLZAZanC5/lO/73Ll/KyldsCelullBoVIkqiqLEjCjdudWqVYv1ikXqzlUURYkZFW1T7nr16vl6Uvgs3/E/y7u6sC4zEbtyTTFQJaooiqIUlSZNmlhFOGLEiOQ5ApYY6+zYsaP9zP/sTzxhwoTkNex1zFAcY6fFQt25iqIoMaMYiy2sXr3azJw5s0ww0aRJk+yYJjtxXXLJJebmm28222+/vVWqbC5CxO1RRx1lr2/RooU5+OCDzVlnnWWnwbCL0QUXXGAjd4sVmQuqRBVFUWJGMZToF198Yfbff//k58suu8z+36tXLzNo0CBzxRVX2LmkzPvE4tx7773tlBZ3a8bnnnvOKk62hyQq95hjjrFzS4uJKlFFUZSYUQwl2qlTp7S/Ier5xhtvtEcqsFqff/55U5HQMVFFURRFCYlaooqiKDFDF6CPDlWiiqIoMUOVaHSoEs2wsg/zkKTB4bPfYIMNzMYbb2wHtYkOW7duXcrfMyDOihqlki6h4n/99VeZl4N0OQSei+tceCauCbuSC/f7+eefbfQe9+c+PD8TuGvUqGE/syLJb7/9Zr8HvmeB6lq1apV5vlQw34yFq0lHyo7VjHbYYYfkNaQ/ZcoUey1pcf/tttvO7LTTTuXyxu4SHCtWrLDfcS3XNWvWLO1zrFq1ygZP+MHqLNyLe9IGpJwpX1aBqVKliv07KjKNT2UL5Ul5USe0QfI5d+5c8+mnn9pAEWDZtn322cdGY26++eY2n0uXLjWff/55cseObt26mZ133jllOkGWlWPRcrcMpU3xXN72zLXS7rluo402ss+mKEFQJZoGlJW8WBz8zUvHC8dLxjmvUHN7eGEFXrHSDdI7dYWSXMs5/kb4hIEJ1LKINR0F7ifClXRQpCg46UhQDpQRgpnrGjRokDENJmQvWLDAKiK/DgiC9N1337XfIWxZt5P7f/nll1ahuut1sgD2jBkz7N8oT56Ja8hHJiUqiFD3g2eRjol0bNasWWPbAIEVhUDaXza0a9fOlhsdFeqHemvdurWdrvDoo4/aumI6AufJC/XL9ShU1l998sknbRlS7j/99FOZe9epU8eWc9Btr0QpUob8TznyTKRL54y88TyUK0hHVd61yo5aotFR8kqUnj3ziYYMGWJfQNYsve+++8xuu+2W871RCgg6UUoIWFFwvKR85xWEvJTy8oZVZsVKl9/Jb0kv1bO5wlWeK5cXCgUEdBBQiAi877//voxAa9q0aZl8sV4nv5PfZoLfM/8MBeXdxxDmzJmTVK7MRcNiwjJisjcKkzlqKMwlS5bYz5SBWFRuWQQFy5I0/GB9ULeMRSllc/+guOnkKhQnT55spk6dmuwQMQ0B65SOS+PGjW0nBgUKn3zyiRk7dqwtP7a34jlQbrzDn332mT0Efn/uuecmp0kEQTqbAu2E+pV3iPdE2g51gYUq18dBOagSjY6SV6JnnnmmfXGfeeYZO+H22WeftSv7T5s2zWyzzTY53ZuerwsvXjpB5ro6w1plxUw3iBDhGtfV7P1tGNiAl/zREZg9e3bSsuW8CF0UKJaLWGSiXLkmCNksii15ccsDFy+KGGUr6aLo2VmCMseSYq/EoCDQcVGTL36PopC6I106h1hKCPwo6jZVPqMUhKNHjy7zmY6ObABAPrAi8QhgTdMBoWOCJcp3vMOUpx94Aeg0Uh54BoIglqYoTteDwvskbRj4ThQ/31G3QYYIFKXkp7jQk3zttdfsbg777ruvHY+5/vrr7f8PP/yw7294qXhh3CMIvHCiyORF9CLfIxjDjElWpHSDPFcQ12QQsLxkAWsZD+aeMgbs5hNLUhQo1kZUK5VwH8kDE7zffvvtMtsridtP2gufUYI8A39jnbrWUyZcdy1tUqxNQVzWIvwpi1SWa0XcqYTfy042y5cvt50j2gxz/LBIKWuWeUNhUaeUpZ9Cp+PQtm3bpKXrbtCcCbE63bFlOis8G+cFytkdNkHZx8HKqijr5pY6Ja1EZZzQXdECeDEJZkgVlODufYcFkQkajAQf8LJxf6+Q4Tt5MaOyGIqVbiYQgOJyFuHjCqVsYQwM5UQ9Yu1tu+22yQAbrE+hfv361iWLCxClgvKKauuvqlWrWvejbMXEvd3xTVHmrvDg+sMPPzwZBMM4XqYxO+qQTgNjfBxiaUuaAgqTZyFIR6ywoB2+KJRlLkKStsFKMtQl5fHKK68k2weucuqRcWU2Uh48eLB1kx900EG2br2gQCkzFCG/yQa8D7zjdNB4Ju5BsJM3b7Q7rkHBSt6LsV1gnBegL2VKWoki+FiU+KabbjLz58+3LyruXFxs9Hb96Nu3rxXOcuBySgcvHhavq8j8xhxdazEKa7BY6QZFgpvkmcQFmy38ThQl9UkeEH7ipnUVi6SL0EM4grg9owCl1qVLF9OjRw9z7LHHllGiYim77mMUHNSuXTt5LpMSdce6gbyKIvN2RCRSVNJ0rfAokQC2KEAhMsaJQsS6ZpiF/4FxUbxE8NVXX9m2iwUv1iUBSN7nktiG6dOn2/c1bDuVjrYErbl1IN4d18vjjUBXlEqpRIGXFOHN+CcCiXUUTzjhhJTBNVwj+99l2gcPoYYi44Xifrju/O7L9yLcvIE3YShWupkgPVe4uGNN8jlb3N+IMBVBB+Sb864y5Tfu56gEHkEtci9crBMnTky2GdlqCStKEOUg/0tHIB0oWVdZypidO/btRg97raKoev/5sCboTJxyyim2jJja8vTTTyentnjHpaU8GR+V897OkIyZgutaz4S0Hzdvrquc826HU+rDrZfKPiaqlmh0lHxgEdbCyJEjrZsGdxcvMJYErqRcQXi7DcWNBEVpifvUfUGjcKkWK11XKXvdxSgzdyzJG5QS1pqRQA7yiFVJ3mVaB9DJQangtpUgHHesEAEcJGgIoY5SdJ+ZMTYC0BD+LHaNoOY56LTwLOIFYJslEbq4mr/55hsbIMO2TShNURS0RSyxdKD8UaR+5clvxW0r49/uvF2/qOwoiEoYdu/ePTlui7v9uOOOK1PW3377rS1f6hu3LgFDXC/lQF24SFAS46mMmWaTH+lkUc5egS9j7fxPeXvnlEo7q8xodG50lLwSFRBAHAQxDB8+3AYbRYnX2pHPbuBPPqzBQqab7qXgOxH83hcw18UWCOpBKbmLLeB+Q8CiRBFyKDaUKYcEHVHfWDJB0qWsvK5WuZ9MrKcDhnsfJUZ+GLts1apVcpxU8spYKFs4cS3KlufETeku3JAKxt3ELStTLSQ6FwVJuZI3NwKZa2SxhXx7G4SwHSLBu0nyDz/8YBUonqM999zTxiJQd5QFHRwie90Nl+msiNXPQgzZPjtl6gYVuYstiFdHhkhEgUpgnjvdRVEysV6ixLsTKEyysOOOO9q96nr37m1fAuahBelNIjBxGSGQ4/LiFCNogvGwYiDWTKH54IMPipJuVIFW2XLLLbcUPM2oO8pBcV3UhYQx4XTDT0EQeUdwXq6rX9HxmDt3biTPVcqUvCVKBRIs9OOPP9qeLVGBvNCV3R2jKIoSFnXnRkfJK1HGXdyxF0VRFCU9qkSjo+SjcxVFURSlWJS8JaooiqJkh1qi0aFKVFEUJWaoEo0OdecqiqIoSkjUElUURYkZaolGhypRRVGUmKFKNDpir0SlIcSpQRQjr7ns8pILxdqNo1gLmBerHWezRVlUxOmdjWN+S4WSX7EoV1ikIch2aIqiKMWEZSYbNGgQyYpFLGUZxYpFixYt0hWLTMxh3VYaJwuJZ7vsHw0SBczvC9mIipFunPIat3TjlNdSTBc7hzWao9qAXu6p7txoiL0SpTeWa+8u05Zq+aIY6cYpr3FLN055LbV0ZUs4peIReyWqKIoSN9QSjQ5VooqiKDFDlWh06GILOcAej9ddd12gTaFLPd045TVu6cYpr3FMV8kvsY/OVRRFiQsSnVurVq1IonN//vlnjc4t9gMoiqIohUXdudGhSlRRFCVmqBKNDh0TVRRFUZSQqCWqKIoSQ9SSjAa1RBWlArN8+XJzww03mAULFhT7UZRK6M7N9VBUiSolDMs0Dh061FRWEFK9evUyv/32m6lfv37aa6+//nrTtm3b5OdTTz3VHHXUUQV4SkWJN6pElQrJwoULzYUXXmiaNm1q59Wx5ujhhx9uRowYYeLCnXfeaacO9O/fP+vf3nfffWbQoEHJz506dTKXXHJJxE+olCpqiUaHjokqFY7Zs2ebvfbay1SvXt0qktatW5s//vjDDB8+3Jx//vlm+vTppjLCtm0bb7xx8vMVV1wR+l661qqSjigUoCrR/49aokqF47zzzrOu2s8//9wcc8wxZocddjAtW7Y0l112mRk7dmzK31155ZX22s0339xasNdee61VvsLkyZPN/vvvb3fswcJr166d+eKLL+x3c+bMsZZujRo1zBZbbGHTe+edd5K/nTp1qjnkkENMlSpV7DZSJ598slm6dGnKZ8EKpBOAu3n77bc3m266qTnooIPsDh5eF+zjjz9umjRpYq+BX375xZx55pmmTp069jkPOOAA++wut912m30O8nLGGWeU28/Tdefy98iRI611Srly0FEJky9FUcqiSlSpUCxbtswMGzbMWpwoMy8oplSgUFBe06ZNswrjscceM/fcc0/y+549e9ode8aPH28mTJhg+vTpYzbaaCP7HemtW7fOjBo1ynz11Vfm9ttvt4pFlBqKbJdddrFKl+djH8XjjjsubV7WrFljbrnlFvP000+bzz77zN7n+OOPL3PNzJkzzWuvvWYGDx5sJk2aZM8de+yxZvHixebdd9+1z7nrrruazp0727KBl19+2SrgW2+91T4P46UPPfRQyuegLDp27GjOOussG6DEgXs8bL6U0kfduRHCsn+KUlEYN24cb2Zi8ODBGa/luiFDhqT8/s4770y0a9cu+blq1aqJQYMG+V7bunXrxPXXX+/73U033ZTo2rVrmXPz5s2z6c+YMcP3NwMHDrTfjx07Nnnum2++sefII1x33XWJjTbaKLF48eLkNZ988kmiWrVqibVr15a5X7NmzRKPPvqo/btjx46J8847r8z3u+++e6JNmzbJz7169UoceeSRyc/77bdf4uKLL845X0pps2LFClu/VapUse9DLgf3MMbYe8YZtUSVCkUuvduXXnrJjqXWq1fPWpHXXHONmTt3bvJ73MG4SQ888EDrDv3++++T31100UXm5ptvtr9nkfApU6Ykv8OV+tFHH9l7ytG8eXP7nXsPLxtuuKHZbbfdkp/5DZb0N998kzy37bbbWretm9bq1avt2qZuerNmzUqmxe933333MmlhaWZL2HwpivJ/aGCRUqFg/JAxu2yDh8aMGWPdtcypZOyRwJoXX3zR3HXXXclrcIGeeOKJ5u2337auUpQl1xx99NFWufI7vnvvvfdsRCy/JUIYpcZ4KS5eL5mmnmTC67ImLe758ccfZ+XKDkM+86VUbDSwKDrUElUqFDVr1rTKbMCAAebXX38t9z3jeH6MHj3aWnVXX321ad++vVXGBAt5IfDo0ksvtYqye/fuZuDAgcnvGCc855xz7Pjkf/7zHzumCoxJfv3116Zx48Zmu+22K3P4jdsKf/75ZzJwCWbMmGGfv0WLFil/Q1pM78GK9aZVu3Ztew2/HzduXJnfpQu4AqJ+//rrr3JphcmXUvromGh0qBJVKhwoUAR+hw4dbNDNd999Z12Y999/f0q3JUoT1y2WJa5Irh0yZEjyexYsuOCCC6yFh3Il0IcAI1FozKFkCg1u04kTJ1o3p3xH0BFBPSeccIL9Dffn2tNOO62cYnIhaAlLFoVHgBBRsnvssYfNVypwNZNHImtR9ETR0kGgcyAK+eKLLzZPPvmk7QB8++231qJGGaYDRclzcD+ib9nGKmy+FEX5P1SJKhUOpqegyJiOgkXYqlUr06VLF7vQwsMPP+z7myOOOMJamChKpo2geJjiImywwQZ278NTTjnFWqNEoDK1A/cvoDRQKijOgw8+2F4jEa9bb721Vbpc07VrVztvFaWLezXdnoxMtWHaDS5kxloZc2TcNh24splas++++1plxnMQ0YviZwoK9OjRw+aNeaRM0+G7c889N+19L7/8clsGO+20kx2DpcMRNl9K6aOWaHToptyKkgeYaoNCSuV+VpRibsrNnGQ6bLmA6li7dq1uyl3sB1AURVEKiwYWRYf6bBRFURQlJOrOVRRFiZk7l2jtKNy5v//+u7pzi/0AiqIoSmFRd250qDtXURRFUUKilqiiKErMUEs0OlSJKoqixAxVotGh7lxFURRFCYlaooqiKDFDLdHoUCWqKIoSM1SJRoe6cxVFUZSCbS7RuHFju+wge+J+/vnnptRRJaooihIzirEAPZsvXHbZZXbXITaYaNOmjd32cPHixaaU0RWLFEVRYrZiUZSsCLhiEZbnbrvtZh588EH7me342MOX7QL79OljShW1RBVFUZScFPNK51i3bl25a1gekD112S9XYLs9Po8ZM8aUMqpEFUVRYgJr5tarVy+y+1WpUsVak1i3cvTv37/cdWwEz761sieuwOeFCxeaUkajcxVFUWICAT2zZs2ylmEUJBKJcgvZb7LJJiZOqBJVFEWJmSLlKCS1a9c2G2ywgVm0aFGZ83yO0jIuBurOVRRFUfLuRm7Xrp0ZMWJE8hyBRXzu2LGjKWXUElUURVHyzmWXXWZ69epl2rdvbzp06GDuvfde8+uvv5rTTjvNlDKqRBVFUZS806NHD7NkyRLTr18/G0zUtm1bM2zYsHLBRqWGzhNVFEVRlJDomKiiKIqihESVqKIoiqKERJWooiiKooRElaiiKIqihESVqKIoiqKERJWooiiKooRElaiiKIqihESVqKIoiqKERJWooiiKooRElaiiKIqihESVqKIoiqKYcPw/a5OvyZY7zqYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Erreurs de la classe 8 (vrais 8 mal classés):\n", + " 8 → 0: 31 erreurs\n", + " 8 → 1: 188 erreurs\n", + " 8 → 2: 148 erreurs\n", + " 8 → 3: 542 erreurs\n", + " 8 → 4: 81 erreurs\n", + " 8 → 5: 493 erreurs\n", + " 8 → 6: 74 erreurs\n", + " 8 → 7: 54 erreurs\n", + " 8 → 9: 545 erreurs\n", + "\n", + "Erreurs vers la classe 8 (mal classés comme 8):\n", + " 0 → 8: 35 erreurs\n", + " 1 → 8: 62 erreurs\n", + " 2 → 8: 167 erreurs\n", + " 3 → 8: 151 erreurs\n", + " 4 → 8: 66 erreurs\n", + " 5 → 8: 174 erreurs\n", + " 6 → 8: 43 erreurs\n", + " 7 → 8: 24 erreurs\n", + " 9 → 8: 36 erreurs\n", + "\n", + "Statistiques classe 8:\n", + "• Total erreurs sortantes (8 mal classé): 2156\n", + "• Total erreurs entrantes (mal classé comme 8): 758\n", + "• Classe 8 semble problématique\n", + "\n", + "OBSERVATION sur la classe 8:\n", + "La classe 8 génère plus d'erreurs qu'elle n'en attire\n", + "Confusion mutuelle entre 3 et 5:\n", + "• 3 -> 5: 163 erreurs\n", + "• 5 -> 3: 340 erreurs\n", + "• Total confusion 3 et 5: 503 erreurs\n", + "\n", + "Pourcentages d'erreur:\n", + "• 2.7% des vrais 3 sont confondus avec 5\n", + "• 6.3% des vrais 5 sont confondus avec 3\n", + "\n", + "Pourquoi une forte confusion entre 3 et 5 ?\n", + "\n", + "RAISONS PROBABLES DE LA CONFUSION 3 ↔ 5:\n", + "\n", + "1. SIMILARITÉ STRUCTURELLE:\n", + " • Même orientation générale (ouverture vers la droite)\n", + " • Présence de courbes dans la partie supérieure\n", + " • Segments horizontaux similaires\n", + "\n", + "2. VARIATIONS D'ÉCRITURE MANUSCRITE:\n", + " • Un '3' avec boucles fermées peut ressembler à un '5'\n", + " • Un '5' avec courbe arrondie peut ressembler à un '3'\n", + " • Épaisseur variable des traits\n", + "\n", + "3. RÉSOLUTION LIMITÉE (28x28 pixels):\n", + " • Perte de détails fins qui distinguent 3 et 5\n", + " • Lissage qui peut faire disparaître certaines caractéristiques\n", + " • Artéfacts de pixellisation\n", + "\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import matplotlib as mpl\n", + "import numpy as np\n", + "\n", + "print(\"=== Analyse des erreurs de classification ===\\n\")\n", + "\n", + "# Créer une copie de la matrice de confusion pour les erreurs\n", + "cm_erreurs = cm_multiclass.copy()\n", + "\n", + "# Remplacer la diagonale par des 0 pour se concentrer sur les erreurs\n", + "np.fill_diagonal(cm_erreurs, 0)\n", + "print(\"Matrice de confusion avec diagonale remplacée par des 0:\")\n", + "print(cm_erreurs)\n", + "\n", + "# Affichage de la matrice d'erreurs avec matshow\n", + "plt.figure(figsize=(12, 10))\n", + "plt.matshow(cm_erreurs, cmap=mpl.cm.gray)\n", + "plt.title('Matrice de confusion des erreurs (diagonale = 0)\\nPlus sombre = Plus d\\'erreurs', pad=20)\n", + "plt.xlabel('Classe prédite')\n", + "plt.ylabel('Classe réelle')\n", + "\n", + "# Ajouter les annotations des valeurs\n", + "for i in range(10):\n", + " for j in range(10):\n", + " if cm_erreurs[i, j] > 0: # Afficher seulement les valeurs non nulles\n", + " plt.text(j, i, str(cm_erreurs[i, j]),\n", + " ha=\"center\", va=\"center\",\n", + " color=\"white\" if cm_erreurs[i, j] > cm_erreurs.max()/2 else \"black\",\n", + " fontsize=10, fontweight='bold')\n", + "\n", + "# Ajouter les ticks et labels\n", + "plt.xticks(range(10))\n", + "plt.yticks(range(10))\n", + "plt.colorbar(label='Nombre d\\'erreurs')\n", + "plt.show()\n", + "\n", + "# a. Analyse de la classe 8\n", + "\n", + "# Analyser les erreurs de la classe 8\n", + "erreurs_classe_8_reelle = cm_erreurs[8, :] # Classe 8 prédite comme autre chose\n", + "erreurs_vers_classe_8 = cm_erreurs[:, 8] # Autres classes prédites comme 8\n", + "\n", + "print(f\"Erreurs de la classe 8 (vrais 8 mal classés):\")\n", + "for j in range(10):\n", + " if erreurs_classe_8_reelle[j] > 0:\n", + " print(f\" 8 → {j}: {erreurs_classe_8_reelle[j]} erreurs\")\n", + "\n", + "print(f\"\\nErreurs vers la classe 8 (mal classés comme 8):\")\n", + "for i in range(10):\n", + " if erreurs_vers_classe_8[i] > 0:\n", + " print(f\" {i} → 8: {erreurs_vers_classe_8[i]} erreurs\")\n", + "\n", + "# Statistiques sur la classe 8\n", + "total_erreurs_8_sortantes = np.sum(erreurs_classe_8_reelle)\n", + "total_erreurs_8_entrantes = np.sum(erreurs_vers_classe_8)\n", + "print(f\"\\nStatistiques classe 8:\")\n", + "print(f\"• Total erreurs sortantes (8 mal classé): {total_erreurs_8_sortantes}\")\n", + "print(f\"• Total erreurs entrantes (mal classé comme 8): {total_erreurs_8_entrantes}\")\n", + "print(f\"• Classe 8 semble {'problématique' if total_erreurs_8_entrantes > 100 else 'correcte'}\")\n", + "\n", + "print(f\"\\nOBSERVATION sur la classe 8:\")\n", + "if total_erreurs_8_entrantes > total_erreurs_8_sortantes:\n", + " print(\"La classe 8 attire beaucoup d'erreurs : d'autres chiffres sont souvent confondus avec 8\")\n", + " print(\"Cela suggère que le modèle a tendance à 'sur-prédire' la classe 8\")\n", + "else:\n", + " print(\"La classe 8 génère plus d'erreurs qu'elle n'en attire\")\n", + "\n", + "# Analyse de la confusion entre les classes 3 et 5\n", + "\n", + "# Analyser spécifiquement la confusion 3 et 5\n", + "confusion_3_vers_5 = cm_erreurs[3, 5] # Classe 3 prédite comme 5\n", + "confusion_5_vers_3 = cm_erreurs[5, 3] # Classe 5 prédite comme 3\n", + "\n", + "print(f\"Confusion mutuelle entre 3 et 5:\")\n", + "print(f\"• 3 -> 5: {confusion_3_vers_5} erreurs\")\n", + "print(f\"• 5 -> 3: {confusion_5_vers_3} erreurs\")\n", + "print(f\"• Total confusion 3 et 5: {confusion_3_vers_5 + confusion_5_vers_3} erreurs\")\n", + "\n", + "# Calculer les totaux pour chaque classe\n", + "total_3 = np.sum(cm_multiclass[3, :])\n", + "total_5 = np.sum(cm_multiclass[5, :])\n", + "\n", + "print(f\"\\nPourcentages d'erreur:\")\n", + "print(f\"• {confusion_3_vers_5/total_3*100:.1f}% des vrais 3 sont confondus avec 5\")\n", + "print(f\"• {confusion_5_vers_3/total_5*100:.1f}% des vrais 5 sont confondus avec 3\")\n", + "\n", + "print(f\"\\nPourquoi une forte confusion entre 3 et 5 ?\")\n", + "print(f\"\"\"\n", + "RAISONS PROBABLES DE LA CONFUSION 3 ↔ 5:\n", + "\n", + "1. SIMILARITÉ STRUCTURELLE:\n", + " • Même orientation générale (ouverture vers la droite)\n", + " • Présence de courbes dans la partie supérieure\n", + " • Segments horizontaux similaires\n", + "\n", + "2. VARIATIONS D'ÉCRITURE MANUSCRITE:\n", + " • Un '3' avec boucles fermées peut ressembler à un '5'\n", + " • Un '5' avec courbe arrondie peut ressembler à un '3'\n", + " • Épaisseur variable des traits\n", + "\n", + "3. RÉSOLUTION LIMITÉE (28x28 pixels):\n", + " • Perte de détails fins qui distinguent 3 et 5\n", + " • Lissage qui peut faire disparaître certaines caractéristiques\n", + " • Artéfacts de pixellisation\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "id": "e7bd2901", + "metadata": { + "id": "e7bd2901" + }, + "source": [ + "# IV- Classification multi-label" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "3fba9380", + "metadata": { + "id": "3fba9380" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Création d'un vecteur multi-label ===\n", + "\n", + "Type: \n", + "Forme: (60000,)\n", + "Exemples (premiers 20): [False False False False True False False False False False False False\n", + " False False False False False True False True]\n", + "Nombre de labels > 7: 11800\n", + "Pourcentage: 19.67%\n", + "Type: \n", + "Forme: (60000,)\n", + "Exemples (premiers 20): [ True False False True True False True True True False True True\n", + " True False True True False False False True]\n", + "Nombre de labels impairs: 30508\n", + "Pourcentage: 50.85%\n", + "Type: \n", + "Forme: (60000, 2)\n", + "Premières 20 lignes:\n", + "[Sup_7, Impair]\n", + "[ 0, 1] - Label original: 5\n", + "[ 0, 0] - Label original: 0\n", + "[ 0, 0] - Label original: 4\n", + "[ 0, 1] - Label original: 1\n", + "[ 1, 1] - Label original: 9\n", + "[ 0, 0] - Label original: 2\n", + "[ 0, 1] - Label original: 1\n", + "[ 0, 1] - Label original: 3\n", + "[ 0, 1] - Label original: 1\n", + "[ 0, 0] - Label original: 4\n", + "[ 0, 1] - Label original: 3\n", + "[ 0, 1] - Label original: 5\n", + "[ 0, 1] - Label original: 3\n", + "[ 0, 0] - Label original: 6\n", + "[ 0, 1] - Label original: 1\n", + "[ 0, 1] - Label original: 7\n", + "[ 0, 0] - Label original: 2\n", + "[ 1, 0] - Label original: 8\n", + "[ 0, 0] - Label original: 6\n", + "[ 1, 1] - Label original: 9\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"=== Création d'un vecteur multi-label ===\\n\")\n", + "\n", + "# a. Stocker True si les labels sont supérieurs à 7, False sinon\n", + "labels_sup_7 = (Y_train > 7)\n", + "print(f\"Type: {type(labels_sup_7)}\")\n", + "print(f\"Forme: {labels_sup_7.shape}\")\n", + "print(f\"Exemples (premiers 20): {labels_sup_7.iloc[:20].values}\")\n", + "\n", + "# Statistiques\n", + "nb_sup_7 = np.sum(labels_sup_7)\n", + "print(f\"Nombre de labels > 7: {nb_sup_7}\")\n", + "print(f\"Pourcentage: {(nb_sup_7 / len(Y_train) * 100):.2f}%\")\n", + "\n", + "# b. Stocker True si les labels sont impairs, False sinon\n", + "labels_impairs = (Y_train % 2 == 1)\n", + "print(f\"Type: {type(labels_impairs)}\")\n", + "print(f\"Forme: {labels_impairs.shape}\")\n", + "print(f\"Exemples (premiers 20): {labels_impairs.iloc[:20].values}\")\n", + "\n", + "# Statistiques\n", + "nb_impairs = np.sum(labels_impairs)\n", + "print(f\"Nombre de labels impairs: {nb_impairs}\")\n", + "print(f\"Pourcentage: {(nb_impairs / len(Y_train) * 100):.2f}%\")\n", + "\n", + "# c. Concaténer les deux vecteurs avec numpy.c_\n", + "vecteur_multilabel = np.c_[labels_sup_7, labels_impairs]\n", + "print(f\"Type: {type(vecteur_multilabel)}\")\n", + "print(f\"Forme: {vecteur_multilabel.shape}\")\n", + "print(f\"Premières 20 lignes:\")\n", + "print(\"[Sup_7, Impair]\")\n", + "for i in range(20):\n", + " sup7 = vecteur_multilabel[i, 0]\n", + " impair = vecteur_multilabel[i, 1]\n", + " label_original = Y_train.iloc[i]\n", + " print(f\"[{sup7:5}, {impair:5}] - Label original: {label_original}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "j8PW9T019PaU", + "metadata": { + "id": "j8PW9T019PaU" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Classification multi-label avec K-plus proches voisins ===\n", + "\n", + "- Nombre de voisins (n_neighbors): 5\n", + "- Métrique de distance: minkowski\n", + "- Algorithme: auto\n", + "- Données d'entraînement: (60000, 784)\n", + "- Vecteur multi-label: (60000, 2)\n", + "Entraînement terminé avec succès\n", + "- Forme de la première instance: (1, 784)\n", + "- Forme de la prédiction: (1, 2)\n", + "- Type de la prédiction: \n", + "\n", + "=== Résultat de la prédiction ===\n", + "Prédiction multi-label: [False, True]\n", + " - Supérieur à 7: False\n", + " - Impair: True\n", + "\n", + "=== Comparaison avec la réalité ===\n", + "Vraie classe: 5\n", + "Vraies caractéristiques: [False, True]\n", + " - Vraiment supérieur à 7: False\n", + " - Vraiment impair: True\n", + "\n", + "=== Évaluation de la prédiction ===\n", + "Prédiction 'Supérieur à 7': Correcte\n", + "Prédiction 'Impair': Correcte\n", + "Prédiction globale: TOTALEMENT CORRECTE\n", + "\n", + "=== Interprétation ===\n", + "Le chiffre 5 est un chiffre impair ≤ 7\n", + "Attendu: [False, True]\n", + "Prédit: [False, True]\n", + "Le modèle KNN a correctement identifié les deux caractéristiques multi-label!\n" + ] + } + ], + "source": [ + "from sklearn.neighbors import KNeighborsClassifier\n", + "import numpy as np\n", + "\n", + "print(\"=== Classification multi-label avec K-plus proches voisins ===\\n\")\n", + "\n", + "# a. Créer un objet de la classe KNeighborsClassifier\n", + "knn_classifier = KNeighborsClassifier()\n", + "print(f\"- Nombre de voisins (n_neighbors): {knn_classifier.n_neighbors}\")\n", + "print(f\"- Métrique de distance: {knn_classifier.metric}\")\n", + "print(f\"- Algorithme: {knn_classifier.algorithm}\")\n", + "\n", + "# b. Appliquer la méthode fit avec la base d'apprentissage et le vecteur multi-label\n", + "print(f\"- Données d'entraînement: {X_train.shape}\")\n", + "print(f\"- Vecteur multi-label: {vecteur_multilabel.shape}\")\n", + "\n", + "knn_classifier.fit(X_train, vecteur_multilabel)\n", + "print(\"Entraînement terminé avec succès\")\n", + "\n", + "# c. Prédire la classe de la première instance\n", + "\n", + "# Extraire la première instance\n", + "premiere_instance = X.iloc[0:1] # Garder la forme 2D\n", + "print(f\"- Forme de la première instance: {premiere_instance.shape}\")\n", + "\n", + "# Effectuer la prédiction\n", + "prediction_multilabel = knn_classifier.predict(premiere_instance)\n", + "print(f\"- Forme de la prédiction: {prediction_multilabel.shape}\")\n", + "print(f\"- Type de la prédiction: {type(prediction_multilabel)}\")\n", + "\n", + "# Analyser la prédiction\n", + "pred_sup_7 = prediction_multilabel[0, 0] # Premier label (> 7)\n", + "pred_impair = prediction_multilabel[0, 1] # Deuxième label (impair)\n", + "\n", + "print(f\"\\n=== Résultat de la prédiction ===\")\n", + "print(f\"Prédiction multi-label: [{pred_sup_7}, {pred_impair}]\")\n", + "print(f\" - Supérieur à 7: {pred_sup_7}\")\n", + "print(f\" - Impair: {pred_impair}\")\n", + "\n", + "# Vérifier avec la vraie classe\n", + "vraie_classe = Y.iloc[0]\n", + "vraie_sup_7 = vraie_classe > 7\n", + "vraie_impair = vraie_classe % 2 == 1\n", + "\n", + "print(f\"\\n=== Comparaison avec la réalité ===\")\n", + "print(f\"Vraie classe: {vraie_classe}\")\n", + "print(f\"Vraies caractéristiques: [{vraie_sup_7}, {vraie_impair}]\")\n", + "print(f\" - Vraiment supérieur à 7: {vraie_sup_7}\")\n", + "print(f\" - Vraiment impair: {vraie_impair}\")\n", + "\n", + "# Vérifier la correction de la prédiction\n", + "correction_sup_7 = (pred_sup_7 == vraie_sup_7)\n", + "correction_impair = (pred_impair == vraie_impair)\n", + "prediction_totalement_correcte = correction_sup_7 and correction_impair\n", + "\n", + "print(f\"\\n=== Évaluation de la prédiction ===\")\n", + "print(f\"Prédiction 'Supérieur à 7': {'Correcte' if correction_sup_7 else 'Incorrecte'}\")\n", + "print(f\"Prédiction 'Impair': {'Correcte' if correction_impair else 'Incorrecte'}\")\n", + "print(f\"Prédiction globale: {'TOTALEMENT CORRECTE' if prediction_totalement_correcte else 'PARTIELLEMENT/TOTALEMENT INCORRECTE'}\")\n", + "\n", + "# Analyse détaillée\n", + "print(f\"\\n=== Interprétation ===\")\n", + "if vraie_classe <= 7:\n", + " if vraie_classe % 2 == 1:\n", + " print(f\"Le chiffre {vraie_classe} est un chiffre impair ≤ 7\")\n", + " interpretation = \"[False, True]\"\n", + " else:\n", + " print(f\"Le chiffre {vraie_classe} est un chiffre pair ≤ 7\")\n", + " interpretation = \"[False, False]\"\n", + "else:\n", + " if vraie_classe % 2 == 1:\n", + " print(f\"Le chiffre {vraie_classe} est un chiffre impair > 7\")\n", + " interpretation = \"[True, True]\"\n", + " else:\n", + " print(f\"Le chiffre {vraie_classe} est un chiffre pair > 7\")\n", + " interpretation = \"[True, False]\"\n", + "\n", + "print(f\"Attendu: {interpretation}\")\n", + "print(f\"Prédit: [{pred_sup_7}, {pred_impair}]\")\n", + "\n", + "if prediction_totalement_correcte:\n", + " print(\"Le modèle KNN a correctement identifié les deux caractéristiques multi-label!\")\n", + "else:\n", + " print(\"Le modèle KNN n'a pas parfaitement prédit toutes les caractéristiques.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3e7ms-l0EeFG", + "metadata": { + "id": "3e7ms-l0EeFG" + }, + "source": [ + "# V- Classification multi-output" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "koqddCi6EP39", + "metadata": { + "id": "koqddCi6EP39" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Ajout de bruit aux images d'apprentissage ===\n", + "\n", + "\n", + " Addition du bruit à la base d'apprentissage...\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib as mpl\n", + "\n", + "print(\"=== Ajout de bruit aux images d'apprentissage ===\\n\")\n", + "\n", + "# a. Créer un vecteur de bruit\n", + "noise_train = np.random.randint(0, 100, (len(X_train), 784))\n", + "\n", + "# b. Ajouter ce bruit avec une simple addition à la base d'apprentissage\n", + "print(f\"\\n Addition du bruit à la base d'apprentissage...\")\n", + "\n", + "# Effectuer l'addition\n", + "X_train_noisy = X_train.values + noise_train # Utiliser .values pour obtenir numpy array\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "i6BX_uYnEixO", + "metadata": { + "id": "i6BX_uYnEixO" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Ajout de bruit aux images d'apprentissage ===\n", + "\n", + "\n", + " Addition du bruit à la base de test...\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib as mpl\n", + "\n", + "print(\"=== Ajout de bruit aux images d'apprentissage ===\\n\")\n", + "\n", + "# a. Créer un vecteur de bruit\n", + "noise_test = np.random.randint(0, 100, (len(X_test), 784))\n", + "\n", + "# b. Ajouter ce bruit avec une simple addition à la base de test\n", + "print(f\"\\n Addition du bruit à la base de test...\")\n", + "\n", + "# Effectuer l'addition\n", + "X_test_noisy = X_test.values + noise_test\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "XpX_Q33xFSYn", + "metadata": { + "id": "XpX_Q33xFSYn" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Labels pour les images bruitées créés ===\n", + "Forme de y_train_noisy: (60000, 784)\n", + "Forme de y_test_noisy: (10000, 784)\n", + "\n", + "=== Affichage d'une image bruitée et de sa version non-bruitée ===\n" + ] + }, + { + "data": { + "image/png": "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6bpO6XqEdG3B830GoevrmfYn6U5Iz6bcfQx5cYaAikj3rL7ttttKF+bOmzevtGFMt6ubMGHCGeN++7d/u/ClL32pdJFXuuVbusA3jev85S2dRzPS0Zx0G7b0xj3ddi0tN33OP23k0hGSdMQoHUF5NumU9TXXXFPKp1vgpduOps95nn3x2jNdXJcu/koXm914442loy7pXtldj3Y9nbQzSzuiD33oQ4WrrrqqtAP65V/+5cKv//qvF7797W+Xlpuey/z580sFIx21So+n5c+ePbu0I/zLv/zLwkc/+tHSNQ9veMMbShdIp8/mpntep9vJpXt+A1wILrT9QNpGptueptuMpt8zk35HQDoolD6bn/YFXW84EZE+GvX617++tO1O65Q+95+eTyoKT1VcuuOVr3xl6SNCaZ+RXq+03PQGP92a9NnOUJe7jyEz5/s2R7xwbjdXX19/zth0K7PLL7/8nMfTrdXSbTW73m7uD//wD4sjR44s3aJu/vz5xUceeeSc26ElTz75ZCmbxg0bNqyUu/fee0vr9NOf/vSMsUuXLi2+6U1vKt2OLd26Ls375je/ufjAAw8843PsvA3oN7/5zeJHP/rR4vDhw0vzpXk3b95c1nNM0i30PvKRjxSHDh1a7NevX+mWb0888URZtx09evRo8W1ve1tx0KBB59yOLt367s477yzNm57X4MGDi7NmzSp+4hOfKB4+fPiMdUivzTXXXFP6+qQ/6dZ873//+4vr1q17xtcAIOf9QLJ79+7iu971rtI2vKampnTL0K63EO16a9G77rrrnPxT3XY0SbdgTdvitD5NTU3F++67r3jLLbeUHivntqN79+59yq9Hmq9TWuaMGTOKdXV1xXHjxpX2Gf/wD/9wzrinen0j+xjyUJX+53yXEng26WhN+k2V6Rd5RX65CwAvDi/0/UC68Dnd2efHP/7x+V4VOIdrCLjgpM8wdpU+O5pO66bbw70QdwIA5LMfaG1tLV2L0NXChQtLHylNvxkZLkSuIeCCky6WTXdNSEdT0kVS99xzT+mzjekzpAC8+L2Q9wPpGoZ016O3v/3tpYuM03rffffdpV+CdvYvHIMLhULABSfdYSJdKJY2/OlCp2nTphW+9a1vFd7ylrec71UDoAJeyPuBdDvqdNOItP7pTkfpjnKvec1rCnfccccZv4ANLiSuIQAAgIy5hgAA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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Affichage terminé.\n" + ] + } + ], + "source": [ + "# 49. Créez une variable qui va contenir les labels de la base d'apprentissage, à savoir les images d'apprentissage non bruitées\n", + "y_train_noisy = X_train.values\n", + "\n", + "# 50. Créez une variable qui va contenir les labels de la base de test, à savoir les images de test non bruitées\n", + "y_test_noisy = X_test.values\n", + "\n", + "print(\"=== Labels pour les images bruitées créés ===\")\n", + "print(f\"Forme de y_train_noisy: {y_train_noisy.shape}\")\n", + "print(f\"Forme de y_test_noisy: {y_test_noisy.shape}\")\n", + "\n", + "# 51. Affichez une image bruitée ainsi que sa version non-bruitée\n", + "print(\"\\n=== Affichage d'une image bruitée et de sa version non-bruitée ===\")\n", + "\n", + "# X_test_noisy contient la base de test bruitée (créée précédemment)\n", + "some_digit = X_test_noisy[0]\n", + "# y_test_noisy contient les labels de la base de test (images de test non bruitée)\n", + "y_some_digit = y_test_noisy[0]\n", + "\n", + "some_digit_image = some_digit.reshape(28, 28)\n", + "y_some_digit_image = y_some_digit.reshape(28, 28)\n", + "\n", + "plt.figure(figsize=(8, 4)) # Ajuster la taille de la figure\n", + "plt.subplot(121)\n", + "plt.imshow(some_digit_image, cmap=mpl.cm.binary)\n", + "plt.title(\"Image bruitée\") # Ajouter un titre\n", + "plt.axis(\"off\")\n", + "\n", + "plt.subplot(122)\n", + "plt.imshow(y_some_digit_image, cmap=mpl.cm.binary)\n", + "plt.title(\"Image originale\") # Ajouter un titre\n", + "plt.axis(\"off\")\n", + "\n", + "plt.tight_layout() # Ajuster l'espacement\n", + "plt.show()\n", + "\n", + "print(\"Affichage terminé.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "9986d865", + "metadata": { + "id": "9986d865" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Apprentissage et débruitage avec KNeighborsClassifier ===\n", + "\n", + "- Nombre de voisins (n_neighbors): 5\n", + "\n", + "Entraînement du débruiteur sur les données bruitées...\n", + "- Données d'entraînement bruitées: (60000, 784)\n", + "- Labels (images originales): (60000, 784)\n", + "Entraînement terminé avec succès\n", + "\n", + "Prédiction de la première instance de test bruitée...\n", + "- Forme de l'instance test bruitée: (1, 784)\n", + "- Forme du vecteur d'image débruitée: (1, 784)\n", + "- Type du vecteur: \n", + "\n", + "i. Valeurs du vecteur d'image débruitée (premiers 20): [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]\n", + " Valeurs du vecteur d'image débruitée (derniers 20): [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]\n", + "\n", + "ii. Affichage de l'image débruitée et de l'image originale...\n", + "- Forme de l'image débruitée 2D: (28, 28)\n", + "- Forme de l'image originale 2D: (28, 28)\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9AAAAH6CAYAAADvBqSRAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAH4tJREFUeJzt3QuwVlXZB/B9EkTzjljekBQMUilvqWiKeUXFTAs1LbXMsTItNR2tzNLGS14q826OZhlO6oyCKU0ZFhoaTGSaY5QXlLTwllGiiO5vnv197/neczjn8MA+eC7+fjMk7LPXete7aVj7v9faa7WUZVkWAAAAQJfe0fWPAQAAgCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEBDP3bDDTcUl156adGX/fa3vy3OOuus4uWXX+7ppgAA8DYnQEM/NWnSpOJzn/tcsfXWW7c5/uSTTxYtLS3FhRde+Ja0o/F5119//VKXnTNnTvHRj360WG211Yo11lhjubQPAHqre+65p+pD479vZf+7NN7znvcURx111HL9DOhNBGj6nOgIokOYOXNm0V81Or3Gr4EDBxZDhgwpdtxxx+KrX/1q8dRTTy2x/NFHH13ceOONVZne5s477yy++c1vdnnO66+/XhxyyCFVp3ziiSe+ZW0DYPl4O/TfDS+88EJxyimnFCNHjixWWmmlYvDgwcXee+9d3HHHHT3dNKCmAXUrAJafT3ziE8W+++5bvPnmm8VLL71UzJgxo/je975XfP/73y+uvfba4tBDD+2w3B//+MfiqquuKg488MCipw0bNqxYsGBB9RCgOUBfdtllXYboP//5z9X3+9KXvvQWtRQA6vvLX/5S7L777sVzzz1XfPrTny623Xbb4l//+lf1UHv//fcvvvKVrxQXXHBBqq5ddtml6kNXXHHFbul/gfoEaOjFYvr1Jz/5ycWmNe+1117FkUceWbzvfe8rPvCBDyxWLqY9Ly///e9/i1VWWSV9fow2xNP3pbXllltWvwCgr4jZUx//+Merh96xhsf222/f+rOYTXX44YdXr1BFqI5ZVp159dVXq9D8jne8Y5n60Dr9L9A1U7jpF2Ka76qrrlpNbR4/fnz1+w022KAa5QwPPfRQsdtuu1XBL57I/vSnP21T/sUXX6yeCI8ePboqu/rqqxf77LNP8eCDDy72WRFgP/KRj1R1vetd76o6xF/84hcdvqP0wAMPFOPGjave333nO99ZjB07trjvvvtqfddof0yDW7hwYfGd73ynzc/iCfeXv/zlYujQocWgQYOKESNGFOeff341gt2R7373u1V9K6+8ctW2hx9+uMPr+thjj1Uj4fEucnT+Xb3ztOuuu1a/OnsHK8o0/l6ap6k3RFtjlH3zzTevOv53v/vdxbHHHlvdjLR31113FTvvvHP1dxFt22+//aqRawD6hv7Wf996661VX3raaae1Cc9hhRVWqGaHrbnmmm1mYDXec77pppuKr3/969X3j8/897//3ek70HF9Ntlkk6r/3m677Ypp06Ytsf9tvt5///vfq4ft8ft11lmnuoZvvPFGm8+IoB+vga299trV52yzzTbFLbfcUmQs7f0I9CVGoOk34h/+6DRjulMEy5gq9cUvfrHqKL/2ta9Vwe+ggw4qrrzyyuKII44oxowZU2y88cZV2ccff7y47bbbigkTJlTH/vnPf1adXHSYjzzySLH++uu3jr5GR/7ss89WU4vXXXfdqjOfOnXqYu359a9/XbUnOpwzzzyzeop83XXXVeWjo4sOb1lF24cPH1788pe/bD32yiuvVO2NTjEC50YbbVT87ne/K04//fSqvRFK26/QPX/+/OK4446rnnTHtPBoW9ysRGhtWLRoUfXe1oc+9KGqM41OvY5o2zPPPFO1/cc//nGHP4/OPqa9nXDCCcUTTzxRrSQ+a9as6ualMRUtysYofLQtOuX4/ldccUXVzjg3Aj4AvV9/6r8nT55c/Tfa2ZEI5AcccEDxox/9qPjb3/5WBcuGs88+uxp1jjD72muvdTptO/q6uD7xADkeAkRQjjC81lprFRtuuGHqekffGQE/+vVf/epXxUUXXVTdV3z+859vPS/uC+KBQ1z/eGgfAT+uc7zHHQ+sO7O09yPQ55TQx1x33XVl/F93xowZrceOPPLI6tg555zTeuyll14qV1555bKlpaW86aabWo8/+uij1blnnnlm67FXX321fOONN9p8zhNPPFEOGjSoPOuss1qPXXTRRVXZ2267rfXYggULylGjRlXHp06dWh178803y0033bTce++9q983vPLKK+XGG29c7rnnnl1+x/jsqO+CCy7o9JwDDjigOufll1+u/nz22WeXq6yy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GgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBiQOYne5fnnn69dxw9+8IPadUyYMKFW+S222KJ2GwCgt7rllltq13HNNdfUrmP99devVX6llVaq3YbDDz+8Vvl11123dhtGjBhRuw4AI9AAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJLSUZVlmTqT3eOCBB2rXscMOO3RLW/q6e+65p+gPXn311dp1zJ8/v1b5hx56qHYbpk2bVqv8rFmzarfhvPPOq1X+2GOPrd0GoH/YeOONa9fx5JNPdktb+rrVV1+9dh2bbbZZt7SF/mHo0KG16zj11FNr17HtttvWroO3lhFoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgISWsizLzIn0L9OnT69dx+23316r/Pnnn1+7DdDbTJ06tXYdu+66a7e0BehZd999d+06Hnzwwdp1bLbZZrXKP/LII7XbMGvWrFrl77nnntptmDt3bq3yG220Ue02PPXUU0V/MHDgwFrlhwwZUrsNzz77bNHTTjrppNp1XHTRRd3SFt46RqABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASGgpy7LMnAgsH8OHD69dx2OPPVb0B4sWLapV/oYbbqjdhqOPPrpW+cmTJ9duw/jx42vXAdCfvPTSS7XrmDVrVq3y2267be02zJgxo+gPBg0aVKv8yJEja7dh1KhRtcq/+OKLtdtw2WWX1a7jC1/4Qu06eGsZgQYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIaCnLssycCNDbLVy4sHYdgwYNqlV+8uTJtdswfvz42nUAQG9166231q5jwoQJtcqPHj26dhumTp1au47BgwfXroO3lhFoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABJayrIsMycC9HYtLS093YTCP6kA9Hfz5s2rVX706NE93oZbbrmldhs+9rGP1a6DvscINAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAEDCgMxJAG+FuXPn9nQTitNPP72nmwAAvdpll11Wq/y8efNqt2HNNdesVX7kyJG128DbkxFoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABJayrIsMycCLG+HHXZYrfITJ06s3YYnn3yyVvlhw4bVbgMALC/33ntv7Tp23333WuUXLlxYuw2/+c1vapXfZZddareBtycj0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkDMicBLAkkydPrl3HxIkTa5U/99xza7dh2LBhtesAgN7qzjvvrF3HwoULa5XfY489ardhzJgxteuAZWEEGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBhQOYkgCWZOHFiTzehOOqoo3q6CQCwXC1YsKBW+SlTptRuw6BBg2qV/9a3vlW7DQMHDqxdBywLI9AAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAzInAT0f9/4xjdqlZ84cWLtNlxxxRW1yq+77rq12wAAvdkFF1xQq/ysWbNqt2GfffapVX7HHXes3QboKUagAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABJayrIsMycCvdfDDz9cu47Ro0cXPe3pp5+uVX7DDTfstrYAQHe74447atdx4IEH1iq/yiqr1G7DXXfdVav8mDFjarcBeooRaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBmROApafmTNn1q7j/PPPL3ralVdeWbuODTfcsFvaAgDd7YUXXqhdxwknnFC7jkWLFtUqv++++9Zuw5gxY2rXAX2VEWgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACAhJayLMvMicDyceyxx9au4+qrr65dx8iRI2uVnz59eu02rLXWWrXrAICOvPHGG7XK77DDDrXbMHPmzNp1jBgxolb5KVOm1G7D8OHDa9cBfZURaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASWsqyLDMnAh1btGhRrfIDBw4seoO77rqrVvlx48Z1W1sAoLvNnj27VvmRI0cWvcGkSZNqld9///27rS3wdmQEGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAICEAZmTgM7Nnj27p5tQ7LDDDrXrGDduXLe0BQC625w5c2rXsddeexU97cILL6xdx/jx47ulLcCyMQINAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkDAgcxL0V6+//nrtOjbffPNa5UeNGlW7DZdffnntOgCgt7rqqqtq1zFnzpyip40dO7Z2HS0tLd3SFmDZGIEGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIGFA5iTor1ZcccWebkJx2GGH1a5jq6226pa2AMDyMG3atFrlL7300m5rC0AdRqABAAAgQYAGAACABAEaAAAAEgRoAAAASBCgAQAAIEGABgAAgAQBGgAAABIEaAAAAEgQoAEAACBBgAYAAIAEARoAAAASBGgAAABIEKABAAAgQYAGAACABAEaAAAAEgZkTgI6t88++9Qqf8YZZ3RbWwCgN7r33ntrlZ8/f37R00aMGFG7jlVXXbVb2gL0HCPQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQMyJwEdO7444/v6SYAAEuw5ZZb1ip/9913127D4MGDa9cB9Cwj0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJLWVZlpkTAQAA4O3MCDQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAACQIEADAABAggANAAAACQI0AAAAJAjQAAAAkCBAAwAAQIIADQAAAAkCNAAAACQI0AAAAJAgQAMAAECCAA0AAAAJAjQAAAAkCNAAAABQLNn/AKTYfqRcD1t/AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Conclusion ===\n", + "L'image bruitée de la base de test a été traitée par le débruiteur K-NN.\n", + "La version prédite (débruitée) est affichée à gauche et l'image originale à droite.\n" + ] + } + ], + "source": [ + "from sklearn.neighbors import KNeighborsClassifier\n", + "\n", + "print(\"=== Apprentissage et débruitage avec KNeighborsClassifier ===\\n\")\n", + "\n", + "# a. Créer un objet de la classe KNeighborsClassifier (utiliser les paramètres par défaut)\n", + "knn_denoiser = KNeighborsClassifier()\n", + "print(f\"- Nombre de voisins (n_neighbors): {knn_denoiser.n_neighbors}\")\n", + "\n", + "# Appliquer la méthode fit avec les images d'apprentissage bruitées et leurs versions non-bruitées\n", + "print(f\"\\nEntraînement du débruiteur sur les données bruitées...\")\n", + "print(f\"- Données d'entraînement bruitées: {X_train_noisy.shape}\")\n", + "print(f\"- Labels (images originales): {y_train_noisy.shape}\")\n", + "\n", + "knn_denoiser.fit(X_train_noisy, y_train_noisy)\n", + "print(\"Entraînement terminé avec succès\")\n", + "\n", + "# b. Prédire la classe (sous forme de vecteurs) de la première instance de la base de test.\n", + "print(\"\\nPrédiction de la première instance de test bruitée...\")\n", + "\n", + "# Extraire la première instance de la base de test bruitée\n", + "instance_test_bruit = X_test_noisy[0:1] # Garder la forme 2D\n", + "print(f\"- Forme de l'instance test bruitée: {instance_test_bruit.shape}\")\n", + "\n", + "# Effectuer la prédiction (débruitage)\n", + "image_debruitee_vector = knn_denoiser.predict(instance_test_bruit)\n", + "print(f\"- Forme du vecteur d'image débruitée: {image_debruitee_vector.shape}\")\n", + "print(f\"- Type du vecteur: {type(image_debruitee_vector)}\")\n", + "\n", + "# i. Afficher les valeurs du vecteur\n", + "print(f\"\\ni. Valeurs du vecteur d'image débruitée (premiers 20): {image_debruitee_vector[0, :20]}\")\n", + "print(f\" Valeurs du vecteur d'image débruitée (derniers 20): {image_debruitee_vector[0, -20:]}\")\n", + "\n", + "\n", + "# ii. Afficher ce vecteur sous-forme d'image\n", + "print(\"\\nii. Affichage de l'image débruitée et de l'image originale...\")\n", + "\n", + "# Redimensionner le vecteur prédit en image 28x28\n", + "image_debruitee_2d = image_debruitee_vector.reshape(28, 28)\n", + "print(f\"- Forme de l'image débruitée 2D: {image_debruitee_2d.shape}\")\n", + "\n", + "# Obtenir l'image originale correspondante pour comparaison\n", + "image_originale_test_2d = y_test_noisy[0].reshape(28, 28)\n", + "print(f\"- Forme de l'image originale 2D: {image_originale_test_2d.shape}\")\n", + "\n", + "\n", + "# Afficher les images\n", + "plt.figure(figsize=(10, 5))\n", + "\n", + "plt.subplot(1, 2, 1)\n", + "plt.imshow(image_debruitee_2d, cmap=mpl.cm.binary)\n", + "plt.title(\"Image Débruitée\")\n", + "plt.axis(\"off\")\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.imshow(image_originale_test_2d, cmap=mpl.cm.binary)\n", + "plt.title(\"Image Originale\")\n", + "plt.axis(\"off\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "print(\"\\n=== Conclusion ===\")\n", + "print(\"L'image bruitée de la base de test a été traitée par le débruiteur K-NN.\")\n", + "print(\"La version prédite (débruitée) est affichée à gauche et l'image originale à droite.\")" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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.13.7" } - ], - "source": [ - "from sklearn.model_selection import cross_val_predict\n", - "from sklearn.metrics import confusion_matrix\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "print(\"=== Matrice de confusion du classifieur SGD multi-classes ===\\n\")\n", - "\n", - "# Prédire les classes avec cross_val_predict pour le modèle multi-classes\n", - "print(\"Prédiction des classes avec cross_val_predict...\")\n", - "y_train_pred_multiclass = cross_val_predict(sgd_multiclass, X_train, Y_train, cv=3)\n", - "\n", - "print(f\"Forme des prédictions: {y_train_pred_multiclass.shape}\")\n", - "print(f\"Classes prédites uniques: {np.unique(y_train_pred_multiclass)}\")\n", - "\n", - "# a. Calculer la matrice de confusion normale et normalisée\n", - "cm_multiclass = confusion_matrix(Y_train, y_train_pred_multiclass)\n", - "print(\"\\na. Matrice de confusion (valeurs absolues) 10x10:\")\n", - "print(cm_multiclass)\n", - "\n", - "cm_normalized_multiclass = confusion_matrix(Y_train, y_train_pred_multiclass, normalize='true')\n", - "print(\"\\nMatrice de confusion normalisée:\")\n", - "print(cm_normalized_multiclass)\n", - "\n", - "# Visualisation des matrices de confusion\n", - "fig, axes = plt.subplots(1, 2, figsize=(15, 6))\n", - "\n", - "# Matrice de confusion absolue\n", - "im1 = axes[0].imshow(cm_multiclass, interpolation='nearest', cmap=plt.cm.Blues)\n", - "axes[0].set_title('Matrice de confusion (valeurs absolues)')\n", - "axes[0].set_xlabel('Classe prédite')\n", - "axes[0].set_ylabel('Classe réelle')\n", - "\n", - "# Ajouter les annotations pour les valeurs\n", - "for i in range(10):\n", - " for j in range(10):\n", - " axes[0].text(j, i, format(cm_multiclass[i, j], 'd'), \n", - " ha=\"center\", va=\"center\", \n", - " color=\"white\" if cm_multiclass[i, j] > cm_multiclass.max()/2 else \"black\",\n", - " fontsize=8)\n", - "\n", - "axes[0].set_xticks(range(10))\n", - "axes[0].set_yticks(range(10))\n", - "axes[0].set_xticklabels(range(10))\n", - "axes[0].set_yticklabels(range(10))\n", - "\n", - "# Matrice de confusion normalisée\n", - "im2 = axes[1].imshow(cm_normalized_multiclass, interpolation='nearest', cmap=plt.cm.Blues)\n", - "axes[1].set_title('Matrice de confusion normalisée')\n", - "axes[1].set_xlabel('Classe prédite')\n", - "axes[1].set_ylabel('Classe réelle')\n", - "\n", - "# Ajouter les annotations pour les pourcentages\n", - "for i in range(10):\n", - " for j in range(10):\n", - " axes[1].text(j, i, format(cm_normalized_multiclass[i, j], '.2f'), \n", - " ha=\"center\", va=\"center\", \n", - " color=\"white\" if cm_normalized_multiclass[i, j] > 0.5 else \"black\",\n", - " fontsize=8)\n", - "\n", - "axes[1].set_xticks(range(10))\n", - "axes[1].set_yticks(range(10))\n", - "axes[1].set_xticklabels(range(10))\n", - "axes[1].set_yticklabels(range(10))\n", - "\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "# b. Interprétation des résultats\n", - "print(\"\\n=== Interprétation des résultats ===\")\n", - "\n", - "# Calcul des métriques globales\n", - "accuracy_total = np.trace(cm_multiclass) / np.sum(cm_multiclass)\n", - "print(f\"\\nAccuracy globale: {accuracy_total:.4f} ({accuracy_total*100:.2f}%)\")\n", - "\n", - "# Analyse par classe (diagonale = bonnes prédictions)\n", - "print(f\"\\nPerformances par classe (rappel):\")\n", - "for i in range(10):\n", - " rappel_classe = cm_normalized_multiclass[i, i]\n", - " nb_instances = cm_multiclass[i, :].sum()\n", - " print(f\" Classe {i}: {rappel_classe:.3f} ({rappel_classe*100:.1f}%) - {nb_instances} instances\")\n", - "\n", - "# Classes les mieux classées\n", - "diagonale = np.diag(cm_normalized_multiclass)\n", - "meilleures_classes = np.argsort(diagonale)[::-1][:3]\n", - "pires_classes = np.argsort(diagonale)[:3]\n", - "\n", - "print(f\"\\nMeilleures classes (rappel élevé):\")\n", - "for classe in meilleures_classes:\n", - " print(f\" Classe {classe}: {diagonale[classe]:.3f} ({diagonale[classe]*100:.1f}%)\")\n", - "\n", - "print(f\"\\nPires classes (rappel faible):\")\n", - "for classe in pires_classes:\n", - " print(f\" Classe {classe}: {diagonale[classe]:.3f} ({diagonale[classe]*100:.1f}%)\")\n", - "\n", - "# Erreurs les plus fréquentes (hors diagonale)\n", - "cm_erreurs = cm_multiclass.copy()\n", - "np.fill_diagonal(cm_erreurs, 0) # Enlever la diagonale\n", - "erreurs_max = np.unravel_index(np.argmax(cm_erreurs), cm_erreurs.shape)\n", - "\n", - "print(f\"\\nErreurs les plus fréquentes:\")\n", - "indices_erreurs = np.argsort(cm_erreurs.ravel())[::-1][:5]\n", - "for idx in indices_erreurs:\n", - " i, j = np.unravel_index(idx, cm_erreurs.shape)\n", - " if cm_erreurs[i, j] > 0:\n", - " print(f\" Classe {i} prédite comme {j}: {cm_erreurs[i, j]} fois\")\n", - "\n", - "print(f\"\\n=== Conclusion ===\")\n", - "print(\"La matrice 10x10 montre les performances du classifieur sur les 10 chiffres.\")\n", - "print(\"La diagonale représente les bonnes classifications.\")\n", - "print(\"Les valeurs hors diagonale montrent les confusions entre classes.\")" - ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "16a1368f", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import matplotlib as mpl\n", - "import numpy as np\n", - "\n", - "print(\"=== Affichage de la matrice de confusion avec matshow ===\\n\")\n", - "\n", - "# Affichage de la matrice de confusion avec matshow\n", - "plt.figure(figsize=(10, 8))\n", - "plt.matshow(cm_multiclass, cmap=mpl.cm.gray)\n", - "plt.title('Matrice de confusion avec matshow (en niveaux de gris)', pad=20)\n", - "plt.xlabel('Classe prédite')\n", - "plt.ylabel('Classe réelle')\n", - "\n", - "# Ajouter les annotations des valeurs\n", - "for i in range(10):\n", - " for j in range(10):\n", - " plt.text(j, i, str(cm_multiclass[i, j]), \n", - " ha=\"center\", va=\"center\", \n", - " color=\"white\" if cm_multiclass[i, j] > cm_multiclass.max()/2 else \"black\",\n", - " fontsize=10)\n", - "\n", - "# Ajouter les ticks et labels\n", - "plt.xticks(range(10))\n", - "plt.yticks(range(10))\n", - "plt.colorbar()\n", - "plt.show()\n", - "\n", - "print(\"=== Analyse de la confusion entre les classes 5 et 8 ===\\n\")\n", - "\n", - "# Analyser spécifiquement la confusion entre 5 et 8\n", - "confusion_5_vers_8 = cm_multiclass[5, 8] # Classe 5 prédite comme 8\n", - "confusion_8_vers_5 = cm_multiclass[8, 5] # Classe 8 prédite comme 5\n", - "total_5 = np.sum(cm_multiclass[5, :]) # Total d'instances de classe 5\n", - "total_8 = np.sum(cm_multiclass[8, :]) # Total d'instances de classe 8\n", - "\n", - "print(f\"Confusion 5 → 8: {confusion_5_vers_8} instances ({confusion_5_vers_8/total_5*100:.1f}% des vrais 5)\")\n", - "print(f\"Confusion 8 → 5: {confusion_8_vers_5} instances ({confusion_8_vers_5/total_8*100:.1f}% des vrais 8)\")\n", - "\n", - "# Performance de la classe 5\n", - "performance_5 = cm_multiclass[5, 5] / total_5\n", - "print(f\"Performance de la classe 5: {performance_5:.3f} ({performance_5*100:.1f}%)\")\n", - "\n", - "# Performance de la classe 8\n", - "performance_8 = cm_multiclass[8, 8] / total_8\n", - "print(f\"Performance de la classe 8: {performance_8:.3f} ({performance_8*100:.1f}%)\")\n", - "\n", - "print(f\"\\n=== Pourquoi la classe 5 est-elle confondue avec le chiffre 8 ? ===\")\n", - "print(f\"\"\"\n", - "RAISONS PROBABLES DE LA CONFUSION 5 ↔ 8:\n", - "\n", - "1. SIMILARITÉ MORPHOLOGIQUE:\n", - " • Les chiffres 5 et 8 partagent des caractéristiques visuelles similaires\n", - " • Courbes fermées et boucles dans les deux chiffres\n", - " • Segments horizontaux en haut et au milieu\n", - "\n", - "2. VARIABILITÉ DE L'ÉCRITURE MANUSCRITE:\n", - " • Un '5' mal écrit peut ressembler à un '8' ouvert\n", - " • Un '8' déformé peut être confondu avec un '5'\n", - " • Variations dans l'épaisseur des traits et la fermeture des boucles\n", - "\n", - "3. QUALITÉ DES DONNÉES:\n", - " • Images de résolution 28x28 pixels (faible résolution)\n", - " • Possibles artéfacts de numérisation\n", - " • Variations de contraste et de netteté\n", - "\"\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cf44022c", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import matplotlib as mpl\n", - "import numpy as np\n", - "\n", - "print(\"=== Analyse des erreurs de classification ===\\n\")\n", - "\n", - "# Créer une copie de la matrice de confusion pour les erreurs\n", - "cm_erreurs = cm_multiclass.copy()\n", - "\n", - "# Remplacer la diagonale par des 0 pour se concentrer sur les erreurs\n", - "np.fill_diagonal(cm_erreurs, 0)\n", - "print(\"Matrice de confusion avec diagonale remplacée par des 0:\")\n", - "print(cm_erreurs)\n", - "\n", - "# Affichage de la matrice d'erreurs avec matshow\n", - "plt.figure(figsize=(12, 10))\n", - "plt.matshow(cm_erreurs, cmap=mpl.cm.gray)\n", - "plt.title('Matrice de confusion des erreurs (diagonale = 0)\\nPlus sombre = Plus d\\'erreurs', pad=20)\n", - "plt.xlabel('Classe prédite')\n", - "plt.ylabel('Classe réelle')\n", - "\n", - "# Ajouter les annotations des valeurs\n", - "for i in range(10):\n", - " for j in range(10):\n", - " if cm_erreurs[i, j] > 0: # Afficher seulement les valeurs non nulles\n", - " plt.text(j, i, str(cm_erreurs[i, j]), \n", - " ha=\"center\", va=\"center\", \n", - " color=\"white\" if cm_erreurs[i, j] > cm_erreurs.max()/2 else \"black\",\n", - " fontsize=10, fontweight='bold')\n", - "\n", - "# Ajouter les ticks et labels\n", - "plt.xticks(range(10))\n", - "plt.yticks(range(10))\n", - "plt.colorbar(label='Nombre d\\'erreurs')\n", - "plt.show()\n", - "\n", - "# a. Analyse de la classe 8\n", - "\n", - "# Analyser les erreurs de la classe 8\n", - "erreurs_classe_8_reelle = cm_erreurs[8, :] # Classe 8 prédite comme autre chose\n", - "erreurs_vers_classe_8 = cm_erreurs[:, 8] # Autres classes prédites comme 8\n", - "\n", - "print(f\"Erreurs de la classe 8 (vrais 8 mal classés):\")\n", - "for j in range(10):\n", - " if erreurs_classe_8_reelle[j] > 0:\n", - " print(f\" 8 → {j}: {erreurs_classe_8_reelle[j]} erreurs\")\n", - "\n", - "print(f\"\\nErreurs vers la classe 8 (mal classés comme 8):\")\n", - "for i in range(10):\n", - " if erreurs_vers_classe_8[i] > 0:\n", - " print(f\" {i} → 8: {erreurs_vers_classe_8[i]} erreurs\")\n", - "\n", - "# Statistiques sur la classe 8\n", - "total_erreurs_8_sortantes = np.sum(erreurs_classe_8_reelle)\n", - "total_erreurs_8_entrantes = np.sum(erreurs_vers_classe_8)\n", - "print(f\"\\nStatistiques classe 8:\")\n", - "print(f\"• Total erreurs sortantes (8 mal classé): {total_erreurs_8_sortantes}\")\n", - "print(f\"• Total erreurs entrantes (mal classé comme 8): {total_erreurs_8_entrantes}\")\n", - "print(f\"• Classe 8 semble {'problématique' if total_erreurs_8_entrantes > 100 else 'correcte'}\")\n", - "\n", - "print(f\"\\nOBSERVATION sur la classe 8:\")\n", - "if total_erreurs_8_entrantes > total_erreurs_8_sortantes:\n", - " print(\"La classe 8 attire beaucoup d'erreurs : d'autres chiffres sont souvent confondus avec 8\")\n", - " print(\"Cela suggère que le modèle a tendance à 'sur-prédire' la classe 8\")\n", - "else:\n", - " print(\"La classe 8 génère plus d'erreurs qu'elle n'en attire\")\n", - "\n", - "# Analyse de la confusion entre les classes 3 et 5\n", - "\n", - "# Analyser spécifiquement la confusion 3 et 5\n", - "confusion_3_vers_5 = cm_erreurs[3, 5] # Classe 3 prédite comme 5\n", - "confusion_5_vers_3 = cm_erreurs[5, 3] # Classe 5 prédite comme 3\n", - "\n", - "print(f\"Confusion mutuelle entre 3 et 5:\")\n", - "print(f\"• 3 -> 5: {confusion_3_vers_5} erreurs\")\n", - "print(f\"• 5 -> 3: {confusion_5_vers_3} erreurs\")\n", - "print(f\"• Total confusion 3 et 5: {confusion_3_vers_5 + confusion_5_vers_3} erreurs\")\n", - "\n", - "# Calculer les totaux pour chaque classe\n", - "total_3 = np.sum(cm_multiclass[3, :])\n", - "total_5 = np.sum(cm_multiclass[5, :])\n", - "\n", - "print(f\"\\nPourcentages d'erreur:\")\n", - "print(f\"• {confusion_3_vers_5/total_3*100:.1f}% des vrais 3 sont confondus avec 5\")\n", - "print(f\"• {confusion_5_vers_3/total_5*100:.1f}% des vrais 5 sont confondus avec 3\")\n", - "\n", - "print(f\"\\nPourquoi une forte confusion entre 3 et 5 ?\")\n", - "print(f\"\"\"\n", - "RAISONS PROBABLES DE LA CONFUSION 3 ↔ 5:\n", - "\n", - "1. SIMILARITÉ STRUCTURELLE:\n", - " • Même orientation générale (ouverture vers la droite)\n", - " • Présence de courbes dans la partie supérieure\n", - " • Segments horizontaux similaires\n", - "\n", - "2. VARIATIONS D'ÉCRITURE MANUSCRITE:\n", - " • Un '3' avec boucles fermées peut ressembler à un '5'\n", - " • Un '5' avec courbe arrondie peut ressembler à un '3'\n", - " • Épaisseur variable des traits\n", - "\n", - "3. RÉSOLUTION LIMITÉE (28x28 pixels):\n", - " • Perte de détails fins qui distinguent 3 et 5\n", - " • Lissage qui peut faire disparaître certaines caractéristiques\n", - " • Artéfacts de pixellisation\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "id": "e7bd2901", - "metadata": {}, - "source": [ - "# IV- Classification multi-label" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3fba9380", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "print(\"=== Création d'un vecteur multi-label ===\\n\")\n", - "\n", - "# a. Stocker True si les labels sont supérieurs à 7, False sinon\n", - "labels_sup_7 = (Y_train > 7)\n", - "print(f\"Type: {type(labels_sup_7)}\")\n", - "print(f\"Forme: {labels_sup_7.shape}\")\n", - "print(f\"Exemples (premiers 20): {labels_sup_7.iloc[:20].values}\")\n", - "\n", - "# Statistiques\n", - "nb_sup_7 = np.sum(labels_sup_7)\n", - "print(f\"Nombre de labels > 7: {nb_sup_7}\")\n", - "print(f\"Pourcentage: {(nb_sup_7 / len(Y_train) * 100):.2f}%\")\n", - "\n", - "# b. Stocker True si les labels sont impairs, False sinon\n", - "labels_impairs = (Y_train % 2 == 1)\n", - "print(f\"Type: {type(labels_impairs)}\")\n", - "print(f\"Forme: {labels_impairs.shape}\")\n", - "print(f\"Exemples (premiers 20): {labels_impairs.iloc[:20].values}\")\n", - "\n", - "# Statistiques\n", - "nb_impairs = np.sum(labels_impairs)\n", - "print(f\"Nombre de labels impairs: {nb_impairs}\")\n", - "print(f\"Pourcentage: {(nb_impairs / len(Y_train) * 100):.2f}%\")\n", - "\n", - "# c. Concaténer les deux vecteurs avec numpy.c_\n", - "vecteur_multilabel = np.c_[labels_sup_7, labels_impairs]\n", - "print(f\"Type: {type(vecteur_multilabel)}\")\n", - "print(f\"Forme: {vecteur_multilabel.shape}\")\n", - "print(f\"Premières 20 lignes:\")\n", - "print(\"[Sup_7, Impair]\")\n", - "for i in range(20):\n", - " sup7 = vecteur_multilabel[i, 0]\n", - " impair = vecteur_multilabel[i, 1]\n", - " label_original = Y_train.iloc[i]\n", - " print(f\"[{sup7:5}, {impair:5}] - Label original: {label_original}\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb817782", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.neighbors import KNeighborsClassifier\n", - "import numpy as np\n", - "\n", - "print(\"=== Classification multi-label avec K-plus proches voisins ===\\n\")\n", - "\n", - "# a. Créer un objet de la classe KNeighborsClassifier\n", - "knn_classifier = KNeighborsClassifier()\n", - "print(f\"- Nombre de voisins (n_neighbors): {knn_classifier.n_neighbors}\")\n", - "print(f\"- Métrique de distance: {knn_classifier.metric}\")\n", - "print(f\"- Algorithme: {knn_classifier.algorithm}\")\n", - "\n", - "# b. Appliquer la méthode fit avec la base d'apprentissage et le vecteur multi-label\n", - "print(f\"- Données d'entraînement: {X_train.shape}\")\n", - "print(f\"- Vecteur multi-label: {vecteur_multilabel.shape}\")\n", - "\n", - "knn_classifier.fit(X_train, vecteur_multilabel)\n", - "print(\"Entraînement terminé avec succès\")\n", - "\n", - "# c. Prédire la classe de la première instance\n", - "\n", - "# Extraire la première instance\n", - "premiere_instance = X.iloc[0:1] # Garder la forme 2D\n", - "print(f\"- Forme de la première instance: {premiere_instance.shape}\")\n", - "\n", - "# Effectuer la prédiction\n", - "prediction_multilabel = knn_classifier.predict(premiere_instance)\n", - "print(f\"- Forme de la prédiction: {prediction_multilabel.shape}\")\n", - "print(f\"- Type de la prédiction: {type(prediction_multilabel)}\")\n", - "\n", - "# Analyser la prédiction\n", - "pred_sup_7 = prediction_multilabel[0, 0] # Premier label (> 7)\n", - "pred_impair = prediction_multilabel[0, 1] # Deuxième label (impair)\n", - "\n", - "print(f\"\\n=== Résultat de la prédiction ===\")\n", - "print(f\"Prédiction multi-label: [{pred_sup_7}, {pred_impair}]\")\n", - "print(f\" - Supérieur à 7: {pred_sup_7}\")\n", - "print(f\" - Impair: {pred_impair}\")\n", - "\n", - "# Vérifier avec la vraie classe\n", - "vraie_classe = Y.iloc[0]\n", - "vraie_sup_7 = vraie_classe > 7\n", - "vraie_impair = vraie_classe % 2 == 1\n", - "\n", - "print(f\"\\n=== Comparaison avec la réalité ===\")\n", - "print(f\"Vraie classe: {vraie_classe}\")\n", - "print(f\"Vraies caractéristiques: [{vraie_sup_7}, {vraie_impair}]\")\n", - "print(f\" - Vraiment supérieur à 7: {vraie_sup_7}\")\n", - "print(f\" - Vraiment impair: {vraie_impair}\")\n", - "\n", - "# Vérifier la correction de la prédiction\n", - "correction_sup_7 = (pred_sup_7 == vraie_sup_7)\n", - "correction_impair = (pred_impair == vraie_impair)\n", - "prediction_totalement_correcte = correction_sup_7 and correction_impair\n", - "\n", - "print(f\"\\n=== Évaluation de la prédiction ===\")\n", - "print(f\"Prédiction 'Supérieur à 7': {'Correcte' if correction_sup_7 else 'Incorrecte'}\")\n", - "print(f\"Prédiction 'Impair': {'Correcte' if correction_impair else 'Incorrecte'}\")\n", - "print(f\"Prédiction globale: {'TOTALEMENT CORRECTE' if prediction_totalement_correcte else 'PARTIELLEMENT/TOTALEMENT INCORRECTE'}\")\n", - "\n", - "# Analyse détaillée\n", - "print(f\"\\n=== Interprétation ===\")\n", - "if vraie_classe <= 7:\n", - " if vraie_classe % 2 == 1:\n", - " print(f\"Le chiffre {vraie_classe} est un chiffre impair ≤ 7\")\n", - " interpretation = \"[False, True]\"\n", - " else:\n", - " print(f\"Le chiffre {vraie_classe} est un chiffre pair ≤ 7\")\n", - " interpretation = \"[False, False]\"\n", - "else:\n", - " if vraie_classe % 2 == 1:\n", - " print(f\"Le chiffre {vraie_classe} est un chiffre impair > 7\")\n", - " interpretation = \"[True, True]\"\n", - " else:\n", - " print(f\"Le chiffre {vraie_classe} est un chiffre pair > 7\")\n", - " interpretation = \"[True, False]\"\n", - "\n", - "print(f\"Attendu: {interpretation}\")\n", - "print(f\"Prédit: [{pred_sup_7}, {pred_impair}]\")\n", - "\n", - "if prediction_totalement_correcte:\n", - " print(\"Le modèle KNN a correctement identifié les deux caractéristiques multi-label!\")\n", - "else:\n", - " print(\"Le modèle KNN n'a pas parfaitement prédit toutes les caractéristiques.\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "b6600aef", - "metadata": {}, - "source": [ - "# V- Classification multi-output" - ] - }, - { - "cell_type": "markdown", - "id": "a782268f", - "metadata": {}, - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib as mpl\n", - "\n", - "print(\"=== Ajout de bruit aux images d'apprentissage ===\\n\")\n", - "\n", - "# a. Créer un vecteur de bruit\n", - "noise_train = np.random.randint(0, 100, (len(X_train), 784))\n", - "\n", - "# b. Ajouter ce bruit avec une simple addition à la base d'apprentissage\n", - "print(f\"\\n Addition du bruit à la base d'apprentissage...\")\n", - "\n", - "# Effectuer l'addition\n", - "X_train_noisy = X_train.values + noise_train # Utiliser .values pour obtenir numpy array\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dc4f7ce8", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib as mpl\n", - "\n", - "print(\"=== Ajout de bruit aux images d'apprentissage ===\\n\")\n", - "\n", - "# a. Créer un vecteur de bruit\n", - "noise_test = np.random.randint(0, 100, (len(X_test), 784))\n", - "\n", - "# b. Ajouter ce bruit avec une simple addition à la base de test\n", - "print(f\"\\n Addition du bruit à la base de test...\")\n", - "\n", - "# Effectuer l'addition\n", - "X_test_noisy = X_test.values + noise_test # Utiliser .values pour obtenir numpy array\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "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.13.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 }