{ "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.7.3\n", "IPython 7.9.0\n", "\n", "torch 1.3.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": [ "# Convolutional Autoencoder with Deconvolutions and Continuous Jaccard Distance" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A convolutional autoencoder using deconvolutional layers that compresses 768-pixel MNIST images down to a 7x7x8 (392 pixel) representation.\n", "\n", "\n", "This convolutional VAE uses a continuous Jaccard distance. I.e., given 2 vectors, $x$ and $y$:\n", "\n", "$$J(x, y)=1-\\frac{\\sum_{i} \\min \\left(x_{i}, y_{i}\\right)}{\\sum_{i} \\max \\left(x_{i}, y_{i}\\right)}$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Reference:\n", " \n", "- [1] https://en.wikipedia.org/wiki/Jaccard_index" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(0.6275)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import torch\n", "\n", "\n", "def continuous_jaccard(x, y):\n", " \"\"\"\n", " Implementation of the continuous version of the\n", " Jaccard distance:\n", " 1 - [sum_i min(x_i, y_i)] / [sum_i max(x_i, y_i)]\n", " \"\"\"\n", " c = torch.cat((x.view(-1).unsqueeze(1), y.view(-1).unsqueeze(1)), dim=1)\n", "\n", " numerator = torch.sum(torch.min(c, dim=1)[0])\n", " denominator = torch.sum(torch.max(c, dim=1)[0])\n", "\n", " return 1. - numerator/denominator\n", "\n", "\n", "\n", "# Example\n", "\n", "x = torch.tensor([7, 2, 3, 4, 5, 6]).float()\n", "y = torch.tensor([1, 8, 9, 10, 11, 4]).float()\n", "\n", "continuous_jaccard(x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Additional Imports" ] }, { "cell_type": "code", "execution_count": 3, "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": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "0it [00:00, ?it/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Device: cuda:0\n", "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to data/MNIST/raw/train-images-idx3-ubyte.gz\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "9920512it [00:02, 3410868.60it/s] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Extracting data/MNIST/raw/train-images-idx3-ubyte.gz to data/MNIST/raw\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "32768it [00:00, 280881.47it/s] \n", "0it [00:00, ?it/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to data/MNIST/raw/train-labels-idx1-ubyte.gz\n", "Extracting data/MNIST/raw/train-labels-idx1-ubyte.gz to data/MNIST/raw\n", "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to data/MNIST/raw/t10k-images-idx3-ubyte.gz\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "1654784it [00:00, 1928783.37it/s] \n", "8192it [00:00, 113077.53it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Extracting data/MNIST/raw/t10k-images-idx3-ubyte.gz to data/MNIST/raw\n", "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to data/MNIST/raw/t10k-labels-idx1-ubyte.gz\n", "Extracting data/MNIST/raw/t10k-labels-idx1-ubyte.gz to data/MNIST/raw\n", "Processing...\n", "Done!\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", "##########################\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": 5, "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 => 28x28x4\n", " self.conv_1 = torch.nn.Conv2d(in_channels=1,\n", " out_channels=4,\n", " kernel_size=(3, 3),\n", " stride=(1, 1),\n", " # (1(28-1) - 28 + 3) / 2 = 1\n", " padding=1) \n", " # 28x28x4 => 14x14x4 \n", " self.pool_1 = torch.nn.MaxPool2d(kernel_size=(2, 2),\n", " stride=(2, 2),\n", " # (2(14-1) - 28 + 2) / 2 = 0\n", " padding=0) \n", " # 14x14x4 => 14x14x8\n", " self.conv_2 = torch.nn.Conv2d(in_channels=4,\n", " out_channels=8,\n", " kernel_size=(3, 3),\n", " stride=(1, 1),\n", " # (1(14-1) - 14 + 3) / 2 = 1\n", " padding=1) \n", " # 14x14x8 => 7x7x8 \n", " self.pool_2 = torch.nn.MaxPool2d(kernel_size=(2, 2),\n", " stride=(2, 2),\n", " # (2(7-1) - 14 + 2) / 2 = 0\n", " padding=0)\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 => 31x31x1 \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=0)\n", " \n", " def forward(self, x):\n", " \n", " ### ENCODER\n", " x = self.conv_1(x)\n", " x = F.leaky_relu(x)\n", " x = self.pool_1(x)\n", " x = self.conv_2(x)\n", " x = F.leaky_relu(x)\n", " x = self.pool_2(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", " logits = x[:, :, 2:30, 2:30]\n", " probas = torch.sigmoid(logits)\n", " return logits, probas\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": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 001/010 | Batch 000/468 | Cost: 0.8663\n", "Epoch: 001/010 | Batch 050/468 | Cost: 0.8086\n", "Epoch: 001/010 | Batch 100/468 | Cost: 0.7729\n", "Epoch: 001/010 | Batch 150/468 | Cost: 0.7322\n", "Epoch: 001/010 | Batch 200/468 | Cost: 0.3983\n", "Epoch: 001/010 | Batch 250/468 | Cost: 0.2963\n", "Epoch: 001/010 | Batch 300/468 | Cost: 0.2927\n", "Epoch: 001/010 | Batch 350/468 | Cost: 0.2783\n", "Epoch: 001/010 | Batch 400/468 | Cost: 0.2780\n", "Epoch: 001/010 | Batch 450/468 | Cost: 0.2609\n", "Time elapsed: 0.14 min\n", "Epoch: 002/010 | Batch 000/468 | Cost: 0.2694\n", "Epoch: 002/010 | Batch 050/468 | Cost: 0.2671\n", "Epoch: 002/010 | Batch 100/468 | Cost: 0.2444\n", "Epoch: 002/010 | Batch 150/468 | Cost: 0.2378\n", "Epoch: 002/010 | Batch 200/468 | Cost: 0.2540\n", "Epoch: 002/010 | Batch 250/468 | Cost: 0.2515\n", "Epoch: 002/010 | Batch 300/468 | Cost: 0.2393\n", "Epoch: 002/010 | Batch 350/468 | Cost: 0.2528\n", "Epoch: 002/010 | Batch 400/468 | Cost: 0.2283\n", "Epoch: 002/010 | Batch 450/468 | Cost: 0.2420\n", "Time elapsed: 0.27 min\n", "Epoch: 003/010 | Batch 000/468 | Cost: 0.2317\n", "Epoch: 003/010 | Batch 050/468 | Cost: 0.2274\n", "Epoch: 003/010 | Batch 100/468 | Cost: 0.2489\n", "Epoch: 003/010 | Batch 150/468 | Cost: 0.2246\n", "Epoch: 003/010 | Batch 200/468 | Cost: 0.2178\n", "Epoch: 003/010 | Batch 250/468 | Cost: 0.2200\n", "Epoch: 003/010 | Batch 300/468 | Cost: 0.2200\n", "Epoch: 003/010 | Batch 350/468 | Cost: 0.2309\n", "Epoch: 003/010 | Batch 400/468 | Cost: 0.2215\n", "Epoch: 003/010 | Batch 450/468 | Cost: 0.2218\n", "Time elapsed: 0.40 min\n", "Epoch: 004/010 | Batch 000/468 | Cost: 0.2124\n", "Epoch: 004/010 | Batch 050/468 | Cost: 0.2191\n", "Epoch: 004/010 | Batch 100/468 | Cost: 0.2121\n", "Epoch: 004/010 | Batch 150/468 | Cost: 0.2184\n", "Epoch: 004/010 | Batch 200/468 | Cost: 0.2118\n", "Epoch: 004/010 | Batch 250/468 | Cost: 0.2090\n", "Epoch: 004/010 | Batch 300/468 | Cost: 0.2114\n", "Epoch: 004/010 | Batch 350/468 | Cost: 0.2150\n", "Epoch: 004/010 | Batch 400/468 | Cost: 0.2218\n", "Epoch: 004/010 | Batch 450/468 | Cost: 0.2015\n", "Time elapsed: 0.53 min\n", "Epoch: 005/010 | Batch 000/468 | Cost: 0.1985\n", "Epoch: 005/010 | Batch 050/468 | Cost: 0.2053\n", "Epoch: 005/010 | Batch 100/468 | Cost: 0.2067\n", "Epoch: 005/010 | Batch 150/468 | Cost: 0.2003\n", "Epoch: 005/010 | Batch 200/468 | Cost: 0.2004\n", "Epoch: 005/010 | Batch 250/468 | Cost: 0.2076\n", "Epoch: 005/010 | Batch 300/468 | Cost: 0.2006\n", "Epoch: 005/010 | Batch 350/468 | Cost: 0.2162\n", "Epoch: 005/010 | Batch 400/468 | Cost: 0.2137\n", "Epoch: 005/010 | Batch 450/468 | Cost: 0.2077\n", "Time elapsed: 0.67 min\n", "Epoch: 006/010 | Batch 000/468 | Cost: 0.1986\n", "Epoch: 006/010 | Batch 050/468 | Cost: 0.2048\n", "Epoch: 006/010 | Batch 100/468 | Cost: 0.2063\n", "Epoch: 006/010 | Batch 150/468 | Cost: 0.2069\n", "Epoch: 006/010 | Batch 200/468 | Cost: 0.2092\n", "Epoch: 006/010 | Batch 250/468 | Cost: 0.1947\n", "Epoch: 006/010 | Batch 300/468 | Cost: 0.2006\n", "Epoch: 006/010 | Batch 350/468 | Cost: 0.1927\n", "Epoch: 006/010 | Batch 400/468 | Cost: 0.2018\n", "Epoch: 006/010 | Batch 450/468 | Cost: 0.1964\n", "Time elapsed: 0.79 min\n", "Epoch: 007/010 | Batch 000/468 | Cost: 0.1809\n", "Epoch: 007/010 | Batch 050/468 | Cost: 0.1996\n", "Epoch: 007/010 | Batch 100/468 | Cost: 0.1942\n", "Epoch: 007/010 | Batch 150/468 | Cost: 0.1909\n", "Epoch: 007/010 | Batch 200/468 | Cost: 0.1894\n", "Epoch: 007/010 | Batch 250/468 | Cost: 0.1937\n", "Epoch: 007/010 | Batch 300/468 | Cost: 0.1956\n", "Epoch: 007/010 | Batch 350/468 | Cost: 0.1938\n", "Epoch: 007/010 | Batch 400/468 | Cost: 0.1963\n", "Epoch: 007/010 | Batch 450/468 | Cost: 0.2060\n", "Time elapsed: 0.92 min\n", "Epoch: 008/010 | Batch 000/468 | Cost: 0.1947\n", "Epoch: 008/010 | Batch 050/468 | Cost: 0.2044\n", "Epoch: 008/010 | Batch 100/468 | Cost: 0.1811\n", "Epoch: 008/010 | Batch 150/468 | Cost: 0.1980\n", "Epoch: 008/010 | Batch 200/468 | Cost: 0.1794\n", "Epoch: 008/010 | Batch 250/468 | Cost: 0.2008\n", "Epoch: 008/010 | Batch 300/468 | Cost: 0.1949\n", "Epoch: 008/010 | Batch 350/468 | Cost: 0.1843\n", "Epoch: 008/010 | Batch 400/468 | Cost: 0.1942\n", "Epoch: 008/010 | Batch 450/468 | Cost: 0.1932\n", "Time elapsed: 1.05 min\n", "Epoch: 009/010 | Batch 000/468 | Cost: 0.1901\n", "Epoch: 009/010 | Batch 050/468 | Cost: 0.1894\n", "Epoch: 009/010 | Batch 100/468 | Cost: 0.1976\n", "Epoch: 009/010 | Batch 150/468 | Cost: 0.1935\n", "Epoch: 009/010 | Batch 200/468 | Cost: 0.1949\n", "Epoch: 009/010 | Batch 250/468 | Cost: 0.1921\n", "Epoch: 009/010 | Batch 300/468 | Cost: 0.1917\n", "Epoch: 009/010 | Batch 350/468 | Cost: 0.1900\n", "Epoch: 009/010 | Batch 400/468 | Cost: 0.1913\n", "Epoch: 009/010 | Batch 450/468 | Cost: 0.1815\n", "Time elapsed: 1.19 min\n", "Epoch: 010/010 | Batch 000/468 | Cost: 0.1845\n", "Epoch: 010/010 | Batch 050/468 | Cost: 0.1910\n", "Epoch: 010/010 | Batch 100/468 | Cost: 0.1929\n", "Epoch: 010/010 | Batch 150/468 | Cost: 0.1919\n", "Epoch: 010/010 | Batch 200/468 | Cost: 0.1822\n", "Epoch: 010/010 | Batch 250/468 | Cost: 0.1974\n", "Epoch: 010/010 | Batch 300/468 | Cost: 0.1919\n", "Epoch: 010/010 | Batch 350/468 | Cost: 0.1750\n", "Epoch: 010/010 | Batch 400/468 | Cost: 0.1879\n", "Epoch: 010/010 | Batch 450/468 | Cost: 0.1785\n", "Time elapsed: 1.32 min\n", "Total Training Time: 1.32 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", " logits, decoded = model(features)\n", " #cost = F.binary_cross_entropy_with_logits(logits, features)\n", " cost = continuous_jaccard(features, decoded)\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_dataset)//batch_size, 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": 7, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABH0AAACqCAYAAAAwYjMwAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nO3dd7wU1fnH8c/EEntXRBS72FBUFLtRwW5UNHbFRMWW2I38NNbEXmMXu0Yx9q6RYOwVRKwIdlEs2Hud3x/wnZndu7t39m6d3e/79fJ1l9nZ3XMfZ2bPnfOc5wRhGGJmZmZmZmZmZq3lN41ugJmZmZmZmZmZVZ9v+piZmZmZmZmZtSDf9DEzMzMzMzMza0G+6WNmZmZmZmZm1oJ808fMzMzMzMzMrAX5po+ZmZmZmZmZWQuq6KZPEAQbBUHwahAErwVBMKRajTIzMzMzMzMzs8oEYRh27YVBMBUwDhgATACeAXYIw/Dl6jXPzMzMzMzMzMy6opJMn1WA18IwfCMMwx+B64EtqtMsMzMzMzMzMzOrxNQVvLYH8G7i3xOAfqVeMNdcc4ULLbRQBR+ZTaNGjZoUhuHc5bzGsUrPsSpPO8brrbfeYtKkSUG5r2vHWIHPw3I4Vuk5Vun5+p6er+/l8XmYnmOVnq9Z6fmaVR6fh+mVilUlN30KHawd5ooFQTAYGAzQs2dPRo4cWcFHZlMQBG+n3M+xcqxSSxurKfu2dbz69u2bet92jxX4PCyHY5WeY5Wer+/p+fpeHp+H6TlW6fmalZ6vWeXxeZheqVhVMr1rArBA4t/zA+/n7xSG4dAwDPuGYdh37rnLvgHcVhyr9Byr8jhe6TlW6TlW6TlW6TlW5XG80nOs0nOs0nOsyuN4pedYpedYlVbJTZ9ngMWDIFg4CIJpge2BO6rTLDMzMzMzMzMzq0SXp3eFYfhzEAR/Bv4DTAVcHobhS1VrmZmZmZmZmZmZdVklNX0Iw/Ae4J4qtcXMzMzMzMzMzKqkkuldZmZmZmZmZmbWpCrK9MmiddddF4AHH3ww2nbMMccA8Lvf/S7npxW37bbbAjB27FgAzj33XADWWWedhrWpUu+/P7kO+cknnwzAFVdcAcDXX39d9DXzzjsvAEceeSQAO+ywQ/TcnHPOWZN2mpmZWXaMGzcOgAsuuACAc845B4AzzzwTgAMPPLAxDTOzVF577TUABg4cCMCLL74YPae/I/XTrBk508fMzMzMzMzMrAW1XaZPMsNHjjvuOAAeeuihnO3O+Ono+++/B+Ddd98F4jvdJ5xwApDtTJ9nn30WgPPOOw+AAQMGALDJJptE+yy11FIA/PDDDwCcdtppAOy///4AjB49Otr3sssuq3GLG+vHH38EYMSIEQBcd911ANx0003RPjpe8q2xxhoAbLPNNtG2PffcE4AZZ5yx+o21zPrqq68AWHHFFaNtK6+8MgBXX301AFNPXfyr7NtvvwVg5513BmDSpEkA3HPP5HJ0M800U5VbbGaWS991L700eb2TIAga2ZyW87///Q+Agw8+GIAFF1wQgNtuu61hbbLW8NFHHwGw/vrrA/HfP8lz2OezZYEzfczMzMzMzMzMWlDbZfqEYQjEtX0gzv7J/6mRA2f8xP773/8C8NRTTwEw3XTTAbDllls2rE3V8t577wGw2WabAXDLLbcApbMIll9+eQD69esHxJkHAFtssQUAv//976vf2DpTdsTRRx8dbbv++usB+Pzzz4H43FIWBsBUU00FwAcffADA22+/DcBjjz0GwOOPPx7tqxG5I444AoANNtigyr+FZdGECRNyfkJ8PH366acAzDPPPEVff/nllwPx8bXqqqsC8Nvf/rb6jW0SvXv3jh4PGjQIgEMPPbRRzTHrVDILOz8jO2v1Ft966y0gt8bf+PHjc/bR9ad79+51a1er+OWXX6LHhx12GBBnVn/55ZcA/PWvf61/w5rIww8/DORm3993330AbLjhhiVfA3D//fcDcX9shhlmqEk7m436sxD/vTN48GAAvvjii6Kv099CrUR1TocOHQrA8ccfD8Daa68NwJgxY6J9i8VGf0dneRZIK3Gmj5mZmZmZmZlZC2q7TB/R3UeIR5VU2yf/31kZXaqHO+64I+ffu+++OwD77rtvI5pTVXvttVfOzzR69uwJwG677QbAKaecEj13+umnA62R6aMsKGVNQLxy2XbbbQfAIYccAsDCCy8c7aNMH42eKGNILr744uixVoDbfvvtgXjedLvU+Pn444+BeCW4m2++GYBpppkGgL/97W/Rvn/+85/r3LrGUaZdMuPum2++AYrXjEpS/S3p1asX0Jpz8LWaYp8+faJtGvFWHa3VVlut/g2ztnfsscfm/Fv9qzS0r7JJm9Un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jRX42CpHu8eq2c7DZuVYpedYpefre3myGqtG9C18HqbnWKXXrtesX3/9NfqvnPO4HWPVVT4P00sTq0pu+jwDLB4EwcJBEEwLbA/cUcH7mZmZmZmZmZlZlXR5elcYhj8HQfBn4D/AVMDlYRi+VLWW1ciXX34JwMSJE6NtSolacsklAfjNbypayb4tKDPqp59+AuI71+XewW5G7733HgAffvghAD/++GP03EwzzQTAxx9/DMBKK62Us93HjpmZmRWiPqj6CjPMMEPOv82suf3888/A5Lo8suCCCwIwzTTTNKJJZqlUUtOHMAzvAe6pUlvMzMzMzMzMzKxKPLRgZmZmZmZmZtaCKsr0yRJNQ5p11lmL7jNmzBgAlltuubq0KYs0Fe6yyy4DYK+99gLgzjvvBGCTTTaJ9s1auvIXX3wBwPzzz1/2a0877TQAll9++WjbOuusA8C0005bhdY1P0350zEC8Xn37bffAnHq63TTTQfA1FPHlyAVwWuWgtnWXN58883o8fTTTw/APPPMA6S71uj4/PXXX4HWmIpqZtmg78D8Pujo0aOB3L6DvwO7rp4r9Fp7UVkH9TuSRo0aBcCKK65Y1zaZlSNbf5WbmZmZmZmZmVkqbZPpk8w+KEYjLW+88QYACy20EOARg6R3330XiDN8ZPPNNwdyix5nLdPn/fffL7j9xBNPjB6vv/76AIwYMQKAI444AoDDDjusw+uefPJJAPr161fVdjaSsqEAnn32WSAuaqfzZu+99079fscdd1z0eK211gLi4tizzDJLZY21lqBssUUWWaTDc5999hkAs802W9HX6/i89957c95vq622Alrz+p78vtN1uBV/T2ttWc/a0HciwKKLLlpwn1dffRXIzfSx9HSte/755wEYO3YsABtuuCEAs88+e7RvVo+jrtD3XKFsaivP22+/DcR/Exby9NNPA62d6aMsaR1bSepn5B93+pm1vwdblf8vmJmZmZmZmZm1oLbJ9Pntb38LxHdjV1lllaL7Xn311QBsu+22ACy++OLRc8m75u1Ioyn5hg0bBsQjc1mku/gPP/wwEB8jOnaSlI2iDIN99923wz6tNKry0UcfAdCtW7eqvu8xxxzTYduZZ54JwJ577gnATDPNVNXPbHYaKfn666+BuCZUsjaURk3aoS6NsnkKKXaOJa9Dd911FxBn9sgPP/wAtFbNLWVaDh06NNo211xzAbDFFlsAcT0ks3rK7xsoQ0O1bpLLH48bNw6Ia7/pu3m++eaL9lEmaDP1yfQ76vuyWHZPUp8+fYDW6i/UWvJYuvTSS4GOGcZPPPEEACuvvHK0rR2+L7/66isAhg8fDkDv3r2j57SseLHvvPHjx0ePldWhY7iZzrNa+v7776PHt912GwA77LBDp69bY401atamekqeW5988gkAr732GhD/bXT44Yd3+j5///vfAVhmmWUAWG211aLn9HeEr3n150wfMzMzMzMzM7MW1B63bonvKOqu/4cffhg9d+211wJw8MEHA3Dsscfm/JwwYUK0b48ePWrd1KaTrA+h0eJ8a6+9NpDtUXONgKu2TCkaMdpuu+2Awpk+qiHSt29fINtzWpO1mop56aWXAFhiiSWibaqnomNIo0d6v2uuuSba96CDDgLi81ArwfXq1auitmfFe++9B8AJJ5wAwIUXXpjz/A033BA9Xm+99QCYc84569S6xtFoUyG6jmvUX9f55PGan+EjOjazfM3Kp6yov/zlLx2e0/dYO36HWW3puq7zT/9++eWXo32eeuopIM7g7Ir77rsvejxgwIAuv0+tqObdRRddlPo1peqEWGHJbIxiNQSVod8O2T0QfxfOO++8OdvPOuus6LHqHCk26pOq71Zo5WLFutUzfb777jsAZphhhtSvufXWW6PHWe+nqs/0+OOPR9vWXXfdLr/fUUcdVfS5SZMmAa3Zf83/W0eZ+8msJl2TGtH3zO5foWZmZmZmZmZmVlRr37otYZ555oke77fffgAccsghQMe55xqhAhg4cGAdWtdcNCJeyJ133gnkzrVvJ3PMMQcQj6LPP//80XPKFNMKX1nO9Jl77rkBuOKKK6JtCy+8MBCvqrTAAgt0eF1no0M77rhj9FiZPqIV0hZbbDGgtUbsdPf/lFNOibaVGhmBuMYYxPUKdPy18txorRhYSLFVGUtdsyTL9ceKSY485lONqHanc0///5NZA19++SUQj0Tqmj3NNNMAuRlkuh7p+FRm4qmnngrA1ltvHe2ra2UWz1PFST9Vgwfi7MRbbrkFiL/rakX1qaC5vk8VG9UA0Xd/Kddddx1QuGaglfb5558XfU590lbMIijklVdeAWDppZcu+LyuaQDffPMNEJ+3+p4slOEjrZrho3NWq+cttdRSnb5GNY9UGynZJ22m61E5lEmtzPLO+qHVoL59sk+bdTq3Lr/8cgD233//ovtqRWhlvSa/12otm0epmZmZmZmZmZmV5Js+ZmZmZmZmZmYtqDXz9sqUn+6d7+23365nc5qOlk8tRAWc252mPxWiJTQ1FSeLlII+aNCgaFs1piqUmrKVXxy0FegaoyLfhVJpVaxU005UePe5556L9tHylx988AEQL4HZShSrZ599tug+ipHS1JVi/dhjjxV9zfnnnw/Ey0G3AhWQ3WeffYru05XfV+egNEMKe/J7euLEiUA8XWH22WcH4lTrpDFjxgC517Ba+Otf/5rzE7JznqovBPDCCy8A8dTl559/Hqh++v9///tfIJ7GC/GS5z179gRg1llnBZp3KpSuP3/84x9Tv0bF+Ntd/jUGil9ndO5ff/31Rd9v+eWXr07DmlhyeluxaV1PP/00kDvNTX0pLcHdv3//op9x//33A601rR7ic/XRRx8F0hUr1hSoLPfh8+laf+655wJw3HHH1e2ztQDONttsAzRHv6Icug7pHAHYaKONUr9e06BVdH233XYD6vO3TqeRDoLg8iAIPgqC4MXEtjmCIBgeBMH4KT9nr20zzczMzMzMzMysHGkyfa4EzgOuTmwbAowIw/DkIAiGTPn34dVvXu0kRxfGjh1bcl8VcYT4Dl9Wsg9U6FQ/u7JE3DPPPFP0uWYdeau3UqMhGoVuhVGCah/3pQruqjB0Vs61NEaPHg3AFltsAeReWzS6rVHt/N+70IjosGHDgLgYffL9sk5Fc//v//6v6D46p3RdVoxU3LIQnautMIKp67oK6ZZS6PgpRkWfZ5555pztyaKg+c/Vmv4fJ7+P+vXrV9c2dJW+A5o100fHxu233x5t+8Mf/lDx+55++unR45133hmIi1aWOv9UKDUryjm3zjnnHKB0dnBnktluWf1+fPjhh4H4Wr3ppptGzyUXxEhSZufBBx9c9H3rfV1qhFKzDy699FIAevfuDeQWYtZxU+z7Mblcd1aurWkkz0/1mXbddddOX/fGG28ArdF3z6eC/NXK8HnwwQdzfpYqZq+C91lbTEPZUSrCrwydQi655BIgPo8KFUufaaaZgPreV+g00ycMw4eBT/M2bwFcNeXxVcCWVW6XmZmZmZmZmZlVoKs1fbqFYTgRIAzDiUEQzNPZC5qF7vhefXWcuNTZPOxkdkwWRlWSo7Gah9+nTx+gvEwf3X3UnP4k3S13ps9kpY6Lt956C4hHXiympccLGTBgAJCNc64z3333HQArrbRSzvbkcuSzzTZbyfdQvZKkH374AcjeiEkaWkq1FNU2UOyUoXLggQcWfc1mm21WhdY1B50/f/rTnzrdN80IuDLvrrnmmoLPjxo1Knr8u9/9LkULq0fXgXnmyUx3I6IR4xVXXLHBLSlMNZC6mt2jelG33norEB8brVQ3qxQt5ZzGp59OHkNVtoayCJLZCKoB9eKLk6sqqOaF+lvJfpfqKmalL6b6Y+uss07O9gceeCB63L17dyD+vRWbhx56qOj7nnnmmUCcKduKlOmk/nwhyv5U3yCZTa3sjmRWFcDFF18M5PZPslZnpRT9HQTFM3xOO+00AP7yl79E27JyTnXFI4880uXXqt81wwwzRNuUubnooosC8d/Y+u5LUi2+rBxj6l8rE65Uhs+1114LxMfOhx9+WHRf9cnqGYeaf1IQBIODIBgZBMHIjz/+uNYfl2mOVXqOVXkcr/Qcq/Qcq/Qcq/Qcq/I4Xuk5Vuk5Vuk5VuVxvNJzrNJzrErraqbPh0EQdJ+S5dMd+KjYjmEYDgWGAvTt27dhw9G6+52c35pWOVW5K1FprHRHv9BIx/vvvw/EcwjTUHbCMccc0+G5/FGaemuW4yoNVf5vZD2oZouX6rVstdVWHZ5TzYPpp5++rm2SWsRK9XpEc4LLyVoodO4OGTIEgMGDBwNdq9lViVrESudJmlVYNFKi67uuWaVUUkujEtWKla4nAGuttVan+w8dOhSIR+KUDaWRKI3+AlxxxRUAHHTQQQXfK7nSSS2zy0rFqlDGW7ObccYZa/r+XT22CtVJ6owyVyEexVT/IgsZPrW4ZqXJShRd1xZZZJFqfHRE36nVrOtWi1iVytYRZRKoL6vMp4033rjoa5S90qjM4Fr2sXSeluoT3XnnnUD8/19ZQclMfdUWy7fLLrsA9e0/1KNPqlXKVlhhhaL7qL6Mjp+u/I1Ya9WKla4RAJtvvnnZr1ffo1Q2nfpkhTJ8pJy/Q8tVi+NKWXP52c0nnnhi9FjXc2Xhqc+vvmkhCy+8cDWaV5auZvrcAWjd00HA7SX2NTMzMzMzMzOzOkuzZPsw4AmgVxAEE4Ig2B04GRgQBMF4YMCUf5uZmZmZmZmZWZPoNI8tDMMdijy1fpXbUlNfffVV6n1PPfVUIJ56oqJyza7UsqFKjy3ndylUgOrkkyff32vU1Jtmd9JJJ0WPtdR0z549gdYoSFwppSmfe+65HZ47/vjjgTjVuJXilX8ulZPeqpTcu+++u8NzilkrLVOrZTFLGT58OBBPj9MUpfvuu6/T1ypFWdO8mjGduxClCWvJ67Q09U/Tf/fdd98ut0Gp8I2ULB5ZiRNOOAHInb690EILAfH0JB2L+m599tlno3379++f+rOa9fxUv2j99dN35xSjJKW5r7766kBzT++qluT0xs4WA0kqNKW5Gt555x0gLqTaTJKxeu655wrus8QSS0SP9f2oAqeamlqICvDON998FbezWRWblgVx317fC7pmqf+kqXIAL730Us5rJ02aBLRef17XtcUXX7zoPmeffTYAa6yxBpCdfkAlVIg4LX1H7rzzzkC6pevTTLHX+Z2VPn6xKVr6vgOYZZZZgLi/PmbMGCC3MHi+eeedt1pNTC0bpbPNzMzMzMzMzKwsrX9rc4o0mT5apnbLLbcEaltsqhZKFdZUlsDSSy8NlF6KUHcqt9566w7PHXHEEQDsuOOOQFzQKyt3bGtFd4KV3ZOkmFs8Un7ooYd2eE5Lz7bikqv5I/0qppscQcgfadJzKpK65557dnhfFSgsNRKaNcklZpNU4BugX79+QJz18eabbwKlR1U0aqVR05tvvhnIzUpQxtB6660HxIX2kvvUO9a6rj/11FMVvU8lGT6SHI1vlGShWo1ya1RNI7sq9pz8XtJjZQ+ouHI53129e/eOHl944YUA7LPPPp2+rlQh0UZKZgFUQv0CFSlW5lArL3mcpEynUtefWiuV6d1oyXOsR48eBfdJZgjoGqtzVQV5C9H3ZiseayrGnJ/lm8yWUrZr/vVN/YfkcaHs1uuvvx7I3t84ab3yyiud7qO+Q62L7DcDHQuPPvpoWa875JBDgHTnlvopI0eO7HTf1VZbrax2NFqxzNXk39zqi6h/Uuq74J///CfQmAxgZ/qYmZmZmZmZmbWgtsn0KVUn4tprrwVg++23B+I75VlTKtPnqKOOAuK72qqbAnFmhbIPzj//fABGjx7d4X00aqA6NZdeeikQj4wn75qXsxx11ukubyHVqkGRZao30Ldv36L7aH5sK2aN5c+FVqacfgKMHTsWiLMUHnvsMQAGDhxY9H2VRdZKMSu2/PEXX3wRPVa2juZRP/LII52+75FHHpnzsxRdA5Xp08hMKl1zVXugWvQ77rfffqlf02x1M7p16wbABhtsUNPP0f+DF198MdrWWYaPao1A89bM0Pe4antceeWV0XPzzz8/AIstthgAd911FwDHHnts0ffTsseiaxhAr169AJhzzjkra3STSPa3qpUxJTfeeCMQX3e0/O/ee+9d9DX1XG67XMnszT322KPgPsrWg7gfqQwDZQEXctBBBwFxP2vbbbcFWiML/d577wU6/n9P1gLJ//10XCrmyVkOn376KRD3R5JZk61A54kygUtRrTvV+Nt1112B3LqnWT52kpTBrL8DS0n+rVxOnaPPPvsMSLcUfNay+Yv9bb3uuutGj5VJnqYm5SqrrAI05l5DNu9umJmZmZmZmZlZSW2T6aO6GIVsvPHGQHYzfOSHH37odJ+DDz4452elio3aQJz9otHYZqfRXM1/1fFQ6LjQKIpGp1ZeeeUO+2jeZivONU/r448/BmDBBRcs+HxyHn+rjTolaXT78ssvB+BPf/pTh32WXHJJIJ6/XyrDRwqtpJNFyZGUO++8s+A+yVEqPVY2xWGHHVbV9ihzpBnm+1drtFGjxssss0zO9jSZPso8aNZVqGrthRdeAMpb5eoPf/hD9LjZ+xa6Ph1wwAHRtvzsNl3Dk1kXyvAtJpmddsoppwCw4YYbArDccssB2R1NT16z0oygd0bxAVhppZWAuC+iLOxSshpH2WabbSp6/V577ZXzU3Vrkn2zRRZZpKLPqIfkcaXMnHzJ7B1lYyjr9csvvwTijNlkLbjDDz8ciOuXlpodkBXJmkWXXXZZ2a9XHU79vOmmm6LnVPNIfbOsrsSbzJIuRpk6abJ7dNwkV5VbYIEFSr5G36GQvdUdFZO33noLgOuuuy5nO8T1/nTMlMqILbWqXK01d0/EzMzMzMzMzMy6pOkyfTSyoar1r7/+OgCzzTZbtI+q/5dTZyH/jvkVV1wRPdYd8qxLrgRULyeddBJQeOWILIwKJ1eFGD58OBDf8dYxp7oeyed22mmnTt9b++oue7vUONK8aiie5aUaCFm7499VGhHQaGb//v2B3PoWioXmX59xxhlAvIJC0iWXXAI0fwZBWsmaGMccc0zq13Ulw2fEiBFAXKskef3XNauZVkPTqKJWvkuO3CqLUJkBOt+0SgvEx4hG5/RTI8K77bZbtG+ypkvSmmuumfNe7ULfa3369Cn7tcn/B1lRapR3rrnmAnLrGORnx+q7TqvpPfzww9G+uo4p20Cr52m11KwdW8nRfq3epVXMynHBBRcAuXVadI6q35qsd1NMshZJs0keV+rTK3twyJAhNfnMiRMnAnGtPIj7cs2cqZFs20YbbVRwn0KZAqpNmqZvqpqem2yyCdCx5mCWqB4ZlHf+KSto9913z9leKuNMmR7FMteblb7rSylnJTfV6EyTaa5s7EZmt1SLMr10zSp0HVFNn/vvvx+Is8YvuuiiaJ9G/m2crW9ZMzMzMzMzMzNLxTd9zMzMzMzMzMxaUNNN73r00UcB+N3vfld0H6UxbrbZZkDp6VlKtRo/fnzO9mSKctbSiotJUwhXKWaaXgLw/PPPA+kKx2rqTn7qutKRk1PMylnur95UGLFUyuESSywBwOmnnx5tS5M6KyrkpeNUKbRKiU8uY6olfZs57bgzP/74IxAXKy7kwQcfBLIx9a8W9HuX+v2VZqu040LTu0pdH7No3LhxNXtvTVnafvvtgewVVtc1YYUVVgAKTzVKc93QPvqpJY5VpLIUTe1pN2kWR8in6XftMHVVx5K+6zVdVT+XWmqpaF9NEdRyyltvvTUQT/PZaquton2baXplMclzbvbZZ+/y++i7/5tvvom2aYpvmqWnVSC1mftbSZpipWml6jMeeeSRXXq/l19+GYin6+rYUd8/2c/KWv9qvvnmA+KizP/+978BOProozvsW07fVDQdU6UMsvS3kP7mUGxKUVFviKe0aZGGHXfcEYgXFUkusnHbbbflvI8Wmdhnn32AbFynoPRCRueeey6Q7nfRufrGG2+k/mz1Y7PW7yokzfVDf4drWXZJLqDRyPMsO2e4mZmZmZmZmZml1jRDA7pr+/TTT3e6r+5o//3vfwdg//33B+IRE4iXklPxy0MPPTTnPbScOMRLzWVtFCCfMgQmTJgQbdMoh4r86Q5j8q6uin+ec845QBxPSS61V2zEN3/Er9kll7wsZs899wTg7bffruiz+vbt2+k+Tz75JBDfHc7isagsMC19mXThhRcCcWZVV+50J0fssnKcVUKZAhqRSo4Et8ry9rruP/LII53ue8stt0SP02Qlikb2WmGkCSq/Nijm+m4sVUhVBRvb4XxLUoyee+65sl+rgv1ZuIbrmlqr/7/KJgOYf/75C+6jQrvJ63tWRtClksVA/vjHP5b9mosvvjh6rGyqLBxvELdT54kyvtJk+qgIdHJ56GLfhbreZ/m6r1gp6/wvf/kLkPu3TlcWM3jiiSeAuCBxljJ8RNdoFY0vRX0AiPtTiq1+d2WG5Wf3tIJkset8ypBTFk+p7wItTrPeeut1+pn6G7uSLMgs02JUkvz+a2R/qtMzPQiCBYIg+F8QBK8EQfBSEAQHTNk+RxAEw4MgGD/lZ3v+nzUzMzMzMzMza0Jpbjf9DBwShuGzQRDMDIwKgmA4sBswIgzDk4MgGAIMAQ7vakN011V3/f/61792+pqjjjoq5+fZZ58dPXfggQeWfK1GDABWXnnl8hrbpBRDLWmflkbjevfunbNdtWySNR+yMprUmU8++aToc8OGDQPiu7Ga61suzf/dfPPNO91XGW46FrMUZ9W9SI6+5Vt00UWB8paF1EiOIiN4RwAADaZJREFUsg00nx3iTA+NUrQijWDqPNQ8csjW8ZFGqZGjjz/+GMhdVlZLkGoJ1eWWW67o61WvwCZTluNqq61WdJ9LLrkEKP+7pFUoRquvvnrq1xx33HFANjKHVTNGo96iZWYh/i5SFktXsgGSMZh11lkL7qP+m5Zyz4rk77b00kvX5TO1zP3GG28cbctaVpQoflr2+eSTT46ey88+VCZoFpZcryV9lyWXGe8s0yeZCaOsi2LnYhYlMwTz5devg7impn6qvufEiRM7/SxlRmXtnCuWZQnxTA/FIXlsqA+uTKFLL720089SHPW+7Sp/1tK8887boJbk6vRbPAzDiWEYPjvl8VfAK0APYAvgqim7XQVsWatGmpmZmZmZmZlZecqaWBYEwULACsBTQLcwDCfC5BtDQRDMU40G6U7qM888A5SXhdNZdk/Sa6+9Fj3WHd8szmutBmVqJFc0g3iOayvGRfPJC9lhhx1Sv49q1Wy77bZAblaB4qaRCB1nWuUqmUGkEZgsxlqr1ZSiFRY0aqyssuTqOJpTrBpSpbI/lBHYyjRKteyyy3Z4ThkuWcgqKEXtXnHFFaNtyRUAofA5odXPtEpQKZpb3Sp1kCpVan6/qLZYFq9HldDI5kMPPVT2a7XqSxZGgZXpk2+DDTbosE0ZGHvvvTeQW79G56/iJvq3MvIgdwWdpFtvvRXIRtyKUb+1VrT6jkbPW+m8VJ2QUrXF9B2Y1e+5astfjThJGeY6l6eddtq6tKnedA6UysbUdeiKK66Itn3++edA3B9I83fjBRdcAMBGG23UtcY2WKnVN9X3OuGEE4DcFRfVVxg8eHCnn3HNNdcAzvBR//XFF18E4pX2mqW2WOpvjiAIZgJuBg4Mw/DLzvZPvG5wEAQjgyAYqTR9K8yxSs+xKo/jlZ5jlZ5jlZ5jlZ5jVR7HKz3HKj3HKj3HqjyOV3qOVXqOVWmpMn2CIJiGyTd8rg3DUEuofBgEQfcpWT7dgY8KvTYMw6HAUIC+ffuGhfZJ0miP7j7qTqNqDEDh1YHKlcwiaJbRg3JjVS2ffvppwe3NPPJWaaxUH+T888+Ptu23336pX//www8DcSaaVloqRHHUT40w5NdTqKVaHltpRsW16sSJJ54IwBtvvAHAjjvu2OlrTz31VCA3u0dz+2uhUedhPl2XCs3H1qpCq666KtC40bxaxKqcUWxl/JSiL/40+9ZSsxxXaVY7afTocKNipazMclZsvOGGGwCYe+65a9KmNMqNlzLkTjrpJKB0n0oZGPp52mmnRc8pi65Pnz4AzDfffECc8ZqsuZafDSTqi9Ure6UWx1a1ry0jRowAYK211gIal6VYy/NQmc86fwo55ZRTgMZfu9OoxzVL16dC9diUkbfmmmsCjb+Gd6Za8dLKZgBXX301ALvuumvOPlqJt1x33303AP379weyex6WyvSRNKvnlbLZZpsBjf97utH9LGX4HHHEEUBcJ6/RcZE0q3cFwGXAK2EYnpl46g5g0JTHg4Dbq988MzMzMzMzMzPrijSZPmsAuwAvBEHw3JRtRwAnAzcEQbA78A7wh9o00czMzMzMzMzMytXpTZ8wDB8FiuUlrV/d5sSU6qs05GSRt/333x/o2vQYpc0m0yObeRpTPWgZbS05q9TsPfbYA2itooGi5diTRZuVDqtU0H/+858ADBgwINpHBRunn356oHlS9hpBBctUqCwNpTyW8uSTTwKw0korAfH52a6x1rSR5JK2KsSqouDNnspdK2mu3RMmTABgkUUWqXVzMmHMmDGd7vPggw8C0KtXL6B9zj1NJ9d01DQUo2Yp1JiG/n9qmXQVaR45cmS0T/J7L6mzJaLTUkHVZGHorFJ/QguEJKe1deaxxx4Dcpd917LJrXzeabrfAQccUHQflXlo9z66qDC6psYlaRn35GIi7SD594n685r+P3bsWCDd9K6zzjoLgO222y7apmW2s34e1uoaqzIDtfyMrNDfQ5rqLLqWN4vW+2vezMzMzMzMzMzKW7K9WcwwwwxAvNyzfqqoIMR3HfOzVDQik/U7t9WkO5Eq5qhRlVbM8MmnpdIhHinZaaedgLg4s4+VwnScaDlZFfEsRcs6ajRqnXXW6fB+Or9tMmWVJUePVYxX1z5l67UbxaaU5Zdfvg4tyY40WWG69rfbte+TTz4p+zXJDI2s0f9fXY9VsBTgm2++AeKi+717967KZ+p9W/E6v+iiiwJxJuYXX3wRPadMMJ1/KgqrPmk79LeS0vy+Kkpsk40ePbrDtmHDhgHxrIh2pnNJx42Wcx80aFC0j4phK0ta/Xydj614Huqak1y0R4vZdMXw4cMBWG655aJt7dZXyPfTTz8V3F5qkZ9GaL2j28zMzMzMzMzMspnpI7p7qZ9ZWNaxmTVqOcJmoTvVabIHLNa9e3cgntOavOOvueeav6+RGEtPI08rrLBCtO3rr78G4hHldh3l07GmLE8dZ8lrmetB5Npggw063WfTTTetQ0uaz6hRo1Lve/bZZwOte3wpE2fZZZcFOl7LIb7m5z+na1Zy1FznaqvGK0l9CPclitPxoGzVpHbNfuqMsgrvuOOOaNuGG24IONOikELXIfUN2vHcTM5sUH2ocePGAfD0008DsO+++3Z43bnnngvEy7KrtqmPuc6prlQyU7aRcfMV1czMzMzMzMysBXnY3cyqotCoXDuM6tbLXHPNFT3Wil5dWcGwFWVp5aRGW2KJJQD46quvgNy6Izqe2m0FGFFsijnllFOix7vssgvQPqOd+j2Tv68zMaxS7bryZFcoq1oZF9A+1x+rLs2M0Qp5Wi138ODB0T6qU6NjzP2s4orFRqvBDRw4MNrWyBkP/sY2MzMzMzMzM2tBvuljZmZmZmZmZtaCPL3LzCwDkksca6nMdl2q3SqnYyd5DKkgb7taZpllCm6/++67AejTp0+0rV2nwJlZY3lKl1VL/rGULMmgx8ni/VaY4njvvfcCsPHGGwNw88035zzfaM70MTMzMzMzMzNrQXXP9AnDsGnueDU7xyo9x6o8jld6zRKrZPE3FXRstkLZzRKrLGjGWDVrYd56xWqWWWYB4JNPPgHiJW6b7f9TZ5rx2GpWjlV6jlV6jlV5HK/06h2rLP9/qXesVl99dSBeKEN9qmbpWzVHK8zMzMzMzMzMrKqCes7VC4LgY+AbYFLdPrRyc1F5excMw3Ducl7gWKWX0VhB5fEqO1aQ2Xg5Vun5PEzPsUqvkbF6u0qfX0++ZqXnWKXna1Z6vmaVx+dheo5Ver5mpVfTWNX1pg9AEAQjwzDsW9cPrUAj2+tYZeOzu8rxSs+xSs+xSs+xSq/R7W3055fLx1Z6jlV6jlV6jW5voz+/XD620nOs0nOs0qt1ez29y8zMzMzMzMysBfmmj5mZmZmZmZlZC2rETZ+hDfjMSjSyvY5VNj67qxyv9Byr9Byr9Byr9Brd3kZ/frl8bKXnWKXnWKXX6PY2+vPL5WMrPccqPccqvZq2t+41fczMzMzMzMzMrPY8vcvMzMzMzMzMrAXV7aZPEAQbBUHwahAErwVBMKRen5tWEAQLBEHwvyAIXgmC4KUgCA6Ysn2OIAiGB0EwfsrP2evUHscrfVscq/RtcazSt8WxKq89jlf6tjhW6dviWKVvi2NVXnscr/RtcazSt8WxSt8Wxyp9W5o6VuB4laMhsQrDsOb/AVMBrwOLANMCY4Cl6/HZZbSxO7DilMczA+OApYFTgSFTtg8BTnG8midejpVj5Vj5mpWleDlWjpVj5WtWluLlWDlWjpVj5XhlP1b1yvRZBXgtDMM3wjD8Ebge2KJOn51KGIYTwzB8dsrjr4BXgB5MbudVU3a7CtiyDs1xvNJzrNJzrNJzrMrjeKXnWKXnWKXnWJXH8UrPsUrPsUrPsUqv6WMFjlc5GhGret306QG8m/j3hCnbmlIQBAsBKwBPAd3CMJwIk/8HAfPUoQmOV3qOVXqOVXqOVXkcr/Qcq/Qcq/Qcq/I4Xuk5Vuk5Vuk5VullKlbgeJWjXrGq102foMC2plw2LAiCmYCbgQPDMPyyUc0osM3xKtKEAtscqyJNKLDNsSrShALbHKsSzSiwzfEq0oQC2xyrIk0osM2xKtKEAtscqxLNKLDN8SrShALbHKsiTSiwzbEq0oQC2xyrIk0osK0pYwWOVznqGat63fSZACyQ+Pf8wPt1+uzUgiCYhsmBvzYMw1umbP4wCILuU57vDnxUh6Y4Xuk5Vuk5Vuk5VuVxvNJzrNJzrNJzrMrjeKXnWKXnWKXnWKWXiViB41WOeseqXjd9ngEWD4Jg4SAIpgW2B+6o02enEgRBAFwGvBKG4ZmJp+4ABk15PAi4vQ7NcbzSc6zSc6zSc6zK43il51il51il51iVx/FKz7FKz7FKz7FKr+ljBY5XORoSq7B+Vao3YXJl6teBI+v1uWW0b00mp349Dzw35b9NgDmBEcD4KT/ncLyaK16OlWPlWPmalaV4OVaOlWPla1aW4uVYOVaOlWPleGU7VsGUDzYzMzMzMzMzsxZSr+ldZmZmZmZmZmZWR77pY2ZmZmZmZmbWgnzTx8zMzMzMzMysBfmmj5mZmZmZmZlZC/JNHzMzMzMzMzOzFuSbPmZmZmZmZmZmLcg3fczMzMzMzMzMWpBv+piZmZmZmZmZtaD/B7s3mgKWnaN4AAAAAElFTkSuQmCC\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": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch 1.3.0\n", "matplotlib 3.1.0\n", "torchvision 0.4.1a0+d94043a\n", "numpy 1.17.2\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.3" }, "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": 4 }