TP-intelligence-artificiell.../IAM2022_TP_Autoencodeurs_Sujet.ipynb
2023-06-23 19:39:56 +02:00

992 lines
131 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "XMMppWbnG3dN"
},
"source": [
"## Auto-encodeurs\n",
"\n",
"L'objectif de ce TP est de manipuler des auto-encodeurs sur un exemple simple : la base de données MNIST. L'idée est de pouvoir visualiser les concepts vus en cours, et notamment d'illustrer la notion d'espace latent.\n",
"\n",
"Pour rappel, vous avez déjà manipulé les données MNIST en Analyse de Données en première année. Les images MNIST sont des images en niveaux de gris, de taille 28x28 pixels, représentant des chiffres manuscrits de 0 à 9.\n",
"\n",
"![mnist](http://i.ytimg.com/vi/0QI3xgXuB-Q/hqdefault.jpg)\n",
"\n",
"Pour démarrer, nous vous fournissons un code permettant de créer un premier auto-encodeur simple, de charger les données MNIST et d'entraîner cet auto-encodeur. **L'autoencodeur n'est pas convolutif !** (libre à vous de le transformer pour qu'il le soit, plus tard dans le TP)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"id": "nndrdDrlSkho"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model_11\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_7 (InputLayer) [(None, 784)] 0 \n",
" \n",
" model_9 (Functional) (None, 32) 104608 \n",
" \n",
" model_10 (Functional) (None, 784) 105360 \n",
" \n",
"=================================================================\n",
"Total params: 209,968\n",
"Trainable params: 209,968\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"Model: \"model_9\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_7 (InputLayer) [(None, 784)] 0 \n",
" \n",
" dense_12 (Dense) (None, 128) 100480 \n",
" \n",
" dense_13 (Dense) (None, 32) 4128 \n",
" \n",
"=================================================================\n",
"Total params: 104,608\n",
"Trainable params: 104,608\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"None\n",
"Model: \"model_10\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_8 (InputLayer) [(None, 32)] 0 \n",
" \n",
" dense_14 (Dense) (None, 128) 4224 \n",
" \n",
" dense_15 (Dense) (None, 784) 101136 \n",
" \n",
"=================================================================\n",
"Total params: 105,360\n",
"Trainable params: 105,360\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"None\n"
]
}
],
"source": [
"from keras.layers import Input, Dense\n",
"from keras.models import Model\n",
"\n",
"# Dimension de l'entrée\n",
"input_img = Input(shape=(784,))\n",
"# Dimension de l'espace latent : PARAMETRE A TESTER !!\n",
"latent_dim = 32\n",
"\n",
"# Définition d'un encodeur\n",
"x = Dense(128, activation='relu')(input_img)\n",
"encoded = Dense(latent_dim, activation='linear')(x)\n",
"\n",
"# Définition d'un decodeur\n",
"decoder_input = Input(shape=(latent_dim,))\n",
"x = Dense(128, activation='relu')(decoder_input)\n",
"decoded = Dense(784, activation='sigmoid')(x)\n",
"\n",
"# Construction d'un modèle séparé pour pouvoir accéder aux décodeur et encodeur\n",
"encoder = Model(input_img, encoded)\n",
"decoder = Model(decoder_input, decoded)\n",
"\n",
"# Construction du modèle de l'auto-encodeur\n",
"encoded = encoder(input_img)\n",
"decoded = decoder(encoded)\n",
"autoencoder = Model(input_img, decoded)\n",
"\n",
"autoencoder.compile(optimizer='Adam', loss='SparseCategoricalCrossentropy')\n",
"autoencoder.summary()\n",
"print(encoder.summary())\n",
"print(decoder.summary())"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"id": "jwYr0GHCSlIq"
},
"outputs": [],
"source": [
"from keras.datasets import mnist\n",
"import numpy as np\n",
"\n",
"# Chargement et normalisation (entre 0 et 1) des données de la base de données MNIST\n",
"(x_train, _), (x_test, _) = mnist.load_data()\n",
"\n",
"x_train = x_train.astype('float32') / 255.\n",
"x_test = x_test.astype('float32') / 255.\n",
"\n",
"# Vectorisation des images d'entrée en vecteurs de dimension 784\n",
"x_train = np.reshape(x_train, (len(x_train), 784))\n",
"x_test = np.reshape(x_test, (len(x_test), 784))"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"id": "eKPiA41bSvxC"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/50\n",
"469/469 [==============================] - 3s 6ms/step - loss: 0.0784 - val_loss: 0.0781\n",
"Epoch 2/50\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0783 - val_loss: 0.0780\n",
"Epoch 3/50\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0783 - val_loss: 0.0781\n",
"Epoch 4/50\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0783 - val_loss: 0.0780\n",
"Epoch 5/50\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0782 - val_loss: 0.0779\n",
"Epoch 6/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0782 - val_loss: 0.0780\n",
"Epoch 7/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0781 - val_loss: 0.0779\n",
"Epoch 8/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0781 - val_loss: 0.0778\n",
"Epoch 9/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0781 - val_loss: 0.0778\n",
"Epoch 10/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0780 - val_loss: 0.0778\n",
"Epoch 11/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0780 - val_loss: 0.0777\n",
"Epoch 12/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0780 - val_loss: 0.0777\n",
"Epoch 13/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0779 - val_loss: 0.0777\n",
"Epoch 14/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0779 - val_loss: 0.0777\n",
"Epoch 15/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0779 - val_loss: 0.0775\n",
"Epoch 16/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0778 - val_loss: 0.0775\n",
"Epoch 17/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0778 - val_loss: 0.0776\n",
"Epoch 18/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0778 - val_loss: 0.0776\n",
"Epoch 19/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0777 - val_loss: 0.0775\n",
"Epoch 20/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0777 - val_loss: 0.0775\n",
"Epoch 21/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0777 - val_loss: 0.0775\n",
"Epoch 22/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0776 - val_loss: 0.0775\n",
"Epoch 23/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0776 - val_loss: 0.0774\n",
"Epoch 24/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0776 - val_loss: 0.0774\n",
"Epoch 25/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0776 - val_loss: 0.0774\n",
"Epoch 26/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0775 - val_loss: 0.0774\n",
"Epoch 27/50\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0775 - val_loss: 0.0774\n",
"Epoch 28/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0775 - val_loss: 0.0773\n",
"Epoch 29/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0775 - val_loss: 0.0773\n",
"Epoch 30/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0774 - val_loss: 0.0773\n",
"Epoch 31/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0774 - val_loss: 0.0772\n",
"Epoch 32/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0774 - val_loss: 0.0774\n",
"Epoch 33/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0774 - val_loss: 0.0773\n",
"Epoch 34/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0774 - val_loss: 0.0772\n",
"Epoch 35/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0774 - val_loss: 0.0772\n",
"Epoch 36/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0774\n",
"Epoch 37/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0771\n",
"Epoch 38/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0772\n",
"Epoch 39/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0772\n",
"Epoch 40/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0772\n",
"Epoch 41/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0771\n",
"Epoch 42/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0773 - val_loss: 0.0772\n",
"Epoch 43/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0772 - val_loss: 0.0771\n",
"Epoch 44/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0772 - val_loss: 0.0771\n",
"Epoch 45/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0772 - val_loss: 0.0770\n",
"Epoch 46/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0772 - val_loss: 0.0771\n",
"Epoch 47/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0772 - val_loss: 0.0771\n",
"Epoch 48/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0771 - val_loss: 0.0771\n",
"Epoch 49/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0771 - val_loss: 0.0771\n",
"Epoch 50/50\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0771 - val_loss: 0.0770\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x7f5274368700>"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Entraînement de l'auto-encodeur. On utilise ici les données de test \n",
"# pour surveiller l'évolution de l'erreur de reconstruction sur des données \n",
"# non utilisées pendant l'entraînement et ainsi détecter le sur-apprentissage.\n",
"autoencoder.fit(x_train, x_train,\n",
" epochs=50,\n",
" batch_size=128,\n",
" shuffle=True,\n",
" validation_data=(x_test, x_test))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ohexDvCYrahC"
},
"source": [
"Le code suivant affiche des exemples d'images de la base de test (1e ligne) et de leur reconstruction (2e ligne)."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"id": "2SC9R1TRTUgN"
},
"outputs": [
{
"data": {
"image/png": "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AAAAgADioQ0AAAAAAEAAUR7lnLvkkkvMtj9L+d/0TObOObdo0aJ47VLSe/fddyUXKFAgbL/Ro0dLTrVVY5JJw4YNJefPn9+0TZ06VbJelQGx4698p+mhp/Gmh/z7+xRpH/v06SP5pptuivl+BYm/osnZZ58teezYsYneHVG2bNmQ/87nYOJFKsOIxcpF+MvcuXPNdpUqVSRXq1bNtDVq1EiyXhVl69atpt+IESOi+t16NZIFCxaE7TdjxgzJ3CNljn891aVsugTRL8HQK2C2atVKsr/ajD4X/bY77rhDsj7WS5YsiWbXU4JfCqPp8+3xxx83bR9++KFkVswLji+++MJs61Jq/R3BOedKlCgh+aWXXpIcqVRUl1v5pViRhCuJOnLkiNl+//33Jd93332mbePGjVH/vuPBSBsAAAAAAIAA4qENAAAAAABAAPHQBgAAAAAAIICY08Y516RJE7N90kknSf78888lf//99wnbp2Sk64WrV68ett/06dMl+7WqyJmqVq0q2a9JnTBhQqJ3JyXcddddkv3a3OzSvHlzyRdeeKFp0/vo76+e0ybZ7dmzx2zrmnw9p4Zzdn6o7du3x3Q/ChcubLbDzS/w7bffxvT3IrRLL71Ucvv27cP227Vrl2SWwo2tHTt2SPaXttfbDz744HH/rjJlykjWc4E5Z68JPXr0OO7flao+++wzs63PHT1vjT/PTLh5NfzX69Kli+SPP/7YtJ177rmS9fwY+nM71RUqVEiyf0+g53577LHHTFvv3r0lDx48WLJeZt05O2/KypUrJS9evDjsPlWsWNFs6++FXG8j85fh1vNB5c2b17TpuWX1vLO//fab6bd27VrJ+j2hv3M451ytWrUyvb9Dhw4124888ohkPV9VIjHSBgAAAAAAIIB4aAMAAAAAABBAKVselTt3bsl66TjnnPvzzz8l6/KcgwcPxn/Hkoi/lLceWqZL0Hx66O/evXtjvl9IjLPOOkvyZZddJnn58uWmn15GD7GjS5ESSQ9pds65Cy64QLK+BkTiL5ObStdefwixXsa3devWpm3SpEmSBwwYkOnfValSJbOtSzJKlSpl2sKVBASl9C7Z6c/TE04I///bPv3000TsDuJMl3z4554uv/KvlYieX1J63XXXSdZl2+np6WFfY9CgQZL9srj9+/dLfu+990ybLv+4+uqrJZctW9b0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AARQysxp4y/3peu+zzjjDNOml05dsGBBfHcsiemaXedsDaquW/Xnr9B/80jLx+q2SEsOI3vo5fz0+afn3nDu6DluwtHHmDmLjubX+uoa8EjnRyyWaNbzMfzjH/8wbbrO++yzzw67T7peuH379qYtlerB/fOhe/fukt944w3TVrp0acl169YNmZ2zf1s9P5A/X4Ku69d1/M7ZeTX0HAKFCxc2/VauXOlw/Px7lj59+kjW57o/v9Bzzz0nmetksOlrYM2aNU1bw4YNJfvHWN+jcoyzzv/s69Gjh2R9n3LXXXeZfnq+DL3Mt3891cdGz9XonJ3zT39mDh061PS78cYbJafCXGL6869169aS9ZyYzjl3zTXXSD7vvPNMm57HRn9W+fcc+jqq50M59dRTTb9I909FixaVrOeYa9Wqlen33XffSU6l+5lY0OdppPtVPfeNvjdyzh5D/++vz+HXX39dchDnhGOkDQAAAAAAQADx0AYAAAAAACCAkro8Sg+H6tixo2nTQ6f0stLO2SXAU2E4Yizpv/nDDz9s2vTQNT08zV9yPdolLPXr+cMXDx8+HNVraH55iS7n0kPV/d+ll1/1S730MqDJzv+7XHnllZL18Rg7dqzpF+3wboaBR+YPG82XL59k/9gUKVJE8mWXXSZZl406Z4+bfg29pKZzznXu3FnyHXfcYdr0UtGRStzeeecdyTNmzHD4ix6mf9VVV5m2kiVLStblFf7x1n/rn376SbJfyqSXAPeXzKxTp07I/dPHF7Hjl6f559zfVqxYYbb18u4INl3Cetttt5k2ff8xdepU08Yxjg99XzpkyBDJw4cPN/10yc2+ffsk+59pettf8vupp56SrD+DdVmcc/bz9JVXXjFtsShtDhr9N9Pv87ffftv0e/fddyUXKlTItOnSqa5du0r2lwbXpVhnnnlm2NfTpcH+Z6ve1lNt6KWjnXOuU6dOkjds2GDakvE4Joo+bt98841k/75Ev68WL15s2p544gnJQSyJ0hhpAwAAAAAAEEA8tAEAAAAAAAigpC6PKlCggOT+/fubNj0czR/6OHv27PjuWBLTw33z5s0btp8egqaHHjpnZ2PXJVB65RnnbPmHP6RNlxTo8jf/d1WqVEmyX9ahS0h27twpWa+U45wd9vr000+btkWLFrlUoYdzO+dc1apVJesyMUpf4kOvEOScLdUr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",
"text/plain": [
"<Figure size 1440x288 with 20 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"# Prédiction des données de test\n",
"decoded_imgs = autoencoder.predict(x_test)\n",
"\n",
"\n",
"n = 10\n",
"plt.figure(figsize=(20, 4))\n",
"for i in range(n):\n",
" # Affichage de l'image originale\n",
" ax = plt.subplot(2, n, i+1)\n",
" plt.imshow(x_test[i].reshape(28, 28))\n",
" plt.gray()\n",
" ax.get_xaxis().set_visible(False)\n",
" ax.get_yaxis().set_visible(False)\n",
"\n",
" # Affichage de l'image reconstruite par l'auto-encodeur\n",
" ax = plt.subplot(2, n, i+1 + n)\n",
" plt.imshow(decoded_imgs[i].reshape(28, 28))\n",
" plt.gray()\n",
" ax.get_xaxis().set_visible(False)\n",
" ax.get_yaxis().set_visible(False)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4oV4eKk4p4Eg"
},
"source": [
"# Travail à faire"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "zjMnHWNtrgwZ"
},
"source": [
"Travail à faire\n",
"\n",
"1. Pour commencer, observez les résultats obtenus avec le code fourni. Les résultats semblent imparfaits, les images reconstruites sont bruitées. Modifiez le code fourni pour transformer le problème de régression en classification binaire. Les résultats devraient être bien meilleurs ! Conservez cette formulation (même si elle est non standard) pour la suite.\n",
"2. Avec la dimension d'espace latent qui vous est fournie, on observe une (relativement) faible erreur de reconstruction. Tracez une **courbe** (avec seulement quelques points) qui montre l'évolution de l'**erreur de reconstruction en fonction de la dimension de l'espace latent**. Quelle semble être la dimension minimale de l'espace latent qui permet encore d'observer une reconstruction raisonnable des données (avec le réseau qui vous est fourni) ?\n",
"3. Pour diminuer encore plus la dimension de l'espace latent, il est nécessaire d'augmenter la capacité des réseaux encodeur et décodeur. Cherchez à nouveau la dimension minimale de l'espace latent qui permet d'observer une bonne reconstruction des données, mais en augmentant à l'envi la capacité de votre auto-encodeur.\n",
"4. Écrivez une fonction qui, étant donné deux images de votre espace de test $I_1$ et $I_2$, réalise l'interpolation (avec, par exemple, 10 étapes) entre la représentation latente ($z_1 = $encoder($I_1$) et $z_2 = $encoder($I_2$)) de ces deux données, et génère les images $I_i$ correspondant aux représentations latentes intermédiaires $z_i$. En pseudo python, cela donne : \n",
"\n",
"```python\n",
"for i in range(10):\n",
" z_i = z1 + i*(z2-z1)/10\n",
" I_i = decoder(z_i)\n",
"```\n",
"Testez cette fonction avec un auto-encodeur avec une faible erreur de reconstruction, sur deux données présentant le même chiffre écrit différemment, puis deux chiffres différents.\n",
"5. Pour finir, le code qui vous est fourni dans la suite permet de télécharger et de préparer une [base de données de visages](http://vis-www.cs.umass.edu/lfw/). ATTENTION : ici les images sont de taille $32\\times32$, en couleur, et comportent donc 3 canaux (contrairement aux images de MNIST, qui n'en comptent qu'un). Par analogie avec la question précédente, on pourrait grâce à la représentation latente apprise par un auto-encodeur, réaliser un morphing entre deux visages. Essayez d'abord d'entraîner un auto-encodeur à obtenir une erreur de reconstruction faible. Qu'observe-t-on ?\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/10\n",
"469/469 [==============================] - 3s 6ms/step - loss: 0.2008 - val_loss: 0.1536\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.1478 - val_loss: 0.1415\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.1400 - val_loss: 0.1366\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.1360 - val_loss: 0.1335\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.1333 - val_loss: 0.1311\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1312 - val_loss: 0.1292\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1295 - val_loss: 0.1278\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1282 - val_loss: 0.1269\n",
"Epoch 9/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1272 - val_loss: 0.1258\n",
"Epoch 10/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1263 - val_loss: 0.1252\n",
"Epoch 1/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1883 - val_loss: 0.1332\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1244 - val_loss: 0.1169\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1158 - val_loss: 0.1118\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1117 - val_loss: 0.1087\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1089 - val_loss: 0.1065\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1069 - val_loss: 0.1049\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1053 - val_loss: 0.1036\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1040 - val_loss: 0.1023\n",
"Epoch 9/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1029 - val_loss: 0.1014\n",
"Epoch 10/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1020 - val_loss: 0.1008\n",
"Epoch 1/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1781 - val_loss: 0.1176\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1092 - val_loss: 0.1010\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0992 - val_loss: 0.0951\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0944 - val_loss: 0.0917\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0916 - val_loss: 0.0894\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0895 - val_loss: 0.0878\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0880 - val_loss: 0.0865\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0868 - val_loss: 0.0854\n",
"Epoch 9/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0858 - val_loss: 0.0845\n",
"Epoch 10/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0850 - val_loss: 0.0840\n",
"Epoch 1/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1735 - val_loss: 0.1119\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1029 - val_loss: 0.0943\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0923 - val_loss: 0.0885\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0872 - val_loss: 0.0844\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0839 - val_loss: 0.0817\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0818 - val_loss: 0.0799\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0802 - val_loss: 0.0789\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0789 - val_loss: 0.0774\n",
"Epoch 9/10\n",
"469/469 [==============================] - 3s 5ms/step - loss: 0.0777 - val_loss: 0.0766\n",
"Epoch 10/10\n",
"469/469 [==============================] - 3s 5ms/step - loss: 0.0768 - val_loss: 0.0757\n",
"Epoch 1/10\n",
"469/469 [==============================] - 3s 5ms/step - loss: 0.1649 - val_loss: 0.1054\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0978 - val_loss: 0.0902\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0876 - val_loss: 0.0835\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0829 - val_loss: 0.0801\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0799 - val_loss: 0.0779\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0779 - val_loss: 0.0765\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0763 - val_loss: 0.0751\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0752 - val_loss: 0.0745\n",
"Epoch 9/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0743 - val_loss: 0.0734\n",
"Epoch 10/10\n",
"469/469 [==============================] - 2s 5ms/step - loss: 0.0734 - val_loss: 0.0726\n",
"Epoch 1/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1591 - val_loss: 0.1011\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0938 - val_loss: 0.0874\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0848 - val_loss: 0.0814\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0804 - val_loss: 0.0779\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0777 - val_loss: 0.0759\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0758 - val_loss: 0.0743\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0744 - val_loss: 0.0731\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0732 - val_loss: 0.0721\n",
"Epoch 9/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0723 - val_loss: 0.0713\n",
"Epoch 10/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0716 - val_loss: 0.0710\n",
"Epoch 1/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.1573 - val_loss: 0.0999\n",
"Epoch 2/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0931 - val_loss: 0.0858\n",
"Epoch 3/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0840 - val_loss: 0.0804\n",
"Epoch 4/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0796 - val_loss: 0.0772\n",
"Epoch 5/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0767 - val_loss: 0.0748\n",
"Epoch 6/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0749 - val_loss: 0.0734\n",
"Epoch 7/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0735 - val_loss: 0.0724\n",
"Epoch 8/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0725 - val_loss: 0.0716\n",
"Epoch 9/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0717 - val_loss: 0.0711\n",
"Epoch 10/10\n",
"469/469 [==============================] - 2s 4ms/step - loss: 0.0711 - val_loss: 0.0704\n"
]
}
],
"source": [
"from keras.layers import Input, Dense\n",
"from keras.models import Model\n",
"\n",
"# Dimension de l'entrée\n",
"input_img = Input(shape=(784,))\n",
"\n",
"errors = []\n",
"latent_dims = [8, 16, 32, 64, 128, 256, 512]\n",
"for latent_dim in latent_dims:\n",
" # Définition d'un encodeur\n",
" x = Dense(128, activation='relu')(input_img)\n",
" encoded = Dense(latent_dim, activation='linear')(x)\n",
"\n",
" # Définition d'un decodeur\n",
" decoder_input = Input(shape=(latent_dim,))\n",
" x = Dense(128, activation='relu')(decoder_input)\n",
" decoded = Dense(784, activation='sigmoid')(x)\n",
"\n",
" # Construction d'un modèle séparé pour pouvoir accéder aux décodeur et encodeur\n",
" encoder = Model(input_img, encoded)\n",
" decoder = Model(decoder_input, decoded)\n",
"\n",
" # Construction du modèle de l'auto-encodeur\n",
" encoded = encoder(input_img)\n",
" decoded = decoder(encoded)\n",
" autoencoder = Model(input_img, decoded)\n",
"\n",
" autoencoder.compile(optimizer='Adam', loss='bce')\n",
"\n",
" autoencoder.fit(\n",
" x_train, x_train,\n",
" epochs=10,\n",
" batch_size=128,\n",
" shuffle=True,\n",
" validation_data=(x_test, x_test)\n",
" )\n",
"\n",
" errors.append(autoencoder.history.history[\"val_loss\"][-1])"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7f521837f280>]"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABIcAAAI/CAYAAADtOLm5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA/n0lEQVR4nO3deXhd93kf+O8PwL0ELhdcUKI2EpTkWJYt2yIkUY4zSdM4q9JkrLS1EzuLtzROn8adtGkz4y6TtG4zmbTNpGnjSe3EW5zFcd2k0ZO4cd0403RNREmUZMmbrFgkJdmiRYCiuAM48wcuSAAERUgk7rnA/Xyehw/uOffcgxeyjw19+f7eX6mqKgAAAAD0p4G6CwAAAACgPsIhAAAAgD4mHAIAAADoY8IhAAAAgD4mHAIAAADoY8IhAAAAgD42VHcBS11++eXVddddV3cZAAAAAOvGPffc89WqqrYt917PhUPXXXdd9uzZU3cZAAAAAOtGKeWx871nWRkAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4RAAAABAHxMOAQAAAPQx4dAq+dd/9IV8z7v/W91lAAAAADwn4dAqefbUdB5+4plUVVV3KQAAAADnJRxaJWOtZk7NzObYqZm6SwEAAAA4L+HQKhlrNZIkU8dP11wJAAAAwPkJh1bJ6EgzSTJ59FTNlQAAAACcn3BolZzpHDqmcwgAAADoXcKhVTK2sdM5dEznEAAAANC7hEOrpG3mEAAAALAGCIdWSbszc2jKzCEAAACghwmHVklzaCAbm4OZNHMIAAAA6GHCoVXUbjUzdVznEAAAANC7hEOrqN1q2K0MAAAA6GnCoVU01mrarQwAAADoacKhVaRzCAAAAOh1wqFVNNZqZkrnEAAAANDDhEOrqN1q5PDx05mdreouBQAAAGBZwqFV1G41M1slz5ywtAwAAADoTcKhVTTWaiRJJs0dAgAAAHqUcGgVtTvhkLlDAAAAQK8SDq2idquZJHYsAwAAAHqWcGgVjXXCoUmdQwAAAECPEg6torEzy8p0DgEAAAC9STi0ijYPN1KKmUMAAABA7xIOraLBgZLRkYbdygAAAICeJRxaZWOtpplDAAAAQM8SDq2y0ZFGDh/XOQQAAAD0JuHQKhtrNXQOAQAAAD1LOLTKxlrNTB7VOQQAAAD0JuHQKmu3mpaVAQAAAD1LOLTK2q1Gnj05nVPTs3WXAgAAAHAO4dAqG2s1kiRTx80dAgAAAHqPcGiVtVvNJMnUMUvLAAAAgN4jHFpl7fnOIeEQAAAA0IOEQ6tsrNM5ZDt7AAAAoBcJh1bZ2c4h4RAAAADQe4RDq+xs55BlZQAAAEDvEQ6tslZzMI3BYuYQAAAA0JOEQ6uslJJ2q2lZGQAAANCThENdMNZqGEgNAAAA9CThUBe0R5qWlQEAAAA9STjUBe1WQzgEAAAA9CThUBeMtZqWlQEAAAA9STjUBe2Nc51DVVXVXQoAAADAIsKhLmiPNHNqZjbHT8/UXQoAAADAIisKh0opd5RSPldKeaSU8s5l3v/GUsq9pZTpUsrrFpyfKKX8j1LKQ6WUB0op33cpi18rxlqNJMmkuUMAAABAj7lgOFRKGUzy7iTfmeSmJG8spdy05LJ9Sd6S5DeXnD+W5E1VVb08yR1J/mUppX2RNa857VYzSTJ51NwhAAAAoLcMreCaVyV5pKqqR5OklPKRJHcmeXj+gqqqvtR5b3bhB6uq+vyC10+UUp5Ksi3J1MUWvpbMdw4dPq5zCAAAAOgtK1lWtj3J/gXHBzrnnpdSyquSNJN88fl+dq070zlkxzIAAACgx3RlIHUp5eokH07y1qqqZpd5/+2llD2llD0HDx7sRkldZeYQAAAA0KtWEg49nmR8wfGOzrkVKaVsSfIHSf5BVVX/c7lrqqp6b1VVu6uq2r1t27aV3nrNGO2EQ1NmDgEAAAA9ZiXh0N1JbiilXF9KaSZ5Q5K7VnLzzvW/m+TXqqr62Asvc23bMDSYVnMwU2YOAQAAAD3mguFQVVXTSd6R5BNJPpPko1VVPVRKeVcp5bVJUkq5vZRyIMnrk7ynlPJQ5+Pfm+Qbk7yllLK382diNX6QXjfWapo5BAAAAPSclexWlqqqPp7k40vO/dSC13dnbrnZ0s/9epJfv8ga14V2q5EpM4cAAACAHtOVgdToHAIAAAB6k3CoS0ZbjRzWOQQAAAD0GOFQl4y1GjqHAAAAgJ4jHOqSsVYzh4+fzuxsVXcpAAAAAGcIh7pkdKSR2So5cmK67lIAAAAAzhAOdclYq5kklpYBAAAAPUU41CVjGxtJhEMAAABAbxEOdUm70zk0ZccyAAAAoIcIh7qkPTLXOTR1XOcQAAAA0DuEQ11yZubQUZ1DAAAAQO8QDnXJlpFGSkmmzBwCAAAAeohwqEsGB0q2DDcydVznEAAAANA7hENdNNZqZNJAagAAAKCHCIe6qN1qWlYGAAAA9BThUBfNdQ4JhwAAAIDeIRzqornOIcvKAAAAgN4hHOqidqshHAIAAAB6inCoi8ZazTx7cjqnpmfrLgUAAAAgiXCoq9qtRpJk6ri5QwAAAEBvEA51UbvVTJIctrQMAAAA6BHCoS4a63QOTQqHAAAAgB4hHOqisU7nkO3sAQAAgF4hHOqi0ZG5ziHLygAAAIBeIRzqorGNOocAAACA3iIc6qKNzcE0BouZQwAAAEDPEA51USkloyPNTOkcAgAAAHqEcKjLxlqNTOkcAgAAAHqEcKjLxlpNM4cAAACAniEc6rK2ziEAAACghwiHuqzdamTquM4hAAAAoDcIh7psblnZ6VRVVXcpAAAAAMKhbmu3mjk1PZvjp2fqLgUAAABAONRtY61GkmTS3CEAAACgBwiHuqzdCYem7FgGAAAA9ADhUJe1W80ksWMZAAAA0BOEQ1021gmHJnUOAQAAAD1AONRlbTOHAAAAgB4iHOqy+XDosM4hAAAAoAcIh7psw9BgWs1BnUMAAABATxAO1WCs1TRzCAAAAOgJwqEajI40cljnEAAAANADhEM1GNvY0DkEAAAA9AThUA3arWamdA4BAAAAPUA4VIP2iM4hAAAAoDcIh2ow1mrm8PHTmZ2t6i4FAAAA6HPCoRq0W43MVsmRE9N1lwIAAAD0OeFQDcZazSSxtAwAAAConXCoBu1WI0kyddxQagAAAKBewqEatHUOAQAAAD1COFSDsfnOIeEQAAAAUDPhUA3OdA4dtawMAAAAqJdwqAajI42UYuYQAAAAUD/hUA0GB0q2DDcsKwMAAABqJxyqyVirkcljOocAAACAegmHajLaauocAgAAAGonHKrJWKuRKZ1DAAAAQM2EQzUZazUzqXMIAAAAqJlwqCajIzqHAAAAgPoJh2oy1mrm2ZPTOT0zW3cpAAAAQB8TDtVkbGMjSXQPAQAAALUSDtWk3WomiR3LAAAAgFoJh2rSHpnrHJrUOQQAAADUSDhUkzGdQwAAAEAPEA7VpN0ycwgAAACon3CoJvPh0KTOIQAAAKBGwqGabNowlKGBkqnjOocAAACA+giHalJKSbvVNHMIAAAAqJVwqEZjrUYmj+ocAgAAAOojHKpRu9UwcwgAAAColXCoRu1WM4fNHAIAAABqJByq0ZjOIQAAAKBmwqEatVvNTB47naqq6i4FAAAA6FPCoRq1W42cmp7N8dMzdZcCAAAA9CnhUI3GWs0kydQxc4cAAACAegiHajTWaiSJuUMAAABAbYRDNRod0TkEAAAA1Es4VKOxjXOdQ8IhAAAAoC7CoRrNzxyyrAwAAACoi3CoRqMj851DwiEAAACgHsKhGg03BjPSGMykZWUAAABATYRDNRtrNcwcAgAAAGqzonColHJHKeVzpZRHSinvXOb9byyl3FtKmS6lvG7Je39YSpkqpfz+pSp6PWm3mpaVAQAAALW5YDhUShlM8u4k35nkpiRvLKXctOSyfUnekuQ3l7nFP0/yQxdX5vrVbjUMpAYAAABqs5LOoVcleaSqqkerqjqV5CNJ7lx4QVVVX6qq6oEks0s/XFXVHyU5cimKXY/GWs1MHbesDAAAAKjHSsKh7Un2Lzg+0DnHJdA2cwgAAACoUU8MpC6lvL2UsqeUsufgwYN1l9NVY52ZQ7OzVd2lAAAAAH1oJeHQ40nGFxzv6Jy7ZKqqem9VVburqtq9bdu2S3nrntduNTJbJUdOTNddCgAAANCHVhIO3Z3khlLK9aWUZpI3JLlrdcvqH+1WM0kyddxQagAAAKD7LhgOVVU1neQdST6R5DNJPlpV1UOllHeVUl6bJKWU20spB5K8Psl7SikPzX++lPJfkvzbJN9SSjlQSvmO1fhB1qqxViNJMmnuEAAAAFCDoZVcVFXVx5N8fMm5n1rw+u7MLTdb7rN/4WIKXO/aZ8IhnUMAAABA9/XEQOp+dmZZmXAIAAAAqIFwqGZjZ8Ihy8oAAACA7hMO1Wx0xMwhAAAAoD7CoZoNDpRsGR6yrAwAAACohXCoB4xtbFpWBgAAANRCONQD2q2m3coAAACAWgiHekB7pKFzCAAAAKiFcKgHjLUaOocAAACAWgiHekC71cxhnUMAAABADYRDPWCs1cyRk9M5PTNbdykAAABAnxEO9YB2q5Ek5g4BAAAAXScc6gHz4dDh4+YOAQAAAN0lHOoBY61mkmRS5xAAAADQZcKhHjDfOTR5VOcQAAAA0F3CoR4w3zlk5hAAAADQbcKhHnBmILWZQwAAAECXCYd6wKYNQxkaKGYOAQAAAF0nHOoBpZS0W41MHdM5BAAAAHSXcKhHtFvNTB7VOQQAAAB0l3CoR4y1GmYOAQAAAF0nHOoRoyNNu5UBAAAAXScc6hFjrUYmzRwCAAAAukw41CPGNuocAgAAALpPONQj2q1GTk7P5vipmbpLAQAAAPqIcKhHtEeaSWJpGQAAANBVwqEeMdZqJBEOAQAAAN0lHOoR7dZc59Bhc4cAAACALhIO9YixjfOdQ8IhAAAAoHuEQz3CzCEAAACgDsKhHtHuzBw6fFznEAAAANA9wqEeMdwYzEhjMJNHdQ4BAAAA3SMc6iHtVsPMIQAAAKCrhEM9pN1qZsrMIQAAAKCLhEM9ZKzVyJSZQwAAAEAXCYd6yFirabcyAAAAoKuEQz1ktNXIlJlDAAAAQBcJh3rIWKuRqWOnMjtb1V0KAAAA0CeEQz1krNXMbJUcOTlddykAAABAnxAO9ZDRkUaS2LEMAAAA6BrhUA8ZazWTJJPmDgEAAABdIhzqIWMbdQ4BAAAA3SUc6iHtTueQHcsAAACAbhEO9ZB2Z+bQpM4hAAAAoEuEQz1k9Ew4pHMIAAAA6A7hUA8ZGhzIluGhHNY5BAAAAHSJcKjHtFtNnUMAAABA1wiHesxYq2HmEAAAANA1wqEe0241c/i4ziEAAACgO4RDPUbnEAAAANBNwqEe0241M3VU5xAAAADQHcKhHtNuNXLk5HROz8zWXQoAAADQB4RDPWas1UwSc4cAAACArhAO9Zh2q5EkmTJ3CAAAAOgC4VCPaXc6hyaP6RwCAAAAVp9wqMeMdTqHJo/qHAIAAABWn3Cox8zPHJoycwgAAADoAuFQjxk1cwgAAADoIuFQj9m8YShDA8XMIQAAAKArhEM9ppSSdquRKeEQAAAA0AXCoR40OtKwrAwAAADoCuFQDxprNTMpHAIAAAC6QDjUg9qtpmVlAAAAQFcIh3rQmJlDAAAAQJcIh3pQu9WwrAwAAADoCuFQD2q3mjk5PZvjp2bqLgUAAABY54RDPWis1UySTB3XPQQAAACsLuFQDxprNZIkk0fNHQIAAABWl3CoB412wqEpc4cAAACAVSYc6kHzy8om7VgGAAAArDLhUA8ycwgAAADoFuFQD2qfWVamcwgAAABYXcKhHjTcGMxwYyCTR3UOAQAAAKtLONSjxlpNM4cAAACAVScc6lHtVjOHzRwCAAAAVplwqEe1Rxo6hwAAAIBVJxzqUWMbG5k8pnMIAAAAWF3CoR7VbjVzWOcQAAAAsMqEQz2qPdLI1PHTqaqq7lIAAACAdUw41KOuaY9kZrbKgcnjdZcCAAAArGPCoR41Md5Okty3f6rWOgAAAID1TTjUo268anM2DA1k776puksBAAAA1rEVhUOllDtKKZ8rpTxSSnnnMu9/Yynl3lLKdCnldUvee3Mp5QudP2++VIWvd43Bgbxi+2juPzBVdykAAADAOnbBcKiUMpjk3Um+M8lNSd5YSrlpyWX7krwlyW8u+ezWJD+d5GuTvCrJT5dSxi6+7P4wMd7Opx8/nNMzs3WXAgAAAKxTK+kcelWSR6qqerSqqlNJPpLkzoUXVFX1paqqHkiyNMX4jiSfrKrqUFVVk0k+meSOS1B3X9g13s7J6dl89skjdZcCAAAArFMrCYe2J9m/4PhA59xKXMxn+94tnaHUey0tAwAAAFZJTwykLqW8vZSyp5Sy5+DBg3WX0zN2jI3kso1NQ6kBAACAVbOScOjxJOMLjnd0zq3Eij5bVdV7q6raXVXV7m3btq3w1utfKSUT4+3s3T9ZdykAAADAOrWScOjuJDeUUq4vpTSTvCHJXSu8/yeSfHspZawziPrbO+dYoV3j7Xzx4NEcPn667lIAAACAdeiC4VBVVdNJ3pG5UOczST5aVdVDpZR3lVJemySllNtLKQeSvD7Je0opD3U+eyjJP8lcwHR3knd1zrFCE525Qw8eOFxvIQAAAMC6NLSSi6qq+niSjy8591MLXt+duSVjy332/UnefxE19rVdO9pJkr37J/MNN1xebzEAAADAutMTA6k5v9FWIy+6fGP27p+quxQAAABgHRIOrQFzQ6kPp6qquksBAAAA1hnh0BowsbOdrz57Mo9PHa+7FAAAAGCdEQ6tAfNDqS0tAwAAAC414dAa8NKrtqQ5NJC9+6bqLgUAAABYZ4RDa0BzaCAvv2ZL7j8wVXcpAAAAwDojHFojJsbbefDxwzk9M1t3KQAAAMA6IhxaIybG2zlxejaf+/KRuksBAAAA1hHh0BoxP5Ta0jIAAADgUhIOrRE7t7aydWPTUGoAAADgkhIOrRGllOzaMWo7ewAAAOCSEg6tIbvG23nk4LM5cuJ03aUAAAAA64RwaA2ZGG+nqpIHDxyuuxQAAABgnRAOrSHzQ6nvs7QMAAAAuESEQ2tIu9XMdZe1zB0CAAAALhnh0BozMd7O3v1Tqaqq7lIAAACAdUA4tMZMjLdz8MjJPHn4RN2lAAAAAOuAcGiN2dWZO2RpGQAAAHApCIfWmJuu2ZLm4EDuFw4BAAAAl4BwaI3ZMDSYl12zxY5lAAAAwCUhHFqDbhlv58EDhzM9M1t3KQAAAMAaJxxag3aNj+b46Zl8/ivP1l0KAAAAsMYJh9agifGxJMn9B6bqLQQAAABY84RDa9B1l7UyOtLI3n1TdZcCAAAArHHCoTWolJJd423b2QMAAAAXTTi0Rk2Mt/P5p47k2ZPTdZcCAAAArGHCoTXqlvF2qip58MDhuksBAAAA1jDh0Bq1a7ydJJaWAQAAABdFOLRGbd3YzM6trezdP1l3KQAAAMAaJhxawybG27l/v2VlAAAAwAsnHFrDJsbb+fIzJ/LlwyfqLgUAAABYo4RDa9jZuUOWlgEAAAAvjHBoDXv5NVvSGCzZa2kZAAAA8AIJh9aw4cZgXnb1Fp1DAAAAwAsmHFrjJsbbefDA4czMVnWXAgAAAKxBwqE1bteOdo6emskXnjpSdykAAADAGiQcWuMmdraTJPfvn6q1DgAAAGBtEg6tcddftjFbhoeyVzgEAAAAvADCoTVuYKBk13g79+2bqrsUAAAAYA0SDq0DE+PtfP4rR3Ls1HTdpQAAAABrjHBoHZgYb2e2Sh48cLjuUgAAAIA1Rji0DuwabyeJuUMAAADA8yYcWgcu37QhO8ZGhEMAAADA8yYcWicmxtu2swcAAACeN+HQOjEx3s4Th0/kqWdO1F0KAAAAsIYIh9aJic7coft0DwEAAADPg3BonXjF9tEMDRRLywAAAIDnRTi0Tgw3BvPSqzcbSg0AAAA8L8KhdWRivJ0HDhzOzGxVdykAAADAGiEcWkd27Wjn2ZPTefTgs3WXAgAAAKwRwqF15Jad7SSGUgMAAAArJxxaR150+aZs3jBk7hAAAACwYsKhdWRgoOTm8dHs3TdVdykAAADAGiEcWmcmxtv53FeO5PipmbpLAQAAANYA4dA6MzE+lpnZKp9+4nDdpQAAAABrgHBondk1PpoklpYBAAAAKyIcWmeu2Dyc7e2R7D0wVXcpAAAAwBogHFqHJsbbOocAAACAFREOrUMT4+08PnU8B4+crLsUAAAAoMcJh9ahXePtJMne/VO11gEAAAD0PuHQOvTK7aMZHCi5XzgEAAAAXIBwaB0aaQ7mxis36xwCAAAALkg4tE7tGm/n/v1TmZ2t6i4FAAAA6GHCoXXqlvF2jpyczqNfPVp3KQAAAEAPEw6tUxM720kMpQYAAACem3BonfqabZuyacNQ9u6frLsUAAAAoIcJh9apwYGSV24f1TkEAAAAPCfh0Do2sbOdzz55JCdOz9RdCgAAANCjhEPr2MR4O9OzVR564nDdpQAAAAA9Sji0jk2Mt5Mk9+2bqrUOAAAAoHcJh9axK7cM5+rR4dx/QOcQAAAAsDzh0Do3Md62YxkAAABwXsKhdW5ivJ39h47n6WdP1l0KAAAA0IOEQ+vcrs7cIVvaAwAAAMsRDq1zr9w+moGS3C8cAgAAAJYhHFrnNm4Yykuu3Jz7hEMAAADAMoRDfWBivJ37909ldraquxQAAACgxwiH+sDEeDvPnJjOl54+WncpAAAAQI8RDvWBiZ3tJIZSAwAAAOcSDvWBG67YnFZzUDgEAAAAnEM41AcGB0peuX1UOAQAAACcY0XhUCnljlLK50opj5RS3rnM+xtKKb/def9PSynXdc43SykfKKU8WEq5v5TyTZe0elZsYmc7n3nymZw4PVN3KQAAAEAPuWA4VEoZTPLuJN+Z5KYkbyyl3LTksh9OMllV1YuT/EKSn+uc/5EkqarqlUm+LcnPl1J0K9XglvF2Ts9UefjJZ+ouBQAAAOghKwlqXpXkkaqqHq2q6lSSjyS5c8k1dyb5UOf1x5J8SymlZC5M+lSSVFX1VJKpJLsvQd08T7vG20mSvfumaq0DAAAA6C0rCYe2J9m/4PhA59yy11RVNZ3kcJLLktyf5LWllKFSyvVJbksyfrFF8/xdPTqSK7dsyP0HpuouBQAAAOghQ6t8//cneVmSPUkeS/Lfk5wz9KaU8vYkb0+SnTt3rnJJ/WtivG0oNQAAALDISjqHHs/ibp8dnXPLXlNKGUoymuTpqqqmq6r621VVTVRVdWeSdpLPL/0GVVW9t6qq3VVV7d62bdsL+DFYiV3j7Tz29LEcOnqq7lIAAACAHrGScOjuJDeUUq4vpTSTvCHJXUuuuSvJmzuvX5fkU1VVVaWUVillY5KUUr4tyXRVVQ9fotp5niY6c4csLQMAAADmXXBZWVVV06WUdyT5RJLBJO+vquqhUsq7kuypququJO9L8uFSyiNJDmUuQEqSK5J8opQym7nuoh9ajR+Clbl5RzulzA2lfs2NV9RdDgAAANADVjRzqKqqjyf5+JJzP7Xg9Ykkr1/mc19KcuPFlcilsmnDUF5yxWZzhwAAAIAzVrKsjHVk1/ho7j8wlaqq6i4FAAAA6AHCoT4zMT6WqWOn89jTx+ouBQAAAOgBwqE+Mz+U2tIyAAAAIBEO9Z2XXLkpI41B4RAAAACQRDjUd4YGB/LK7aPCIQAAACCJcKgvTexs5+EnnsnJ6Zm6SwEAAABqJhzqQxPj7Zyamc1nnjxSdykAAABAzYRDfWjX/FDqfZP1FgIAAADUTjjUh64ZHc62zRty/4HDdZcCAAAA1Ew41IdKKZkYbxtKDQAAAAiH+tXEeDt//tWjmTp2qu5SAAAAgBoJh/rURGfukKVlAAAA0N+EQ33q5h2jKSXZu2+q7lIAAACAGgmH+tTm4UZevG1T9u63YxkAAAD0M+FQH9vVGUpdVVXdpQAAAAA1EQ71sYnxdiaPnc7+Q8frLgUAAACoiXCoj80Ppb7P0jIAAADoW8KhPnbjVZuzYWgge/dP1V0KAAAAUBPhUB9rDA7kldtHc79wCAAAAPqWcKjPTYy38+knnsmp6dm6SwEAAABqIBzqcxM72zk1PZvPfvmZuksBAAAAaiAc6nO7drSTxNwhAAAA6FPCoT63Y2wkl29qCocAAACgTwmH+lwpJRPjbeEQAAAA9CnhENm1o51HDx7N4WOn6y4FAAAA6DLhEJnY2U6SPPD4VK11AAAAAN0nHCI3zw+l3jdVax0AAABA9wmHyOhII1+zbaO5QwAAANCHhEMkSXZ1hlJXVVV3KQAAAEAXCYdIktwy3s7TR0/lwOTxuksBAAAAukg4RJJkYnwsSSwtAwAAgD4jHCJJcuNVm9McGhAOAQAAQJ8RDpEkaQ4N5BXXbMn9wiEAAADoK8IhzpgYH8uDjx/O6ZnZuksBAAAAukQ4xBm7xkdzcno2n/vykbpLAQAAALpEOMQZtxhKDQAAAH1HOMQZ41tHsnVjUzgEAAAAfUQ4xBmllEyMt4VDAAAA0EeEQyyya0c7Xzz4bJ45cbruUgAAAIAuEA6xyMTOdqoqefDA4bpLAQAAALpAOMQiu3aMJjGUGgAAAPqFcIhF2q1mrr98Y+7bN1V3KQAAAEAXCIc4x/xQ6qqq6i4FAAAAWGXCIc4xMd7OV589mScOn6i7FAAAAGCVCYc4x8R4O0my19IyAAAAWPeEQ5zjpVdvTnNwIHv3T9ZdCgAAALDKhEOcY8PQYG66Zkvu3287ewAAAFjvhEMsa2K8nQcfP5zpmdm6SwEAAABWkXCIZU2Mt3P89Ez++xefrrsUAAAAYBUJh1jWN7/silx/+cb8+Efuy5e+erTucgAAAIBVIhxiWVuGG3n/W25Pkrz1g3dn8uipmisCAAAAVoNwiPO6/vKNee+bdufxyeP50Q/fk5PTM3WXBAAAAFxiwiGe0+3Xbc0/f/3N+bMvHcr/8bEHUlVV3SUBAAAAl9BQ3QXQ++6c2J79h47lX/zHz2fnZRvzE9/2krpLAgAAAC4R4RAr8mOveXEee/pY/tUffSHXbm3lr962o+6SAAAAgEtAOMSKlFLyM3/5lXl86nje+TsP5Jr2SL7uay6ruywAAADgIpk5xIo1hwbyyz94W669bGN+9MN78shTz9ZdEgAAAHCRhEM8L6MjjXzgLbenOTSQt37wz/LVZ0/WXRIAAABwEYRDPG/jW1v51TffnqeeOZkf+bU9OXHaFvcAAACwVgmHeEEmxtv5xTdMZO/+qfzER/dmdtYW9wAAALAWCYd4we54xdX5+9/5snz8wS/n5z7x2brLAQAAAF4Au5VxUf7aX7g+X3r6aN7znx/NtVs35vu/dmfdJQEAAADPg3CIi1JKyT9+7ctzYPJ4/s/f+3S2j43kL75kW91lAQAAACtkWRkXbWhwIL/0/bfkhis25cd+49589svP1F0SAAAAsELCIS6JzcONfOCtt2fjhsG87QN35yvPnKi7JAAAAGAFhENcMlePjuR9b749U8dP54c/dHeOnZquuyQAAADgAoRDXFKv2D6aX/r+W/LwE8/kf/ut+zJji3sAAADoacIhLrlvfumV+UevfXn+02eeyj/9g4frLgcAAAB4DnYrY1W86euuy5e+eizv/29/nmu3tvKWr7++7pIAAACAZQiHWDX/4Ltelv2Tx/Ku3384O8Za+dabrqy7JAAAAGAJy8pYNYMDJb/4hom8Yvto/uZv3ZdPP3647pIAAACAJYRDrKpWcyi/+ubd2bqxmbd98O48MXW87pIAAACABYRDrLorNg/n/W+5PcdPzeRtH7w7R06crrskAAAAoEM4RFfceNXm/L8/eGu+8NSz+bHfvC+nZ2brLgkAAACIcIgu+gs3bMvPfM8r8iefP5ifvuuhVFVVd0kAAADQ9+xWRle94VU789ihY/nl/++LuXZrKz/6F7+m7pIAAACgrwmH6Lqf/PYbs+/Qsfzsf/hsxre28pdeeXXdJQEAAEDfsqyMrhsYKPn51+/KrTvb+du/vTf37pusuyQAAADoW8IhajHcGMyvvGl3rtwynB/50J7sP3Ss7pIAAACgLwmHqM1lmzbkA2+9PdOzVd7ygT/L4WO2uAcAAIBuEw5Rq6/Ztinv+aHbsu/Qsfz1X78np6ZtcQ8AAADdtKJwqJRyRynlc6WUR0op71zm/Q2llN/uvP+npZTrOucbpZQPlVIeLKV8ppTy9y5x/awDr37RZflnr7s5/+PRp/P3fudBW9wDAABAF10wHCqlDCZ5d5LvTHJTkjeWUm5actkPJ5msqurFSX4hyc91zr8+yYaqql6Z5LYkPzofHMFCf/mWHflb33pD/t29B/JLn3qk7nIAAACgb6ykc+hVSR6pqurRqqpOJflIkjuXXHNnkg91Xn8sybeUUkqSKsnGUspQkpEkp5I8c0kqZ9358W+5IX/llu35+U9+Pr+39/G6ywEAAIC+sJJwaHuS/QuOD3TOLXtNVVXTSQ4nuSxzQdHRJE8m2ZfkX1RVdegia2adKqXkZ//qK/O112/NT/7bB/Jnf+6/KgAAALDaVnsg9auSzCS5Jsn1Sf5OKeVFSy8qpby9lLKnlLLn4MGDq1wSvWzD0GDe80O3ZcfWkbz9w3vy6MFn6y4JAAAA1rWVhEOPJxlfcLyjc27ZazpLyEaTPJ3k+5P8YVVVp6uqeirJf0uye+k3qKrqvVVV7a6qave2bdue/0/ButJuNfOBt9yegVLytg/enUNHT9VdEgAAAKxbKwmH7k5yQynl+lJKM8kbkty15Jq7kry58/p1ST5VzW05tS/JNydJKWVjklcn+eylKJz17drLNuZX3rQ7Txw+kbf/2p6cOD1Td0kAAACwLl0wHOrMEHpHkk8k+UySj1ZV9VAp5V2llNd2LntfkstKKY8k+Ykk89vdvzvJplLKQ5kLmT5QVdUDl/qHYH267dqx/ML3TmTPY5P5yY89kNlZW9wDAADApTa0kouqqvp4ko8vOfdTC16fyNy29Us/9+xy52Glvuvmq7Pv0Evzc3/42Vy7tZW/+x031l0SAAAArCsrCoegTn/9L74ojz19NL/0x49k59ZWvvf28Qt/CAAAAFgR4RA9r5SSf/I9r8jjU8fz93/3wWwfG8nXv/jyussCAACAdWG1t7KHS6IxOJB3/8Ct+Zptm/LXf/2efOErR+ouCQAAANYF4RBrxpbhRt7/1tsz3BjMWz5wdw4eOVl3SQAAALDmCYdYU7a3R/K+N+/OoaOn8tc+dHeOn7LFPQAAAFwM4RBrzs072vnFN0zkgccP52/99n22uAcAAICLIBxiTfr2l1+Vf/hdN+UTD30lP/sfPlN3OQAAALBm2a2MNettX39d9j19NL/yX/48Oy/bmB969bV1lwQAAABrjnCINauUkp/6X1+eA5PH89O/9+nsGBvJa268ou6yAAAAYE2xrIw1bXCg5F+98Za87Ootecdv3JuHn3im7pIAAABgTREOseZt3DCU97/l9mwZaeRtH7w7Xz58ou6SAAAAYM0QDrEuXLllOO978+05cuJ03vbBu/Psyem6SwIAAIA1QTjEunHTNVvy7h+4NZ/7ypH8zd+8N9Mzs3WXBAAAAD1POMS68k03XpF//NqX548/dzDv+v2HU1VV3SUBAABAT7NbGevOD7762uw7dCzv/ZNHc+1lG/PD33B93SUBAABAzxIOsS69846XZt/Tx/JP/+Dh7BgbyXe8/Kq6SwIAAICeZFkZ69LAQMkvfN9Ebt7Rzo9/5L48cGCq7pIAAACgJwmHWLdGmoP51TftzuWbNuRtH9yTA5PH6i4JAAAAeo5wiHVt2+YN+cBbbs/J6Zm87YN355kTp+suCQAAAHqKcIh174YrN+c9P3hbHj14NH/j1+/NaVvcAwAAwBnCIfrC//Liy/Ozf+WV+a+PfDX/8Hc/bYt7AAAA6LBbGX3j9bvHs+/QsfzrTz2Say9v5W9804vrLgkAAABqJxyir/zEt70kjz19LP/sDz+XnVtb+e6br6m7JAAAAKiVcIi+UkrJP3vdzXny8PH8xEfvz9Wjw7nt2q11lwUAAAC1MXOIvjPcGMx7fmh3rhkdzo/82j157OmjdZcEAAAAtREO0Ze2bmzmA299VWarKm/94N2ZOnaq7pIAAACgFsIh+tb1l2/Mr7xpdw4cOp63f/ienJyeqbskAAAA6DrhEH3t9uu25p+//ub82Z8fyjv/3YO2uAcAAKDvGEhN37tzYnv2PX0sP//Jz2fn1lb+9re9pO6SAAAAoGuEQ5DkHd/84jx26Fh+8Y++kJ1bW/mrt+2ouyQAAADoCuEQZG6L+//rL78yT0wdzzt/54Fc0x7J133NZXWXBQAAAKvOzCHoaA4N5Jd/8LZce9nG/OiH9+SRp56tuyQAAABYdcIhWGB0pJEPvOX2NIcG8rYP3p2njpyouyQAAABYVaXXdmfavXt3tWfPnrrLoM/dt28yb3jv/8zpmdm85MrNufXasdy6cyy37mzn+ss3ppRSd4kAAACwYqWUe6qq2r3se8IhWN7DTzyTTzz05dy7bzJ7903lyMnpJMlYq5Fbdo7ltmvHcsvOdnbtaGfjBuO7AAAA6F3PFQ75N1o4j5uu2ZKbrtmSJJmdrfLIwWdzz2OTufexydy7bzKf+uxTSZKBkrz0qi259dp2p7toLNde1tJdBAAAwJqgcwheoKljp3Lf/qnc99hk7t03lb37p/Jsp7voso3N3LKznVs6YdGu8dG0mrJYAAAA6qFzCFZBu9XMa268Iq+58YokycxslS88dST3PjaVe/fNdRf9p8/MdRcNDpS89KrNc51FnQ6jnVt1FwEAAFA/nUOwiiaPnsre/WfDor37pnL01EyS5PJNzTOdRbfubOfmHe2MNAdrrhgAAID1SOcQ1GRsYzOveekVec1Lz3YXfe7LR86ERfftm8onH/5KkmRooORlV2/JrTvbZ3ZH2zE2orsIAACAVaVzCGp26Oip3NcJi+59bCr3H5jKsTPdRRsWhUU37xjNcEN3EQAAAM+PziHoYVs3NvMtL7sy3/KyK5Mk0zOz+dxXjuTeffPDrifzHxd0F910zZbcunMst+xs6y4CAADgoukcgjXg6WdP5r59c7OL7nlsMg8cOJzjp+e6i67YvGHRoOtXbNddBAAAwGI6h2CNu2zThnzrTVfmW28621302fnZRY9N5t59U/nDh76cJGkMltx0zejccrSdY7n12rFcMzqsuwgAAIBl6RyCdeLgkZOd2UVzHUYPHJjKidOzSZIrt3S6izodRi+/RncRAABAP9E5BH1g2+YN+faXX5Vvf/lVSZLTM7P57JNnd0a7d99k/sOn57qLmoMDZ2YX3XbtXGB09ehIneUDAABQE51D0EeeOnLizOyi+zo7o52cnusuunp0+Oyg62vH8vJrtmTDkO4iAACA9UDnEJAkuWLzcL7j5VflOzrdRaemZ/OZJ5/pdBZN5d7HJvMHDz6ZZK676BXbt5yZW3TrzrFcNTpcZ/kAAACsAp1DwCJPPXNiUVj0wOOHc6rTXXTN6HBu6QRFt+6cm13UHBqouWIAAAAuROcQsGJXbBnOHa+4One84uokc91FDz/5TGdXtMnct28qf/BAp7toaCCv3D46N7eoszvaFVt0FwEAAKwlOoeA5+3LhzvdRZ3A6NOPP5NTM3PdRdvbI51laHNh0U3XbEljUHcRAABAnZ6rc0g4BFy0k9MzeeiJue6i+YHXTx4+kSTZMDSQm3eMdoZdz+2MdsVm3UUAAADdJBwCuu7Jw8dz72NTnflFk3loQXfRjrGRM3OLbr12LC+7WncRAADAahIOAbU7cXquu+i+Tlh072NT+fIzc91Fw42B3LyjvSgwunzThporBgAAWD8MpAZqN9wYzG3XjuW2a8fOnHti6nju3TeZex6b2x3tff/10fybmbnAeufW1pmg6NadY3npVZszpLsIAADgkhMOAbW5pj2Sa9oj+e6br0ky11306ccPn+ks+u9ffDr/fu8TSZKRxuDc7KJOWHTrznYu010EAABw0YRDQM8Ybgxm93Vbs/u6rUmSqqry+NTx3LtvqjPsejK/8iePZnp2rrvo2stai5ai3Xil7iIAAIDnSzgE9KxSSnaMtbJjrJXX7jrbXfTg44dz72Nzs4v+6yNfze/e93iSpNUczK4d7dx6bfvM7mhbNzbr/BEAAAB6nnAIWFOGG4O5/bqtuX1Bd9GByeOdpWhzs4v+zX9+NDOd7qLrL9+YW3bOD7sey41Xbc7gQKnzRwAAAOgpwiFgTSulZHxrK+NbW7lzYnuS5PipmTxwYGpuOdq+yfzJ5w/md+6d6y7a2BzMrvFOWHRtO7eMj2VMdxEAANDHhEPAujPSHMzXvuiyfO2LLksy1120/1Cnu6jz55f/8xfPdBe96PKNuWXn3E5qt17bzg1X6C4CAAD6R6mqqu4aFtm9e3e1Z8+eussA1rljp6bzwIGzO6Pdu28yh46eSpJs2jCUifF2bt3ZztXtkWzaMJRNw0PZ3Pm6acNQNm9oZOOGQQOwAQCANaGUck9VVbuXe0/nENCXWs2hvPpFl+XVC7qLHnv62Nnuosem8kt//EhmL5CfjzQGs3n43PBo04bG3PmFgdKZr41Fx5uGh9IQMgEAADURDgFkbnbRdZdvzHWXb8xfuXVHkrmd0aaOnc6zJ0/nyInpPHtyOs+emM6R+a8npvPsydN59uT0oveffvbY2eOT02eWrz2XDUMD54RJ8wHTwvNnA6hzA6ZNG4Yy3Bhc7X9UAADAOiMcAjiP4cZgrhodTDL8gu9RVVVOnJ7NkfmAqRManQ2TOuHSybPvzQdPj08dnwufOsfTKwiZmoMDC8KlZTqa5o83DGXTcCd8Ouf9RoYbAynF3CUAAOgHwiGAVVRKyUhzMCPNwVyx+YXfp6qqnJyePRMeLQqYOsHTwu6ls++fzleOnMgXD549d3J69oLfb3CgnAmYFnUuLV0St9w8pvnOpuGhbGwOCpkAAKDHCYcA1oBSSoYbgxluDObyTRsu6l4np2dy9ORMZ4nc6bMdSyfPDZieOXH2/UNHT2Xf08fOdDkdPz2zgrrTGeC9sDupsaB7aZl5TMPnhlIbm0N2kAMAgFUiHALoMxuGBrNhaDBbNzYv6j7TM7M5enJmLmBasBzu7BK504tmNM0HUM8cP50npo7nSCd4OnrqwiFTkmxsDp4TMJ13HtM5A8KHsmXYDnMAALAc4RAAL8jQ4EBGWwMZbTUu6j4zs1WOnlpuHtP0omHgi2Y2deY1PXXkxNkA6uR0qguPZcpIY3DZ8GhpwLRoGPiCeUzzn2kOCZkAAFgfhEMA1GpwoGTLcCNbhi8uZJqdrXL89MyZIOnIiQUdTUu6l5YOBN936PnvMNccGjgTJp0NkRrn7CC3XMC0efjs+xuGDP8GAKBewiEA1oWBgZKNG4ayccNQrtzywu+zcIe5RTvInVk6d54d5k5O54mp4wsCqNM5PXPhkKkxWBaESY1ld5fbvGA53TlDwjtfRxqGfwMA8MIIhwBggUu1w1wyN/x70XK4BTvMndPRdGI6z3SW0j115EQefZ47zA3MD/8efq7upWXmMQ0PZcuCWU2txmAGDP8GAOgrwiEAWCUbhgazYdPF7zB3ano2R5cuievMY1q6w9yR+WHgJ6czefRU9h06dmZY+Ip3mGsuDo/O2VFuSffS2ePGojlOdpgDAFgbhEMA0OOaQwNpDjUztho7zC2zPG5+J7mFM5qePHxi0bmVaDUHFy2J27zhPB1NC47nw6f5440bhtKwwxwAwKoSDgFAn7hUO8zNzu8wt2g53Lk7zC3eXW7u9cEjJ3PkxOnntcPccGNg8bDvpfOYhpcsmTtPALVhaPCifm4AgPVKOAQAPC8DA6Uz26iRjL7w+1RVlWOnZhYvl1sSMC39Or/D3P5DxxZ1Nq1oh7nBgUU7xZ3bvXRuALVpw1AGSsngQMngwNxMqsHO8cCZr3P/TObPlzK3C99gKWfOD5SSgYEs+dzcZw0SBwDqJhwCAGpRyqXbYe7k9GxnBtN5lswtnMfUOffMiek8MXXiee8wd6kNdMKkxcHT2SDpQsHTmXMLAqeFIdTZey+45/z3GZg7PzB//fx9B9K5d1nBvbMoMDtzzYI6567PkmDsbOi2qKbzhG5nArYL/DM5+3Ms8/MK4wBgWcIhAGBNK6VkuDGY4cZgtm2+uOHfJ6dnFg33PnpyOjNVldnZzH2tqszOVpmZ7byucub1TOd8Vc1du/D8bDW3HG/+fFVVmZm/Z+f82Xunc+9qyb3ngrCF955dco8z379zfGp6du7z89//TN1n6zpb9/w9lnz/+fo652eqakXLAXvVQMk5odvCIOpst1fn3KLgKYuCseUCs7P3fo4wriwOz5YGZMuFbst3qS34Pou61J47jLsUHXCLwkNhHMCaJxwCAOiY32HusovcYW69qxaGXvPhVSekWhqYLQyxzgmnlgndlgZRzxXGLQ3dztxnwfmFodvZGhfee/kw7txAL4t+zplqwTXPM4ybXXCPc0K/pfde42HcfMh14eBpcRhXXkAH3DkdePNdeEvCuJV1wHUCtucI45Z22624A27ZQO+5O+CWC+OW+17COOCFEg4BAPC8lFIyNOhfQrulWq5LrapSXSB0W9wRdv4w7pzga0nwd/bey3XAnQ3DloZuc/deeXj4XGHc0tBtpqoyPTubk9NLvv/SgO85Qrf587PV4u+/nsK483XAnQ21VtYBtzR0W9oBd/4wbvkQ69zQbXEYt+z3f45uu+fsgFtU9/PvgBssJWVg+U4+YRzriXAIAAB62MKOFVbfcmHc8+qAW3D+3A64FXbbLQnjFgd6OSd0W9oBd/57L7jneTrgloZ+859fGMaduWbBvauFS2qXCd3O14G3XsK4paHb2eDpucO4F9IBt/y8teU2PThf6Hb2/KIOuPOEccvf+zwdcIvqfu7QbbnwcLkwbmBBXayeFYVDpZQ7kvxiksEkv1pV1f+95P0NSX4tyW1Jnk7yfVVVfamU8gNJfnLBpTcnubWqqr2XoHYAAIBLShjXXUvDuJV0wC0K3c7bbbZMt10ndDt3yefZMG5poLdcGLf8vbMoMDsn4FumA25xoLe4A24+jFsUKJ75zIU74BbWufD7z67TMG5hGLZ4M4TnDuNWsvHDwEDy2l3bc8crrqr7H8GqumA4VEoZTPLuJN+W5ECSu0spd1VV9fCCy344yWRVVS8upbwhyc9lLiD6jSS/0bnPK5P8e8EQAAAAiTCu2y4Uxi3tgFuu2+tMQHee0G1pQHbeMG5JmLVcd93S0O2ca2aXbKiwtNvuPMtOl3bAnS+Mm++IO3T0VN3/0a26lXQOvSrJI1VVPZokpZSPJLkzycJw6M4k/6jz+mNJfqmUUqpqUZPgG5N85KIrBgAAAJ43YRznM7CCa7Yn2b/g+EDn3LLXVFU1neRwksuWXPN9SX7rhZUJAAAAwGpYSTh00UopX5vkWFVVnz7P+28vpewppew5ePBgN0oCAAAAICsLhx5PMr7geEfn3LLXlFKGkoxmbjD1vDfkObqGqqp6b1VVu6uq2r1t27aV1A0AAADAJbCScOjuJDeUUq4vpTQzF/TcteSau5K8ufP6dUk+NT9vqJQykOR7Y94QAAAAQM+54EDqqqqmSynvSPKJzG1l//6qqh4qpbwryZ6qqu5K8r4kHy6lPJLkUOYCpHnfmGT//EBrAAAAAHpHWbyhWP12795d7dmzp+4yAAAAANaNUso9VVXtXu69rgykBgAAAKA3CYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPCYcAAAAA+phwCAAAAKCPlaqq6q5hkVLKwSSP1V1Hx+VJvlp3EdDDPCNwYZ4TeG6eEXhunhG4MM/JylxbVdW25d7ouXCol5RS9lRVtbvuOqBXeUbgwjwn8Nw8I/DcPCNwYZ6Ti2dZGQAAAEAfEw4BAAAA9DHh0HN7b90FQI/zjMCFeU7guXlG4Ll5RuDCPCcXycwhAAAAgD6mcwgAAACgjwmHzqOUckcp5XOllEdKKe+sux6oQynl/aWUp0opn15wbmsp5ZOllC90vo51zpdSyr/qPDMPlFJura9y6I5Syngp5Y9LKQ+XUh4qpfx457znBJKUUoZLKX9WSrm/84z8487560spf9p5Fn67lNLsnN/QOX6k8/51tf4A0CWllMFSyn2llN/vHHtGYIFSypdKKQ+WUvaWUvZ0zvl96xISDi2jlDKY5N1JvjPJTUneWEq5qd6qoBYfTHLHknPvTPJHVVXdkOSPOsfJ3PNyQ+fP25P8cpdqhDpNJ/k7VVXdlOTVSX6s8/8XnhOYczLJN1dVtSvJRJI7SimvTvJzSX6hqqoXJ5lM8sOd6384yWTn/C90roN+8ONJPrPg2DMC53pNVVUTC7as9/vWJSQcWt6rkjxSVdWjVVWdSvKRJHfWXBN0XVVVf5Lk0JLTdyb5UOf1h5J8z4Lzv1bN+Z9J2qWUq7tSKNSkqqonq6q6t/P6SOZ+sd8ezwkkSTr/XX+2c9jo/KmSfHOSj3XOL31G5p+djyX5llJK6U61UI9Syo4k35XkVzvHJZ4RWAm/b11CwqHlbU+yf8Hxgc45ILmyqqonO6+/nOTKzmvPDX2t09p/S5I/jecEzugsl9mb5Kkkn0zyxSRTVVVNdy5Z+ByceUY67x9OcllXC4bu+5dJ/vcks53jy+IZgaWqJP+xlHJPKeXtnXN+37qEhuouAFi7qqqqSim2PKTvlVI2Jfl3Sf5WVVXPLPxLXM8J/a6qqpkkE6WUdpLfTfLSeiuC3lFK+e4kT1VVdU8p5ZtqLgd62TdUVfV4KeWKJJ8spXx24Zt+37p4OoeW93iS8QXHOzrngOQr822Zna9Pdc57buhLpZRG5oKh36iq6nc6pz0nsERVVVNJ/jjJ12WuxX/+LykXPgdnnpHO+6NJnu5updBVX5/ktaWUL2VulMU3J/nFeEZgkaqqHu98fSpzf9Hwqvh965ISDi3v7iQ3dHYJaCZ5Q5K7aq4JesVdSd7cef3mJL+34PybOrsDvDrJ4QVtnrAudeY8vC/JZ6qq+n8WvOU5gSSllG2djqGUUkaSfFvmZnP9cZLXdS5b+ozMPzuvS/Kpqqr8TTDrVlVVf6+qqh1VVV2XuX/n+FRVVT8QzwicUUrZWErZPP86ybcn+XT8vnVJFf9bsrxSyl/K3PrfwSTvr6rqZ+qtCLqvlPJbSb4pyeVJvpLkp5P8+yQfTbIzyWNJvreqqkOdf0n+pcztbnYsyVurqtpTQ9nQNaWUb0jyX5I8mLOzIv5+5uYOeU7oe6WUmzM3JHQwc38p+dGqqt5VSnlR5roktia5L8kPVlV1spQynOTDmZvfdSjJG6qqerSe6qG7OsvK/m5VVd/tGYGzOs/D73YOh5L8ZlVVP1NKuSx+37pkhEMAAAAAfcyyMgAAAIA+JhwCAAAA6GPCIQAAAIA+JhwCAAAA6GPCIQAAAIA+JhwCAAAA6GPCIQAAAIA+JhwCAAAA6GP/PxUD77f7d6+EAAAAAElFTkSuQmCC",
"text/plain": [
"<Figure size 1440x720 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(20, 10))\n",
"plt.plot(latent_dims, errors)"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [],
"source": [
"from keras.datasets import mnist\n",
"import numpy as np\n",
"\n",
"# Chargement et normalisation (entre 0 et 1) des données de la base de données MNIST\n",
"(x_train, _), (x_test, _) = mnist.load_data()\n",
"\n",
"x_train = x_train.astype('float32') / 255.0\n",
"x_test = x_test.astype('float32') / 255.0"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model_83\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_53 (InputLayer) [(None, 28, 28, 1)] 0 \n",
" \n",
" conv2d_37 (Conv2D) (None, 28, 28, 64) 640 \n",
" \n",
" conv2d_38 (Conv2D) (None, 28, 28, 64) 36928 \n",
" \n",
" max_pooling2d_8 (MaxPooling (None, 14, 14, 64) 0 \n",
" 2D) \n",
" \n",
" conv2d_39 (Conv2D) (None, 14, 14, 128) 73856 \n",
" \n",
" conv2d_40 (Conv2D) (None, 14, 14, 128) 147584 \n",
" \n",
" max_pooling2d_9 (MaxPooling (None, 7, 7, 128) 0 \n",
" 2D) \n",
" \n",
" flatten_2 (Flatten) (None, 6272) 0 \n",
" \n",
" dense_114 (Dense) (None, 5) 31365 \n",
" \n",
"=================================================================\n",
"Total params: 290,373\n",
"Trainable params: 290,373\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"None\n",
"Model: \"model_84\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_54 (InputLayer) [(None, 5)] 0 \n",
" \n",
" dense_115 (Dense) (None, 6272) 37632 \n",
" \n",
" reshape_2 (Reshape) (None, 7, 7, 128) 0 \n",
" \n",
" up_sampling2d_5 (UpSampling (None, 14, 14, 128) 0 \n",
" 2D) \n",
" \n",
" conv2d_41 (Conv2D) (None, 14, 14, 128) 147584 \n",
" \n",
" conv2d_42 (Conv2D) (None, 14, 14, 128) 147584 \n",
" \n",
" up_sampling2d_6 (UpSampling (None, 28, 28, 128) 0 \n",
" 2D) \n",
" \n",
" conv2d_43 (Conv2D) (None, 28, 28, 64) 32832 \n",
" \n",
" conv2d_44 (Conv2D) (None, 28, 28, 1) 577 \n",
" \n",
"=================================================================\n",
"Total params: 366,209\n",
"Trainable params: 366,209\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"None\n",
"Model: \"model_85\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_53 (InputLayer) [(None, 28, 28, 1)] 0 \n",
" \n",
" model_83 (Functional) (None, 5) 290373 \n",
" \n",
" model_84 (Functional) (None, 28, 28, 1) 366209 \n",
" \n",
"=================================================================\n",
"Total params: 656,582\n",
"Trainable params: 656,582\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"Epoch 1/10\n",
"116/469 [======>.......................] - ETA: 3:19 - loss: 0.2379"
]
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m/home/laurent/Documents/Cours/ENSEEIHT/S9 - IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb Cell 12\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=50'>51</a>\u001b[0m autoencoder\u001b[39m.\u001b[39msummary()\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=52'>53</a>\u001b[0m \u001b[39m# Entraînement de l'auto-encodeur. On utilise ici les données de test \u001b[39;00m\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=53'>54</a>\u001b[0m \u001b[39m# pour surveiller l'évolution de l'erreur de reconstruction sur des données \u001b[39;00m\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=54'>55</a>\u001b[0m \u001b[39m# non utilisées pendant l'entraînement et ainsi détecter le sur-apprentissage.\u001b[39;00m\n\u001b[0;32m---> <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=55'>56</a>\u001b[0m autoencoder\u001b[39m.\u001b[39;49mfit(x_train, x_train,\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=56'>57</a>\u001b[0m epochs\u001b[39m=\u001b[39;49m\u001b[39m10\u001b[39;49m,\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=57'>58</a>\u001b[0m batch_size\u001b[39m=\u001b[39;49m\u001b[39m128\u001b[39;49m,\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=58'>59</a>\u001b[0m shuffle\u001b[39m=\u001b[39;49m\u001b[39mTrue\u001b[39;49;00m,\n\u001b[1;32m <a href='vscode-notebook-cell:/home/laurent/Documents/Cours/ENSEEIHT/S9%20-%20IAM/IAM2022_TP_Autoencodeurs_Sujet.ipynb#X16sZmlsZQ%3D%3D?line=59'>60</a>\u001b[0m validation_data\u001b[39m=\u001b[39;49m(x_test, x_test))\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/keras/utils/traceback_utils.py:64\u001b[0m, in \u001b[0;36mfilter_traceback.<locals>.error_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 62\u001b[0m filtered_tb \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m 63\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m---> 64\u001b[0m \u001b[39mreturn\u001b[39;00m fn(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m 65\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mException\u001b[39;00m \u001b[39mas\u001b[39;00m e: \u001b[39m# pylint: disable=broad-except\u001b[39;00m\n\u001b[1;32m 66\u001b[0m filtered_tb \u001b[39m=\u001b[39m _process_traceback_frames(e\u001b[39m.\u001b[39m__traceback__)\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/keras/engine/training.py:1384\u001b[0m, in \u001b[0;36mModel.fit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)\u001b[0m\n\u001b[1;32m 1377\u001b[0m \u001b[39mwith\u001b[39;00m tf\u001b[39m.\u001b[39mprofiler\u001b[39m.\u001b[39mexperimental\u001b[39m.\u001b[39mTrace(\n\u001b[1;32m 1378\u001b[0m \u001b[39m'\u001b[39m\u001b[39mtrain\u001b[39m\u001b[39m'\u001b[39m,\n\u001b[1;32m 1379\u001b[0m epoch_num\u001b[39m=\u001b[39mepoch,\n\u001b[1;32m 1380\u001b[0m step_num\u001b[39m=\u001b[39mstep,\n\u001b[1;32m 1381\u001b[0m batch_size\u001b[39m=\u001b[39mbatch_size,\n\u001b[1;32m 1382\u001b[0m _r\u001b[39m=\u001b[39m\u001b[39m1\u001b[39m):\n\u001b[1;32m 1383\u001b[0m callbacks\u001b[39m.\u001b[39mon_train_batch_begin(step)\n\u001b[0;32m-> 1384\u001b[0m tmp_logs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mtrain_function(iterator)\n\u001b[1;32m 1385\u001b[0m \u001b[39mif\u001b[39;00m data_handler\u001b[39m.\u001b[39mshould_sync:\n\u001b[1;32m 1386\u001b[0m context\u001b[39m.\u001b[39masync_wait()\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/util/traceback_utils.py:150\u001b[0m, in \u001b[0;36mfilter_traceback.<locals>.error_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 148\u001b[0m filtered_tb \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m 149\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 150\u001b[0m \u001b[39mreturn\u001b[39;00m fn(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m 151\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mException\u001b[39;00m \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m 152\u001b[0m filtered_tb \u001b[39m=\u001b[39m _process_traceback_frames(e\u001b[39m.\u001b[39m__traceback__)\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/eager/def_function.py:915\u001b[0m, in \u001b[0;36mFunction.__call__\u001b[0;34m(self, *args, **kwds)\u001b[0m\n\u001b[1;32m 912\u001b[0m compiler \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mxla\u001b[39m\u001b[39m\"\u001b[39m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_jit_compile \u001b[39melse\u001b[39;00m \u001b[39m\"\u001b[39m\u001b[39mnonXla\u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m 914\u001b[0m \u001b[39mwith\u001b[39;00m OptionalXlaContext(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_jit_compile):\n\u001b[0;32m--> 915\u001b[0m result \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_call(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwds)\n\u001b[1;32m 917\u001b[0m new_tracing_count \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mexperimental_get_tracing_count()\n\u001b[1;32m 918\u001b[0m without_tracing \u001b[39m=\u001b[39m (tracing_count \u001b[39m==\u001b[39m new_tracing_count)\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/eager/def_function.py:947\u001b[0m, in \u001b[0;36mFunction._call\u001b[0;34m(self, *args, **kwds)\u001b[0m\n\u001b[1;32m 944\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_lock\u001b[39m.\u001b[39mrelease()\n\u001b[1;32m 945\u001b[0m \u001b[39m# In this case we have created variables on the first call, so we run the\u001b[39;00m\n\u001b[1;32m 946\u001b[0m \u001b[39m# defunned version which is guaranteed to never create variables.\u001b[39;00m\n\u001b[0;32m--> 947\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_stateless_fn(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwds) \u001b[39m# pylint: disable=not-callable\u001b[39;00m\n\u001b[1;32m 948\u001b[0m \u001b[39melif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_stateful_fn \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m 949\u001b[0m \u001b[39m# Release the lock early so that multiple threads can perform the call\u001b[39;00m\n\u001b[1;32m 950\u001b[0m \u001b[39m# in parallel.\u001b[39;00m\n\u001b[1;32m 951\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_lock\u001b[39m.\u001b[39mrelease()\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/eager/function.py:2956\u001b[0m, in \u001b[0;36mFunction.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2953\u001b[0m \u001b[39mwith\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_lock:\n\u001b[1;32m 2954\u001b[0m (graph_function,\n\u001b[1;32m 2955\u001b[0m filtered_flat_args) \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_maybe_define_function(args, kwargs)\n\u001b[0;32m-> 2956\u001b[0m \u001b[39mreturn\u001b[39;00m graph_function\u001b[39m.\u001b[39;49m_call_flat(\n\u001b[1;32m 2957\u001b[0m filtered_flat_args, captured_inputs\u001b[39m=\u001b[39;49mgraph_function\u001b[39m.\u001b[39;49mcaptured_inputs)\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/eager/function.py:1853\u001b[0m, in \u001b[0;36mConcreteFunction._call_flat\u001b[0;34m(self, args, captured_inputs, cancellation_manager)\u001b[0m\n\u001b[1;32m 1849\u001b[0m possible_gradient_type \u001b[39m=\u001b[39m gradients_util\u001b[39m.\u001b[39mPossibleTapeGradientTypes(args)\n\u001b[1;32m 1850\u001b[0m \u001b[39mif\u001b[39;00m (possible_gradient_type \u001b[39m==\u001b[39m gradients_util\u001b[39m.\u001b[39mPOSSIBLE_GRADIENT_TYPES_NONE\n\u001b[1;32m 1851\u001b[0m \u001b[39mand\u001b[39;00m executing_eagerly):\n\u001b[1;32m 1852\u001b[0m \u001b[39m# No tape is watching; skip to running the function.\u001b[39;00m\n\u001b[0;32m-> 1853\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_build_call_outputs(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_inference_function\u001b[39m.\u001b[39;49mcall(\n\u001b[1;32m 1854\u001b[0m ctx, args, cancellation_manager\u001b[39m=\u001b[39;49mcancellation_manager))\n\u001b[1;32m 1855\u001b[0m forward_backward \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_select_forward_and_backward_functions(\n\u001b[1;32m 1856\u001b[0m args,\n\u001b[1;32m 1857\u001b[0m possible_gradient_type,\n\u001b[1;32m 1858\u001b[0m executing_eagerly)\n\u001b[1;32m 1859\u001b[0m forward_function, args_with_tangents \u001b[39m=\u001b[39m forward_backward\u001b[39m.\u001b[39mforward()\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/eager/function.py:499\u001b[0m, in \u001b[0;36m_EagerDefinedFunction.call\u001b[0;34m(self, ctx, args, cancellation_manager)\u001b[0m\n\u001b[1;32m 497\u001b[0m \u001b[39mwith\u001b[39;00m _InterpolateFunctionError(\u001b[39mself\u001b[39m):\n\u001b[1;32m 498\u001b[0m \u001b[39mif\u001b[39;00m cancellation_manager \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[0;32m--> 499\u001b[0m outputs \u001b[39m=\u001b[39m execute\u001b[39m.\u001b[39;49mexecute(\n\u001b[1;32m 500\u001b[0m \u001b[39mstr\u001b[39;49m(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49msignature\u001b[39m.\u001b[39;49mname),\n\u001b[1;32m 501\u001b[0m num_outputs\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_num_outputs,\n\u001b[1;32m 502\u001b[0m inputs\u001b[39m=\u001b[39;49margs,\n\u001b[1;32m 503\u001b[0m attrs\u001b[39m=\u001b[39;49mattrs,\n\u001b[1;32m 504\u001b[0m ctx\u001b[39m=\u001b[39;49mctx)\n\u001b[1;32m 505\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m 506\u001b[0m outputs \u001b[39m=\u001b[39m execute\u001b[39m.\u001b[39mexecute_with_cancellation(\n\u001b[1;32m 507\u001b[0m \u001b[39mstr\u001b[39m(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39msignature\u001b[39m.\u001b[39mname),\n\u001b[1;32m 508\u001b[0m num_outputs\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_num_outputs,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 511\u001b[0m ctx\u001b[39m=\u001b[39mctx,\n\u001b[1;32m 512\u001b[0m cancellation_manager\u001b[39m=\u001b[39mcancellation_manager)\n",
"File \u001b[0;32m~/.local/lib/python3.10/site-packages/tensorflow/python/eager/execute.py:54\u001b[0m, in \u001b[0;36mquick_execute\u001b[0;34m(op_name, num_outputs, inputs, attrs, ctx, name)\u001b[0m\n\u001b[1;32m 52\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m 53\u001b[0m ctx\u001b[39m.\u001b[39mensure_initialized()\n\u001b[0;32m---> 54\u001b[0m tensors \u001b[39m=\u001b[39m pywrap_tfe\u001b[39m.\u001b[39;49mTFE_Py_Execute(ctx\u001b[39m.\u001b[39;49m_handle, device_name, op_name,\n\u001b[1;32m 55\u001b[0m inputs, attrs, num_outputs)\n\u001b[1;32m 56\u001b[0m \u001b[39mexcept\u001b[39;00m core\u001b[39m.\u001b[39m_NotOkStatusException \u001b[39mas\u001b[39;00m e:\n\u001b[1;32m 57\u001b[0m \u001b[39mif\u001b[39;00m name \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n",
"\u001b[0;31mKeyboardInterrupt\u001b[0m: "
]
}
],
"source": [
"from keras.layers import Input, Dense\n",
"from keras.models import Model\n",
"\n",
"import keras\n",
"from keras.layers import *\n",
"from keras import *\n",
"\n",
"# Dimension de l'entrée\n",
"input_img = Input(shape=(28, 28, 1))\n",
"latent_dim = 5\n",
"\n",
"# Définition d'un encodeur\n",
"x = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(input_img)\n",
"x = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"x = MaxPooling2D(pool_size=(2, 2))(x)\n",
"\n",
"x = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"x = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"x = MaxPooling2D(pool_size=(2, 2))(x)\n",
"x = Flatten()(x)\n",
"\n",
"encoded = Dense(latent_dim, activation='linear')(x)\n",
"\n",
"# Définition d'un decodeur\n",
"decoder_input = Input(shape=(latent_dim,))\n",
"\n",
"x = Dense(7*7*128, activation='linear')(decoder_input)\n",
"x = Reshape((7,7,128))(x)\n",
"\n",
"x = UpSampling2D(size = (2,2))(x)\n",
"x = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"x = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"\n",
"x = UpSampling2D(size = (2,2))(x)\n",
"x = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"decoded = Conv2D(1, 3, activation = 'sigmoid', padding = 'same', kernel_initializer = 'he_normal')(x)\n",
"\n",
"# Construction d'un modèle séparé pour pouvoir accéder aux décodeur et encodeur\n",
"encoder = Model(input_img, encoded)\n",
"print(encoder.summary())\n",
"\n",
"decoder = Model(decoder_input, decoded)\n",
"print(decoder.summary())\n",
"\n",
"# Construction du modèle de l'auto-encodeur\n",
"encoded = encoder(input_img)\n",
"decoded = decoder(encoded)\n",
"autoencoder = Model(input_img, decoded)\n",
"\n",
"autoencoder.compile(optimizer='Adam', loss='bce')\n",
"autoencoder.summary()\n",
"\n",
"# Entraînement de l'auto-encodeur. On utilise ici les données de test \n",
"# pour surveiller l'évolution de l'erreur de reconstruction sur des données \n",
"# non utilisées pendant l'entraînement et ainsi détecter le sur-apprentissage.\n",
"autoencoder.fit(x_train, x_train,\n",
" epochs=10,\n",
" batch_size=128,\n",
" shuffle=True,\n",
" validation_data=(x_test, x_test))\n"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7f521b374df0>]"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 1440x720 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(20, 10))\n",
"plt.plot(factors, errors)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "B6dM--9H_wEM"
},
"source": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"# Visages"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Ot-zkfDBQUkl"
},
"outputs": [],
"source": [
"import pandas as pd\n",
"import tarfile, tqdm, cv2, os\n",
"from sklearn.model_selection import train_test_split\n",
"import numpy as np\n",
"\n",
"# Télécharger les données de la base de données \"Labelled Faces in the Wild\"\n",
"!wget http://www.cs.columbia.edu/CAVE/databases/pubfig/download/lfw_attributes.txt\n",
"!wget http://vis-www.cs.umass.edu/lfw/lfw-deepfunneled.tgz\n",
"!wget http://vis-www.cs.umass.edu/lfw/lfw.tgz\n",
" \n",
"ATTRS_NAME = \"lfw_attributes.txt\"\n",
"IMAGES_NAME = \"lfw-deepfunneled.tgz\"\n",
"RAW_IMAGES_NAME = \"lfw.tgz\"\n",
"\n",
"def decode_image_from_raw_bytes(raw_bytes):\n",
" img = cv2.imdecode(np.asarray(bytearray(raw_bytes), dtype=np.uint8), 1)\n",
" img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n",
" return img\n",
"\n",
"def load_lfw_dataset(\n",
" use_raw=False,\n",
" dx=80, dy=80,\n",
" dimx=45, dimy=45):\n",
"\n",
" # Read attrs\n",
" df_attrs = pd.read_csv(ATTRS_NAME, sep='\\t', skiprows=1)\n",
" df_attrs = pd.DataFrame(df_attrs.iloc[:, :-1].values, columns=df_attrs.columns[1:])\n",
" imgs_with_attrs = set(map(tuple, df_attrs[[\"person\", \"imagenum\"]].values))\n",
"\n",
" # Read photos\n",
" all_photos = []\n",
" photo_ids = []\n",
"\n",
" # tqdm in used to show progress bar while reading the data in a notebook here, you can change\n",
" # tqdm_notebook to use it outside a notebook\n",
" with tarfile.open(RAW_IMAGES_NAME if use_raw else IMAGES_NAME) as f:\n",
" for m in tqdm.tqdm_notebook(f.getmembers()):\n",
" # Only process image files from the compressed data\n",
" if m.isfile() and m.name.endswith(\".jpg\"):\n",
" # Prepare image\n",
" img = decode_image_from_raw_bytes(f.extractfile(m).read())\n",
"\n",
" # Crop only faces and resize it\n",
" img = img[dy:-dy, dx:-dx]\n",
" img = cv2.resize(img, (dimx, dimy))\n",
"\n",
" # Parse person and append it to the collected data\n",
" fname = os.path.split(m.name)[-1]\n",
" fname_splitted = fname[:-4].replace('_', ' ').split()\n",
" person_id = ' '.join(fname_splitted[:-1])\n",
" photo_number = int(fname_splitted[-1])\n",
" if (person_id, photo_number) in imgs_with_attrs:\n",
" all_photos.append(img)\n",
" photo_ids.append({'person': person_id, 'imagenum': photo_number})\n",
"\n",
" photo_ids = pd.DataFrame(photo_ids)\n",
" all_photos = np.stack(all_photos).astype('uint8')\n",
"\n",
" # Preserve photo_ids order!\n",
" all_attrs = photo_ids.merge(df_attrs, on=('person', 'imagenum')).drop([\"person\", \"imagenum\"], axis=1)\n",
"\n",
" return all_photos, all_attrs\n",
"\n",
"# Prépare le dataset et le charge dans la variable X\n",
"X, attr = load_lfw_dataset(use_raw=True, dimx=32, dimy=32)\n",
"# Normalise les images\n",
"X = X/255\n",
"# Sépare les images en données d'entraînement et de test\n",
"X_train, X_test = train_test_split(X, test_size=0.1, random_state=42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "kt5l_VB7pI5i"
},
"outputs": [],
"source": [
"n = 10\n",
"plt.figure(figsize=(20, 4))\n",
"for i in range(n):\n",
"\n",
"\n",
" ax = plt.subplot(1, n, i+1)\n",
" plt.imshow(X_train[i].reshape(32, 32, 3))\n",
" plt.gray()\n",
" ax.get_xaxis().set_visible(False)\n",
" ax.get_yaxis().set_visible(False)\n",
"\n",
"plt.show()"
]
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"collapsed_sections": [
"B6dM--9H_wEM"
],
"machine_shape": "hm",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3.10.7 64-bit",
"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.10.8"
},
"vscode": {
"interpreter": {
"hash": "767d51c1340bd893661ea55ea3124f6de3c7a262a8b4abca0554b478b1e2ff90"
}
}
},
"nbformat": 4,
"nbformat_minor": 0
}