{ "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.1\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": [ "# Convolutional Autoencoder with Deconvolutions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A convolutional autoencoder using deconvolutional layers that compresses 768-pixel MNIST images down to a 7x7x8 (392 pixel) representation." ] }, { "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", "##########################\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 => 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": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 001/010 | Batch 000/468 | Cost: 0.7191\n", "Epoch: 001/010 | Batch 050/468 | Cost: 0.6911\n", "Epoch: 001/010 | Batch 100/468 | Cost: 0.5994\n", "Epoch: 001/010 | Batch 150/468 | Cost: 0.3594\n", "Epoch: 001/010 | Batch 200/468 | Cost: 0.3035\n", "Epoch: 001/010 | Batch 250/468 | Cost: 0.2480\n", "Epoch: 001/010 | Batch 300/468 | Cost: 0.2085\n", "Epoch: 001/010 | Batch 350/468 | Cost: 0.1780\n", "Epoch: 001/010 | Batch 400/468 | Cost: 0.1586\n", "Epoch: 001/010 | Batch 450/468 | Cost: 0.1532\n", "Epoch: 002/010 | Batch 000/468 | Cost: 0.1442\n", "Epoch: 002/010 | Batch 050/468 | Cost: 0.1366\n", "Epoch: 002/010 | Batch 100/468 | Cost: 0.1362\n", "Epoch: 002/010 | Batch 150/468 | Cost: 0.1293\n", "Epoch: 002/010 | Batch 200/468 | Cost: 0.1286\n", "Epoch: 002/010 | Batch 250/468 | Cost: 0.1244\n", "Epoch: 002/010 | Batch 300/468 | Cost: 0.1226\n", "Epoch: 002/010 | Batch 350/468 | Cost: 0.1240\n", "Epoch: 002/010 | Batch 400/468 | Cost: 0.1239\n", "Epoch: 002/010 | Batch 450/468 | Cost: 0.1193\n", "Epoch: 003/010 | Batch 000/468 | Cost: 0.1196\n", "Epoch: 003/010 | Batch 050/468 | Cost: 0.1197\n", "Epoch: 003/010 | Batch 100/468 | Cost: 0.1217\n", "Epoch: 003/010 | Batch 150/468 | Cost: 0.1167\n", "Epoch: 003/010 | Batch 200/468 | Cost: 0.1115\n", "Epoch: 003/010 | Batch 250/468 | Cost: 0.1144\n", "Epoch: 003/010 | Batch 300/468 | Cost: 0.1092\n", "Epoch: 003/010 | Batch 350/468 | Cost: 0.1164\n", "Epoch: 003/010 | Batch 400/468 | Cost: 0.1141\n", "Epoch: 003/010 | Batch 450/468 | Cost: 0.1071\n", "Epoch: 004/010 | Batch 000/468 | Cost: 0.1121\n", "Epoch: 004/010 | Batch 050/468 | Cost: 0.1130\n", "Epoch: 004/010 | Batch 100/468 | Cost: 0.1043\n", "Epoch: 004/010 | Batch 150/468 | Cost: 0.1098\n", "Epoch: 004/010 | Batch 200/468 | Cost: 0.1104\n", "Epoch: 004/010 | Batch 250/468 | Cost: 0.1095\n", "Epoch: 004/010 | Batch 300/468 | Cost: 0.1105\n", "Epoch: 004/010 | Batch 350/468 | Cost: 0.1088\n", "Epoch: 004/010 | Batch 400/468 | Cost: 0.1040\n", "Epoch: 004/010 | Batch 450/468 | Cost: 0.1098\n", "Epoch: 005/010 | Batch 000/468 | Cost: 0.1045\n", "Epoch: 005/010 | Batch 050/468 | Cost: 0.1030\n", "Epoch: 005/010 | Batch 100/468 | Cost: 0.1029\n", "Epoch: 005/010 | Batch 150/468 | Cost: 0.1063\n", "Epoch: 005/010 | Batch 200/468 | Cost: 0.1056\n", "Epoch: 005/010 | Batch 250/468 | Cost: 0.1046\n", "Epoch: 005/010 | Batch 300/468 | Cost: 0.1074\n", "Epoch: 005/010 | Batch 350/468 | Cost: 0.1062\n", "Epoch: 005/010 | Batch 400/468 | Cost: 0.1029\n", "Epoch: 005/010 | Batch 450/468 | Cost: 0.1074\n", "Epoch: 006/010 | Batch 000/468 | Cost: 0.1051\n", "Epoch: 006/010 | Batch 050/468 | Cost: 0.0983\n", "Epoch: 006/010 | Batch 100/468 | Cost: 0.1031\n", "Epoch: 006/010 | Batch 150/468 | Cost: 0.1060\n", "Epoch: 006/010 | Batch 200/468 | Cost: 0.1044\n", "Epoch: 006/010 | Batch 250/468 | Cost: 0.1013\n", "Epoch: 006/010 | Batch 300/468 | Cost: 0.0992\n", "Epoch: 006/010 | Batch 350/468 | Cost: 0.1010\n", "Epoch: 006/010 | Batch 400/468 | Cost: 0.1020\n", "Epoch: 006/010 | Batch 450/468 | Cost: 0.1047\n", "Epoch: 007/010 | Batch 000/468 | Cost: 0.0979\n", "Epoch: 007/010 | Batch 050/468 | Cost: 0.0978\n", "Epoch: 007/010 | Batch 100/468 | Cost: 0.1001\n", "Epoch: 007/010 | Batch 150/468 | Cost: 0.1023\n", "Epoch: 007/010 | Batch 200/468 | Cost: 0.1008\n", "Epoch: 007/010 | Batch 250/468 | Cost: 0.0943\n", "Epoch: 007/010 | Batch 300/468 | Cost: 0.0968\n", "Epoch: 007/010 | Batch 350/468 | Cost: 0.1017\n", "Epoch: 007/010 | Batch 400/468 | Cost: 0.0988\n", "Epoch: 007/010 | Batch 450/468 | Cost: 0.0992\n", "Epoch: 008/010 | Batch 000/468 | Cost: 0.1015\n", "Epoch: 008/010 | Batch 050/468 | Cost: 0.0995\n", "Epoch: 008/010 | Batch 100/468 | Cost: 0.0988\n", "Epoch: 008/010 | Batch 150/468 | Cost: 0.0980\n", "Epoch: 008/010 | Batch 200/468 | Cost: 0.0986\n", "Epoch: 008/010 | Batch 250/468 | Cost: 0.0958\n", "Epoch: 008/010 | Batch 300/468 | Cost: 0.0958\n", "Epoch: 008/010 | Batch 350/468 | Cost: 0.0928\n", "Epoch: 008/010 | Batch 400/468 | Cost: 0.0986\n", "Epoch: 008/010 | Batch 450/468 | Cost: 0.0972\n", "Epoch: 009/010 | Batch 000/468 | Cost: 0.0985\n", "Epoch: 009/010 | Batch 050/468 | Cost: 0.0958\n", "Epoch: 009/010 | Batch 100/468 | Cost: 0.1002\n", "Epoch: 009/010 | Batch 150/468 | Cost: 0.0980\n", "Epoch: 009/010 | Batch 200/468 | Cost: 0.0973\n", "Epoch: 009/010 | Batch 250/468 | Cost: 0.0966\n", "Epoch: 009/010 | Batch 300/468 | Cost: 0.0948\n", "Epoch: 009/010 | Batch 350/468 | Cost: 0.0983\n", "Epoch: 009/010 | Batch 400/468 | Cost: 0.0986\n", "Epoch: 009/010 | Batch 450/468 | Cost: 0.0976\n", "Epoch: 010/010 | Batch 000/468 | Cost: 0.0975\n", "Epoch: 010/010 | Batch 050/468 | Cost: 0.0971\n", "Epoch: 010/010 | Batch 100/468 | Cost: 0.0976\n", "Epoch: 010/010 | Batch 150/468 | Cost: 0.0953\n", "Epoch: 010/010 | Batch 200/468 | Cost: 0.0982\n", "Epoch: 010/010 | Batch 250/468 | Cost: 0.0964\n", "Epoch: 010/010 | Batch 300/468 | Cost: 0.1003\n", "Epoch: 010/010 | Batch 350/468 | Cost: 0.0914\n", "Epoch: 010/010 | Batch 400/468 | Cost: 0.0971\n", "Epoch: 010/010 | Batch 450/468 | Cost: 0.0959\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", " 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": 6, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABIAAAACqCAYAAADV0DQZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzsnWeYHMXVhd8mmJwRwYAQOX8kkUEEEUwUSGQTDQgsg0nGlkEgG5OzychEkZPIGZGTQCByEEkGkWWCwQQDnu+HdLpqemdXvbuT57zPo2dHPT2zNXerqmv6njo3KRQKGGOMMcYYY4wxxpjmZYpaN8AYY4wxxhhjjDHGVBbfADLGGGOMMcYYY4xpcnwDyBhjjDHGGGOMMabJ8Q0gY4wxxhhjjDHGmCbHN4CMMcYYY4wxxhhjmhzfADLGGGOMMcYYY4xpcnwDyBhjjDHGGGOMMabJ6dYNoCRJfpUkyRtJkryVJMngcjXKGGOMMcYYY4wxxpSPpFAodO2FSTIlMBbYCBgPPAPsVCgUXi1f84wxxhhjjDHGGGNMd5mqG69dFXirUCi8A5AkyTVAP6DdG0BzzjlnoVevXt34lY3Js88+O6FQKPTozGscq/w4Vp2jFeM1btw4JkyYkHT2da0YK/A47AyOVX4cq/x4fs+P5/fO4XGYH8cqP56z8uM5q3N4HOYnb6y6cwNoPuD96P/jgdWyJyVJMhAYCNCzZ09Gjx7djV/ZmCRJ8s+c5zlWjlVu8sZq0rktHa/evXvnPrfVYwUeh53BscqPY5Ufz+/58fzeOTwO8+NY5cdzVn48Z3UOj8P85I1VdzyASt25bLOfrFAoDCsUCr0LhULvHj06fWO4pXCs8uNYdQ7HKz+OVX4cq/w4VvlxrDqH45Ufxyo/jlV+HKvO4Xjlx7HKj2OVn+7cABoPLBD9f37gw+41xxhjjDHGGGOMMcaUm+7cAHoGWCxJkoWSJPkFsCNwa3maZYwxxhhjjDHGGGPKRZc9gAqFwk9JkuwP3ANMCVxcKBReKVvLjDHGGGOMMcYYY0xZ6I4JNIVC4U7gzjK1xRhjjDHGGGOMMcZUgG7dAGpE1l9/fQAeeuih9NjQoUMBWG+99Yp+mvbZfvvtAXj99dcBOOusswBYd911a9am7vLhhxMtrE444QQALrnkEgC++eabdl8zzzzzAHDEEUcAsNNOO6XPzTHHHBVppzHGGGMah7FjxwJw7rnnAnDmmWcCcNpppwFw0EEH1aZhxphcvPXWWwD0798fgJdffjl9Tt8j9dOYeqc7HkDGGGOMMcYYY4wxpgFoOQVQrPwRf/3rXwF4+OGHi45bCdSW77//HoD3338fCHfAjz32WKCxFUDPPfccAGeffTYAG220EQCbbbZZes5SSy0FwA8//ADAySefDMDvf/97AMaMGZOee9FFF1W4xbXlv//9LwAjR44E4KqrrgLghhtuSM9Rf8my1lprAbDtttumx/bZZx8AZphhhvI31jQsX3/9NQArrbRSemyVVVYBYPjw4QBMNVX7l7Jvv/0WgF122QWACRMmAHDnnRN3L88444xlbrExxhSja90rr0y0ykySpJbNaToefPBBAA455BAAFlxwQQBuvvnmmrXJNAeffvopAH379gXC9594DHs8m0bDCiBjjDHGGGOMMcaYJqflFECFQgEIXkAQVEHZn8ooWAkUuP/++wEYNWoUANNOOy0AW2+9dc3aVC4++OADALbYYgsARowYAXSsLlh++eUBWG211YCgSADo168fAFtttVX5G1tlpJo46qij0mPXXHMNAF9++SUQxpbUGQBTTjklAB9//DEA//znPwF4/PHHAXjiiSfSc5WpO/zwwwHYeOONy/wpTCMyfvz4op8Q+tPnn38OwFxzzdXu6y+++GIg9K/VV18dgGmmmab8ja0TlltuufTx7rvvDsAf/vCHWjXHmMkSq7OzSu1G82ccN24cUOwJ+Oabbxado/ln3nnnrVq7moWff/45fXzYYYcBQXH973//G4A//vGP1W9YHfHII48Axar8u+++G4BNNtmkw9cA3HvvvUBYj00//fQVaWe9ofUshO87AwcOBOCrr75q93X6LtRMyBd12LBhABx99NEA9OnTB4AXXnghPbe92Oh7dCPvDmlWrAAyxhhjjDHGGGOMaXJaTgEkdFcSQrZJXkDZ/zdK1qka3HrrrUX/32uvvQAYNGhQLZpTVvbdd9+in3no2bMnAHvssQcAJ554YvrcKaecAjSHAkjqKKkpIFRA22GHHQA49NBDAVhooYXSc6QAUlZFSiJxwQUXpI9VSW7HHXcEwj7rVvEE+uyzz4BQUe7GG28EYOqppwZgyJAh6bn7779/lVtXO6TAi5V4//nPf4D2PaZi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o+j1118f8FawjmivtHIcq6uvvhponO3AsgnQ319rBJVPBrj//vuBYoNfgAUWWAAo/vyyGlDJ7nhbdaOjLb7nnnsuELZ3vf766+k52sKktWm2eEtsfK2tUDpH/1dRhUZEW25KodLusSH/qquuWnTO22+/DYTtlqeeemr63KWXXgrANddcA8Dmm28OdG27VL0iO4HOcMIJJwBw7LHHpsey18ZevXoBoYCEtuI3E+pfpZCliPpVvAWsXr4D1kcrjDHGGGOMMcYYY0zFqDsZh+7sZxU7cTnR+DGUVvPofbLnZp9vRpQlAPj222+BoLjoyOTtqaeeAuBf//oXEEoexn+LZlH+KGsSx0qfd/fddwdCZiouu6yynMq2xCWUs5x//vlFP1ViudTd30MOOQQIZT4bkQ8++ACAww8/PD0m9dNpp50GODtuSqMMW0fEJtkyWFe59lNOOQUoVhKJ/fbbrxxNbDo0L3388cdtnpNSTcq9VuXhhx8GgpFjR8UTVG55gw02qHzDGhCpWRTTrNq7kddk6667LhDWUCKrDusIXSMBTj/99A7PXWGFFdLHjaL8yaKyyVITXHfddelz9957b9G5Cy20EAD33HMPAIsttlg1mlg39O/fv+inzJsBjjjiCADeeOONoteUmqukPJaCRd8HevbsWeYWV4+XXnopfZwdb+pjWdVPzCKLLFL0f+2SgBBnFak444wzgOZSAOX52//0009AWDNojV+qj8mMXorrBRdcsCztbDTiEvEAG264YY1a0j5WABljjDHGGGOMMcY0OZOVcyRJsgAwHJgH+B8wrFAo/D1JktmBa4FewDhg+0Kh8EV775OXrmSBsmqh9o7FxMqgRs48lULljTuLsk66260se1x2uFlQ1vs///lPekzle7OlHuNyviNHjgRCGU2hffjLLLNMekzZqhNPPBEI/jel+Pnnn4HGVgANHjwYKM5EKXa77LJLl99Xe9ivv/769Njjjz9e9FPlTZsReRrIjwRC/5VPVaOjUsYAZ599NgDzzTcfEBQV8R5qjRPFRJ4RyhxLwQJWsWTR+NScVQrNY53xqmomVN51jz32yP0azX+rr756JZpUEZTB1RpIShYIip1yo9/Rnjq7EZG6t6Ny7R2pxyZ37gwzzADABRdcABTPl83Co48+2u5zim+rKX/aI74WSo0h9Yt8g7Ruaua1EQTFCbQd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jXiOhuIxLGWa5qxDDjkEgB122KFCLa498edvxWuWaTyy110IittGVWNrvQkwZMgQAC699FIgfN4555wTsAKoq0jReeyxxwKw+OKLA7D99tsDjdt3uotU6qWuBXli4mI4IYZ33303AF988QUQvivquyPAY489BoT+N8MMM1StnZVGfUFrMans4u94ipViov/POOOMVWun6Rz+ZmWMMcYYY4wxxhjT5LTMrfH5558fCBmlWNWhkt7KJOy+++4AXHDBBUDIJEBrZhPiu9za+5rN1q2++upAY6h+YlSSFYJKTKjE+4YbbpgeU0n4W2+9tejc6667DoB33nknPaa+IrVLIytZ5F8waNCg9NhNN90EBIVFz549AXjkkUfSc/SZr732WiBkBQ499FAAtttuu/RcZVD+9a9/AbDtttuW+VPUN5qbhLItir1iBzDvvPMCzT0fSXX2/PPPA7Dbbrulz/3+978HYMCAAUBQ22n++c9//pOeq4zcRx99BMDOO+8MhHm9mTKcGovHH398emyOOeYAYMsttwRgwQUXrH7DTEsSe+JpnGle03hUKeUHHnggPVc+fBrPZ555JhDGO9T39VQZ8t69e6fHsurhE044AShei5p8aJ6DsD4bNWoUENSg8lbS/NcqaE174oknAmGtAOGaJyW31g9ac73xxhvpufrOtNpqqwHNdZ0sheYqfR8EGDp0KBDWr/F8BsX9cKGFFgIaX/kTf8ZPP/0UCN65Uj7pu1Ks7tFjKfe1nlWfk8cuwHzzzVeRtpvOUb9XUGOMMcYYY4wxxhhTFpo3fZxB2SLd0d1nn33S56Rq+eqrr4CQiXr99dcBuO2229Jz47uYrcL48ePTxx9//HHRc1IHKVPXaN4T8d36PfbYY7Ln6861nP5fe+21oue1VxiC743uhNdzxnJySNn05JNPtnlOVU022GADAJZccsn0OWVIVHFJY0xKqhdeeCE9d+zYsUBQFk033XRAUC40ExonigeEOK6wwgoAjBkzBggZ4jg7t9ZaawHQr18/oFil1yxo3KjvxQo9eW5JzaIssFSc8iSDYuUUtM3iNQOK1UUXXQTAZZddlj4njxHN0XvvvTfQGPNzvVDKDyOrglU/kwIhfj7rUdVsaEzJI0MKA6n3IChq77//fiD0Sylh9Nr49Yr37373OwDWXnvt9JxY2VAv6Hp38sknA8EjMEaf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\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.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 }