{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Deep Learning Models -- A collection of various deep learning architectures, models, and tips for TensorFlow and PyTorch in Jupyter Notebooks.\n", "- Author: Sebastian Raschka\n", "- GitHub Repository: https://github.com/rasbt/deeplearning-models" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sebastian Raschka \n", "\n", "CPython 3.6.8\n", "IPython 7.2.0\n", "\n", "torch 1.0.0\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -a 'Sebastian Raschka' -v -p torch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Runs on CPU or GPU (if available)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Model Zoo -- Convolutional Autoencoder with Deconvolutions (without pooling operations)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A convolutional autoencoder using deconvolutional layers that compresses 768-pixel MNIST images down to a 7x7x8 (392 pixel) representation without using pooling operations but increasing the stride in convolutional layers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import time\n", "import numpy as np\n", "import torch\n", "import torch.nn.functional as F\n", "from torch.utils.data import DataLoader\n", "from torchvision import datasets\n", "from torchvision import transforms\n", "\n", "\n", "if torch.cuda.is_available():\n", " torch.backends.cudnn.deterministic = True" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Device: cuda:0\n", "Image batch dimensions: torch.Size([128, 1, 28, 28])\n", "Image label dimensions: torch.Size([128])\n" ] } ], "source": [ "##########################\n", "### SETTINGS\n", "##########################\n", "\n", "# Device\n", "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", "print('Device:', device)\n", "\n", "# Hyperparameters\n", "random_seed = 456\n", "learning_rate = 0.005\n", "num_epochs = 10\n", "batch_size = 128\n", "\n", "##########################\n", "### MNIST DATASET\n", "##########################\n", "\n", "# Note transforms.ToTensor() scales input images\n", "# to 0-1 range\n", "train_dataset = datasets.MNIST(root='data', \n", " train=True, \n", " transform=transforms.ToTensor(),\n", " download=True)\n", "\n", "test_dataset = datasets.MNIST(root='data', \n", " train=False, \n", " transform=transforms.ToTensor())\n", "\n", "\n", "train_loader = DataLoader(dataset=train_dataset, \n", " batch_size=batch_size, \n", " shuffle=True)\n", "\n", "test_loader = DataLoader(dataset=test_dataset, \n", " batch_size=batch_size, \n", " shuffle=False)\n", "\n", "# Checking the dataset\n", "for images, labels in train_loader: \n", " print('Image batch dimensions:', images.shape)\n", " print('Image label dimensions:', labels.shape)\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Model" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "##########################\n", "### MODEL\n", "##########################\n", "\n", "\n", "class ConvolutionalAutoencoder(torch.nn.Module):\n", "\n", " def __init__(self):\n", " super(ConvolutionalAutoencoder, self).__init__()\n", " \n", " # calculate same padding:\n", " # (w - k + 2*p)/s + 1 = o\n", " # => p = (s(o-1) - w + k)/2\n", " \n", " ### ENCODER\n", " \n", " # 28x28x1 => 14x14x4 \n", " self.conv_1 = torch.nn.Conv2d(in_channels=1,\n", " out_channels=4,\n", " kernel_size=(3, 3),\n", " stride=(2, 2),\n", " # floor((2(14-1) - 28 + 3) / 2) = 0\n", " padding=0)\n", " \n", " # 14x14x4 => 7x7x8\n", " self.conv_2 = torch.nn.Conv2d(in_channels=4,\n", " out_channels=8,\n", " kernel_size=(3, 3),\n", " stride=(2, 2),\n", " # ceil((2(7-1) - 14 + 3) / 2) = 1\n", " padding=1) \n", " \n", " ### DECODER\n", " \n", " # 7x7x8 => 15x15x4 \n", " self.deconv_1 = torch.nn.ConvTranspose2d(in_channels=8,\n", " out_channels=4,\n", " kernel_size=(3, 3),\n", " stride=(2, 2),\n", " padding=0)\n", " \n", " # 15x15x4 => 29x29x1 \n", " self.deconv_2 = torch.nn.ConvTranspose2d(in_channels=4,\n", " out_channels=1,\n", " kernel_size=(3, 3),\n", " stride=(2, 2),\n", " padding=1)\n", " \n", " def forward(self, x):\n", " \n", " ### ENCODER\n", " x = self.conv_1(x)\n", " x = F.leaky_relu(x)\n", " x = self.conv_2(x)\n", " x = F.leaky_relu(x)\n", " \n", " ### DECODER\n", " x = self.deconv_1(x) \n", " x = F.leaky_relu(x)\n", " x = self.deconv_2(x)\n", " x = F.leaky_relu(x)\n", " x = x[:, :, :-1, :-1]\n", " x = torch.sigmoid(x)\n", " return x\n", "\n", " \n", "torch.manual_seed(random_seed)\n", "model = ConvolutionalAutoencoder()\n", "model = model.to(device)\n", "\n", "optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Training" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 001/010 | Batch 000/469 | Cost: 0.7184\n", "Epoch: 001/010 | Batch 050/469 | Cost: 0.6903\n", "Epoch: 001/010 | Batch 100/469 | Cost: 0.6569\n", "Epoch: 001/010 | Batch 150/469 | Cost: 0.5834\n", "Epoch: 001/010 | Batch 200/469 | Cost: 0.2208\n", "Epoch: 001/010 | Batch 250/469 | Cost: 0.1800\n", "Epoch: 001/010 | Batch 300/469 | Cost: 0.1723\n", "Epoch: 001/010 | Batch 350/469 | Cost: 0.1573\n", "Epoch: 001/010 | Batch 400/469 | Cost: 0.1450\n", "Epoch: 001/010 | Batch 450/469 | Cost: 0.1451\n", "Time elapsed: 0.12 min\n", "Epoch: 002/010 | Batch 000/469 | Cost: 0.1384\n", "Epoch: 002/010 | Batch 050/469 | Cost: 0.1396\n", "Epoch: 002/010 | Batch 100/469 | Cost: 0.1325\n", "Epoch: 002/010 | Batch 150/469 | Cost: 0.1306\n", "Epoch: 002/010 | Batch 200/469 | Cost: 0.1284\n", "Epoch: 002/010 | Batch 250/469 | Cost: 0.1256\n", "Epoch: 002/010 | Batch 300/469 | Cost: 0.1235\n", "Epoch: 002/010 | Batch 350/469 | Cost: 0.1256\n", "Epoch: 002/010 | Batch 400/469 | Cost: 0.1251\n", "Epoch: 002/010 | Batch 450/469 | Cost: 0.1195\n", "Time elapsed: 0.24 min\n", "Epoch: 003/010 | Batch 000/469 | Cost: 0.1189\n", "Epoch: 003/010 | Batch 050/469 | Cost: 0.1209\n", "Epoch: 003/010 | Batch 100/469 | Cost: 0.1197\n", "Epoch: 003/010 | Batch 150/469 | Cost: 0.1147\n", "Epoch: 003/010 | Batch 200/469 | Cost: 0.1152\n", "Epoch: 003/010 | Batch 250/469 | Cost: 0.1142\n", "Epoch: 003/010 | Batch 300/469 | Cost: 0.1146\n", "Epoch: 003/010 | Batch 350/469 | Cost: 0.1182\n", "Epoch: 003/010 | Batch 400/469 | Cost: 0.1150\n", "Epoch: 003/010 | Batch 450/469 | Cost: 0.1099\n", "Time elapsed: 0.36 min\n", "Epoch: 004/010 | Batch 000/469 | Cost: 0.1165\n", "Epoch: 004/010 | Batch 050/469 | Cost: 0.1159\n", "Epoch: 004/010 | Batch 100/469 | Cost: 0.1092\n", "Epoch: 004/010 | Batch 150/469 | Cost: 0.1112\n", "Epoch: 004/010 | Batch 200/469 | Cost: 0.1147\n", "Epoch: 004/010 | Batch 250/469 | Cost: 0.1148\n", "Epoch: 004/010 | Batch 300/469 | Cost: 0.1136\n", "Epoch: 004/010 | Batch 350/469 | Cost: 0.1141\n", "Epoch: 004/010 | Batch 400/469 | Cost: 0.1098\n", "Epoch: 004/010 | Batch 450/469 | Cost: 0.1160\n", "Time elapsed: 0.48 min\n", "Epoch: 005/010 | Batch 000/469 | Cost: 0.1103\n", "Epoch: 005/010 | Batch 050/469 | Cost: 0.1097\n", "Epoch: 005/010 | Batch 100/469 | Cost: 0.1102\n", "Epoch: 005/010 | Batch 150/469 | Cost: 0.1116\n", "Epoch: 005/010 | Batch 200/469 | Cost: 0.1120\n", "Epoch: 005/010 | Batch 250/469 | Cost: 0.1110\n", "Epoch: 005/010 | Batch 300/469 | Cost: 0.1137\n", "Epoch: 005/010 | Batch 350/469 | Cost: 0.1110\n", "Epoch: 005/010 | Batch 400/469 | Cost: 0.1087\n", "Epoch: 005/010 | Batch 450/469 | Cost: 0.1150\n", "Time elapsed: 0.60 min\n", "Epoch: 006/010 | Batch 000/469 | Cost: 0.1111\n", "Epoch: 006/010 | Batch 050/469 | Cost: 0.1038\n", "Epoch: 006/010 | Batch 100/469 | Cost: 0.1088\n", "Epoch: 006/010 | Batch 150/469 | Cost: 0.1112\n", "Epoch: 006/010 | Batch 200/469 | Cost: 0.1080\n", "Epoch: 006/010 | Batch 250/469 | Cost: 0.1091\n", "Epoch: 006/010 | Batch 300/469 | Cost: 0.1073\n", "Epoch: 006/010 | Batch 350/469 | Cost: 0.1089\n", "Epoch: 006/010 | Batch 400/469 | Cost: 0.1058\n", "Epoch: 006/010 | Batch 450/469 | Cost: 0.1121\n", "Time elapsed: 0.71 min\n", "Epoch: 007/010 | Batch 000/469 | Cost: 0.1064\n", "Epoch: 007/010 | Batch 050/469 | Cost: 0.1049\n", "Epoch: 007/010 | Batch 100/469 | Cost: 0.1067\n", "Epoch: 007/010 | Batch 150/469 | Cost: 0.1061\n", "Epoch: 007/010 | Batch 200/469 | Cost: 0.1078\n", "Epoch: 007/010 | Batch 250/469 | Cost: 0.1010\n", "Epoch: 007/010 | Batch 300/469 | Cost: 0.1028\n", "Epoch: 007/010 | Batch 350/469 | Cost: 0.1076\n", "Epoch: 007/010 | Batch 400/469 | Cost: 0.1061\n", "Epoch: 007/010 | Batch 450/469 | Cost: 0.1049\n", "Time elapsed: 0.83 min\n", "Epoch: 008/010 | Batch 000/469 | Cost: 0.1087\n", "Epoch: 008/010 | Batch 050/469 | Cost: 0.1069\n", "Epoch: 008/010 | Batch 100/469 | Cost: 0.1079\n", "Epoch: 008/010 | Batch 150/469 | Cost: 0.1028\n", "Epoch: 008/010 | Batch 200/469 | Cost: 0.1044\n", "Epoch: 008/010 | Batch 250/469 | Cost: 0.1024\n", "Epoch: 008/010 | Batch 300/469 | Cost: 0.1015\n", "Epoch: 008/010 | Batch 350/469 | Cost: 0.1013\n", "Epoch: 008/010 | Batch 400/469 | Cost: 0.1052\n", "Epoch: 008/010 | Batch 450/469 | Cost: 0.1024\n", "Time elapsed: 0.95 min\n", "Epoch: 009/010 | Batch 000/469 | Cost: 0.1059\n", "Epoch: 009/010 | Batch 050/469 | Cost: 0.1008\n", "Epoch: 009/010 | Batch 100/469 | Cost: 0.1059\n", "Epoch: 009/010 | Batch 150/469 | Cost: 0.1046\n", "Epoch: 009/010 | Batch 200/469 | Cost: 0.1038\n", "Epoch: 009/010 | Batch 250/469 | Cost: 0.1039\n", "Epoch: 009/010 | Batch 300/469 | Cost: 0.0989\n", "Epoch: 009/010 | Batch 350/469 | Cost: 0.1043\n", "Epoch: 009/010 | Batch 400/469 | Cost: 0.1048\n", "Epoch: 009/010 | Batch 450/469 | Cost: 0.1047\n", "Time elapsed: 1.06 min\n", "Epoch: 010/010 | Batch 000/469 | Cost: 0.1054\n", "Epoch: 010/010 | Batch 050/469 | Cost: 0.1027\n", "Epoch: 010/010 | Batch 100/469 | Cost: 0.1027\n", "Epoch: 010/010 | Batch 150/469 | Cost: 0.1005\n", "Epoch: 010/010 | Batch 200/469 | Cost: 0.1035\n", "Epoch: 010/010 | Batch 250/469 | Cost: 0.1006\n", "Epoch: 010/010 | Batch 300/469 | Cost: 0.1039\n", "Epoch: 010/010 | Batch 350/469 | Cost: 0.0974\n", "Epoch: 010/010 | Batch 400/469 | Cost: 0.1034\n", "Epoch: 010/010 | Batch 450/469 | Cost: 0.1026\n", "Time elapsed: 1.18 min\n", "Total Training Time: 1.18 min\n" ] } ], "source": [ "start_time = time.time()\n", "for epoch in range(num_epochs):\n", " for batch_idx, (features, targets) in enumerate(train_loader):\n", " \n", " # don't need labels, only the images (features)\n", " features = features.to(device)\n", "\n", " ### FORWARD AND BACK PROP\n", " decoded = model(features)\n", " cost = F.binary_cross_entropy(decoded, features)\n", " optimizer.zero_grad()\n", " \n", " cost.backward()\n", " \n", " ### UPDATE MODEL PARAMETERS\n", " optimizer.step()\n", " \n", " ### LOGGING\n", " if not batch_idx % 50:\n", " print ('Epoch: %03d/%03d | Batch %03d/%03d | Cost: %.4f' \n", " %(epoch+1, num_epochs, batch_idx, \n", " len(train_loader), cost))\n", " \n", " print('Time elapsed: %.2f min' % ((time.time() - start_time)/60))\n", " \n", "print('Total Training Time: %.2f min' % ((time.time() - start_time)/60))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Evaluation" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABIAAAACqCAYAAADV0DQZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzsnXe4JEX5hd8WQUlKXlZgyTnDknNGguAuEiRKWBAUQQz8EEFRJIOCpFVAco6SZYmCIEvOS1xYJLjkqAL9+2P3dNX0nZnbc+/cmZ65532efaa3p2em5rtV1TVVp86XpGmKMcYYY4wxxhhjjOlevtTuAhhjjDHGGGOMMcaYgcUTQMYYY4wxxhhjjDFdjieAjDHGGGOMMcYYY7ocTwAZY4wxxhhjjDHGdDmeADLGGGOMMcYYY4zpcjwBZIwxxhhjjDHGGNPleALIGGOMMcYYY4wxpsvp1wRQkiQbJ0nyTJIkzyVJcmCzCmWMMcYYY4wxxhhjmkeSpmnfXpgkUwDjgA2ACcD9wHZpmj7ZvOIZY4wxxhhjjDHGmP7y5X68dkXguTRNXwBIkuQiYAug5gTQLLPMks4zzzz9+MjO5IEHHpiYpumsjbzGsSqOY9UYgzFeL730EhMnTkwafd1gjBW4HTaCY1Ucx6o47t+L4/69MdwOi+NYFcd9VnHcZzWG22FxisaqPxNAcwCvRP+fAKyUvyhJklHAKIBhw4YxduzYfnxkZ5IkyfiC1zlWjlVhisZq8rWDOl7Dhw8vfO1gjxW4HTaCY1Ucx6o47t+L4/69MdwOi+NYFcd9VnHcZzWG22FxisZqwE2g0zQdnabp8DRNh886a8MTw4MKx6o4jlVjOF7FcayK41gVx7EqjmPVGI5XcRyr4jhWxXGsGsPxKo5jVRzHqjj9mQB6FZgr+v+ck88ZY4wxxhhjjDHGmBLRnwmg+4EFkySZN0mSqYBtgWuaUyxjjDHGGGOMMcYY0yz67AGUpulnSZL8ALgJmAI4M03TJ5pWMmOMMcYYY4wxxhjTFPpjAk2aptcD1zepLMYYY4wxxhhjjDFmAOjXBFAnss466wBw++23Z+cOPfRQANZee+2KR1ObrbfeGoCnn34agJNOOgmAtdZaq21l6i//+te/ADjyyCMBOOusswD48MMPa75m9tlnB+AXv/gFANttt1323Mwzzzwg5TTGGGNM5zBu3DgATjnlFABOPPFEAI4//ngA9ttvv/YUzBhTiOeeew6AESNGAPD4449nz+l3pB6NKTsDngXMGGOMMcYYY4wxxrSXQacAipU/4te//jUAd9xxR8V5K4F68umnnwLwyiuvAGEG/PDDDwc6WwH04IMPAvDHP/4RgA022ACATTbZJLtm0UUXBeA///kPAMcccwwA++67LwAPPfRQdu0ZZ5wxwCVuL//9738BGDNmDAAXXHABAJdddll2jepLntVWWw2ArbbaKju3xx57ADDttNM2v7CmY/nggw8AWG655bJzK6ywAgDnnHMOAF/+cu1b2ccffwzADjvsAMDEiRMBuP76SbuXp5tuuiaX2BhjKtG97oknJlllJknSzuJ0HbfddhsAP/7xjwGYe+65AbjqqqvaVibTHbz55psArLfeekD4/RO3Ybdn02lYAWSMMcYYY4wxxhjT5Qw6BVCapkDwAoKgCso/akXBSqDALbfcAsB9990HwFe/+lUAttxyy7aVqVm8+uqrAGy22WYAXHHFFUB9dcHSSy8NwEorrQQERQLAFltsAcC3vvWt5he2xUg1ccghh2TnLrroIgDeffddILQtqTMApphiCgBef/11AMaPHw/A3XffDcA999yTXauVuoMOOgiADTfcsMnfwnQiEyZMqHiEUJ/efvttAGabbbaarz/zzDOBUL9WXnllAL7yla80v7AlYckll8yOd955ZwB+8pOftKs4xvRKrM7OK7U7zZ/xpZdeAio9AZ999tmKa9T/DB06tGXl6hY+//zz7PinP/0pEBTX77//PgA/+9nPWl+wEnHnnXcClar8G2+8EYCNNtqo7msAbr75ZiCMx6aZZpoBKWfZ0HgWwu+dUaNGAfDee+/VfJ1+C3UT8kUdPXo0AIcddhgAa665JgCPPPJIdm2t2Oh3dCfvDulWrAAyxhhjjDHGGGOM6XIGnQJIaFYSwmqTvIDy/++UVadWcM0111T8f7fddgNg7733bkdxmsqee+5Z8ViEYcOGAbDLLrsAcNRRR2XPHXvssUB3KICkjpKaAkIGtG222QaAAw44AIB55503u0YKIK2qSEkkTj/99OxYmeS23XZbIOyzHiyeQP/+97+BkFHu8ssvB2DKKacE4OCDD86u/cEPftDi0rUPKfBiJd5HH30E1PaYipFfl1h44YWB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+lLxEHdCrPI0K3ZlipW+k/598cUX2b/8NbX+P5A0GivozLrVLByr4pSpHZYdx6o4jlVx3L83RqfHqsxjh7LFqpU4VsUZrH1W/DuhEaaYYopGP6fjY9VX3A6L02is+jMB9CowV/T/OSefM8YYY4wxxhhjjDEloj9bwO4HFkySZF4mTfxsC3y3KaUaQD7//HMAXn01zFXNMsssAEw99dQAJEnD2zIHDYqfYqSZb806dnLs9F1ef/11AN5///2K/wNMO+20AHz5y5Oazuyzzw6EOqTzZhKqL42ueBhjjDHdghTEH374IRDGTBpTGGM6g48++giA//73v9m56aabDoApp5yyLWUyplH6/Gs1TdPPkiT5AXATMAVwZpqmTzStZMYYY4wxxhhjjDGmKfRLrpCm6fXA9U0qizHGGGOMMcYYY4wZALp+v4q29Xz66acAjBkzBoAHHnggu2attdYCYL755gNgttlmA+CrX/1qy8pZZmLz448//hiAf//73wD861//AkLshgwZkl3badt+XnjhBQBuuukmINSZzz77LLtGW7ymn356AL7+9a8DsMwyywAw99xzZ9dONdVUQGdvi6tHXC8gfM8333wzO6fjoUOHAkHurmu/8pWv9Hi9MdWI61ujhpPV3qc/72GMMb2hLdAAzz77LABPPDFJKC/LgeWWWw4I405w39Qf3L+bgeKVVyYlvr7tttuA8DsAYN555wVgscUWA8L435iy4h7SGGOMMcYYY4wxpssZNAqgxx9/HICf/OQnADzzzDM9rl1ppZUA2G233QBYb731gKBuGazEygwZGJ5zzjkA/PrXvwbgtNNOA2DnnXfOru0EBVCcvvG5554DYJ999qm45uijj86OF1poIQBee+01AMaNGwfA008/DQQzaAix6CaTx//85z/Z8fPPPw+E7y7F1Pbbb1/4/a6++ursWO1snnnmAYKpnhncqI3dcccd2TkZLe60005A/dVeKfhuuOEGIKgW11xzTQAWWWSR7NpuUaHF/ZrapRQHxpQd1d98euVOUXVI+fPyyy9n5/7xj38AsOuuu1Zce+qppwKw1VZbZeficYSpj4x4x44dC8A///lPABZYYAEANt544+zawZikIx6zxYrrasTtTXHt7TXdjGL34IMPArDFFlsAYQdEzOmnnw5Ujie6BcVByZP0e1qqJ4B3330XCLtAnnzySQCWWmqpHtd2yzir0+mMu6kxxhhjjDHGGGOM6TNdPx2uWWx5tNx6660AXH755dk1H3zwAQC/+MUvgOAPJJXLXHPNlV07GFP8xSsI99xzDxCUP7vssgsAM8wwA9DTF6bsxDPRWjHS33/JJZcEgu9RfP0777wDBPWLFGWxomW11VYDgi+Q/II6afZbK5mKwSWXXJI9t/vuuwMwfPhwADbbbLOG3//hhx/Ojl9//XUAXnrpJQDWX399YPB4cantSLGi1W61v7jvUR3q5v5Ide6pp54CYNSoUT2uGTlyJBD24qu+/u9//8uuueWWWwD41re+BcBvfvMbAF588UUgqPqgM1SL1VDd0f3u/vvvz55TTFZZZRVgcK/oDiR5tUr8/05RrjSLfCwg1FH1Xe+//37F87F3nK795JNPgKAGjdXYaquxz067UXmljr3zzjuz5/LKYqEV8plnnnmAS9c9xGNSjbm22WYbICgdpUqv5uHYjeRVcxqbvvHGG9k1q666KhDuAfn7hvypIPwuWnnllYHuH4dJKatHCF4/UrxUU/4I9Ued9htIqN68/fbb2Tn1rYrDI488AsARRxwBVP6Wyff58kA6+eSTgaCegvBbSO2xm++PimG1WLV7vNm9UTfGGGOMMcYYY4wxwCBQAGmmW7NvWmUZMWJEds19991X8RqtGGi/41tvvZU9N+usswLtn7lrJfHM5cSJEyueW3rppQFYfvnlgc72mZDSS4odzU5/7Wtfy67RzK1issQSSwAhO8DNN9+cXau951KySCXVSWgVSEodqX4A5pxzTiB4Zmnv85VXXplds/jiiwMwfvx4IMRU/49XW9TO9txzTyDUtW5eeYqVKg899BAA9957LxAUMKpz888/f3bt2muvDYT+rBv7I/XD9dqNVoLls/Xee+8BwecHQnY/oVhJ+dMNsZM3m5Q/6nMgqDXVr6nddrN6rBp5BYoeYwWJFBy6j6luqJ1WUxPoGila9B4aK0D3rnAqLqp/ao/yf4gz4UjZqXuKFD+/+tWvev2cY489FoB99903OxcrgKopjlqJPl8KAWWb3W+//Wq+5vjjjweCgraTlMHtRn4jEJQ/4rDDDgNgxRVXBLpf8ai6p98reb+aY445JrtWKlCh8dff//53IPhUQei/Vl999YEodttRXy7102OPPQaEWEDwjBXqqzQWixVl8riZZpppBqbAA4Takr5/rB7WPfOnP/1p1dfW63cV1z322AOoHOuusMIKQPjd2A0oFrq/6V6o7I/xeGvYsGFAGL+3S5nYnaMSY4wxxhhjjDHGGJPR9Qqg/KqKVqS+8Y1vZOe0Wqo9w3vttRcQZj2XW2657Fr5xGgVtVtX9mI0gwmV+4khxEOZmzp5FSterYyJv5OO5Tki5YFWW2LlgVaC5TUiNUwn1Rmpn+aYYw4gZNqA8LfX7HW9bGcLLrhgxf81W3777bdn5+L3hlDvtBofq8s6tZ5pRUWrAzfeeGP2XKxKhLCSqe8a1y0psuSJodWnblCzCNWnvIInRqtMalOqI8rSB/DRRx9VvEbXSoGgvgs61yNC5ZZfUsyhhx4KwOabbw6EttxNqF2pX9HfWIowCAodxUortfI1gNBnK0YzzjgjEDxd4n5Hr5dCUT4RaoNSx0JYTZf3QSfcA7Riq9gqbhMmTMiukUpTfbVWPzWGapYfhlZKq73fVFNN1fb7geqNvADrKX9Eu1VLnYhiFqsU8qg/l+Kx21Gmub/+9a8A/PCHPwRCXzX77LNn10rtMdNMMwGh75NPyzXXXJNde8oppwBBIdmp98Y8GjNoPKX+TH5d8oKN0e89ZYrWb8a4P6r1+6Gs6O+qOEj99X//93/ZNc30M9p7772zY2U+lM9qp6r01O9D2AWiLISqZ/lxB4SYS0mm3ROtbmPlH4UYY4wxxhhjjDHGmH7hCSBjjDHGGGOMMcaYLqc7NH0NUE0qLAl3LQNjmaoBDB06FOgMCXeziE0ytZ1ArLnmmkD3yEMbRfVAW8Jiw1rJA2X41Yl1RmWO0+/mn+uL/F6v0TYy6GnGfs899wBh+1inmetVQ7JQpa7dbrvtsueOPvpoAIYMGQKELYMyT40lxpItSzobG0R3C6pf9UzAY6NdCEZ78WsOOuigimsUs27aNqf+V2a81dBWgEa+r7YBSbJctm0Vak933303AK+//joQtnhr6xaEPiffdn7+85/3+jlKQ14vvvW4/vrrgZAYoKzE25I07tG2QtUxbTmB6tsleuPwww8HYJZZZgFgk002ASq3bWoLns5pW09ZtwqoncTpyXtjscUWAzpv60iz0PYStUcl4YDaYwrVT7XzaijVeScnJOmNeGvOXXfdBYStX+LEE08EYL311svOaSyvrbHvvPMOENqVjMkhGPR2W/1UW1W922qrrYCe9hYARx55JACrrbYaEIzFu+H3juqQ6kSR+2Cz6PT07xoPads3hPqz0047AeE+p98t8b1BW6SPOuooINwLWk1nRt8YY4wxxhhjjDHGFKbXacwkSc4ENgPeTNN0icnnZgIuBuYBXgK2TtP0nYErZvPQbGc8Gycj2p133rnqa2JzW608LLzwwkA5ZzDjVTytkGrVV4/xNbW+g1JEKj11jFJLDlYjw3w6eBHXq4MPPhgIq5dLLbUUUM460xvNVklo9SFeoY/N5yCsQMmwsJNRv3PHHXcAQflzxBFHZNfINHbZZZcFgrpF52XyCMFsVfHTylQ3rXrKVE8m6jGKjfo3KX/0f6mmqiHlj4xl220g2x/0fW+++WagZxuCsIqZT21eDbVLqRevvfZaAN566y0Avve972XXtrquqWy6LwGcccYZQGV68IGgr8ofISWLUg/HKWHbie5jeoxTIN92221AsTTtQmapSqAh01QIqh496u+p+qjXxsTJOsqM0r9XM2HPI8NdrYLn+zAI9URItVCt3uTbc9nHF/q7614opVmshp1tttmA8F3ypu5KeV6Nsn//vqD2qXHELbfckj234447VlyrsbnaWRyPjz/+GAj19NFHH614/3XXXTe7VuPVTr4/irj/1neWajSv/Dn22GOz4y233BLoHoV1/HtNbShOxFIUKVcWXXTR7Jx++4wbNw6orw5VPazXr5URKX90n4zHpvouSmajcbySkFxxxRXZtVLrqZ/XY6vVdkV6yr8Aed3ygcCYNE0XBMZM/r8xxhhjjDHGGGOMKSG9KoDSNL0zSZJ5cqe3ANaefHw2cDvQug2EDZBXqGil5tJLL83O9bZ6GO87lz9HmVYZ8moUpZ2D4DUi7yLN6scrTFoJ1/fU++h9tfobo9U6pQkfbORXRbQKF3uPKMZKJdwNKynNplqaSaU/117+boibVgfGjx9fcT5Oya22mfe1Ud2SzxSENq5VF6kiukEBpDqhNO3VVpLk61Nr5UjxrobqVad6G8SebEo5mvd/iJGXlFKQ54ljpb7+iSeeAGDbbbetuHbEiBHZse4XA30v/OKLL/jggw8yv4p41W2glT/NQkqWsnlHSOn1wgsvAGH1Emorf37/+99nx/Jnk4pFalfdB+V/Bz093LrBe0uoDfzsZz+rec1vfvMbIHhx6fGGG24AKuOjv4f6Qq0iyycnbnNSo6udlx2pMdR3HXjgpPXjOKXyFltsAYTvlld4Vqub8ryReqibkPJJniOPPfZYj2vkIbjGGmsAYWwe72CQ6uPhhx8GgmL0sssuA0JabujscZfajX7DaCwBQb2heieOO+44ANZZZ53sXLcof0T8e1h9TCP30HPOOQcIXoDLLLNM9pxUrkV2hej3aKeMV1WfNH+gMYh2eUBQ+Ej5k/ewjH9za0x71VVXAe1TQPV15DYkTVM59r0ODKl1YZIko5IkGZskyVgFz1THsSqOY9UYjldxHKviOFbFcayKE8dq4sSJ7S5O6XHdKo5jVRzHqjiOVWM4XsVxrIrjWBWn38tRaZqmSZLUnPJL03Q0MBpg+PDhLTeM0Sy2ZoCvu+46oNisp5QIK6+8cnZuIN26+xorfUet5MaePfI1Oumkk4Aw4ypfEeiZZUczuNob+5Of/KTHZ0pxUMsLZ6Bpd73Ko9neaqoC7RvNex60krLES7Pg9957L1CZoUJIZdcuf6mBiJVW3Pbcc8+K8yussEJ2rBXhPFL5aBUY4JBDDqm4RpkHWs1AxEo+a4pZNaQ+idUwEFZSlOEj5te//jUQVpqL1K9m9mv9jZVWv2OfFh3nPY9iHwPFSivv6u/lHxV76miFuVr88p8jxYKUaQMZq+mnnz7LxBV/zuWXXw7AyJEjm/bZA4EUewN1v+xr3dK9SGOGs846q8c1GgdJ9au/AwT1sFQGUmzo+5VN8QTN67Ni9WrcJmuh9qLxwNtvvw2ETEz11EP1UFvX36eZnnnNilXc18p7Ja/AiBWKUq/kla5StFej3WqCgbgXakyvzHvyr5FCE4Ifi9SaUokp5rEvlcZd8uhSe99oo42A1o7jB3JMqu8ulYt+90HPbFfyYdx0002B0IeViWbFSt47UN3bNY8UnVJcr7XWWkBQ2cW/HTUOUDbf1VdfHajeN+bHYGUaZ1VDfpTyxVPfdcIJJ2TXSOWrcZZ+C8qfcffdd+/xvgMZhyL0VQH0RpIkQwEmP77Zy/XGGGOMMcYYY4wxpk30dQLoGkAps3YGak/LG2OMMcYYY4wxxpi2UiQN/IVMMnyeJUmSCcChwJHAJUmS7AaMB7YeyEI2A0nbTznllF6vPfzwwwHYeutJX2uBBRYYuII1EcnJ4lTkQlJsmc7GhrJ5JG2uZtCr2AwfPhwolxl2O5F0L7+dDoI8vptML/vKk08+WfEYo22KnW7SK2LZe97H5PTTTwcakxtXqz/a1jTLLLP0pYilRFuS4i1veZReOi/7V3906KGH9niNtu+qPerv8+6772bXaHuGUsVr20Y7DTFVTpmm6hGqf0+oNP5UvbnggguA8H3zWzCKEBu1tlq2rM+RgSTAEkssUfVamcTGbUb9ibYYaqtpfI0k7/qe2uKkLXFxQggZqtZLdytkmlm2+6W+u7YhaZwUo22VkrjHSSbUPhQ3jTM62UC2KHH/Hsekt+tlNpvfttlXtEVf2zNUR+M+oN3EsarVr8cm1mqbamNKGb/PPvv0eJ3GpLonlK2N9Qdtcdb2X20bjLeA7brrrkDY+iXUBuPEE4qftqJr65fqSre125tuugmA/fffv8dzMs1ef/31gXJu/Wo28Zb5atYeEMzqAZZbbjkgtE3de6uZFqvuaAtqvW2xSh7QKW1VYwb18zIKn2+++bJr9J2ee+45IBi177LLLj3e73e/+x3Q0zC61RTJArZdjad6mncYY4wxxhhjjDHGmNJRPoe+JqP0iVpJePDBB3tco9VCpV+WeVWnKH9kFCfT5tj0TWiGX6n64tUCrdp98MEHFY8y64vRirvM1RZaaCEgzAjHqqH8rGa3rS7EaMVY9QxCvdL31uz7YFQCyYR93LhxQE9FDASz9U5JZ9sIWgFV2kyt6MkwDkIbVV2SYfbtt98OwHbb9ZyLl7Kgmlqvk4hXiKXC2W+//SqukdoJQn+jvk99zd/+9rean6FVHJkfa6Xqrrvuyq6REkL3ACkmY9WJPqtVJuUqk/p1pe6tx/e///0BKYuMZqF2WvmBJjYW1v1H9z71M/q7xeou/b2k0pTpeqxqUruUWkhtUqq02Ey0kdSt+UQAZVn51HhA37saRerbrbfeCoQVUSkSY4We4t+p97+84k19D1TWs1rUWnGvhoyd9VmnnnoqEFaXq6H7RbtSCtcjToFcKzOPxurxNaqfsVF9HqmF9Pr8OCvupzttDKr+TO3rl7/8JQB/+tOfsmsWWWQRoGf9VJ8Ym/1K3aG+TzHqtLjUQn97mfX+8Ic/7HGNFGO6R7z11ltAMEiupuIvS3/dKLrfqH+v9ptO6J635JJLZuekdNFz9fruvPKlHjJV7hTVnsY6ehw7dixQ2e/LoF2/cXbbbbea76dxuxS0nWYCbYwxxhhjjDHGGGM6hK5XAMkPp16KSO1flwKh0/aC6jtqlSReSRFa5dSM8HnnnZc9N8MMMwA90wRfccUVPd5H+2a1EjF69GggpAWMV3rkubHUUksBnbvKEK8g5Vdw9Z10/sILL8yu3X777YGgJOvUlc/+oLhoNUorevLNOP/887NrtR+2U+tJnvh7qC1ss802QPWVKSmfpFBRfdEe/2rofTvdLymOlfqjPFrphbASo1jJV6KawlNUi3kttFIqPyr5n7QD9eux4qAZSKEoTxII6s886ufjPe9lQooT1QupILTCBqGO6Tn9P75G/in59OV6TTyO0D2wFsccc0x2LOVUWVY6dU9T/6xVX6VEhhAXlVn1UCmkY9Zdd92K/2ucIMUjhHGA/laddj/M35fiv2Uz/HbOPffc7Fh9nZQaM844IxAUxtXqnlaVy3gviGNVy38yVjdJnSHvrXoKNdUjjVvHjBkDBJV7rN5UevmytMOiKHX5wQcfDITfKhCUrFK/qE1LdRh7lJ144okALL/88kB3eQdCGF9KoVEN9fGKk3xdzj77bCB4uUCIc6d6JOm3oZRkak/V0PeXlxjU/90MlTse7rnnHiCkjM+z4YYbZsfabdMp7XDYsGFA6Ls0Top93KTUzivXReytJOWy+vV21avOiL4xxhhjjDHGGGOM6TOlUwBpZUqP1fwWGpkt0wybZoZF7Cchv4f555+/DyVuP5qlffrppwG48cYbe1wj13/NQva2elkL+WhUy8YAcNhhh2XHUh0tuuiiQGUGlTIiFYFW2lXeeNZcq03TTTcdEFbbrr32WqAyi4reRzErm/9DK9AMubxZDjjgACDsYd9ss82ya7stLrEvjzyP8vvL49V0ZWVSe67XXuQJoWxVnU68whtnuYqJlY3KCiNVgvbxF8nIlCdevf/tb38LwNprrw1UKhjytGrVRqu09bKi1UNKH/XD+m5aIZZyAOB73/te1fdQjOI6XSYFh/qOfJspcs+J/4555Y/GHfK8ixVm1bJtxihTJoRsKmVB31krm3qMlTyKhZQFes3uu++eXXPvvfcCIT5//etfgeCnoechKIw1ztJKe2+rzGUl9qWJlc99Ja5PirVWlVW/643bytQe88TxUV+d52c/+1l2LEWaxrT11I/qs9TPKVaPPPIIABtssEF2rfpAjdvKqOiI+1h9B3HppZcCsMYaa2TnNBZVjFUv5W0Xj8mV1VD3Et1TpZzsxDFY3A6l/qrnW9aIF9edd94JBOWrlHn5+0RZ0f3vgQceAOp7A6r+xIrYWuieEKv2pPKvRZx1NFYZdQKKo+6P+b4GQluSf5valDJ+HXLIIdm1W265JdD+TI2d19qNMcYYY4wxxhhjTEOUbhpTq79yCdde4DjjiFa9i6wc1VoViX0myuprUBTNQsqHZ5NNNsmei1fgoO/KHyEFkWaJtdKnlXLNDEPwUirjnnQRr0wpVlrl1XeJ1Wfyi5K6Sd4Qo0aN6vHeUp8pDp24utIX4hWshx9+GAjKH8VEdTT2Ayjjalx/iP/eSy+9NBBWjlTHxo8fn12j51TfFKs//OEPAPzoRz/KrtUKSrsyMTWbuB1KMZdHPgh9Rdk/VOfkxxRncpDXj1ZVy4D6mPXWWw+AM888M3tO5VWbU/8U782Xj4g8MfTd1L/JQ6IaeS+kMvWbwqEAABWkSURBVGYYGkjUz6uOFLl/nnHGGUCIGZRXnZHvc6v9ffOr3fJRAVh//fWB0I99+9vfBkK/r5VngBEjRgBBBXnyyScDQc0Qe290AnFcmrGSG8de9UXqv2qZXYW8g+SrUUbi76asVbq/SeUUUySTUB6tygvFJfa56YSsV3HZ8vf3Z555BqhUBsnDRpnT9B01Xoh9vfR30D1QPp/6TSV1KJR/vKpxUqyM3WWXXXp9ndTnUsAeeuihNa9dc801ATjppJMA2GGHHYDwdylzPYLwN8z7Q8XoHr/EEksAxX6vVYt9rOCLkS9qrFore93Kozal+Yi99toLgBdffDG7Rr+J1R41x7D//vsDlW1ZvwnbXX86669gjDHGGGOMMcYYYxrGE0DGGGOMMcYYY4wxXU7ptoBJxvid73yn4rxke/E1Shst6VUsb5M897777gNg3333rXg/yU/j15VVpt0bkpxJmi3Jf4xMryX3j+VoSiWtbXeSl0pSe/zxx2fXauuO0thJWqjYxdulyijz099aprNxqnulbRfaMiLzSgimxtouds0119T8LJnTSQ4viba2LsbGhqqvnSBRzqO/ubbx3HTTTdlzI0eOBML2RKVG1vdt5Hv21Qi+DGjbg9KvCvVhEOqDZLUyBdX2k7jN6vt3ap+ldqjvoTSl0Dcj53ooJbXMZ1dYYQUg1Ek9lh0ZCWs7IYS/f95gXtssILRLXaP/SwL/6KOP1vxMxSxOpTwYUFvUPTG/lboe2hYdb4cuK/ktAXEfW6tvie/rOs4n0FB9ibfBaQuAtmnoXqDP0fZFKOdWQ8VGfVa8Baza1opGiU3u9X4yQd57770rrj3yyCOz4wUWWKDiNWW8J8Rl0vZl2T3oMU6pXOseoDFpbOGge+pKK60EhPumfhfEY4VOGDfEZVSfr98xilW8bTn/WylPbIgsQ2ht2VQdVszirdhltW7I96vqo+uh7YAA66yzDhC+qxLaKNmL0plDaHc//OEPAVhllVWAYBsSb4ctY7tTuvJ6CQv0e1HbWKuNs/O/m2677TagMilCnuOOOw4IxuvdgO5LMgPXI4T6JAuDv//97wC8/fbbQGW7Vh1u932ufL/QjTHGGGOMMcYYY0xTKZ0CqNZs4R577NHj3FFHHQWEFVwZNkKYoZX6Io8Mr6CcSpVG0Myi0vlKIQVw+eWXA2E2X2qfeBZdRqJa9ZSRo9CKOQTlT7VVsPh8WZGa56GHHgJ6qn4ARo8eDfRMRQphdlyrTUUUQDK/1GyxzNLiVcOllloKCEavZVxNqIVWLrUaEBt/Cq08aQUlNmbsDdXLeBVDM+dlTufaKPpOMumVakiGjbEJslbYO/V7x6tM0HgffOKJJwKh/5k4cSJQmWpT6P6glWKZIXcK+b9xtb4hH79qqc3V52sVT0qzasbGer1MQYukU+8mtHL6/PPPAz0VxNVQnJUQQe23jKj9KSW3+th4RbMZxCvkc845JwC77rorENqq6mU8Jmn3ymg16rVDfbf+ENcxqYTjMW3M8OHDs2OprzplzKB2IUW0xpdSsldDiifVzziN9LBhw4Dw/ctYd/qKxgIyaZaqSSneiyCTXwgqD8VeqmKNPzshduq79Fhv7KBxgpRU0FPxq3atthb/NswjdZDGZLESrYyoX69nEK+6JSVQrERUW5WqRWN8GYxLNRWjmEuprP4p/1ux29D306N+a0q1KNUUlGc81dkzH8YYY4wxxhhjjDGmV3qdkkuSZC7gHGAIkAKj0zT9Q5IkMwEXA/MALwFbp2naM59jgzz77LOFr62XFliz3rXStr7wwgvZsWZ8O2H2uxpxKm0Ie1yhZ2ppzZbH6pNx48YB8MQTT1R9/1h5UeZ95kXQylqcvk/I80ez5YpZHF+lLNXKqZAaTWqqate88cYbQEiJHnPqqacCYWWrE+Kr+Mjz4v777wcq07GecMIJQFBdaEVBKoRqM+F5XxitwsfpYrXyIlVHWWbUm4H+9to7LKVGvCdfKj+t6HXa6kp+1S5WS6gtqY7oe0t1B7D66qsDIUannXZazc9S/66V4sFG3i9KqYTr+doce+yxQFiBHizo/qZV0DjdMsA222yTHV988cUVz1166aUArLzyykC51XnyWxkzZgwQ+nKldYfQZ6utNqLS0/vFffaTTz4J9Ly3Sfmh9g7lVk+JeAwlxVizqKX8kZeJFBtQ7vTv9ZA67I477gDqK4B07QYbbACEdMyDBf2NNe6JPUylrpZKQ799VlxxRaDy94B8StS+iqhoyob6D30XqaKqccYZZwBB5QLhd57UMXofKddfffXVmu+nsYR8t8rcx0NQeL388ss1r9FvIbWx+HewxtXysxk1alTN9zniiCOAoO6XD+FgG0OoLeXbY6xw1XG7x+1FWv1nwAFpmi4GrAzskyTJYsCBwJg0TRcExkz+vzHGGGOMMcYYY4wpGb1OP6Vp+hrw2uTjD5IkeQqYA9gCWHvyZWcDtwO1JTkF0azhSSedBAT39UappfwR8Yx3J6gt6pGfha6mZMpfE39/rV5JoSJ+97vfAdWzznQqmnGVh4pWTyBknrjkkkuAoBaSvwiEWV1lRpNfkNQF8vCBkGVHPkNSWmn14LXXXsuu1WprJypZVJe0xzXecy6vAu0v1oqEVAixqkVKH/2NtPqu9//HP/6RXavMF1pt7ybyig2t1sWrwlq9ynvpdAr5/ihe0dQqk1ROaqvxSp/qhFaX8t4/sSJInixlX60baPT9lTWlmoJW9w5lb8qrS7udvL/EQQcdVPF8XvUDob+TOqHdq3pFkNpCdUBKumpcdNFFQOjLY/VFLaWO/O/i95WiM5/hqVNXiuOxkDKY6V5//vnnN+UzNL7Q6vxmm20GBOVnJ6P+SJlpqyHvH2WQG2zKnzzqW+K/v8akGq+uueaaQBgbxb8H8r8NOvGemPcfjf0k9ZtF/bbGkLEvpbxZpPSRT2o8vswj5Z2Ux52imFI5q2WGFvLXVRzUd0MYn9dT/gj9BlK965Tsqs1Gv+E0dlKbi/2NyzJGaKgWJ0kyD7AscB8wZPLkEMDrTNoiVu01o5IkGZskydjYnNj0xLEqjmPVGI5XcRyr4jhWxXGsiuNYNYbjVRzHqjiOVXEcq8ZwvIrjWBXHsSpO4WmoJEmmAy4H9kvT9P145jhN0zRJkqpL0WmajgZGAwwfPrzX5Wp5qGy44YYAXHHFFUCl2/hNN90EwHXXXVe0+Bm//vWvAZh55pmzc9rL3e5Z3UZj1R/i/etSouRX5uScH8eqLPQ1Vpr912pmtRXMrbfeuuL/cWYUZer45z//WfE+Im4Xec+RfOarVs6QD0TdkgpF3j9ql3qEoMSQGkqz4ePHjwfC3wNCndReYqn4FMejjz46u1YqqoGgle2wGvLC0H5/Zd67+eabs2vklab6qxXoVq/oNStW8cqkvne9DBtSaMQquhipDSAop9qtMGh3vZIfSxybPAcffDAQVELykWj1vbFdsZIy5sYbbyz8mjjTVbtoNF7KknrVVVcBYYVc/4fgzbLtttsCcNxxxwGVKlfdG6Ui1uqn7o9SJ0BPNffJJ58MBG+vVvVdzapbcZtQH62Mn/1VACnLk5R46u9arcAeyHao/khq4GqKRKnDlEmozAxkrOQZovGSfhfFnHfeeUDos8uiMqhFf+Ol/kKqcoAtttgCCOoL9TmxL2Vv6DcihIzIygjcLvoaK93PpBiTJyfA/vvvX3HtyJEjgUoF/6GHHlr3/aXQm1wuoNJvqR20a+yg30O33norAPvttx8Q2moZf0cXGtUlSTIlkyZ/zk/TVD3PG0mSDJ38/FCg9qjSGGOMMcYYY4wxxrSNXieAkknTrGcAT6Vpenz01DXAzpOPdwaubn7xjDHGGGOMMcYYY0x/KaIRXA3YEXgsSZKHJ587CDgSuCRJkt2A8cDWNV7fEDKd0nYZyYNj07PVVlsNgK222goIMuM4jai2D+RNsLQNoIyGTK0kllsvuOCCQNh6I0n7uuuu2+PaTkcSdRnmxaaCf/nLX4Cw/UGms7F0T4aySsPaTbHpK//5z39qPvfQQw8BcPrpp1d9PpaQ6m9z9tlnA6EeKub1jOy6Acn81R+p71Lfp/ToENJ0t2vrV1mIt7LGxObiks8rvoM1VjJ3rJc2V5JvpTQfbGgr0z777FP4NUoN3EnGvBoHSa6vNNOxGbu2eslg/YADDmjKZ6sf22ijjYCwhaOT26XGq4rnhRdeCITEDxDGHIq9Ej9oS6bGYRC2frXblmAgUD+s7Uz10r/LdLcTk2M0E/VLTz75JBDGqBC2N6m9Klad3J4aIf6e2oop75c//elPQGVqd8VrlVVWAUJCEW2n129OaP/Wr/6itrbwwgsDPRP9VKO3BEoQ7hPLL798di7uvwYjGmcqxmqHqk9l7MOKZAH7O1CrJ1mvxnljjDHGGGOMMcYYUxJKK32Rgkezu7ERr4zwpMLQzNtnn32WXaNVTj2nVfXBvgpcjbnnnhuA3XffHQizu52e8r0aMpvV31/fHUId00q54hCn2s6n0RzMKBZapZN5XrxqqXZ4zDHHAEHVIwWeVmEgtF/NlCvu3bgKWg3VyXzfJDVLrJb6+OOPAXjuuecAWGyxxYDBo2ZUjKSAknGhVq9iA2n3+ZOQSqFeym8hg9ayJEgYSGIV2b333lv4dervOnFsoTJKAav/yxwa4JprrgHgpJNOAoKZeqz4lDH0u+++W/VzYkNRKaU22WQTIIwvuqFu6bssvfTSQDAvjlXpMqbVmFT3z06qN81A31OK3mom/TKr1VigG+pIf9A46vHHHwcq68pMM80E9BzbDkbUF6+xxhpA2C2iOgZByZhPtqH7QDeNoTTe1qNUlwCHH3440DP5Tz2kntp4442BYDgOg7veQVDnTZw4EQj3SSUqicekUoO2O2aDu1c1xhhjjDHGGGOMGQSUdqozP+NfTY2imVo9Vttjl5/NbfeMW1mI46DVhXreEN1C/u8fK3p0rJU6Ux/FUsqCvffeGwgp3yEoNDQrPnToUKD6yp7banW0N33YsGHZuZdffhkIviODNVaqM6NGjQJgww03BCpTkdZLJz+YUKzWXnttAP785z8DQfkJQdGhlK7dtBpahEbugVKPLrDAAkBntsFaakOAzTbbDIAJEyYAwSdIfQ/AvvvuCwSPDfXv48ePB2DOOefMrlU77EZlsdD9TCu8eqx2jejEetMMNN5S/xOrEzRuiFVBgxn1w1JOx3FZeeWVgcExfi+K2pgei4zpu1Fllv9O8lsD2HLLLYGgVlT/Lt9OgI8++ggIuyFWWGEFAJZbbjlg8PZd1dB9TQpP8dRTTwGVu02q3RfaQffVeGOMMcYYY4wxxhhTweBa3jPGNB2tTkkJpMcY7UE2jaOVBWWGgeDVoRXBwb4SM9tss1U8mtrIi0V1Z/PNN8+e06p8nA2q24nbTq2scuLcc8/NjtdZZx0AvvGNbwxMwdqMlIdSOIlqmXHy/btVd6Yo7rt7Rz5R8luM+6luVtSZ5hLXFSl/1L9L/SlFGQSfpLyy0fREqrx8tmKNqcqo0LMCyBhjjDHGGGOMMabL8QSQMcYYY4wxxhhjTJfjLWDGGNMBzDXXXNmxzFa1/a4bDQxNc8lvE5QcXOaP1a4ZDMTfWemDL7/88ornZp55ZgCWWmqp7FpvczLGtIL8Ni9v+zL9RWPG2PgfqifGMb0jk+dll10WgEsvvRQIW8Xj7XNlGWf5V4MxxhhjjDHGGGNMl2MF0AAi4zbTO45VcRyrxuiWeMUqH60ANlv50y2xagWdHiuVvxWrUWWOVVw2pcnVqp3MVuul9R7I8pjecbyK41gVx7EqjmPVGGWOV1y2MihVyhyrGI3JF1xwQQAWXnhhAKaddlqgnOMsK4CMMcYYY4wxxhhjupyklbNrSZL8G/gImNiyD+0/s9D/8s6dpumsvV8WcKyK06Gxgv7Hq+FYQcfGy7EqjtthcRyr4rQzVuOb9PmtxH1WcRyr4rjPKo77rMZwOyyOY1Uc91nFaVmsWjoBBJAkydg0TYe39EP7QTvL61h1xmf3FcerOI5VcRyr4jhWxWl3edv9+Y3iulUcx6o4jlVx2l3edn9+o7huFcexKo5jVZxWltdbwIwxxhhjjDHGGGO6HE8AGWOMMcYYY4wxxnQ57ZgAGt2Gz+wP7SyvY9UZn91XHK/iOFbFcayK41gVp93lbffnN4rrVnEcq+I4VsVpd3nb/fmN4rpVHMeqOI5VcVpW3pZ7ABljjDHGGGOMMcaY1uItYMYYY4wxxhhjjDFdjieAjDHGGGOMMcYYY7qclk0AJUmycZIkzyRJ8lySJAe26nOLkiTJXEmS3JYkyZNJkjyRJMmPJp+fKUmSvyVJ8uzkxxlbVB7Hq3hZHKviZXGsipfFsWqsPI5X8bI4VsXL4lgVL4tj1Vh5HK/iZXGsipfFsSpeFseqeFlKHStwvBqh7bFK03TA/wFTAM8D8wFTAY8Ai7Xisxso41BgucnH0wPjgMWAo4EDJ58/EDjK8SpPvBwrx8qxcp/VSfFyrBwrx8p9VifFy7FyrBwrx8rx6q5YtUoBtCLwXJqmL6Rp+l/gImCLFn12IdI0fS1N0wcnH38APAXMwaRynj35srOBLVtQHMerOI5VcRyr4jhWjeF4FcexKo5jVRzHqjEcr+I4VsVxrIrjWBWn9LECx6sR2h2rVk0AzQG8Ev1/wuRzpSRJknmAZYH7gCFpmr42+anXgSEtKILjVRzHqjiOVXEcq8ZwvIrjWBXHsSqOY9UYjldxHKviOFbFcayK01GxAserEdoRK5tA50iSZDrgcmC/NE3fj59LJ+mx0rYUrKQ4XsVxrIrjWBXHsWoMx6s4jlVxHKviOFaN4XgVx7EqjmNVHMeqMRyv4rQrVq2aAHoVmCv6/5yTz5WKJEmmZNIf4fw0Ta+YfPqNJEmGTn5+KPBmC4rieBXHsSqOY1Ucx6oxHK/iOFbFcayK41g1huNVHMeqOI5VcRyr4nRErMDxaoR2xqpVE0D3AwsmSTJvkiRTAdsC17ToswuRJEkCnAE8labp8dFT1wA7Tz7eGbi6BcVxvIrjWBXHsSqOY9UYjldxHKviOFbFcawaw/EqjmNVHMeqOI5VcUofK3C8GqHtsUpb53a9CZMcrp8HftGqz22gfKszSWb1KPDw5H+bADMDY4BngVuAmRyvcsXLsXKsHCv3WZ0UL8fKsXKs3Gd1UrwcK8fKsXKsHK/uiVUyuRDGGGOMMcYYY4wxpkuxCbQxxhhjjDHGGGNMl+MJIGOMMcYYY4wxxpguxxNAxhhjjDHGGGOMMV2OJ4CMMcYYY4wxxhhjuhxPABljjDHGGGOMMcZ0OZ4AMsYYY4wxxhhjjPn/duxABgAAAGCQv/U9vsJoTgABAAAAzAU9tznu6aUmiAAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "##########################\n", "### VISUALIZATION\n", "##########################\n", "\n", "n_images = 15\n", "image_width = 28\n", "\n", "fig, axes = plt.subplots(nrows=2, ncols=n_images, \n", " sharex=True, sharey=True, figsize=(20, 2.5))\n", "orig_images = features[:n_images]\n", "decoded_images = decoded[:n_images]\n", "\n", "for i in range(n_images):\n", " for ax, img in zip(axes, [orig_images, decoded_images]):\n", " curr_img = img[i].detach().to(torch.device('cpu'))\n", " ax[i].imshow(curr_img.view((image_width, image_width)), cmap='binary')" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "numpy 1.15.4\n", "torch 1.0.0\n", "\n" ] } ], "source": [ "%watermark -iv" ] } ], "metadata": { "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.7.1" }, "toc": { "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }