{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# A Simple Autoencoder\n", "\n", "We'll start off by building a simple autoencoder to compress the MNIST dataset. With autoencoders, we pass input data through an encoder that makes a compressed representation of the input. Then, this representation is passed through a decoder to reconstruct the input data. Generally the encoder and decoder will be built with neural networks, then trained on example data.\n", "\n", "![Autoencoder](assets/autoencoder_1.png)\n", "\n", "In this notebook, we'll be build a simple network architecture for the encoder and decoder. Let's get started by importing our libraries and getting the dataset." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "import numpy as np\n", "import tensorflow as tf\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.\n", "Extracting MNIST_data/train-images-idx3-ubyte.gz\n", "Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.\n", "Extracting MNIST_data/train-labels-idx1-ubyte.gz\n", "Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.\n", "Extracting MNIST_data/t10k-images-idx3-ubyte.gz\n", "Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.\n", "Extracting MNIST_data/t10k-labels-idx1-ubyte.gz\n" ] } ], "source": [ "from tensorflow.examples.tutorials.mnist import input_data\n", "mnist = input_data.read_data_sets('MNIST_data', validation_size=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below I'm plotting an example image from the MNIST dataset. These are 28x28 grayscale images of handwritten digits." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\ngg==\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "img = mnist.train.images[2]\n", "plt.imshow(img.reshape((28, 28)), cmap='Greys_r')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll train an autoencoder with these images by flattening them into 784 length vectors. The images from this dataset are already normalized such that the values are between 0 and 1. Let's start by building basically the simplest autoencoder with a **single ReLU hidden layer**. This layer will be used as the compressed representation. Then, the encoder is the input layer and the hidden layer. The decoder is the hidden layer and the output layer. Since the images are normalized between 0 and 1, we need to use a **sigmoid activation on the output layer** to get values matching the input.\n", "\n", "![Autoencoder architecture](assets/simple_autoencoder.png)\n", "\n", "\n", "> **Exercise:** Build the graph for the autoencoder in the cell below. The input images will be flattened into 784 length vectors. The targets are the same as the inputs. And there should be one hidden layer with a ReLU activation and an output layer with a sigmoid activation. Feel free to use TensorFlow's higher level API, `tf.layers`. For instance, you would use [`tf.layers.dense(inputs, units, activation=tf.nn.relu)`](https://www.tensorflow.org/api_docs/python/tf/layers/dense) to create a fully connected layer with a ReLU activation. The loss should be calculated with the cross-entropy loss, there is a convenient TensorFlow function for this `tf.nn.sigmoid_cross_entropy_with_logits` ([documentation](https://www.tensorflow.org/api_docs/python/tf/nn/sigmoid_cross_entropy_with_logits)). You should note that `tf.nn.sigmoid_cross_entropy_with_logits` takes the logits, but to get the reconstructed images you'll need to pass the logits through the sigmoid function." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Size of the encoding layer (the hidden layer)\n", "encoding_dim = 32\n", "\n", "image_size = mnist.train.images.shape[1]\n", "\n", "inputs_ = tf.placeholder(tf.float32, (None, image_size), name='inputs')\n", "targets_ = tf.placeholder(tf.float32, (None, image_size), name='targets')\n", "\n", "# Output of hidden layer\n", "encoded = tf.layers.dense(inputs_, encoding_dim, activation=tf.nn.relu)\n", "\n", "# Output layer logits\n", "logits = tf.layers.dense(encoded, image_size, activation=None)\n", "# Sigmoid output from\n", "decoded = tf.nn.sigmoid(logits, name='output')\n", "\n", "loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=targets_, logits=logits)\n", "cost = tf.reduce_mean(loss)\n", "opt = tf.train.AdamOptimizer(0.001).minimize(cost)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Training" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Create the session\n", "sess = tf.Session()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here I'll write a bit of code to train the network. I'm not too interested in validation here, so I'll just monitor the training loss and the test loss afterwards. \n", "\n", "Calling `mnist.train.next_batch(batch_size)` will return a tuple of `(images, labels)`. We're not concerned with the labels here, we just need the images. Otherwise this is pretty straightfoward training with TensorFlow. We initialize the variables with `sess.run(tf.global_variables_initializer())`. Then, run the optimizer and get the loss with `batch_cost, _ = sess.run([cost, opt], feed_dict=feed)`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "epochs = 20\n", "batch_size = 200\n", "sess.run(tf.global_variables_initializer())\n", "for e in range(epochs):\n", " for ii in range(mnist.train.num_examples//batch_size):\n", " batch = mnist.train.next_batch(batch_size)\n", " feed = {inputs_: batch[0], targets_: batch[0]}\n", " batch_cost, _ = sess.run([cost, opt], feed_dict=feed)\n", "\n", " print(\"Epoch: {}/{}...\".format(e+1, epochs),\n", " \"Training loss: {:.4f}\".format(batch_cost))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Checking out the results\n", "\n", "Below I've plotted some of the test images along with their reconstructions. For the most part these look pretty good except for some blurriness in some parts." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "image/png": "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\nySerXN26db1427Ztqmbv3r1JGxPCu+yyy1ROns+8vDxVY+WCmDp1qspNmzbNi3ft2qVqLr74YpW7\n8cYbY76f3ANu9OjRMY9BME888YTKFStWzIuvuuoqVWPtpZdqmZmZXnzKKaeomrBzHNEUdB9kadGi\nRQkeCWKR+zV36NBB1cj9f51z7oUXXkjamBKpS5cuXiz3+3TOuU8++cSLrf2NEV6DBg1UrkePHjGP\n27hxo8rJXg2nn356oDHIvWdvvvlmVfPMM8+o3IYNGwK9PsIpXry4F8+ePVvVyP2qnXPu5Zdf9uKw\na0bWftUWa80GBcMHH3ygcueff74XB91Df9myZV5sPbMMGDBA5WT/IIu1937v3r29+O2331Y1sj+I\ntYb0wAMPqNxLL73kxfndh4JfWAMAAAAAAAAAIoEFawAAAAAAAABAJLBgDQAAAAAAAACIBBasAQAA\nAAAAAACRUGCaLibT9u3bVW7KlCkxj0tk48frr7/ei2WDRed0g4n//u//Ttj7I7Fat27txVaDRctH\nH33kxVZjNMA557p37x6zZvfu3SkYCY6X1TDz9ddfV7nSpUuHen3ZLPH9999XNYMGDVK5IA1df/jh\nB5WTTdSscQ8bNsyLrSYmI0aMULnDhw/HHFNhcsMNN6hcixYtVE42XJ05c2bSxhSPp59+2outBouy\nwbT1zIaCo3379jFrjhw5onK33HJLMoaDY5CfR+vzuXXrVpU7ePBg0sYUhHUPGjdunMr16dMn5mtd\neOGFCRkTbNb1QDbbW7lypaqxGvTK5wrrmnHvvfeqXKVKlby4bNmyqmbevHkqJ++9VqNzBFOuXDmV\n+8tf/uLFZ599tqrZv3+/yt19991eHOTZFulHXg/Gjh2rarp27Rrzdaw5NnHiRJWT827v3r0xXzuo\n8uXLq9wJJ/jLuH/+859VzeTJk724QoUKCRtTKvELawAAAAAAAABAJLBgDQAAAAAAAACIBBasAQAA\nAAAAAACRwII1AAAAAAAAACASaLqYD2rUqKFysrFARkaGqhk5cqQX09whGubPn69yZ555ZszjrCZY\n1113XULGhPTXvHnzmDVjxoxJwUhwvEqUKKFyYRssyoZ0zjnXsWNHL968eXOo17asWrVK5Z588kkv\nlg0WnXOuWLFiXnzPPfeoGqvx5LJly453iGmtf//+Kif/ts459/zzz6diOMfFajbao0cPL/7tt99U\nzfDhw72YRpwFh9XQqH79+jGPs86x1fQM+a9Zs2Yqt3jxYi/es2ePqpH3jXh07tzZi+U90DnnTjrp\npJiv88UXXyRsTAimZMmSMWseeeSRQK+Vm5vrxVaTtWuuuUblZNNFq7nogQMHVC6/m4umkwEDBsTM\nWY3krevPzp07EzcwFFiXX365F19//fWBjpPNEq+44gpVM2PGjPADE4oWLerF1jOS9f1IjiHItdRa\nX5w9e7bKRa25Ob+wBgAAAAAAAABEAgvWAAAAAAAAAIBIYMEaAAAAAAAAABAJ7GGdD+6//36Vk/uX\nWntlff/990kbE4KpU6eOyjVu3FjlTjjB/2jt379f1QwePFjlcnJy4hgd0lWXLl1UTu7N5Zxz69ev\n9+I333wzaWNC6q1du1blunXrpnKJ3LM6iIkTJ3pxv379VE29evVSNZy0IvfWPP300wMd98ADDyRj\nOHG59957Va5UqVJevGXLFlUzZcqUpI0JydWmTZtQx02aNCnBI0EYo0aN8uJp06apmrJly6rcKaec\nEvO1J0+eHH5gCSL3uh04cGA+jaTwuvbaa2PW9OrVS+VeeeWVUO9n9VIIwtrfnO9sidOpU6eYNcuX\nL1e51atXJ2E0SAdyb2irR4rlyJEjXtyuXTtVY33PCfJ8bq3vyf4KJ554oqqx1pHKlCkT8/2kffv2\nqdytt96qclHrFcMvrAEAAAAAAAAAkcCCNQAAAAAAAAAgEliwBgAAAAAAAABEAgvWAAAAAAAAAIBI\noOlikl166aUqd/3118c8rnfv3ir39ddfJ2RMCG/27NkqJ5tGWaxGNcuWLUvEkFAIXHLJJSpnzbuf\nf/7Zi3Nzc5M2JiRWRkZGzJqsrKzkDySEIkX8//dt/VuC/PvGjx+vch06dAg/sDRQsmRJLy5Xrpyq\nmTt3bqqGE5fTTjstZs3KlStTMBKkSvv27QPVyUZEY8aMScZwcJzkM69sDuWcc+eff77K9ejRw4v7\n9u2raqwmUm+//fbxDfB/Pffcc1785ZdfBjpONrPnuTz1Xn31VZVr0aKFFzdt2lTVnHXWWSrXunVr\nL7766qtVjbynOqevP1bNVVddpXLPPvusFy9YsEDVIJjOnTvHrGnWrJnKyc++c879/e9/9+I5c+aE\nHxgKLHk/GTx4sKo588wzVa5ChQpefP/996uavLy8mO9v1QT5LmQJ0mDRej+5dnjllVeqmnXr1oUa\nUyrxC2sAAAAAAAAAQCSwYA0AAAAAAAAAiAQWrAEAAAAAAAAAkcCCNQAAAAAAAAAgEmi6mGSXX365\nyskGVc7pRh8ffvhh0saE4P7whz94cd26dQMd99NPP3nxjTfemKghoRBq2bKlylnNFSZOnJiK4SBO\n9913n8oFaeARVX369PHiOnXqqBr577P+vX/84x8TO7A0sHv3bi/esGGDqmnYsKHKZWZmevG2bdsS\nO7AYatSooXLnnntuzONmzJiRjOEgRbp16+bF7dq1C3TcwYMHvXj16tWJGhISaPv27SpnNUqUuf79\n+ydtTM4Fa+hqXTutpnxIrbfeekvlnnzySS+27ifffvttqPdbsmSJysmGirLZqHP6nuqccyNHjvTi\n7t27hxoTnCtdurTKyefEE07Qy1Y33XSTyslnyalTp6qaTz/9VOVkY/Ply5ermvnz56ucZH1nmz59\nuspxn0su2dj3nHPOUTWVK1dWOXn9Oe+881TNrl27VG7NmjVeXKpUKVXTuHFjlatXr57KhfH++++r\n3LXXXuvFO3bsSMh7pRq/sAYAAAAAAAAARAIL1gAAAAAAAACASGDBGgAAAAAAAAAQCexhnWByD6aL\nLrpI1Rw5ckTl7rrrLi8+fPhwYgeGmKpVq6ZyI0aM8OKiRYsGeq2FCxd6cU5OTviBodCpVauWFzdp\n0kTVWHvSvvzyy0kbExLHui9EUfXq1VWudevWKjdkyJDjfm25t5xzeh9b6L/TunXrVI11Tr7++msv\nfuyxxxI2pjPPPFPl5L58NWvWVDVB9mkvyHu5w7mqVat6cUZGRqDjvvjii2QMB4XEc889F7NGfs9y\nzrlNmzYlYzg4DtazrNzzfMKECaqmZMmSKifvH9b+6v369VO53NxcL37vvfdUjdwL1jnn2rZt68WN\nGjVSNbJHFWyTJk1SubB7zMv7jtVPzMolk/XM+91333mxnE9IPmtPZ9m/LJFmzZqlckH2sD506JDK\n3X///V78xBNPqBprzbEg4hfWAAAAAAAAAIBIYMEaAAAAAAAAABAJLFgDAAAAAAAAACKBBWsAAAAA\nAAAAQCTQdDHBZGOj2rVrq5pFixap3EcffZS0MSGYhx9+WOWCbIQvm1s559yNN96YkDGhcJJN7GQz\nV+ec+/LLL1M1HBRSTz/9tMr17Nkz1Gvt2rXLi62mJtnZ2aFeuzC55ZZbVM5qONaiRYuYNWHJBlXO\n6WZX1jUriMcffzzUcYiGIM2KDhw4oHJjx45NwmiQjv74xz+qXMeOHb3YalC1cePGpI0JifXmm2/G\nrLn++utVTjZwvOGGG1SNdf+SBg8erHJW8/Mg99lOnTrFfD/oRpvOOffKK694sTUvihYtqnLly5f3\n4qDNf5PJeiY699xzvdh65r711luTNiYkl/Vc065du1CvNXToUJV75plnQr1WQcQvrAEAAAAAAAAA\nkcCCNQAAAAAAAAAgEliwBgAAAAAAAABEAgvWAAAAAAAAAIBIoOliHPr27atyN910kxcfPHhQ1dx7\n771JGxPC69evX6jjevXqpXI5OTnxDgeF2MknnxyzZuvWrSkYCQqT7777zovr1q2bsNdes2aNF0+b\nNi1hr12YLFy4UOXatGmjcrKxS6NGjRI2hhdffDFmzcyZM1WuQ4cOMY/bv39/qDEh9bKyslQuSEMh\n2YDVOXu+AJYgjX+/+uorlfvss8+SMRykgNVsL0hjxrCs+9CECRNUTjZdbN68uarJzMz0YtkYEv/j\nyJEjKifvC/JveTTye3mxYsVUzYMPPqhy9erVC/T6iSKbQbZu3Tql74/Euueee7zYat5apEjs3wpv\n3rxZ5f7617+GH1ga4BfWAAAAAAAAAIBIYMEaAAAAAAAAABAJLFgDAAAAAAAAACKBPawDqlatmso9\n9dRTKif3I5o/f76qmT59euIGhnx34oknqtyhQ4cS8to7duxQucOHD6uc3J+rcuXKMV+7atWqKmft\n6RXEr7/+qnJyT/B9+/aFeu3C6Pzzz49Z8/bbbyd/IEgKeZ84Wk665pprAr3+888/78Vly5YNNa68\nvLxAxwXRrFmzhL0WYpszZ84x42RbtmyZygXZw7pVq1YqZ+1Hi/zXtWtXlQtyHXv//feTMRwUEtY+\nr/K5ePjw4akaDgoJ+VzlnHNXXXWVF7dt21bVjBw50otvueWWhI4L2ltvvRWzxtpv/I477vDi3377\nTdV89NFHKvfEE0948ahRo1RNkP4OKDg6d+6scvK8Fy9ePNBryTWjgQMHqpoDBw4cx+jSD7+wBgAA\nAAAAAABEAgvWAAAAAAAAAIBIYMEaAAAAAAAAABAJLFgDAAAAAAAAACKBpotHUbRoUS+2midWrFhR\n5Xbu3OnFN954Y2IHhsj5+uuvk/ban3/+ucqtX79e5WrWrOnFVuOPVHvooYe8+LbbbsunkURbjx49\nVK5MmTL5MBKkyosvvqhy99xzT8zjXn/9dZUL0hgxbPPEsMdNnTo11HFIH2Ebi9JgseDIzMyMWbN/\n/36VGzZsWDKGgzRkzRXr+UjOs88++yxpY0LhZDXgu++++7x41qxZqmbQoEFe/MILL6iaxYsXxzk6\nHK93331X5WTTxSJF9O86L730UpVr0KCBF5966qmhxrRhw4ZQxyH1rrzySpUL0mRRNgh2zrmrr77a\niz/44IPwA0tT/MIaAAAAAAAAABAJLFgDAAAAAAAAACKBBWsAAAAAAAAAQCSwh/VRNG7c2Ivr1KkT\n6LghQ4Z48bJlyxI2JiTXt99+q3ItW7bMh5H8f23atEnYa8n914LuTyv36J43b16g42bOnBlsYIVc\n7969VU7u9WrtW/7Pf/4zaWNCcr388ssqN3jwYJUrXbp0KoZzVNb+s9ZcvOKKK7x47dq1SRsTCgbr\n/hJ2T3REk9V/Qdq+fbvK7dixIxnDQRq66aabAtVZ/V6kChUqqFyVKlW8ODs7O9jAAKe/Dz355JOq\n5u677/biv/71r6qmU6dOKmc9fyFxvvnmG5WT5/O8884L9FqnnXZazBprD3S57tC3b99A74fUsu4d\nAwYMCPVaH3/8scq98847oV6rMOEX1gAAAAAAAACASGDBGgAAAAAAAAAQCSxYAwAAAAAAAAAigQVr\nAAAAAAAAAEAk0HTROdegQQOVmzNnTszjHnvsMZWbOHFiQsaE1GvVqpXKjR071ouLFy8e6rWbNWum\ncm3btg31Wv/6179Ubvny5TGPe+2117x44cKFod4f4ZUpU0blOnfuHPO4KVOmqNyRI0cSMiak3qpV\nq1SuT58+Kicbcl511VVJG5Pl8ccfV7lRo0aldAwomII2DP3111+TPBIkQrFixVSudu3aMY87fPhw\noBwQD3kdufXWW1XNXXfdpXIrV670Yqv5HRDUuHHjVG7gwIFefM4556iapk2bqtyXX36ZuIFBsZpa\nymfsDz74QNU0bNhQ5eR3u127dqmav//97yo3aNCgmONE6pUrV86L161bp2qKFIn9m9+NGzeq3JVX\nXhl+YIUYv7AGAAAAAAAAAEQCC9YAAAAAAAAAgEhgwRoAAAAAAAAAEAksWAMAAAAAAAAAIoGmi865\n++67T+XKly8f8zir+V1eXl5CxoRoGDp0aH4PAWnk0KFDKpeTk6Nya9as8eLhw4cnbUyIhnfffTdm\n7r333lM1t912m8q1aNHCi+fPn69qnnrqKZXLyMjwYpr+IKxevXqp3MGDB1XuiSeeSMVwEKfffvtN\n5ZYsWaJy1atX92J5LwOSoUuXLseMnXNu+vTpKnfzzTcnbUwofDZt2qRyssmibPTpnHOPPvqoynXo\n0CFxA0Mgv/zyixc3a9ZM1dx+++0qd/7553vxTTfdpGqsBnyIpp49e3qxbMLoXLD1Puv7WW5ubviB\nFWL8whoAAAAAAAAAEAksWAMAAAAAAAAAIoEFawAAAAAAAABAJBS6Pax79Oihcn369MmHkQAobA4f\nPqxyDRo0yIeRoCCaPHlyoByQ35YvX65yDz30kMpNmTIlFcNBnI4cOaJyAwYMULmXX37Zi+fOnZu0\nMSH9WXvBWvv9zpo1y4vHjBmjarZt26ZyVl8RIJGys7O9eOnSpaqmdevWKte8eXMvXrBgQWIHhlDG\njRsXKIeC68EHH/TioP3pXn/9dS/m+TZx+IU1AAAAAAAAACASWLAGAAAAAAAAAEQCC9YAAAAAAAAA\ngEhgwRoAAAAAAAAAEAmFruni+eefr3LFixePedzOnTsD5QAAAAqzs88+O7+HgCRbu3atyl144YX5\nMBKkq2nTpgXKAQVF27ZtVe7nn39WuSZNmngxTReB1ChbtqwXZ2RkqJp9+/ap3LBhw5I2psKOX1gD\nAAAAAAAAACKBBWsAAAAAAAAAQCSwYA0AAAAAAAAAiAQWrAEAAAAAAAAAkVDomi4G9csvv3jxWWed\npWq2bduWquEAAAAAAIACaNeuXSpXqVKlfBgJAMtzzz3nxffdd5+qefzxx1Vu3bp1SRtTYccvrAEA\nAAAAAAAAkcCCNQAAAAAAAAAgEliwBgAAAAAAAABEQqHbw3rIkCGBcgAAAAAAAADS25/+9Kdjxkg9\nfmENAAAAAAAAAIgEFqwBAAAAAAAAAJHAgjUAAAAAAAAAIBJYsAYAAAAAAAAARELopot5eXmJHAcK\nGeYP4sH8QTyYPwiLuYN4MH8QD+YP4sH8QTyYP4gH8wdh8QtrAAAAAAAAAEAksGANAAAAAAAAAIiE\njOP5eX5GRsZW59ya5A0HBVy9vLy8qkf7j8wfHANzB/Fg/iAezB/Eg/mDeDB/EA/mD+LB/EE8mD+I\nxzHnz38c14I1AAAAAAAAAADJwpYgAAAAAAAAAIBIYMEaAAAAAAAAABAJLFgDAAAAAAAAACKBBWsA\nAAAAAAAAQCSwYA0AAAAAAAAAiAQWrAEAAAAAAAAAkXDC8RRnZmbmZWVlJWkoKOgWLFiwLS8vr+rR\n/jvzB0efyxe1AAAgAElEQVTD3EE8mD+IB/MH8WD+IB7MH8SD+YN4MH8QD+YP4hFr/vzHcS1YZ2Vl\nuW+++Sb8qJDWMjIy1hzrvzN/cDTMHcSD+YN4MH8QD+YP4sH8QTyYP4gH8wfxYP4gHrHmz38c14K1\neIOwhyKN5OXlhTqO+QPnmD+ID/MH8Qgzf5g7cI5rD+LD/EE8mD+IB/MH8WD+IB5h5g97WAMAAAAA\nAAAAIoEFawAAAAAAAABAJLBgDQAAAAAAAACIBBasAQAAAAAAAACRwII1AAAAAAAAACASWLAGAAAA\nAAAAAEQCC9YAAAAAAAAAgEhgwRoAAAAAAAAAEAksWAMAAAAAAAAAIoEFawAAAAAAAABAJLBgDQAA\nAAAAAACIBBasAQAAAAAAAACRwII1AAAAAAAAACASTsjvAQD5oWTJkipXvnx5lbv00ku9+Nxzz1U1\n1atXV7kmTZrEfL/Dhw+r3J49e7z41VdfVTUvvfRSzOPy8vJUDQqOjIyMmDVhz3GRIvr/U8r3s15b\n5phjBUexYsUC1f3222/HjJ3jvAMAgPQR5BkYyA/J/D4IFBT8whoAAAAAAAAAEAksWAMAAAAAAAAA\nIoEFawAAAAAAAABAJLBgDQAAAAAAAACIBJouolA44QR/qteqVUvV9O7dW+Wuv/56L65SpYqqKVGi\nhMoVLVrUi61GdxbZOOGBBx5QNfv371c52ZzxwIEDgd4PBYPVdMOadzVq1PDili1bqpoTTzxR5Vas\nWOHFX3/9taqRjT1//fVXe7BIKXltc865s88+24ufeuopVdOwYUOV27lzpxcPGTJE1UyfPl3lmAv5\ny7q/yJx1DbEa9chGm0EasMZDjjNIU9gjR46oGqtBKKLJmovyvFuNqkuVKqVy8lnHej5ibqRekGfe\nZF9bgCAN65L9fvL7oCXsvTjdyb9n0O/SUfzbVaxYUeW6d+/uxVlZWaomJyfHi998801Vs2nTJpXj\nvod0wi+sAQAAAAAAAACRwII1AAAAAAAAACASWLAGAAAAAAAAAEQCe1ijUAiyf5W1P+LBgwe92Nob\n2nptue9WsWLFVI21F5d8Lbl3lXPOZWdnq9yhQ4dUDgVXkPlq7Rss513z5s1VzRlnnBHztRYuXBhq\nTEgua3/E8uXLq9zNN9/sxU2bNlU11t7XMif3wnbOuRkzZqgce1gnT5A9HK17l9yrvnr16qrG2ltz\n7dq1Xrx582ZVE+Q+aM1VKyf3Ki5Xrpyqkf9muZ++c87l5uaqHHs4JlfQfdGDkNexm266SdV07dpV\n5aZOnerFL7/8sqrZvXu3F3MvC8+6b1SoUEHlGjRo4MXWs+yOHTtUTn62Dx8+rGqsPewTdU6ta6Kc\nm1YfEOuaKK+d1jUK4QW5x1jn07ovWHMqjLDfB61nKHnfs8Zt5dLp+ib/BlbvHov8ewbtexHkb2fN\nqSZNmnjxxIkTVc0pp5yictb1VJLjHDlypKr55JNPVO6OO+7w4nXr1qmadJorSG/8whoAAAAAAAAA\nEAksWAMAAAAAAAAAIoEFawAAAAAAAABAJLBgDQAAAAAAAACIhALTdFFuTF+8eHFVU7p0aZWTDXys\nphdWAx/ZQIPmPenFauSyZcsWlfvss8+8+Ntvv1U1c+fOVbmtW7d6ceXKlVXNpZdeqnJXX321F+/f\nv1/VbNq0SeVonJDerPNrXZNko5FKlSqpGqvhi2zGsXPnzkDvh9Symgyde+65Knf55Zd7sWxsdzSy\nzmq6aM0peU3iepQ48m8ZtFFQs2bNvPiCCy5QNVajp3feeceLrftiEEGaTzmnmypZDdxkU0mrgRIN\nzcIL0rzMOp9hP+fW+zVu3NiL7777blVjPePLBniTJk1SNbLpIoKTzwvyPDnn3AMPPKBysrnzkiVL\nVM0LL7ygcvJ5OmgzvLDXSXn9sZoYd+vWzYs7d+6salasWKFy7777rhcvXbpU1SSq2V+6s64Z1vWg\nUaNGXlyzZk1V8+WXX6qc/M6WyGcYeY6tRntWM2XJOs5q9plO5Of44MGDqsZqgij/Vtbf17pGyOtd\n+/btVc348eNVrnbt2jHfL4iw1yhrPUHO/euuu07VWNck5L+w8yedv6fzC2sAAAAAAAAAQCSwYA0A\nAAAAAAAAiAQWrAEAAAAAAAAAkRCJPazl/kNly5ZVNaeeeqoXt2zZUtWccsopKif38LH2/7X2Hlu1\napUX//LLL6rG2vta7o1s7ZVs7cVVsWJFL5b78gUdk7UnJfS+VNYeUN99953KTZ061Ys3bNigaoLs\nQbd58+aYNc45d/PNN3tx/fr1VY019xcvXuzFUdhDNsgemOnO+qxLify7yH3UTjvtNFWzbNkylfvi\niy+82LpuFcbzFzXW/tEvvviiysl7qDUPrfMpr5PWXp133HGHyo0bN86Lresde3UmhnXerJ4enTp1\n8uKOHTuqmnnz5qmcfM6w9sgMey2wjpPPLNa9We6ba+3TZ+27L9+Pa1h4Qa8hQVj7Mw4dOtSLrXlg\nkfcq67mc826T59Q6L/I71COPPKJq2rRpo3Lytax9gxcsWKBye/fu9eJEXmuC1FnfPy+++GIvPv30\n01XNjh07VE7e85iHwcm5Wb16dVXz0ksvqVy7du282LpXfPjhhyp35513enEiewXJ44KuC8jnscI4\nf4LsTW9dt+QzkbX/t+yN4Zz+fv3EE0+oGvk9yxqDda6sZ+CcnBwvtuaG3Kvd6kNk/V1q1KhxzNdB\ncEH6fDin1zOt55guXbqo3MiRI73YmmOHDh1SuR9//NGLp0yZomqmT5/uxevXr1c1ch46p+dUfu+P\nzS+sAQAAAAAAAACRwII1AAAAAAAAACASWLAGAAAAAAAAAEQCC9YAAAAAAAAAgEhIedNFuSG5c3pz\nfKuxlGweJhsKOWc3qCtTpowXn3HGGaqmffv2Kic3469QoULM13ZOb7QvG4g459zBgwdVTm6Gb/2d\nlixZ4sU33nijqvnpp59ULshG6YlsqJPfgmyEv3v3blWzZs0alUtUAxiL1bxMbrRvvZ9sKuKcc6+9\n9poX5/fm+M4V3PkTVpCmDIn8m5QsWVLlevTo4cVW89Z//vOfKic/D4Xt3EWVbK5iNdSwmhEFafZp\nkcdZzfys+45szjh+/HhV849//MOLaY6WONbn/IILLvBiq5nYnDlzVE42m0r1vaRWrVoqJ5//tmzZ\nompk8xnnmE9BpfreJZuMO+fc+eefH3NM1lycMGGCF+fm5sY3uEJENgqzvnfI+0tWVpaqsY7bunWr\nF48ZM0bVWM15U/2ZlfPM+o7YvHlzL7Yao82dO1flVq5c6cVReC4vKOS8s5p2Wo3J5Pm0/uYXXnih\nyt17771ePHr0aFWzfft2lQsyX2VN0AbUQZp2co+zn1NlszurYd2+fftUbv/+/V5srddYOXkerIbW\nt912m8rJJtdBrsHyXumcc6eccorKffXVV168aNEiVQP7WUM26axWrZqqadWqlcrJa0vXrl1VjfWd\nzWqkGWtMzukGwCeffLKqGTRokBd/8803qmb48OEqF7X7F7+wBgAAAAAAAABEAgvWAAAAAAAAAIBI\nYMEaAAAAAAAAABAJLFgDAAAAAAAAACIh5U0XrU27ZWMBq1GhbAqWnZ0d6P127drlxdbG+1WqVFE5\n2XjDanBmbZJ+4MABL167dm3MGuecO+uss7xYNmF0zrkmTZp4caNGjVSN1XQxiHRv3CCbJMjGUs45\n9+uvv6pcov4usimZc871799f5WQTHKu5w+uvv65yQZt4IHmCNK6yBJljcl44Zzc/+t3vfufFVpOG\n7777TuWSOX8S9TdId9Y5luezbdu2gY6TrL9vkHuxVWPNKdkUb+zYsaqmWbNmXjxs2DBVs2PHDpVj\nbvispjy9e/dWuapVq3qx1ajw888/Vzk5B4Je14I0u7LIZ53/+q//UjX16tXzYmvczJPwktl00bo+\ndenSReXkPLDeTzbEck4/DyVyHqT7vUv++6xri2yCajU4sz7rH3/8sRevWLFC1YT924VtEm8dJxvW\nPvDAA6pGfkf86KOPVI3VdNFqzgjNagj8wQcfeHGdOnVUTZB5YH2Hsq5J8h5qNbEbMmSIysnGZEG+\nRwad9wX52pIs1rkrVaqUyuXk5Bwzds6+bsnmnjfccIOqsRpDL1y40IvlvHDOnhtB/Pzzz15srStZ\na0ZyrYnrkX2Pk8/Kzjl3zTXXePHAgQNVjdWIUc7FoM/P8lytW7dO1VhN0uX3KtmE0Tl9fa1cubKq\nsZqWP/zww14cdv4mCr+wBgAAAAAAAABEAgvWAAAAAAAAAIBIYMEaAAAAAAAAABAJKd/D2tqTSe6L\nIvedds65Tz/91It/+OGHQO+3b98+L7b2sC5RooTKyf2hzzvvvEDHTZ061YutfWHknmnOOffvf//b\ni8uUKaNq5N471p5M7HkVbI4lW8WKFb34jTfeUDXWXkpy/9Dhw4ermlmzZqkc5z2aErUPqDVXzjnn\nHJWTe6utX79e1cj90BIp3ff8TCZrP7RXXnnFi639oy3yb7x161ZVs3TpUpWT161ffvlF1dStW1fl\n6tev78XWnoK9evXy4sWLF6ua1157TeWsfWsLs5o1a6pcz549VU7OFfkM5ZxzGzZsUDk5d6zPtHU9\nkqz9Ia39J3v06OHFl1xyiaqR+/tZfUCC7pkNzbomB7mWW+Rx1p7HHTp0UDk5p4LsL+qcc9u2bTve\nIYaWbveuIJ91+T3H+m5ikd9PEvm3s64jQV6/Ro0aKiefp629kuV98LnnnlM127dvDzWmwibI/tHO\n6T5SFuv7/IwZM7xYfrd2zrl+/fqpnNz7tV27dqpm0KBBKjd69Ggvtp61kDjly5dXOeuaJHt2BF0D\n2LNnjxd/8cUXqsb6XMtcMvsCWa9t9X6Dvt7Url1b1bz00ksqJ/sFBVmvcU734ZFrgs7ZazhfffWV\nF1t91qy+eU888YQXy354zum/gfX9rHHjxoHeLz/xC2sAAAAAAAAAQCSwYA0AAAAAAAAAiAQWrAEA\nAAAAAAAAkcCCNQAAAAAAAAAgElLedNESZLN62cBDNlN0zm7SInNBm8vIRi5yQ3TnnDt8+LDKWU0g\ngozTaiIkyaYBq1atUjU0+Ug9q4nI9ddf78Vly5ZVNdb8efvtt734qaeeUjVhmzkkqgEgbNbfU37W\nwzYGs5rtde7cWeVkk4Tdu3ermp07d6occyG1rPP5+uuvq5x13ZCsZjLPP/+8F48YMULVWPe9ChUq\neLE1X1u1aqVyd911lxc3bdpU1chmOXfffbeqee+991SusDddlM1errjiClVjNXKWTYcefvhhVWP9\nbYM0Ygv7rGU1qh4yZIgXWw1h5D3PakhD08XEkucv7D3COp+dOnVSOTnPrOecCRMmqFyqG2qnE/k3\ntz7r1atX92KrwZn1uZZNqypVqqRqrGcRyWraWbp0aZWT86VBgwaq5s0331Q52UT44MGDqubRRx/1\n4oULF6oa5mEw1vWgb9++MY+TzfCcc65NmzYqt2LFCi+2nqEGDhyocvI+a30WMjMzVc6aL0iccuXK\nefFVV12lapYvX65yVkPpIOR9LujnWs6XoI1hE/Xdi+9wtpIlS3rx0KFDVU379u1VTn6Xtp5HrMbx\nAwYM8OIff/xR1QR5frZY3xs7duzoxUEaoltzUzaLdC65jUPD4BfWAAAAAAAAAIBIYMEaAAAAAAAA\nABAJLFgDAAAAAAAAACKBBWsAAAAAAAAAQCREsumiJUjzsrCbzlvHyY32rc3Hwzb5kU1MnHOuTp06\nMV9bNn785ZdfQr0/ggvSSKFZs2Yq179/fy+2GjdYTSGGDRsW87gggjZ8QHKFvUbIeScb1jlnN4qQ\nPvroI5UL28TOakITBPNOq1evnsrJJlUWaz49/fTTKicbi1jHWdcI2aTTOm7r1q0q17JlSy9u0qSJ\nqpHNQKpUqaJqatWqpXLr16/34sI2nypWrOjFPXr0UDXW88mzzz7rxWvXrlU1Qf6WVk3YZizWvG/Y\nsKEXW9eZlStXevG6detCvT9sQZpBBf3cyfN36qmnqhrrcy5Z96lPPvkk0BjCsOZdYbvWWJ9r+Tew\n7htWrnHjxl78+OOPqxqraZXVWEqymtRXrVrVi2+44QZVY91zpLlz56qcbIhsNUxHMFYTROv5VjYB\n69Onj6pZtmxZzPerUaOGyp188skqJ+ew9d1LPotYxyE8q2HcoEGDvPi6665TNc8884zKzZ49O9QY\nglzzrXuFnAfWvOC6kXryvtCzZ09VIxssWnJzc1Vu3LhxKpednR3ztay5IeeU9VmwGkbK52eLnNNW\no9j58+ernPz+l9/PSFxpAQAAAAAAAACRwII1AAAAAAAAACASWLAGAAAAAAAAAERCJPawDiKZ+6QE\n2Zs17F601n5s9913n8qVKFHCi639+0aOHOnF1j40SCy531q3bt1UzcMPP6xyct+knTt3qhpr361N\nmzbFHFOQ/bMsYecwtLD7OQclz6e1J3CFChVUTu4v/M4776iasPuiJ/M6Wdh07dpV5YLso7Z3716V\nk/cF54Kdh7DnytrLTe6PHPbzUbJkyVDHpQvrOn7aaad5cWZmpqrZvn27yr333ntenOrPpjUHLr30\nUpWTz0jW/Bo9erQX5+TkxDm6wivoZzPsM7d8lr322mtVjXWtk/PT2q9627ZtKhd2z9Ewr5Nu5N/c\n2sNa7m1p7YV/0kknqZycB7169VI1V155pcrJ55PVq1erGqt/T4sWLby4cuXKqsaaB3I/bNlHxqpB\ncEH6AFl7Q//0009evGTJElVjvZbcD/vtt99WNUGetQ4dOqRylSpVUjm5H/+ePXtUTdieD+lOzo26\ndeuqmltvvdWLrc+1tS/x+PHjvTiRzz/WvSLIHtaF8R6TStb1Xc6pMmXKBHotea6s/cebN2+ucnLP\n/NKlS6sa6xm3VKlSXvz73/9e1TRq1EjlrL2uJTn2efPmqZo5c+aoXNTmK7+wBgAAAAAAAABEAgvW\nAAAAAAAAAIBIYMEaAAAAAAAAABAJLFgDAAAAAAAAACKhwDRdTJSgDevkZuNBNx+Xr3/yySermu7d\nu6ucbMowbdo0VbNw4cJQY0Iw1ub1/fv39+JHHnlE1ViNwoI0jpFNsZwL1kjTGqdsXGU1DEE0Wdck\nOacuueQSVWPNg3//+99enJ2drWpoUpV68h7TrFmzQMfJ+8KECRNUzb59+8IPLEFkU4+wDToL+3XL\n+kzLRitWwyirSbP8fFrnJJGfYfn6FStWVDU33XSTysl5YDV/mTFjxjGPwdEl81puvbZsCmo97wZp\nfjd27FhVE7ZhMGzyvFufqx9++MGLhw4dqmpuueUWlZPffaznZKsZnWzqOHXqVFVjXVvOO+88Lw56\nvfvuu++8eOnSpYGOQzhWA8tvv/1W5WRzPatpp9WA75prrvHirKwsVWOdTzmuLVu2qJpWrVqp3N13\n3+3F1udj8+bNKgf9XNy5c2dVI+8n8ruuc/Z5ad26tRdbjebCPkdYz2lWQ07Jun9xT0scay2vatWq\nXrx7925VE/TeJPXt21flypUr58XWXLGuP/J+Zc1zi3wt6zvUzJkzvXjMmDGqZtOmTSoXtbnJL6wB\nAAAAAAAAAJHAgjUAAAAAAAAAIBJYsAYAAAAAAAAARAIL1gAAAAAAAACASEj7potyI3NrA3SrOYfc\ncD1I4xrnnCtVqpQXX3nllaqmRIkSKrdhwwYvHjFihKqRja2QWHKzfOecu+eee7xYnt+jyc3N9eJx\n48apml9++UXlZBOIoI1j5Eb7NKVKrKCf/0S9dpUqVbzYakZiNZj48ssvvdhqcJMoNCIKTp6rU089\nVdVYTT7k+Xv11VdVTarPg9UMRDZWs+amZDX0WLduncoVxHkW9Lot66yGivJabj3DlClTRuXkOXnz\nzTdVzc6dO1VOzsMgTX6dc658+fJefO+996qa2rVrq5z89/39739XNcm8jiE863N+5plnenGQZlTO\nObd161YvXrRokapJ1LWgIF5TkiFI08WcnBwv/te//qVqPv30U5UrW7bsMd/LOf2c7JxuImwdV6tW\nLZXr06ePF1vP6lZDqgcffNCLrcbn8jrN/AlO/q2sv+/PP/+schdddJEX/+EPf1A11jmW58p6zrAa\njL3zzjtebDX27NKli8pddtllXmw1WPzTn/7kxXyX/x/y/tGkSZOYNdazlbWm8u6773qx/Jw759x7\n772ncrIpX82aNVXNoEGDVO6UU07x4m3btqma999/P+Y4rWcyrjfBWH8n2Wxz2LBhqsaad/J51ppj\nTZs2Vbn69et7sdXQ0XqmDvKdybo/y/vlQw89pGomT57sxdbctK5J8v3yex7yC2sAAAAAAAAAQCSw\nYA0AAAAAAAAAiAQWrAEAAAAAAAAAkZBWe1hbe8AE2f/I2js0yF4t1n6Tbdq08eJevXqpml27dqnc\n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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, axes = plt.subplots(nrows=2, ncols=10, sharex=True, sharey=True, figsize=(20,4))\n", "in_imgs = mnist.test.images[:10]\n", "reconstructed, compressed = sess.run([decoded, encoded], feed_dict={inputs_: in_imgs, targets_: in_imgs})\n", "\n", "for images, row in zip([in_imgs, reconstructed], axes):\n", " for img, ax in zip(images, row):\n", " ax.imshow(img.reshape((28, 28)), cmap='Greys_r')\n", " ax.get_xaxis().set_visible(False)\n", " ax.get_yaxis().set_visible(False)\n", "\n", "fig.tight_layout(pad=0.1)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "sess.close()" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Up Next\n", "\n", "We're dealing with images here, so we can (usually) get better performance using convolution layers. So, next we'll build a better autoencoder with convolutional layers.\n", "\n", "In practice, autoencoders aren't actually better at compression compared to typical methods like JPEGs and MP3s. But, they are being used for noise reduction, which you'll also build." ] } ], "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.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }