{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "version": "0.3.2",
      "views": {},
      "default_view": {},
      "name": "Untitled",
      "provenance": []
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9yupXUk1DKOe",
        "colab_type": "text"
      },
      "source": [
        "# MNIST from scratch\n",
        "\n",
        "This notebook walks through an example of training a TensorFlow model to do digit classification using the [MNIST data set](http://yann.lecun.com/exdb/mnist/). MNIST is a labeled set of images of handwritten digits.\n",
        "\n",
        "An example follows."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sbUKaF8_uDI_",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "outputId": "67a51332-3aea-4c29-8c3d-4752db08ccb3"
      },
      "source": [
        "from IPython.display import Image\n",
        "import base64\n",
        "Image(data=base64.decodestring(\"[encoded data, 8472 characters]\"), embed=True)"
      ],
      "outputs": [
        {
          "output_type": "execute_result",
          "execution_count": 1,
          "metadata": {},
          "data": {
            "image/png": "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\ngUDg/wHX+3lgThDIegAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          }
        }
      ],
      "execution_count": 1
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "J0QZYD_HuDJF",
        "colab_type": "text"
      },
      "source": [
        "We're going to be building a model that recognizes these digits as 5, 0, and 4.\n",
        "\n",
        "# Imports and input data\n",
        "\n",
        "We'll proceed in steps, beginning with importing and inspecting the MNIST data. This doesn't have anything to do with TensorFlow in particular -- we're just downloading the data archive."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "w5vKZqr6CDz9",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 110,
          "status": "ok",
          "timestamp": 1446749124399,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "794eac6d-a918-4888-e8cf-a8628474d7f1"
      },
      "source": [
        "import os\n",
        "import urllib\n",
        "\n",
        "SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/'\n",
        "WORK_DIRECTORY = \"/tmp/mnist-data\"\n",
        "\n",
        "def maybe_download(filename):\n",
        "  \"\"\"A helper to download the data files if not present.\"\"\"\n",
        "  if not os.path.exists(WORK_DIRECTORY):\n",
        "    os.mkdir(WORK_DIRECTORY)\n",
        "  filepath = os.path.join(WORK_DIRECTORY, filename)\n",
        "  if not os.path.exists(filepath):\n",
        "    filepath, _ = urllib.urlretrieve(SOURCE_URL + filename, filepath)\n",
        "    statinfo = os.stat(filepath)\n",
        "    print 'Succesfully downloaded', filename, statinfo.st_size, 'bytes.'\n",
        "  else:\n",
        "    print 'Already downloaded', filename\n",
        "  return filepath\n",
        "\n",
        "train_data_filename = maybe_download('train-images-idx3-ubyte.gz')\n",
        "train_labels_filename = maybe_download('train-labels-idx1-ubyte.gz')\n",
        "test_data_filename = maybe_download('t10k-images-idx3-ubyte.gz')\n",
        "test_labels_filename = maybe_download('t10k-labels-idx1-ubyte.gz')"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Already downloaded train-images-idx3-ubyte.gz\n",
            "Already downloaded train-labels-idx1-ubyte.gz\n",
            "Already downloaded t10k-images-idx3-ubyte.gz\n",
            "Already downloaded t10k-labels-idx1-ubyte.gz\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gCtMhpIoC84F",
        "colab_type": "text"
      },
      "source": [
        "## Working with the images\n",
        "\n",
        "Now we have the files, but the format requires a bit of pre-processing before we can work with it. The data is gzipped, requiring us to decompress it. And, each of the images are grayscale-encoded with values from [0, 255]; we'll normalize these to [-0.5, 0.5].\n",
        "\n",
        "Let's try to unpack the data using the documented format:\n",
        "\n",
        "    [offset] [type]          [value]          [description] \n",
        "    0000     32 bit integer  0x00000803(2051) magic number \n",
        "    0004     32 bit integer  60000            number of images \n",
        "    0008     32 bit integer  28               number of rows \n",
        "    0012     32 bit integer  28               number of columns \n",
        "    0016     unsigned byte   ??               pixel \n",
        "    0017     unsigned byte   ??               pixel \n",
        "    ........ \n",
        "    xxxx     unsigned byte   ??               pixel\n",
        "    \n",
        "Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).\n",
        "\n",
        "We'll start by reading the first image from the test data as a sanity check."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "P_3Fm5BpFMDF",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 57,
          "status": "ok",
          "timestamp": 1446749125010,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "c8e777e0-d891-4eb1-a178-9809f293cc28"
      },
      "source": [
        "import gzip, binascii, struct, numpy\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "with gzip.open(test_data_filename) as f:\n",
        "  # Print the header fields.\n",
        "  for field in ['magic number', 'image count', 'rows', 'columns']:\n",
        "    # struct.unpack reads the binary data provided by f.read.\n",
        "    # The format string '>i' decodes a big-endian integer, which\n",
        "    # is the encoding of the data.\n",
        "    print field, struct.unpack('>i', f.read(4))[0]\n",
        "    \n",
        "  # Read the first 28x28 set of pixel values. \n",
        "  # Each pixel is one byte, [0, 255], a uint8.\n",
        "  buf = f.read(28 * 28)\n",
        "  image = numpy.frombuffer(buf, dtype=numpy.uint8)\n",
        "  \n",
        "  # Print the first few values of image.\n",
        "  print 'First 10 pixels:', image[:10]"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "magic number 2051\n",
            "image count 10000\n",
            "rows 28\n",
            "columns 28\n",
            "First 10 pixels: [0 0 0 0 0 0 0 0 0 0]\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7NXKCQENNRQT",
        "colab_type": "text"
      },
      "source": [
        "The first 10 pixels are all 0 values. Not very interesting, but also unsurprising. We'd expect most of the pixel values to be the background color, 0.\n",
        "\n",
        "We could print all 28 * 28 values, but what we really need to do to make sure we're reading our data properly is look at an image."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "F_5w-cOoNLaG",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 887,
          "status": "ok",
          "timestamp": 1446749126640,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "77dabc81-e3ee-4fcf-ac72-88038494fb6c"
      },
      "source": [
        "%matplotlib inline\n",
        "\n",
        "# We'll show the image and its pixel value histogram side-by-side.\n",
        "_, (ax1, ax2) = plt.subplots(1, 2)\n",
        "\n",
        "# To interpret the values as a 28x28 image, we need to reshape\n",
        "# the numpy array, which is one dimensional.\n",
        "ax1.imshow(image.reshape(28, 28), cmap=plt.cm.Greys);\n",
        "\n",
        "ax2.hist(image, bins=20, range=[0,255]);"
      ],
      "outputs": [
        {
          "output_type": "display_data",
          "metadata": {},
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAEACAYAAABI5zaHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnWuQbFd1339rHt3T3dM974eu5iKJCINExRGULYeSXRli\nLAtCJIoPisDlAuQkrgIZqpQHuny5oziJEAk42C7ZQWBKUBBZOIUlV1FGKNQ4RQISGInXFfIF50r3\nzp3Hnff0PLqnu1c+dO+j3We6586jex5n1q9qV59z5pyz9+mZ+ffqtddeS1QVwzAMI1q0HPYADMMw\njMZj4m4YhhFBTNwNwzAiiIm7YRhGBDFxNwzDiCAm7oZhGBHExN2INCLyCyLyvIh8v/K6JCIfFpEe\nEXlaRF4Ska+LSJd3zRkROS8iL4rI7Yc5fsPYK2Jx7sZJQURagEvArwD3AXOq+gkR+SjQo6oPiMjN\nwJeAXwZGgGeA16n9oxjHDLPcjZPE24Cfq+pF4C7gscrxx4B3VbbvBB5X1YKqXgDOA7ce9EANY7+Y\nuBsniX8BfLmyPaSq0wCqOgUMVo5fC1z0rpmoHDOMY4WJu3EiEJF2ylb5VyqHwm4Wc7sYkaLtsAdg\nGAfE24G/VdXZyv60iAyp6rSIDAMzleMTwGnvupHKsS2IiH0gGE1FVWWv15rlbpwU3gP8D2//KeD9\nle33AU96x+8RkZiI3ADcCDxX76aq2tR29uzZSPQRpWc5qPdrv5jlbkQeEUlSnkz9197hh4EnRORe\n4GXgbgBVPSciTwDngE3gg9qI/zTDOGBM3I3Io6prwEDo2Dxlwa91/kPAQwcwNMNoGuaWMYwjzOjo\naCT6OKh+otJHI7BFTIaxR0TEPDZG0xAR1CZUDcMwDB8Td8MwjAhi4m4Y+6CtLb6l/cmffOawh2UY\nFi1jGPuhWFwOHfmv/OxnPz+UsRiGj4m7YeyLeGjf/qWMo4G5ZQzDMCKIibthGEYEMXE3DMOIICbu\nhmEYEcTE3TAMI4KYuBuGYUQQE3fDMIwIYuJuGIYRQUzcDcMwIoiJu2EYRgQxcTcMw4ggJu6GYRgR\nxMTdMAwjgpi4G4ZhRBATd8MwjAhi4m4YhhFBTNyNyCMiXSLyFRF5UUR+IiK/IiI9IvK0iLwkIl8X\nkS7v/DMicr5y/u2HOXbD2Csm7sZJ4NPA11T1JuAfAT8FHgCeUdXXA98EzgCIyM3A3cBNwNuBR0RE\nDmXUhrEPTNyNSCMiGeDXVPXzAKpaUNUl4C7gscppjwHvqmzfCTxeOe8CcB649WBHbRj7x8TdiDo3\nALMi8nkR+b6IfEZEksCQqk4DqOoUMFg5/1rgonf9ROWYYRwrrJqvEXXagDcDH1LV74nIH1B2yWjo\nvPD+Dhnztkf3dgvDAMbHxxkfH2/Y/UzcjahzCbioqt+r7P9PyuI+LSJDqjotIsPATOXnE8Bp7/qR\nyrE6jIX2n23AkI2TyOjoKKOjo8H+gw8+uK/77cstIyJ3iMhPReTvROSj+xqJYTSBiuvlooj8QuXQ\nrwM/AZ4C3l859j7gycr2U8A9IhITkRuAG4HnDm7EhtEY9my5i0gL8MeU/1kuA98VkSdV9aeNGpxh\nNIgPA18SkXbg74EPAK3AEyJyL/Ay5QgZVPWciDwBnAM2gQ+q6h5dNoZxeOzHLXMrcF5VXwYQkccp\nRyBUibuI2D+G0VRUddtQRVX9AfDLNX70tjrnPwQ81IChGcahsR+3TDiq4BJ1ogpUFVXl7NmzwXaz\nm/V1vPraa3+GYdTGQiENwzAiyH7cMhPAa7z9ulEFY2NjwKuhPv6MsGHshkaHixlGVNmPuH8XuFFE\nrgMmgXuA99Q60Rf3gxL2g/wAsb4Orr9Gh4sZRlSR/fgtReQOynk7WoDPqerHa5yj5hs1moWIoFeZ\nUG1i37p17dPD3H//PJ/85MOHMSQjQuz3b3tfi5hU9a+B1+/nHoZhGEbjsQlVwzCMCGLibhiGEUFM\n3A3DMCKIibthGEYEMXE3DMOIICbuhmEYEcTE3TAMI4KYuBuGYUQQE3fDMIwIYuJuGIYRQUzcDcMw\nIoiJu2EYRgQxcTcMw4ggJu6GYRgRxMTdMAwjgpi4G4ZhRBATd8MwjAhi4m5EHhG5ICI/EJHnReS5\nyrEeEXlaRF4Ska+LSJd3/hkROS8iL4rI7Yc3csPYOybuxkmgBIyq6ptU9dbKsQeAZ1T19cA3gTMA\nInIzcDdwE/B24BEROZQarYaxH/Yl7rUsIsM4gghb/9bvAh6rbD8GvKuyfSfwuKoWVPUCcB64FcM4\nZuyrQDavWkQLjRiMYTQJBb4hIkXgv6vqZ4EhVZ0GUNUpERmsnHst8G3v2onKMcM4VuxX3GtZRIZx\n1LhNVSdFZAB4WkReoiz4PuH9HTLmbY/u7RaGAYyPjzM+Pt6w++1X3H2L6DOq+mgDxmQYDUVVJyuv\nV0TkLym7WaZFZEhVp0VkGJipnD4BnPYuH6kcq8NYaP/ZBo3aOGmMjo4yOjoa7D/44IP7ut9+xd23\niL4hIi+q6rfCJ42NjQXb4QcwjN2wW+tGRJJAi6pmRSQF3A48CDwFvB94GHgf8GTlkqeAL4nIH1B2\nx9wI2HyScewQ1T1+Gw3fSOQssKKqnwod10b1YRhhRARVrRvNIiI3AF+l/C2zDfiSqn5cRHqBJyhb\n6S8Dd6vqYuWaM8DvAJvAR1T16Tr31q3enIe5//55PvnJh/f7aMYJ52p/21djz5b7NhaRYRwZVPX/\nAbfUOD4PvK3ONQ8BDzV5aIbRVPbjlhkCvlq2XgKLqKaFYxiGYRwsexb3ehaRYRiGcfjsd0L1yKKq\nlEqlqqaqFAoFisUihUIh2HZzAiJS1Rz+dktLC62trUFra2ujpaUlOMe/ttarLXY0DOMgiLS4OxH3\nxTyXy5HL5djY2Ai2i8UiLS0tVS0s0m67ra2NWCxGPB4nFosFLfzB4N+j1geGYRhGMzkR4p7P59nc\n3CSfz7O6urqlFQqFwBL3LXPYas3HYjESiQTJZDJ4VdUtHwzhDws3JhN4wzAOgkiLe6lUCkTdWerL\ny8ssLS2xtLQUbG9ubtLW1ha4Wdx2LUu8o6ODdDpNPp+nWCxuEXL/A0JVA5ePE3jDMIyDINLi7ix3\n54ZZX19neXmZhYUF5ubmmJ+fZ35+nnw+T1tbG+3t7VWvvqi711QqVSXs7gPBWfulUikQ9rD1b/H+\nhmEcFJEVd2e1r6+vk81mWVlZYWVlhbm5uS0tl8ttEXZ/otR/TSaTVf5696HhC7wv+O5e/jeCo0q9\nCeCwi8l9C6k3cWwYxuETaXHf2NhgeXk5sNDn5+dZWFgI2uLiIouLi+Tz+S2C7NwrYZ97IpEgm82y\ntLTE3Nwc6XSaVCq1RdxbW1tpb2/f0traju5bHp4zEJFgAtm1eDxOe3t7zQ8+wzCODkdXafZJsVhk\nY2ODlZUV5ufnmZqaYmpqiuXl5cDXvrKyEvjcwxOq9aJdak2odnR0bBF29y3Aj6xxwnhUCYd5trS0\nEI/HSSaTQSsWiySTyap5BfdqlrthHB0iK+6+5T43N8fU1BQXL14km80GbXV1lWw2S6FQqBvhEnY5\nOMEOW7JhcW9tbSUWi9HR0UFHRwfxeJyOjg5isdihvSdXw3cdue1EIkEmkyGTyQTvk/uZqlbNTRiG\ncXSIrLj7lvvc3ByTk5NcvHiRtbU11tbWWF9fD5oTre1i0t1+2Lqt11paWujo6CCRSJBIJOjo6CCZ\nTBKPxw/j7dgR7tuG31KpFBsbG8EEcnt7Ox0dHcE1/gehYRhHh8iKu7Pcs9ksCwsLzMzMMDExUTUZ\nms/ng0VMe8W5I3zRd9tO0P3mC+NRIxaLbflmkslkAmF3lnw+nw+uceJukUCGcbSIrLi3trYGLoX+\n/n5OnTq1ZWVqeIVqeGWpi5UPv4a3S6USQCBwvtC5qJ2NjQ1Ulc3NzZr+fHeui433Y+R3Q61vHLVW\n2ob7UdWqSV+3XSgUqvbdB4D7NuI+3I7yXIJhnEQiK+4tLS0kEgm6uroYGBggl8uhqoG4+68uNj3c\nisXillYrN029bREJxN3F3efz+ZqLntzPw/lwdkM9YQ9HwQBb+imVSjUXcrkFXr64x2IxCoUCpVIp\nsOjNcjeMo0Vkxd25RTKZDLlcjlKpRHt7O+vr62xsbGwRdz++3W1vbm5SKBSCV7edz+erUhpsbm5u\n+TaQz+cD0dzc3KwS9vDCp7a2Nkql0pYPj/2Iu9sORwC5OPtaH1y1zt3c3KwS9ng8TjwerxL2jo6O\nXY/VMIzmEmlxd5a7i+pIpVJV4u62VbXKz+xcD07EfTEPu3bcvdxErRNPJ+phNw5Q84OkVCpVfZC4\nD4TdUGtRUa1FVUDwYeW3WrHrvrg7YY/H44Gwx+Px4JuJYRhHh8iLu/Mlp1Ipenp6ggiZsLi7cEU/\nZLGekK+vr1dF3KytrbGyshKEBZZKJfL5fGCF+5Z/sVismrgsFou0t7cH5/nfCAqFwq6euZZ/PbxC\n1o1xc3NzS3PX+a/5fH6LsMfj8UDYU6kUhULBxN0wjhiRFXc/bM/fdgLtu2aALeLlxD3c/BBKv6VS\nqarm0hSE3TiFQqEqRt5tF4vFwJ3j2l7F3RfocM6c9vZ2SqVSMG5gS157eHVSOBaLVY3HuYvCefIN\nwzhaRFrcnd/Yj+Twk3054YLqMEAngv5K03g8Tj6fD8IbfcHf2NhgdXWVtbW1qlTCvlg7kXeWe7g5\nn3zYDbSb53WvfquVEK1QKLC0tMTi4iJLS0uBJe9H68CrAu/PE/gfSu5+FuduGEePq4q7iHwOeCcw\nraq/WDnWA/w5cB1wgXLl+KUmjnPXOHF3PmZf7J1I+e4I3w/u+8OdsPsTqr77xG3X8uXncrkqy73Z\n4l5L4P3ncNu5XI6ZmRk6OjoCv/rq6mpNi9x/H/1JVXc/P1WDYRhHh51Y7p8H/gj4gnfsAeAZVf2E\niHwUOFM5dqRwYl5L2P0oEaBmbhkXweKam/QMR7XUEuZazXfLhCs51XLL7EXca1nuYYFfX18P3FWb\nm5tks1na2tqqJnCdsIe/AYUtd/deGYZxtLiquKvqt0TkutDhu4B/Utl+DBjniIm7H9PtJ7kKL0qq\nVT/VWaLh8+otaKoVxuiHTfrW+9Usd1/g9+Nz385yX1lZCYR9dXWV+fn5LYuQ/NDG8Aejm3D2xd0s\nd8M4WuzV5z6oqtMAqjolIoMNHFND8IW62YTF3227En9hn3t4iX8j3TLbWe5ue2lpKShcMjs7SyKR\nCAqNuPG7by61rHYXWXScfO4i0gJ8D7ikqndu51oUkTPAvUAB+IiqPn04ozaMvdOoCdVtwyXGxsaC\n7dHRUUZHRxvU7dEivIio1oSuWwka9lm71Ln+as/dFvaoJ/BOqN03i3B+HecucguT3Jja29uDnDip\nVIrOzs6guTw5sVgsCK88CMbHxxkfH9/LpR8BzgGZyn5N16KI3AzcDdwEjADPiMjr1EKCjGPGXsV9\nWkSGVHVaRIaBme1O9sU9yvgC5wTbL87hRLZWrdZak7+7WcRUK3+Mcy255uLRnbj7wu5cQLXKCrrW\n2dlJOp0mnU4H4u5cMwcl7mHj4MEHH7zqNSIyArwD+E/A/ZXD9VyLdwKPq2oBuCAi54FbgWcb8gCG\ncUDsVNyl0hxPAe8HHgbeBzzZ2GEdP5yQhgXe33ax5OEMkmG3hrOe97KkP7wIyU9l4EQ8bLW71bB+\n9SXXfKvdt96dayYWix2ouO+RPwD+HdDlHRuq41q8Fvi2d95E5ZhhHCt2Egr5ZWAU6BORV4CzwMeB\nr4jIvcDLlL/Gnnh8ga/lFnEul/ASfyfm7nzf/73b/sP7LseNs9D9VbfhxUnt7e2BuDv/uhN3X9jT\n6fSW0oFHVdxF5J9RDuN9QURGtzl1j26XMW97u9sbxvbsw+VYk51Ey7y3zo/e1rBRRIiw393f98U6\nbGG7bRfV0yj8rJRugjfslnHC7z5g/CpS9dwy4QRjR1XcgduAO0XkHUACSIvIF4GpOq7FCeC0d/1I\n5VgdxkL75r0x9sZeXI7bcfTDHI4B9SYynWXup/cNu2NqnbuTSk+1Kj/5zd3TTaC6oiVXrlxhZmaG\n+fl5lpeXWVtbC8S/tbU1yBfT1dVFX18ffX19dHd3k8lkSKVSVWGQ/pzBURV3Vf2Yqr5GVV8L3AN8\nU1V/G/gryq5FqHYtPgXcIyIxEbkBuBF47oCHbRj7JrLpB04q4fQBuVyObDbL/Pw8c3NzzM3NMT09\nzfT0NPPz82Sz2SDXvV8ztbe3l97eXoaGhujv76erq4tUKkUsFqv5IXYM+TjwRNi1qKrnROQJypE1\nm8AHLVLGOI6YuEeIWtWVcrkcKysrzM/PMz09zeTkJNPT01y5ciUQ93w+v0Xc+/r6GBoaYmhoiL6+\nPrq6ukgmk8RisapvBscJVf0b4G8q2/PUcS2q6kPAQwc4NMNoOCbuEcMX9lKpVGW5T01NcfHiRaam\nplhaWmJpaSkQdyCIa3fiPjw8zNDQEL29vVWWu4vuqTVvYBjG0cDEPUKEhT0s7tPT01y6dInJycmq\nnPS13DK+uKfTaTKZTGC5m6gbxtHHxD1C+KLuEp1tbGywsrISTKZOTk4yOTm5pXwgEJTMS6fT9PT0\n0N/fz8DAAIlEgmQySSKRoL29/VikGzCMk46Je0QI57JxbXFxkZWVFdbW1tjY2KiqEOXna6+V6jgc\nFWMJwgzj+GDiHhFUlc3NzSCfvGtLS0usrKywurpaJe7OfeMnV6tVtckvXGLibhjHBxP3iOAWKeVy\nOVZXV8lms2Sz2UDcfcvdz1njx9ib5W4Y0cHEPSL4lvva2hrLy8tBKT1nua+vrweWe60UCGFx99ML\nuNS+Ju6GcTywmbGI4Cx3V891eXmZhYWFLZa7n97Xd8v4mSr9knpmuRvG8cQs92NIeKESENRxzWaz\nLC4uMjc3x8zMDLOzsywuLgYrUZ3V7nLH+G1wcDBYsJRKpYKCHOF0CYZhHH1M3I8h4RJ/LuTR+did\nsF++fJnZ2VkWFhbIZrNsbGxQLBZpbW0lkUjQ1dUVtEwmw8jICNdccw19fX2k02ni8XhgtZvlbhjH\nCxP3Y4gTd79uq5tIXVpaYn5+PhD3xcVFlpeXWVlZIZfLBcVCkskk3d3dDA4OMjAwwMDAQFW6AV/c\nzWo3jOOHifsxxK/R6hYjOV+7E3eXRyabzQYrUZ3lHovFSCaT9PT0MDQ0xMjICCMjI/T09ATNF3c/\nosYE3jCOBybuxxBVpVgsUiwWg3zs9dwy/iSqs/KdW6a7u5uhoSFe85rX8NrXvjaotuRyt3d0dFSV\n/vNfDcM42pi4H0N8y91fuLS6usrKygpLS0ssLCwwOzsbpBZwtLS0EIvFqnK2Dw8PMzIyUlU6z59M\nNQzj+GHifgxx1ZWcqK+trQXhji6WvV691tbWVrq6ukin06RSqaDQtRPz41CAwzCMq3PVOHcR+ZyI\nTIvID71jZ0Xkkoh8v9LuaO4wDR/njsnlcqytrZHNZuumGGhpaaG9vT2osJTJZAJx7+zsDMS9Vky7\nYRjHl538B38e+M0axz+lqm+utL9u8LiMbXAuGSfuKysrLC8v1xR3t/LU1UP1xT2VSpFIJAJxj8Vi\ntmDJMCLCTgpkf0tErqvxI/vPPyScW6aW5V7LLdPe3h6Ie2dnJ5lMZovlHo/Ht9RiNXE3jOPLfr57\n3yciL4jIZ0Wkq2EjMq6K73N3lvvV3DL1LPd6bhkTdsM43ux1QvUR4D+oqorIfwQ+BfxOvZPHxsaC\n7dHRUUZHR/fY7cmiXl1m53P3xd13y2xublIqlQACYe/s7KS7u5u+vj4GBgaCWHa/AIc/iXpUxX18\nfJzx8fHDHoZhHHn2JO6qesXbfRT4q+3O98Xd2DtO7H1xd+GP4eRgpVIJEamqi9rb28vg4GCwCtWV\nzmtvbz8Wwg5bjYMHH3zw8AZjGEeYnbplBM/HLiLD3s/eDfy4kYMyqvEThLkFTPl8/qqWuxN3VxfV\nF/fe3l4ymUxguTuOsrAbhrFzrmq5i8iXgVGgT0ReAc4CbxWRW4AScAH43SaO0YC64u4s9+3E3bfc\nXf6YWkWvTdgNIzrsJFrmvTUOf74JYzFq4PvdnQVfyy1zNcs9nU5XWe6JRCJozi0TRUQkDvxvIFZp\nT6rqx0SkB/hz4DrKBsrdqrpUueYMcC9QAD6iqk8fxtgNYz/YSpUjjhNzl2rAWexuZaoTd5fS1wm7\nHyWTTCZJp9N0dXUFicEymQypVIqOjo4gxYCz3v123FHVHPBWVX0T8IvAPxWR24AHgGdU9fXAN4Ez\nACJyM3A3cBPwduARicIbYZw4LP3AEcZVV8rn80GCsHw+z8LCAouLi0GVJZf50dVGdZZ4LBYLYtqT\nyWTVgqVwEY4oo6prlc04ZYNmAbgL+CeV448B45QF/07gcVUtABdE5DxwK/DsQY7ZMPaLifsRxtVF\nzeVyrK+vB2l7nbgvLy+zvLxMNptlfX09SMvrSuS1tLQEi5Wcle4Sg7myeidhsZKItAB/C/wD4E9V\n9ZyIDKnqNICqTonIYOX0a4Fve5dPVI4ZxrHCxP0IE66Lurq6SjabDWqj+pb7+vp6kELAibuz3N1i\npUQisaV03klYsKSqJeBNIpIBvi4io0B4EUHtRQVXZczbHt3bLQyDxq/hMHE/wjhx96ssuXS+foUl\n55ZxrhgXIeNWpPp5ZJzl7hffiLq4O1R1WUS+BvwSMO2s90po70zltAngtHfZSOVYHcZC++a9MfZG\no9dwRNvZeszxLfe1tTWWl5eZn5+v6XNfX18Pcrc7cQ9nf/TF3bfeoyzwItLv0mOISAL4DeB54Cng\n/ZXT3gc8Wdl+CrhHRGIicgNwI/DcgQ7aMBqAWe5HhFKpFIQ6uu1cLrelutLMzAxXrlypKnrtomNc\nEQ4X097f309fXx9dXV2kUqkgOVhUhbwO1wCPVSJeWoAvqur/EpHngSdE5F7gZcoRMlT88U8A54BN\n4INaLw+EYRxhTNyPCH7pPNfW19dZWVlhcXGR2dnZoC7q3NwcCwsLrK6uks/nUdUgra/LIdPf38/Q\n0BD9/f1bxP0koao/At5c4/g88LY61zwEPNTkoRlGUzFxPyKE49kLhUKQWmBhYYG5uTmmp6eZmJgI\nImSy2Wwg7rUShA0PD9Pb20t3dzednZ3E4/HIhz0ahlHGxP2I4NwxLpY9n8/XtNwvX74cWOyuAVWW\ne1dXV2C5++l9T6LlbhgnFRP3I4JvuYeTgi0uLgaW++XLl8nlcoF/3rWwW6avr4+hoSE6OzuDNAMu\n9t0wjOhj4n7AbJej3VnrLqZ9YWGhZisWi0FRjba2tiB/jF9pyaUacKLuImRM3A3jZGDifoj4mR7z\n+Tyrq6ssLi6yuLgY+NmnpqaYn59nZWUlsNj9otcutLG7u5tMJlMV9lir6PUJi5QxjBOLifsh4btU\nADY3NwNxd+GOMzMzTE9PV4l7qVQKrHWXFCyZTG4R93CqASfuhmGcDEzcD4Gwv9y33BcWFpiZmeHy\n5ctMTU0xNzfH/Pw82Ww2sNxbW1uromPS6XRNcfddMVb02jBOFibuh0gtcV9cXOTKlStMTExw+fLl\nIMWAb7m7BUvOz57JZKrE3U810NbWFqxANXE3jJODifsh4Qu7C4EMi/ulS5fY2Nggl8sFr77PvaOj\ng1QqRVdXV13L3UIfDeNkYuLeRGpFxqhqINR+m5iYYHp6mtnZWRYXF6uKbxSLRUSEtrY2VJVEIhHE\ns/f19TEwMMDAwMCWuqhRzhljGMb27KSG6gjwBWCIcs3UR1X1D7crU2bUp1QqkcvlgtJ4zuUyNTXF\n5OQks7OzLC0tsba2FrhhVDUQ95aWlqCyUnd3d1XpvJ6eHtLpdORL5xmGcXV2Ej5RAO5X1TcCbwE+\nJCJvoE6ZMmN7SqUSGxsbLC8vMzc3x+XLl3n55Zd55ZVXAnFfXFwMVqEWi8Ut4Y++uDvLfXBwkN7e\n3ipxNwzj5LKTAtlTwFRlOysiL1LOcV2vTJlRB+djd+I+OzvL5OQkk5OTXLlyhbm5Oebm5qosdxej\n7rdkMlm1EnVwcJDBwcEgLDKZTNLWZh43wzjJ7EoBROR64BbgO0C9MmVGDZz/3bllnLg7y31+fp6l\npaWgdJ6z3IEgyqWtrY1YLLbFcnfi7ldhMreMYZxsdizuItIJ/AXwkYoF36AyZdHGX4Xq8sfkcrmg\n+IbL0+5S+K6trbG2tha4ZEQkKL7hWk9PDz09PXR3d9PV1RUkBwtb+IZhnFx2JO4i0kZZ2L+oqq5i\nTb0yZVsYGxsLtsOlpKJOeLFSsVgMil5vbGwEuWTW1tbY2Nggn89TKBQCP3s8HiedTgfhjl1dXZw6\ndYqBgYEgT3t4FWqUo2QaXWfSMKLKTi33PwPOqeqnvWOuTNnDVJcp24Iv7ieJcCx7qVQK8rW7JGFr\na2uBuOdyucBiL5VKiAjxeJzOzk56e3uDkMfh4WEGBgbo6emhs7MzEHd/JWpUaXSdScOIKjsJhbwN\n+C3gR5XSZAp8jLKobylTZlTjC7ursORb7mtra0GBa1ekw1nura2tgeXe29vL8PAwp06dCsrndXd3\nV+Vp9632qFruhmHsjJ1Ey/wfoJ4Dt2aZMqOMb7U7YXeWuy/uznJ3FrtrYct9aGiI06dP09PTQyaT\nIZPJVLllfFE3cTeMk43FyzUAt8io3opU33J3xThyuVzglnGWe5iWlhY6OjpIp9NB2bzTp08Hq1AT\niQTJZDLIIWMYhuEwRWgQfvpe9+qiYlxbX19ncXGRS5cuceXKFZaWltjY2AgqKflhjLFYjHQ6XeVf\nT6fTNTM+mpVuGEYYE/cGUSuNr0sz4ApwuHJ5Fy9eZGZmJhB3l6M9mUySSqWC1t3dHUTGOFeMy/YY\ni8WCBU4m7oZhhDFxbyC+j90XdxfL7opvTE9PMzMzw/LyciDu8XicRCIRhDz29PQEk6i1LPe2tjYT\nd8Mw6hLdmLkDJhzy6BYrraysBCtRL1y4wM9//vMqt8z6+nrglkkmk3R1dTEwMMCpU6c4ffo011xz\nTVXGR+cXRHVQAAAPJ0lEQVRjN7fMzhCRERH5poj8RER+JCIfrhzvEZGnReQlEfm6iHR515wRkfMi\n8qKI3H54ozeMvWOWewMJC7xLMzA3N8fk5CQXLlzg4sWLgf99fX19i1vGF/fh4WH6+vro7e2tyvjo\nF+CwsMer4hLfvVBZZf23IvI08AHKie8+ISIfpZz47gERuZlyWO9NlHMoPSMir9N6lc0N44hi4t4g\n/DBH95rNZgOf+/z8PHNzc8zOzpLP54OFTJubm0FMu583pqenh/7+/iC1gHPHtLe3W2qBXbCHxHd3\nAo+ragG4ICLngVuBZw946IaxL0zcG4CfM2ZjYyMoxrG4uMjS0lIQ6ujSC7gPAOebh3JcuhN4v/C1\nK3RtLpj9s8PEd9cC3/Yum6gcM4xjhYl7gygUCkHo4+rqalDsenl5eYu4Oyvf5Wp3eWRcOKTL2e5q\nofqRMcbeaF7iuzFve3RvtzAMGp83ycS9Aahqlbi7KkuLi4uBuDv/ej6fr5p0LZVKwFbL3S1Qspj2\n/bPLxHcTwGnv8pHKsTqMhfbNe2PsjUbnTbJomQbhi/vy8jILCwtV4r66urqtW6aW5Z5MJoO4drPc\n98V2ie+gOvHdU8A9IhITkRuAG4HnDmqghtEozHLfJfVSDDhxX11dDcTdd8usr6+Ty+XY3NyseV9n\nube3t1dZ7s5q9y13s953zm4T36nqORF5AjgHbAIftEgZ4zhi4r4HnMXtwh4LhULgjnGrUKenp5md\nnWVhYYFsNsvGxgaFQuGwh37i2EviO1V9CHioaYMyjAPAxH2XhBcquXh23x0zOzvLzMwMV65cYXFx\nMRD3YrF42MM3DOOEYOK+S/wUvi73unPHhC33+fl5VlZWTNwNwzhwTNx3ie+K2dzcDHKzh90yri6q\nK6WXy+XMLWMYxoFh4r4HfMt9O3FfWlqqWrVqlrthGAeFifsu8S33fD4fVFNyzS1gctWV/MpKLqYd\n2JIbxs/yGI6KsegYwzB2y1Xj3Gtk1fu9yvGzInJJRL5faXc0f7iHTy1hX11drVqk5Mex+5E1jpaW\nliDsMRaLEY/HgxQDLneMX+zaEoQZhrFbdmK518qq943Kzz6lqp9q3vCOHi6PjHPHuFJ5fnoB54Jx\nwu4vVoJXxd231v0CHH6edhN1wzD2wk4KZNfKqucSKZ041fHFvZbl7hYq+eLuNxGpEndnrcdisWDB\nkm+5m2vGMIy9sKv0A15WPZdA4z4ReUFEPusXO4gybjLVL3Jdyy1TLBa3CLzD+dhruWXqWe5mwRuG\nsRt2LO7hrHrAI8BrVfUWypb9iXDPOMs97HevlzfGt9id1e6E3aX27ezsDDJAOpEP+90NwzB2w46i\nZWpl1VPVK94pjwJ/Ve/6sbGxYDuc+SzKODF3bhgn7Ol0mnQ6TSaTIZ1O09nZyTXXXMOpU6fo7+8n\nk8kEdVJruWhOMo1Oi2oYUWWnoZBbsuqJyHDFHw/wbuDH9S72xf0k4ScDc+6WeDweFL/228DAAIOD\ngwwMDATi3traWjM08iTT6LSohhFVriru22TVe6+I3AKUgAvA7zZxnMcS3wXjJkyTySQ9PT0MDg4y\nODjI0NAQQ0ND9Pb20t3dTVdXV5Xl7oTdLHfDMHbDTqJl6mXV++vGDyda+JZ7PB6no6ODVCpFd3c3\nAwMDXHvttYyMjHDttdeSyWSCHO4uj7tfCNswDGM32ArVJhIW90QiQWdnZ2C5nzp1iuuuu47rr7+e\nVCoV+NidC8esdcMw9oqJ+y5xk6N+Iet0Ok13dzfr6+tBGb2WlhZUlUQiETR37vDwMAMDA/T399PT\n00MmkyGRSFS5X9y2YRjGXjBx3yUtLS2BsHd2dgZhjqVSiba2NhKJBJlMhr6+PlQ1cMe4WPZUKsXw\n8DCDg4N0dXWRSCSqYtoBc8UYhrFvTNx3SUtLC+3t7SQSCVQ12G9vbyeZTNLV1UV/fz9LS0uoalWZ\nPPeh0NvbS09PD93d3YG4OyvdRN0wjEZg4r5LnJg7YY/FYoHLJZPJsL6+HuSbAQIfugtrjMVipFKp\noCWTyWDiFMxqNwyjMRyouI+Pjx/YAqZm9eXE3Ql1qVRifHyct7zlLVW5211hDt+P7hYjOUvezwK5\nU1GPwnt4VPozjChj4r5LaqUD+N73vsc73/nOhvdViyi8h0elP8OIMhaOYRiGEUFM3A3DMCKI+Klo\nm9KBSHM7ME48qrrtZIWIfA54JzCtqr9YOdYD/DlwHeX0GXer6lLlZ2eAeykXqvmIqj5d575azsbh\n8zD33z/PJz/58D6eyDDK83VX+9vejqb73PczOMNoEJ8H/gj4gnfsAeAZVf2EiHwUOAM8ICI3A3cD\nNwEjwDMi8jptthVkGA3G3DJG5FHVbwELocN3AY9Vth8D3lXZvhN4XFULqnoBOA/cehDjNIxGYuJu\nnFQGVXUaglKSg5Xj1wIXvfMmeLWspGEcG2wRk2GU2aPbZczbHm3AMIyTSqML0RyIuIvIHcB/o/xN\n4XOq2rTZJhG5ACxRzjO/qaoN/Uq928m5JvR1FvhXwEzltI+p6r7TL4vICGWf9BDl9+5RVf3DZjxb\njb4+o6p/1Kxnq8O0iAyp6rSIDHt9TgCnvfNGKsfqMBbaf7bWSYZxVRpdiKbpbhkRaQH+GPhN4I3A\ne0TkDU3ssgSMquqbGi3sFT5P+Vl83OTc64FvUp6ca1ZfAJ9S1TdXWqPErwDcr6pvBN4CfKjye2rG\ns4X7us/7m2jGswFIpTmeAt5f2X4f8KR3/B4RiYnIDcCNwHMNHIdhHAgH4XO/FTivqi+r6ibwOOXJ\nrGYhNPG5djk514y+oFqkGoKqTqnqC5XtLPAiZau14c9Wpy/n1274s4nIl4H/C/yCiLwiIh8APg78\nhoi8BPx6ZR9VPQc8AZwDvgZ80CJljOPIQbhlwhNUl2hu9IEC3xCRIuWv+482sS9H1eSciAxe7YJ9\ncp+I/DbwPeDfNMIF5CMi1wO3AN8Bhpr5bF5fzwK/ShOeTVXfW+dHb6tz/kPAQ/vt1zAOkyhGy9ym\nqm8G3kHZtfCrhzCGZlp6jwCvVdVbgCngU428uYh0An9BefFOlq3P0rBnq9FXU5/NME4SByHuE8Br\nvP2rTFDtD1WdrLxeAb7KwcQoT4vIEEBocq7hqOoVz03wKPDLjbq3iLRRFtsvqqrzQTfl2Wr11cxn\nM4yTxkGI+3eBG0XkOhGJAfdQnrRqOCKSrFiDiEgKuB34cTO6YmeTcw3vqyKwjnfT2Of7M+Ccqn7a\nO9asZ9vSV5OfzTBOFAeRfqAoIvcBT/NqKOSLTepuCPhqJZ9NG/ClenlB9kplcm4U6BORV4CzlCfj\nviIi9wIvU16+3qy+3ioit1COCroA/G6D+roN+C3gRyLyPGX3y8eAh4EnGvls2/T13mY8m2GcRJqe\nOMwwooolDjOayX4Th0VxQtUwDOPEY+JuGIYRQUzcDcMwIoiJu2EYRgQxcTcMw4ggJu6GYRgRxMTd\nMAwjgpi4G4ZhRBATd8MwjAhi4m4YhhFBTNwNwzAiiIm7YRjGNgwPX4+I1GzDw9cf9vDqciAFsg3D\nMI4r09MvU69GzfR0w6tCNgyz3A3DMCKIibthGEYEMXE3DMOIICbuhmEYEcTE3TBqICJ3iMhPReTv\nROSjhz0ew9gtJu6GEUJEWoA/Bn4TeCPwHhF5w2GMZXx8PBJ9HFQ/B/MsB9HH/jFxN4yt3AqcV9WX\nVXUTeBy46zAG8s53vqvpMdbHWdzDMehvfetbr/re1Itb3/n7Od6AkTcfE3fD2Mq1wEVv/1Ll2I74\n0z/9bMMEeXV1iXKM9dY2PT117BbWNJpXY9BdO0v5vXl5F9ds/34eV2wRk2Hsg0zmn1ft53I/Y21t\nnvqLXjpqCkZLS5JSaW2Xvedq9lOvD4ChoeuYmrqw5fjw8PV1BbHe2LYbc72f/f7v/5ddX7O39ya+\nB2Gu/X7C8RR4Ua39R2gYJxUR+cfAmKreUdl/AFBVfTh0nv3zGE1FVff8yWLibhghRKQVeAn4dWAS\neA54j6q+eKgDM4xdYG4ZwwihqkURuQ94mvK81OdM2I3jhlnuhmEYEcSiZQxjlzRrgZOIXBCRH4jI\n8yLyXOVYj4g8LSIvicjXRaRrD/f9nIhMi8gPvWN17ysiZ0TkvIi8KCK376OPsyJySUS+X2l37LOP\nERH5poj8RER+JCIfbtKzhPv5vUY/j4jEReTZyu/6JyLynxv+LKpqzZq1HTbKBtHPgOuAduAF4A0N\nuvffAz2hYw8D/76y/VHg43u4768CtwA/vNp9gZuB5ym7bK+vPKvssY+zwP01zr1pj30MA7dUtjsp\nz4u8oQnPUq+fRj9PsvLaCnwHuK2Rz2KWu2HsjmYucBK2fpu+C3issv0Y8K7d3lRVvwUs7PC+dwKP\nq2pBVS8A5yk/8176gNpxhHftsY8pVX2hsp0FXgRGmvAstfpx6xwa+TwuvjNO+fe+0MhnMXE3jN2x\nrwVOV0GBb4jId0XkX1aODanqNJRFBxhsUF+Dde4bfr4J9vd894nICyLyWc/FsO8+ROR6yt8UvkP9\n96iR/TxbOdSw5xGRFhF5HpgCxlX1XCOfxcTdMI4Ot6nqm4F3AB8SkV9j66qaZkVANOO+jwCvVdVb\nKAvYJxtxUxHpBP4C+EjFsm7Ke1Sjn4Y+j6qWVPVNlL99/JqIjNLAZzFxN4zdMQG8xtsfqRzbN6o6\nWXm9Avwl5a/d0yIyBCAiw8BMI/ra5r4TwGnvvD0/n6pe0YrDGHiUV90Ie+5DRNooC+4XVfXJyuGG\nP0utfprxPJX7LgNfA36pkc9i4m4Yu+O7wI0icp2IxIB7gKf2e1MRSVYsRUQkBdwO/Khy7/dXTnsf\n8GTNG+ygC6r9xfXu+xRwj4jEROQG4EbKi7h23UdFnBzvBn7cgD7+DDinqp9u8rNs6aeRzyMi/c6t\nIyIJ4DcoT5g27ll2O/NuzdpJb8AdlCMozgMPNOieN1COvHmesqg/UDneCzxT6e9poHsP9/4ycJly\n8pRXgA8APfXuC5yhHI3xInD7Pvr4AvDDynP9JWV/8n76uA0oeu/T9yu/i7rvUYP7adjzAP+wct/n\ngR8A//Zqv+/d9mGLmAzDMCKIuWUMwzAiiIm7YRhGBDFxNwzDiCAm7oZhGBHExN0wDCOCmLgbhmFE\nEBN3wzCMCGLibhiGEUH+P+TZ+wgUbGx9AAAAAElFTkSuQmCC\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f22841b4d50>"
            ]
          }
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "weVoVR-nN0cN",
        "colab_type": "text"
      },
      "source": [
        "The large number of 0 values correspond to the background of the image, another large mass of value 255 is black, and a mix of grayscale transition values in between.\n",
        "\n",
        "Both the image and histogram look sensible. But, it's good practice when training image models to normalize values to be centered around 0.\n",
        "\n",
        "We'll do that next. The normalization code is fairly short, and it may be tempting to assume we haven't made mistakes, but we'll double-check by looking at the rendered input and histogram again. Malformed inputs are a surprisingly common source of errors when developing new models."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jc1xCZXHNKVp",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 531,
          "status": "ok",
          "timestamp": 1446749126656,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "bd45b3dd-438b-41db-ea8f-d202d4a09e63"
      },
      "source": [
        "# Let's convert the uint8 image to 32 bit floats and rescale \n",
        "# the values to be centered around 0, between [-0.5, 0.5]. \n",
        "# \n",
        "# We again plot the image and histogram to check that we \n",
        "# haven't mangled the data.\n",
        "scaled = image.astype(numpy.float32)\n",
        "scaled = (scaled - (255 / 2.0)) / 255\n",
        "_, (ax1, ax2) = plt.subplots(1, 2)\n",
        "ax1.imshow(scaled.reshape(28, 28), cmap=plt.cm.Greys);\n",
        "ax2.hist(scaled, bins=20, range=[-0.5, 0.5]);"
      ],
      "outputs": [
        {
          "output_type": "display_data",
          "metadata": {},
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f22841b4550>"
            ]
          }
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PlqlwkX-O0Hd",
        "colab_type": "text"
      },
      "source": [
        "Great -- we've retained the correct image data while properly rescaling to the range [-0.5, 0.5].\n",
        "\n",
        "## Reading the labels\n",
        "\n",
        "Let's next unpack the test label data. The format here is similar: a magic number followed by a count followed by the labels as `uint8` values. In more detail:\n",
        "\n",
        "    [offset] [type]          [value]          [description] \n",
        "    0000     32 bit integer  0x00000801(2049) magic number (MSB first) \n",
        "    0004     32 bit integer  10000            number of items \n",
        "    0008     unsigned byte   ??               label \n",
        "    0009     unsigned byte   ??               label \n",
        "    ........ \n",
        "    xxxx     unsigned byte   ??               label\n",
        "\n",
        "As with the image data, let's read  the first test set value to sanity check our input path. We'll expect a 7."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "d8zv9yZzQOnV",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 90,
          "status": "ok",
          "timestamp": 1446749126903,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "ad203b2c-f095-4035-e0cd-7869c078da3d"
      },
      "source": [
        "with gzip.open(test_labels_filename) as f:\n",
        "  # Print the header fields.\n",
        "  for field in ['magic number', 'label count']:\n",
        "    print field, struct.unpack('>i', f.read(4))[0]\n",
        "\n",
        "  print 'First label:', struct.unpack('B', f.read(1))[0]"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "magic number 2049\n",
            "label count 10000\n",
            "First label: 7\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zAGrQSXCQtIm",
        "colab_type": "text"
      },
      "source": [
        "Indeed, the first label of the test set is 7.\n",
        "\n",
        "## Forming the training, testing, and validation data sets\n",
        "\n",
        "Now that we understand how to read a single element, we can read a much larger set that we'll use for training, testing, and validation.\n",
        "\n",
        "### Image data\n",
        "\n",
        "The code below is a generalization of our prototyping above that reads the entire test and training data set."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ofFZ5oJeRMDA",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 734,
          "status": "ok",
          "timestamp": 1446749128718,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "ff2de90b-aed9-4ce5-db8c-9123496186b1"
      },
      "source": [
        "IMAGE_SIZE = 28\n",
        "PIXEL_DEPTH = 255\n",
        "\n",
        "def extract_data(filename, num_images):\n",
        "  \"\"\"Extract the images into a 4D tensor [image index, y, x, channels].\n",
        "  \n",
        "  For MNIST data, the number of channels is always 1.\n",
        "\n",
        "  Values are rescaled from [0, 255] down to [-0.5, 0.5].\n",
        "  \"\"\"\n",
        "  print 'Extracting', filename\n",
        "  with gzip.open(filename) as bytestream:\n",
        "    # Skip the magic number and dimensions; we know these values.\n",
        "    bytestream.read(16)\n",
        "    \n",
        "    buf = bytestream.read(IMAGE_SIZE * IMAGE_SIZE * num_images)\n",
        "    data = numpy.frombuffer(buf, dtype=numpy.uint8).astype(numpy.float32)\n",
        "    data = (data - (PIXEL_DEPTH / 2.0)) / PIXEL_DEPTH\n",
        "    data = data.reshape(num_images, IMAGE_SIZE, IMAGE_SIZE, 1)\n",
        "    return data\n",
        "\n",
        "train_data = extract_data(train_data_filename, 60000)\n",
        "test_data = extract_data(test_data_filename, 10000)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Extracting /tmp/mnist-data/train-images-idx3-ubyte.gz\n",
            "Extracting /tmp/mnist-data/t10k-images-idx3-ubyte.gz\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0x4rwXxUR96O",
        "colab_type": "text"
      },
      "source": [
        "A crucial difference here is how we `reshape` the array of pixel values. Instead of one image that's 28x28, we now have a set of 60,000 images, each one being 28x28. We also include a number of channels, which for grayscale images as we have here is 1.\n",
        "\n",
        "Let's make sure we've got the reshaping parameters right by inspecting the dimensions and the first two images. (Again, mangled input is a very common source of errors.)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0AwSo8mlSja_",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {},
            {}
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 400,
          "status": "ok",
          "timestamp": 1446749129657,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "11490c39-7c67-4fe5-982c-ca8278294d96"
      },
      "source": [
        "print 'Training data shape', train_data.shape\n",
        "_, (ax1, ax2) = plt.subplots(1, 2)\n",
        "ax1.imshow(train_data[0].reshape(28, 28), cmap=plt.cm.Greys);\n",
        "ax2.imshow(train_data[1].reshape(28, 28), cmap=plt.cm.Greys);"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Training data shape (60000, 28, 28, 1)\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "metadata": {},
          "data": {
            "image/png": "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\nbj/C7XFdaAAAAABJRU5ErkJggg==\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f22684f2890>"
            ]
          }
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cwBhQ3ouTQcW",
        "colab_type": "text"
      },
      "source": [
        "Looks good. Now we know how to index our full set of training and test images."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PBCB9aYxRvBi",
        "colab_type": "text"
      },
      "source": [
        "### Label data\n",
        "\n",
        "Let's move on to loading the full set of labels. As is typical in classification problems, we'll convert our input labels into a [1-hot](https://en.wikipedia.org/wiki/One-hot) encoding over a length 10 vector corresponding to 10 digits. The vector [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], for example, would correspond to the digit 1."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9pK1j2WlRwY9",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 191,
          "status": "ok",
          "timestamp": 1446749131421,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "1ca31655-e14f-405a-b266-6a6c78827af5"
      },
      "source": [
        "NUM_LABELS = 10\n",
        "\n",
        "def extract_labels(filename, num_images):\n",
        "  \"\"\"Extract the labels into a 1-hot matrix [image index, label index].\"\"\"\n",
        "  print 'Extracting', filename\n",
        "  with gzip.open(filename) as bytestream:\n",
        "    # Skip the magic number and count; we know these values.\n",
        "    bytestream.read(8)\n",
        "    \n",
        "    buf = bytestream.read(1 * num_images)\n",
        "    labels = numpy.frombuffer(buf, dtype=numpy.uint8)\n",
        "  # Convert to dense 1-hot representation.\n",
        "  return (numpy.arange(NUM_LABELS) == labels[:, None]).astype(numpy.float32)\n",
        "\n",
        "train_labels = extract_labels(train_labels_filename, 60000)\n",
        "test_labels = extract_labels(test_labels_filename, 10000)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Extracting /tmp/mnist-data/train-labels-idx1-ubyte.gz\n",
            "Extracting /tmp/mnist-data/t10k-labels-idx1-ubyte.gz\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hb3Vaq72UUxW",
        "colab_type": "text"
      },
      "source": [
        "As with our image data, we'll double-check that our 1-hot encoding of the first few values matches our expectations."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uEBID71nUVj1",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 127,
          "status": "ok",
          "timestamp": 1446749132853,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "3f318310-18dd-49ed-9943-47b4aae7ee69"
      },
      "source": [
        "print 'Training labels shape', train_labels.shape\n",
        "print 'First label vector', train_labels[0]\n",
        "print 'Second label vector', train_labels[1]"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Training labels shape (60000, 10)\n",
            "First label vector [ 0.  0.  0.  0.  0.  1.  0.  0.  0.  0.]\n",
            "Second label vector [ 1.  0.  0.  0.  0.  0.  0.  0.  0.  0.]\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5EwtEhxRUneF",
        "colab_type": "text"
      },
      "source": [
        "The 1-hot encoding looks reasonable.\n",
        "\n",
        "### Segmenting data into training, test, and validation\n",
        "\n",
        "The final step in preparing our data is to split it into three sets: training, test, and validation. This isn't the format of the original data set, so we'll take a small slice of the training data and treat that as our validation set."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "e7aBYBtIVxHE",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 176,
          "status": "ok",
          "timestamp": 1446749134110,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "bdeae1a8-daff-4743-e594-f1d2229c0f4e"
      },
      "source": [
        "VALIDATION_SIZE = 5000\n",
        "\n",
        "validation_data = train_data[:VALIDATION_SIZE, :, :, :]\n",
        "validation_labels = train_labels[:VALIDATION_SIZE]\n",
        "train_data = train_data[VALIDATION_SIZE:, :, :, :]\n",
        "train_labels = train_labels[VALIDATION_SIZE:]\n",
        "\n",
        "train_size = train_labels.shape[0]\n",
        "\n",
        "print 'Validation shape', validation_data.shape\n",
        "print 'Train size', train_size"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Validation shape (5000, 28, 28, 1)\n",
            "Train size 55000\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1JFhEH8EVj4O",
        "colab_type": "text"
      },
      "source": [
        "# Defining the model\n",
        "\n",
        "Now that we've prepared our data, we're ready to define our model.\n",
        "\n",
        "The comments describe the architecture, which fairly typical of models that process image data. The raw input passes through several [convolution](https://en.wikipedia.org/wiki/Convolutional_neural_network#Convolutional_layer) and [max pooling](https://en.wikipedia.org/wiki/Convolutional_neural_network#Pooling_layer) layers with [rectified linear](https://en.wikipedia.org/wiki/Convolutional_neural_network#ReLU_layer) activations before several fully connected layers and a [softmax](https://en.wikipedia.org/wiki/Convolutional_neural_network#Loss_layer) loss for predicting the output class. During training, we use [dropout](https://en.wikipedia.org/wiki/Convolutional_neural_network#Dropout_method).\n",
        "\n",
        "We'll separate our model definition into three steps:\n",
        "\n",
        "1. Defining the variables that will hold the trainable weights.\n",
        "1. Defining the basic model graph structure described above. And,\n",
        "1. Stamping out several copies of the model graph for training, testing, and validation.\n",
        "\n",
        "We'll start with the variables."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Q1VfiAzjzuK8",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 2081,
          "status": "ok",
          "timestamp": 1446749138298,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "f53a39c9-3a52-47ca-d7a3-9f9d84eccf63"
      },
      "source": [
        "import tensorflow as tf\n",
        "\n",
        "# We'll bundle groups of examples during training for efficiency.\n",
        "# This defines the size of the batch.\n",
        "BATCH_SIZE = 60\n",
        "# We have only one channel in our grayscale images.\n",
        "NUM_CHANNELS = 1\n",
        "# The random seed that defines initialization.\n",
        "SEED = 42\n",
        "\n",
        "# This is where training samples and labels are fed to the graph.\n",
        "# These placeholder nodes will be fed a batch of training data at each\n",
        "# training step, which we'll write once we define the graph structure.\n",
        "train_data_node = tf.placeholder(\n",
        "  tf.float32,\n",
        "  shape=(BATCH_SIZE, IMAGE_SIZE, IMAGE_SIZE, NUM_CHANNELS))\n",
        "train_labels_node = tf.placeholder(tf.float32,\n",
        "                                   shape=(BATCH_SIZE, NUM_LABELS))\n",
        "\n",
        "# For the validation and test data, we'll just hold the entire dataset in\n",
        "# one constant node.\n",
        "validation_data_node = tf.constant(validation_data)\n",
        "test_data_node = tf.constant(test_data)\n",
        "\n",
        "# The variables below hold all the trainable weights. For each, the\n",
        "# parameter defines how the variables will be initialized.\n",
        "conv1_weights = tf.Variable(\n",
        "  tf.truncated_normal([5, 5, NUM_CHANNELS, 32],  # 5x5 filter, depth 32.\n",
        "                      stddev=0.1,\n",
        "                      seed=SEED))\n",
        "conv1_biases = tf.Variable(tf.zeros([32]))\n",
        "conv2_weights = tf.Variable(\n",
        "  tf.truncated_normal([5, 5, 32, 64],\n",
        "                      stddev=0.1,\n",
        "                      seed=SEED))\n",
        "conv2_biases = tf.Variable(tf.constant(0.1, shape=[64]))\n",
        "fc1_weights = tf.Variable(  # fully connected, depth 512.\n",
        "  tf.truncated_normal([IMAGE_SIZE / 4 * IMAGE_SIZE / 4 * 64, 512],\n",
        "                      stddev=0.1,\n",
        "                      seed=SEED))\n",
        "fc1_biases = tf.Variable(tf.constant(0.1, shape=[512]))\n",
        "fc2_weights = tf.Variable(\n",
        "  tf.truncated_normal([512, NUM_LABELS],\n",
        "                      stddev=0.1,\n",
        "                      seed=SEED))\n",
        "fc2_biases = tf.Variable(tf.constant(0.1, shape=[NUM_LABELS]))\n",
        "\n",
        "print 'Done'"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Done\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QHB_u04Z4HO6",
        "colab_type": "text"
      },
      "source": [
        "Now that we've defined the variables to be trained, we're ready to wire them together into a TensorFlow graph.\n",
        "\n",
        "We'll define a helper to do this, `model`, which will return copies of the graph suitable for training and testing. Note the `train` argument, which controls whether or not dropout is used in the hidden layer. (We want to use dropout only during training.)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "V85_B9QF3uBp",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 772,
          "status": "ok",
          "timestamp": 1446749138306,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "457d3e49-73ad-4451-c196-421dd4681efc"
      },
      "source": [
        "def model(data, train=False):\n",
        "  \"\"\"The Model definition.\"\"\"\n",
        "  # 2D convolution, with 'SAME' padding (i.e. the output feature map has\n",
        "  # the same size as the input). Note that {strides} is a 4D array whose\n",
        "  # shape matches the data layout: [image index, y, x, depth].\n",
        "  conv = tf.nn.conv2d(data,\n",
        "                      conv1_weights,\n",
        "                      strides=[1, 1, 1, 1],\n",
        "                      padding='SAME')\n",
        "\n",
        "  # Bias and rectified linear non-linearity.\n",
        "  relu = tf.nn.relu(tf.nn.bias_add(conv, conv1_biases))\n",
        "  \n",
        "  # Max pooling. The kernel size spec ksize also follows the layout of\n",
        "  # the data. Here we have a pooling window of 2, and a stride of 2.\n",
        "  pool = tf.nn.max_pool(relu,\n",
        "                        ksize=[1, 2, 2, 1],\n",
        "                        strides=[1, 2, 2, 1],\n",
        "                        padding='SAME')\n",
        "  conv = tf.nn.conv2d(pool,\n",
        "                      conv2_weights,\n",
        "                      strides=[1, 1, 1, 1],\n",
        "                      padding='SAME')\n",
        "  relu = tf.nn.relu(tf.nn.bias_add(conv, conv2_biases))\n",
        "  pool = tf.nn.max_pool(relu,\n",
        "                        ksize=[1, 2, 2, 1],\n",
        "                        strides=[1, 2, 2, 1],\n",
        "                        padding='SAME')\n",
        "  \n",
        "  # Reshape the feature map cuboid into a 2D matrix to feed it to the\n",
        "  # fully connected layers.\n",
        "  pool_shape = pool.get_shape().as_list()\n",
        "  reshape = tf.reshape(\n",
        "      pool,\n",
        "      [pool_shape[0], pool_shape[1] * pool_shape[2] * pool_shape[3]])\n",
        "  \n",
        "  # Fully connected layer. Note that the '+' operation automatically\n",
        "  # broadcasts the biases.\n",
        "  hidden = tf.nn.relu(tf.matmul(reshape, fc1_weights) + fc1_biases)\n",
        "  \n",
        "  # Add a 50% dropout during training only. Dropout also scales\n",
        "  # activations such that no rescaling is needed at evaluation time.\n",
        "  if train:\n",
        "    hidden = tf.nn.dropout(hidden, 0.5, seed=SEED)\n",
        "  return tf.matmul(hidden, fc2_weights) + fc2_biases\n",
        "\n",
        "print 'Done'"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Done\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7bvEtt8C4fLC",
        "colab_type": "text"
      },
      "source": [
        "Having defined the basic structure of the graph, we're ready to stamp out multiple copies for training, testing, and validation.\n",
        "\n",
        "Here, we'll do some customizations depending on which graph we're constructing. `train_prediction` holds the training graph, for which we use cross-entropy loss and weight regularization. We'll adjust the learning rate during training -- that's handled by the `exponential_decay` operation, which is itself an argument to the `MomentumOptimizer` that performs the actual training.\n",
        "\n",
        "The vaildation and prediction graphs are much simpler the generate -- we need only create copies of the model with the validation and test inputs and a softmax classifier as the output."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "9pR1EBNT3sCv",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 269,
          "status": "ok",
          "timestamp": 1446749139596,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "570681b1-f33e-4618-b742-48e12aa58132"
      },
      "source": [
        "# Training computation: logits + cross-entropy loss.\n",
        "logits = model(train_data_node, True)\n",
        "loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(\n",
        "  logits, train_labels_node))\n",
        "\n",
        "# L2 regularization for the fully connected parameters.\n",
        "regularizers = (tf.nn.l2_loss(fc1_weights) + tf.nn.l2_loss(fc1_biases) +\n",
        "                tf.nn.l2_loss(fc2_weights) + tf.nn.l2_loss(fc2_biases))\n",
        "# Add the regularization term to the loss.\n",
        "loss += 5e-4 * regularizers\n",
        "\n",
        "# Optimizer: set up a variable that's incremented once per batch and\n",
        "# controls the learning rate decay.\n",
        "batch = tf.Variable(0)\n",
        "# Decay once per epoch, using an exponential schedule starting at 0.01.\n",
        "learning_rate = tf.train.exponential_decay(\n",
        "  0.01,                # Base learning rate.\n",
        "  batch * BATCH_SIZE,  # Current index into the dataset.\n",
        "  train_size,          # Decay step.\n",
        "  0.95,                # Decay rate.\n",
        "  staircase=True)\n",
        "# Use simple momentum for the optimization.\n",
        "optimizer = tf.train.MomentumOptimizer(learning_rate,\n",
        "                                       0.9).minimize(loss,\n",
        "                                                     global_step=batch)\n",
        "\n",
        "# Predictions for the minibatch, validation set and test set.\n",
        "train_prediction = tf.nn.softmax(logits)\n",
        "# We'll compute them only once in a while by calling their {eval()} method.\n",
        "validation_prediction = tf.nn.softmax(model(validation_data_node))\n",
        "test_prediction = tf.nn.softmax(model(test_data_node))\n",
        "\n",
        "print 'Done'"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Done\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4T21uZJq5UfH",
        "colab_type": "text"
      },
      "source": [
        "# Training and visualizing results\n",
        "\n",
        "Now that we have the training, test, and validation graphs, we're ready to actually go through the training loop and periodically evaluate loss and error.\n",
        "\n",
        "All of these operations take place in the context of a session. In Python, we'd write something like:\n",
        "\n",
        "    with tf.Session() as s:\n",
        "      ...training / test / evaluation loop...\n",
        "  \n",
        "But, here, we'll want to keep the session open so we can poke at values as we work out the details of training. The TensorFlow API includes a function for this, `InteractiveSession`.\n",
        "\n",
        "We'll start by creating a session and initializing the varibles we defined above."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "z6Kc5iql6qxV",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        },
        "cellView": "both"
      },
      "source": [
        "# Create a new interactive session that we'll use in\n",
        "# subsequent code cells.\n",
        "s = tf.InteractiveSession()\n",
        "\n",
        "# Use our newly created session as the default for \n",
        "# subsequent operations.\n",
        "s.as_default()\n",
        "\n",
        "# Initialize all the variables we defined above.\n",
        "tf.initialize_all_variables().run()"
      ],
      "outputs": [],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hcG8H-Ka6_mw",
        "colab_type": "text"
      },
      "source": [
        "Now we're ready to perform operations on the graph. Let's start with one round of training. We're going to organize our training steps into batches for efficiency; i.e., training using a small set of examples at each step rather than a single example."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LYVxeEox71Pg",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 386,
          "status": "ok",
          "timestamp": 1446749389138,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "9184b5df-009a-4b1b-e312-5be94351351f"
      },
      "source": [
        "BATCH_SIZE = 60\n",
        "\n",
        "# Grab the first BATCH_SIZE examples and labels.\n",
        "batch_data = train_data[:BATCH_SIZE, :, :, :]\n",
        "batch_labels = train_labels[:BATCH_SIZE]\n",
        "\n",
        "# This dictionary maps the batch data (as a numpy array) to the\n",
        "# node in the graph it should be fed to.\n",
        "feed_dict = {train_data_node: batch_data,\n",
        "             train_labels_node: batch_labels}\n",
        "\n",
        "# Run the graph and fetch some of the nodes.\n",
        "_, l, lr, predictions = s.run(\n",
        "  [optimizer, loss, learning_rate, train_prediction],\n",
        "  feed_dict=feed_dict)\n",
        "\n",
        "print 'Done'"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Done\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7bL4-RNm_K-B",
        "colab_type": "text"
      },
      "source": [
        "Let's take a look at the predictions. How did we do? Recall that the output will be probabilities over the possible classes, so let's look at those probabilities."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2eNitV_4_ZUL",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 160,
          "status": "ok",
          "timestamp": 1446749519023,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "f1340dd1-255b-4523-bf62-7e3ebb361333"
      },
      "source": [
        "print predictions[0]"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[  2.25393465e-04   4.76219648e-05   1.66868104e-03   5.67830284e-05\n",
            "   6.03432834e-01   4.34970111e-02   2.19316153e-05   1.41285171e-04\n",
            "   1.54902827e-05   3.50893021e-01]\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "X5MgraJb_eQZ",
        "colab_type": "text"
      },
      "source": [
        "As expected without training, the predictions are all noise. Let's write a scoring function that picks the class with the maximum probability and compares with the example's label. We'll start by converting the probability vectors returned by the softmax into predictions we can match against the labels."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "wMMlUf5rCKgT",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 220,
          "status": "ok",
          "timestamp": 1446750411574,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "2c10e96d-52b6-47b0-b6eb-969ad462d46b"
      },
      "source": [
        "# The highest probability in the first entry.\n",
        "print 'First prediction', numpy.argmax(predictions[0])\n",
        "\n",
        "# But, predictions is actually a list of BATCH_SIZE probability vectors.\n",
        "print predictions.shape\n",
        "\n",
        "# So, we'll take the highest probability for each vector.\n",
        "print 'All predictions', numpy.argmax(predictions, 1)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "First prediction 4\n",
            "(60, 10)\n",
            "All predictions [4 4 2 7 7 7 7 7 7 7 7 7 0 8 9 0 7 7 0 7 4 0 5 0 9 9 7 0 7 4 7 7 7 0 7 7 9\n",
            " 7 9 9 0 7 7 7 2 7 0 7 2 9 9 9 9 9 0 7 9 4 8 7]\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8pMCIZ3_C2ni",
        "colab_type": "text"
      },
      "source": [
        "Next, we can do the same thing for our labels -- using `argmax` to convert our 1-hot encoding into a digit class."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "kZWp4T0JDDUe",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 232,
          "status": "ok",
          "timestamp": 1446750498351,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "47b588cd-bc82-45c3-a5d0-8d84dc27a3be"
      },
      "source": [
        "print 'Batch labels', numpy.argmax(batch_labels, 1)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Batch labels [7 3 4 6 1 8 1 0 9 8 0 3 1 2 7 0 2 9 6 0 1 6 7 1 9 7 6 5 5 8 8 3 4 4 8 7 3\n",
            " 6 4 6 6 3 8 8 9 9 4 4 0 7 8 1 0 0 1 8 5 7 1 7]\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bi5Z6whtDiht",
        "colab_type": "text"
      },
      "source": [
        "Now we can compare the predicted and label classes to compute the error rate and confusion matrix for this batch."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "U4hrLW4CDtQB",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            },
            {
              "item_id": 2
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 330,
          "status": "ok",
          "timestamp": 1446751307304,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "720494a3-cbf9-4687-9d94-e64a33fdd78f"
      },
      "source": [
        "correct = numpy.sum(numpy.argmax(predictions, 1) == numpy.argmax(batch_labels, 1))\n",
        "total = predictions.shape[0]\n",
        "\n",
        "print float(correct) / float(total)\n",
        "\n",
        "confusions = numpy.zeros([10, 10], numpy.float32)\n",
        "bundled = zip(numpy.argmax(predictions, 1), numpy.argmax(batch_labels, 1))\n",
        "for predicted, actual in bundled:\n",
        "  confusions[predicted, actual] += 1\n",
        "\n",
        "plt.grid(False)\n",
        "plt.xticks(numpy.arange(NUM_LABELS))\n",
        "plt.yticks(numpy.arange(NUM_LABELS))\n",
        "plt.imshow(confusions, cmap=plt.cm.jet, interpolation='nearest');"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "0.0666666666667\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "metadata": {},
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPcAAAD7CAYAAAC2TgIoAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAADpNJREFUeJzt3X+MXWWdx/H3p7TUli6UXYnrUtuxMV2RhECDSKzQ64Ju\nxQ1m12wEJGTJhv1DmxJNDMo/vf1jN9kYY0jWf4jYFVp0Q6GBRJcUJVMDG1nsT6QtdWUHCkKDsdOG\nlGBLv/vHPe1O6S3z3DnPM515+nklN3Nmcvqdb+6dT89zz7n3exURmFl9ZpzpBsysDIfbrFIOt1ml\nHG6zSjncZpVyuM0qNTNXIUm+pmZ2hkSE3v2zbOEGYH5ivt/qwpxu0q7LDzye/OtHuusY6t6avP9m\n/TJxz2Ggk1x3MAPWXtdN2+/hLnwxcV+AlYn7DfDYQe2PX6m6g9Ze0/enXpabVcrhNqvUmQn3zE6R\nsvM7lxWpC0OF6hasfUmnTN1Cjx1Mx8evVN08tc9MuGd1ipSdfn8cBWt/rFOmbqHHDqbj41eqbp7a\nSeGWtELSHkl7Jd3V+reaWXHjhlvSDODfgL8GLgVulvTR0o2ZWTspR+6rgN9ExEsRcQT4MfCFsm2Z\nWVsp4b4Y2Dfm+1ean5nZFOZLYWaVSnmF2qvAwjHfL2h+dqq3umMqd4qeWTU7e400t/eWEu5ngY9I\nWgS8BtwE3Nx3zwFelmhmEzXEyZfKNvfda9xwR8Q7klYCm+gt4++LiN3tGzSzkpLeOBIRjwN/WbgX\nM8vIJ9TMKuVwm1XK4TarlMNtVimH26xSDrdZpZTrs8J6AxJXZ6llZoNY03dAoo/cZpVyuM0q5XCb\nVcrhNquUw21WKYfbrFIOt1mlUqaf3idpv6Sdk9GQmeWRcuReS2+ssZlNI+OGOyKeAg5MQi9mlpGf\nc5tVKu/nczM8ZnuIsp+lZHa2GiHX9NMBdPKWM7M+hkiZfpq6LFdzM7NpIuVS2IPAfwFLJL0s6fby\nbZlZWylzy2+ZjEbMLC+fLTerlMNtVimH26xSDrdZpRxus0o53GaVyjvaeH6eWicZ7eavOZ3N75ap\n6/t5cpR4/Ebl0cZmZxOH26xSDrdZpRxus0o53GaVcrjNKpXyls8Fkp6U9Lyk5yStmozGzKydlEks\nR4GvR8R2SfOALZI2RcSewr2ZWQsp009fj4jtzfabwG7g4tKNmVk7Az3nljQEXA48U6IZM8sneUBi\nsyTfANzZHMFP9VZ3TOUOzOq06c3M+jkyDEeHx90tKdySZtIL9gMR8ehpd5zTTerNzFqY1Tn5wPn2\nmr67pS7LfwDsioh7WrZlZpMk5VLYMuDLwF9J2iZpq6QV5VszszZSpp8+DZwzCb2YWUZ+hZpZpRxu\ns0o53GaVcrjNKuVwm1XK4TarVN7pp6zOUsvMBrHG00/NziYOt1mlHG6zSjncZpVyuM0q5XCbVWrc\nd4VJmg38Aji3uT0aEXeXbszM2kl5y+fbkj4dEYclnQM8LWlZ81ZQM5uikpblEXG42Zzd/JsDxToy\nsyySwi1phqRtwOvAcETsKtuWmbWVeuQ+FhFXAAuAayUtL9uWmbWVPNoYICIOSfoJcCWw+dQ9hsds\nDzU3M8trpLm9t5Sz5e8HjkTEQUlzgM8A/Wep0knvz8wmaIiTD5x9jrOkHbk/CPxQkugt4x+IiJ+3\n7M7MCku5FPYcsHQSejGzjPwKNbNKOdxmlXK4zSrlcJtVyuE2q5TDbVYph9usUgO9/PSMmN8tV3u0\nUO3p2PN0tK5bpu7KMmWBSX38fOQ2q5TDbVYph9usUg63WaUcbrNKOdxmlUoOdzNHbaukx0o2ZGZ5\nDHLkvhPwYESzaSJ1+ukC4Abg+2XbMbNcUo/c3wW+AUTBXswso5QBiZ8H9kfEdkkdQKffe3jM9hCe\nfmpWwghZpp8Cy4AbJd0AzAH+RNL9EXHbqbt2BmjQzCZmiJTpp+MuyyPi7ohYGBGLgZuAJ/sH28ym\nEl/nNqvUoJ84spnTrQHMbErxkdusUg63WaUcbrNKOdxmlXK4zSrlcJtVaupPP52O0z6nY8/TUakp\npZU8fj5ym1XK4TarlMNtVimH26xSDrdZpRxus0olXQqTNAIcBI4BRyLiqpJNmVl7qde5jwGdiDhQ\nshkzyyd1Wa4B9jWzKSA1sAE8IelZSXeUbMjM8khdli+LiNckXUQv5Lsj4qlTdxsesz2Ep5+alTBC\nrumnRMRrzdc3JG0ErgL6hLuT3J6ZTdQQWaafSporaV6zfR7wWeDXrfszs6JSjtwfADZKimb/9RGx\nqWxbZtbWuOGOiP8FLp+EXswsI1/eMquUw21WKYfbrFIOt1mlHG6zSjncZpWa+tNPzU6nkimlpfjI\nbVYph9usUg63WaUcbrNKOdxmlXK4zSqVFG5JF0h6SNJuSc9L+kTpxsysndTr3PcAP42Iv5c0E5hb\nsCczy2DccEs6H7gmIv4BICKOAocK92VmLaUsyz8M/F7SWklbJd0raU7pxsysnZRwzwSWAt+LiKXA\nYeCbRbsys9ZSnnO/AuyLiF81328A7uq/6/CY7SE82tishBGyjDaOiP2S9klaEhF7geuAXf337gzQ\noJlNzBApo41Tz5avAtZLmgW8CNzeojMzmwSpH0qwA/h44V7MLCO/Qs2sUg63WaUcbrNKOdxmlXK4\nzSrlcJtVyuE2q1TW0cbL4+qc5QDYvH5F9ponrCxTdvmBx8sUpuD94fvihNW3qkhdgOH4z+w1N5+m\nXR+5zSrlcJtVyuE2q5TDbVYph9usUg63WaXGDbekJZK2NfPTtkk6KGnVZDRnZhOXMollL3AFgKQZ\n9MYubSzcl5m1NOiy/HrgtxGxr0QzZpbPoOH+EvCjEo2YWV7JLz9t5qfdyHuMNR7prjuxPb9zGfM7\nl7VqzsxONTq8k9HhnePuN8hryz8HbImIN063w1D31gHKmdlEvPvA+dKa9X33G2RZfjNekptNG6mf\n8jmX3sm0R8q2Y2a5pI42PgxcVLgXM8vIr1Azq5TDbVYph9usUg63WaUcbrNKOdxmlVJE5CkkBazO\nUusk87v5a5Y22j3THdhZZQ0RccoMVB+5zSrlcJtVyuE2q5TDbVYph9usUg63WaVS3/L5LUnPS9op\nab2kc0s3ZmbtpIw2XgTcAVwREZfRe5voTaUbM7N2Ut7PfQj4I3CepGPAXOB3Rbsys9bGPXJHxAHg\nO8DLwKvAaET8rHRjZtbOuEduSYuBrwGLgIPABkm3RMSDp+49PGZ7qLmZWV4jze29pSzLrwSejog/\nAEh6BPgk0CfcneT2zGyihjj5wLm5714pZ8tfAK6W9D5JAq4DdrfszswKS3nOvQO4H9gC7AAE3Fu4\nLzNrKXX66beBbxfuxcwy8ivUzCrlcJtVyuE2q5TDbVYph9usUg63WaUcbrNK5R1tvC5PrZOszF/y\nhFIjiKfjOOZpaPmBx4vU3XzhiiJ1ixmVRxubnU0cbrNKOdxmlXK4zSrlcJtVKnX66Z2Snmtuq0o3\nZWbtpUw/vRT4R3oTWS4H/qYZvWRmU1jKkfsS4JmIeDsi3gF+Afxd2bbMrK2UcP8auEbShZLmAjcA\nHyrblpm1Ne4klojYI+lfgSeAN4FtwDulGzOzdlLHLK0F1gJI+mdgX98dH+7+//YlHfhYp113Znaq\nI8NwdHjc3ZLCLemiiHhD0kLgb4Gr++74xW5yf2Y2QbM6vdtxb6/pu1tSuIGHJf0pcAT4SkQcated\nmZWWuiy/tnQjZpaXX6FmVimH26xSDrdZpc5MuHcNl6l7pFDdhE9UnLBSPU+3ugVrjw7vLFJ3qt8X\nZybcu4fL1E249jcxI4XqUq7n6Va3YO1i4Z7i94WX5WaVcrjNKpV3+qmZnRH9pp9mC7eZTS1elptV\nyuE2q9SkhlvSCkl7JO2VdFfGuvdJ2i8p6zUPSQskPSnp+Zzz4yTNlvSMpG1N7X/JUXdM/RmStkp6\nLHPdEUk7mr7/O2PdCyQ9JGl3c398IlPdJU2vW5uvBzM+ht9qet0pab2kczPVzTevMCIm5UbvP5L/\nARYBs4DtwEcz1f4UvfluOzP3/OfA5c32POCFjD3Pbb6eA/wSWJax768B64DHMt8fLwIXFvjb+Hfg\n9mZ7JnB+gd8xA/gd8KEMtRY198W5zff/AdyWoe6lwE5gdvN3sQlYPNF6k3nkvgr4TUS8FBFHgB8D\nX8hROCKeAg7kqPWuuq9HxPZm+01gN3BxptqHm83Z9P7wsvQvaQG9UVjfz1Hv3eXJvNqTdD5wTfQG\nghARR6PMW4qvB34bEf0HjQzmEPBH4DxJM4G59P7jaCvrvMLJDPfFnDzB5RUyBWUySBqitzp4JlO9\nGZK2Aa8DwxGxK0dd4LvAN4ASl0ECeELSs5LuyFTzw8DvJa1tls/3SpqTqfZYXwJ+lKNQRBwAvgO8\nDLwKjEbEzzKUzjqv0CfUEkiaB2wA7myO4K1FxLGIuAJYAFwraXnbmpI+D+xvVhtqbjkti4il9P7o\nvirpUxlqzgSWAt9rah8Gvpmh7gmSZgE3Ag9lqreY3lOfRcBfAPMk3dK2bkTsAY7PK/wpLecVTma4\nXwUWjvl+QfOzKa1Zdm0AHoiIR3PXb5agP6E3F76tZcCNkl6kd5T6tKT7M9QFICJea76+AWyk91Sr\nrVeAfRHxq+b7DfTCntPngC1N3zlcCTwdEX9ols+PAJ/MUTgi1kbElRHRAUaBvROtNZnhfhb4iKRF\nzZnFm4CcZ3NLHKkAfgDsioh7chWU9H5JFzTbc4DP0DvB2EpE3B0RCyNiMb3798mIuK1tXQBJc5sV\nDJLOAz5LbxnZSkTsB/ZJWtL86Dog11OU424m05K88QJwtaT3SRK9nnfnKCzpoubr8XmFD060VuoM\ntdYi4h1JK+mdAZwB3BcRue6QB4EO8GeSXgZWHz9B07LuMuDLwHPN8+MA7o6Itp/6/kHgh80fxgx6\nq4Kft6xZ2geAjc3LjGcC6yNiU6baq4D1zfL5ReD2THVpnrteD/xTrpoRsaNZEW2ht2zeBtybqXy2\neYV++alZpXxCzaxSDrdZpRxus0o53GaVcrjNKuVwm1XK4TarlMNtVqn/A4ohGJAOEeBVAAAAAElF\nTkSuQmCC\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f226836c350>"
            ]
          }
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iZmx_9DiDXQ3",
        "colab_type": "text"
      },
      "source": [
        "Now let's wrap this up into our scoring function."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "DPJie7bPDaLa",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 178,
          "status": "ok",
          "timestamp": 1446751995007,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "a06c64ed-f95f-416f-a621-44cccdaba0f8"
      },
      "source": [
        "def error_rate(predictions, labels):\n",
        "  \"\"\"Return the error rate and confusions.\"\"\"\n",
        "  correct = numpy.sum(numpy.argmax(predictions, 1) == numpy.argmax(labels, 1))\n",
        "  total = predictions.shape[0]\n",
        "\n",
        "  error = 100.0 - (100 * float(correct) / float(total))\n",
        "\n",
        "  confusions = numpy.zeros([10, 10], numpy.float32)\n",
        "  bundled = zip(numpy.argmax(predictions, 1), numpy.argmax(labels, 1))\n",
        "  for predicted, actual in bundled:\n",
        "    confusions[predicted, actual] += 1\n",
        "    \n",
        "  return error, confusions\n",
        "\n",
        "print 'Done'"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Done\n"
          ],
          "name": "stdout"
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sLv22cjeB5Rd",
        "colab_type": "text"
      },
      "source": [
        "We'll need to train for some time to actually see useful predicted values. Let's define a loop that will go through our data. We'll print the loss and error periodically.\n",
        "\n",
        "Here, we want to iterate over the entire data set rather than just the first batch, so we'll need to slice the data to that end.\n",
        "\n",
        "(One pass through our training set will take some time on a CPU, so be patient if you are executing this notebook.)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4cgKJrS1_vej",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          }
        },
        "cellView": "both"
      },
      "source": [
        "# Train over the first 1/4th of our training set.\n",
        "steps = int(train_size / BATCH_SIZE)\n",
        "for step in xrange(steps):\n",
        "  # Compute the offset of the current minibatch in the data.\n",
        "  # Note that we could use better randomization across epochs.\n",
        "  offset = (step * BATCH_SIZE) % (train_size - BATCH_SIZE)\n",
        "  batch_data = train_data[offset:(offset + BATCH_SIZE), :, :, :]\n",
        "  batch_labels = train_labels[offset:(offset + BATCH_SIZE)]\n",
        "  # This dictionary maps the batch data (as a numpy array) to the\n",
        "  # node in the graph it should be fed to.\n",
        "  feed_dict = {train_data_node: batch_data,\n",
        "               train_labels_node: batch_labels}\n",
        "  # Run the graph and fetch some of the nodes.\n",
        "  _, l, lr, predictions = s.run(\n",
        "    [optimizer, loss, learning_rate, train_prediction],\n",
        "    feed_dict=feed_dict)\n",
        "  \n",
        "  # Print out the loss periodically.\n",
        "  if step % 100 == 0:\n",
        "    error, _ = error_rate(predictions, batch_labels)\n",
        "    print 'Step %d of %d' % (step, steps)\n",
        "    print 'Mini-batch loss: %.5f Error: %.5f Learning rate: %.5f' % (l, error, lr)\n",
        "    print 'Validation error: %.1f%%' % error_rate(\n",
        "        validation_prediction.eval(), validation_labels)[0]\n"
      ],
      "outputs": [],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "J4LskgGXIDAm",
        "colab_type": "text"
      },
      "source": [
        "The error seems to have gone down. Let's evaluate the results using the test set.\n",
        "\n",
        "To help identify rare mispredictions, we'll include the raw count of each (prediction, label) pair in the confusion matrix."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6Yh1jGFuIKc_",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            },
            {
              "item_id": 2
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 436,
          "status": "ok",
          "timestamp": 1446752934104,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "4e411de4-0fe2-451b-e4ca-8a4854f0db89"
      },
      "source": [
        "test_error, confusions = error_rate(test_prediction.eval(), test_labels)\n",
        "print 'Test error: %.1f%%' % test_error\n",
        "\n",
        "plt.xlabel('Actual')\n",
        "plt.ylabel('Predicted')\n",
        "plt.grid(False)\n",
        "plt.xticks(numpy.arange(NUM_LABELS))\n",
        "plt.yticks(numpy.arange(NUM_LABELS))\n",
        "plt.imshow(confusions, cmap=plt.cm.jet, interpolation='nearest');\n",
        "\n",
        "for i, cas in enumerate(confusions):\n",
        "  for j, count in enumerate(cas):\n",
        "    if count > 0:\n",
        "      xoff = .07 * len(str(count))\n",
        "      plt.text(j-xoff, i+.2, int(count), fontsize=9, color='white')"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Test error: 1.4%\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "metadata": {},
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f22684b17d0>"
            ]
          }
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yLnS4dGiMwI1",
        "colab_type": "text"
      },
      "source": [
        "We can see here that we're mostly accurate, with some errors you might expect, e.g., '9' is often confused as '4'.\n",
        "\n",
        "Let's do another sanity check to make sure this matches roughly the distribution of our test set, e.g., it seems like we have fewer '5' values."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "x5KOv1AJMgzV",
        "colab_type": "code",
        "colab": {
          "autoexec": {
            "startup": false,
            "wait_interval": 0
          },
          "output_extras": [
            {
              "item_id": 1
            }
          ]
        },
        "cellView": "both",
        "executionInfo": {
          "elapsed": 352,
          "status": "ok",
          "timestamp": 1446753006584,
          "user": {
            "color": "#1FA15D",
            "displayName": "Michael Piatek",
            "isAnonymous": false,
            "isMe": true,
            "permissionId": "00327059602783983041",
            "photoUrl": "//lh6.googleusercontent.com/-wKJwK_OPl34/AAAAAAAAAAI/AAAAAAAAAlk/Rh3u6O2Z7ns/s50-c-k-no/photo.jpg",
            "sessionId": "716a6ad5e180d821",
            "userId": "106975671469698476657"
          },
          "user_tz": 480
        },
        "outputId": "2acdf737-bab6-408f-8b3c-05fa66d04fe6"
      },
      "source": [
        "plt.xticks(numpy.arange(NUM_LABELS))\n",
        "plt.hist(numpy.argmax(test_labels, 1));"
      ],
      "outputs": [
        {
          "output_type": "display_data",
          "metadata": {},
          "data": {
            "image/png": "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\nqSGWviQ15H8Bc9sQZEMpbW0AAAAASUVORK5CYII=\n",
            "text/plain": [
              "<matplotlib.figure.Figure at 0x7f2265a72d50>"
            ]
          }
        }
      ],
      "execution_count": 0
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E6DzLSK5M1ju",
        "colab_type": "text"
      },
      "source": [
        "Indeed, we appear to have fewer 5 labels in the test set. So, on the whole, it seems like our model is learning and our early results are sensible.\n",
        "\n",
        "But, we've only done one round of training. We can greatly improve accuracy by training for longer. To try this out, just re-execute the training cell above."
      ]
    }
  ]
}