{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'2.0.8'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import keras\n",
    "keras.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Classifying newswires: a multi-class classification example\n",
    "\n",
    "This notebook contains the code samples found in Chapter 3, Section 5 of [Deep Learning with Python](https://www.manning.com/books/deep-learning-with-python?a_aid=keras&a_bid=76564dff). Note that the original text features far more content, in particular further explanations and figures: in this notebook, you will only find source code and related comments.\n",
    "\n",
    "----\n",
    "\n",
    "In the previous section we saw how to classify vector inputs into two mutually exclusive classes using a densely-connected neural network. \n",
    "But what happens when you have more than two classes? \n",
    "\n",
    "In this section, we will build a network to classify Reuters newswires into 46 different mutually-exclusive topics. Since we have many \n",
    "classes, this problem is an instance of \"multi-class classification\", and since each data point should be classified into only one \n",
    "category, the problem is more specifically an instance of \"single-label, multi-class classification\". If each data point could have \n",
    "belonged to multiple categories (in our case, topics) then we would be facing a \"multi-label, multi-class classification\" problem."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Reuters dataset\n",
    "\n",
    "\n",
    "We will be working with the _Reuters dataset_, a set of short newswires and their topics, published by Reuters in 1986. It's a very simple, \n",
    "widely used toy dataset for text classification. There are 46 different topics; some topics are more represented than others, but each \n",
    "topic has at least 10 examples in the training set.\n",
    "\n",
    "Like IMDB and MNIST, the Reuters dataset comes packaged as part of Keras. Let's take a look right away:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.datasets import reuters\n",
    "\n",
    "(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Like with the IMDB dataset, the argument `num_words=10000` restricts the data to the 10,000 most frequently occurring words found in the \n",
    "data.\n",
    "\n",
    "We have 8,982 training examples and 2,246 test examples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8982"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(train_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2246"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(test_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As with the IMDB reviews, each example is a list of integers (word indices):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1,\n",
       " 245,\n",
       " 273,\n",
       " 207,\n",
       " 156,\n",
       " 53,\n",
       " 74,\n",
       " 160,\n",
       " 26,\n",
       " 14,\n",
       " 46,\n",
       " 296,\n",
       " 26,\n",
       " 39,\n",
       " 74,\n",
       " 2979,\n",
       " 3554,\n",
       " 14,\n",
       " 46,\n",
       " 4689,\n",
       " 4329,\n",
       " 86,\n",
       " 61,\n",
       " 3499,\n",
       " 4795,\n",
       " 14,\n",
       " 61,\n",
       " 451,\n",
       " 4329,\n",
       " 17,\n",
       " 12]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_data[10]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here's how you can decode it back to words, in case you are curious:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "word_index = reuters.get_word_index()\n",
    "reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\n",
    "# Note that our indices were offset by 3\n",
    "# because 0, 1 and 2 are reserved indices for \"padding\", \"start of sequence\", and \"unknown\".\n",
    "decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'? ? ? said as a result of its december acquisition of space co it expects earnings per share in 1987 of 1 15 to 1 30 dlrs per share up from 70 cts in 1986 the company said pretax net should rise to nine to 10 mln dlrs from six mln dlrs in 1986 and rental operation revenues to 19 to 22 mln dlrs from 12 5 mln dlrs it said cash flow per share this year should be 2 50 to three dlrs reuter 3'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "decoded_newswire"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The label associated with an example is an integer between 0 and 45: a topic index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_labels[10]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparing the data\n",
    "\n",
    "We can vectorize the data with the exact same code as in our previous example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def vectorize_sequences(sequences, dimension=10000):\n",
    "    results = np.zeros((len(sequences), dimension))\n",
    "    for i, sequence in enumerate(sequences):\n",
    "        results[i, sequence] = 1.\n",
    "    return results\n",
    "\n",
    "# Our vectorized training data\n",
    "x_train = vectorize_sequences(train_data)\n",
    "# Our vectorized test data\n",
    "x_test = vectorize_sequences(test_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "To vectorize the labels, there are two possibilities: we could just cast the label list as an integer tensor, or we could use a \"one-hot\" \n",
    "encoding. One-hot encoding is a widely used format for categorical data, also called \"categorical encoding\". \n",
    "For a more detailed explanation of one-hot encoding, you can refer to Chapter 6, Section 1. \n",
    "In our case, one-hot encoding of our labels consists in embedding each label as an all-zero vector with a 1 in the place of the label index, e.g.:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def to_one_hot(labels, dimension=46):\n",
    "    results = np.zeros((len(labels), dimension))\n",
    "    for i, label in enumerate(labels):\n",
    "        results[i, label] = 1.\n",
    "    return results\n",
    "\n",
    "# Our vectorized training labels\n",
    "one_hot_train_labels = to_one_hot(train_labels)\n",
    "# Our vectorized test labels\n",
    "one_hot_test_labels = to_one_hot(test_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that there is a built-in way to do this in Keras, which you have already seen in action in our MNIST example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.utils.np_utils import to_categorical\n",
    "\n",
    "one_hot_train_labels = to_categorical(train_labels)\n",
    "one_hot_test_labels = to_categorical(test_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Building our network\n",
    "\n",
    "\n",
    "This topic classification problem looks very similar to our previous movie review classification problem: in both cases, we are trying to \n",
    "classify short snippets of text. There is however a new constraint here: the number of output classes has gone from 2 to 46, i.e. the \n",
    "dimensionality of the output space is much larger. \n",
    "\n",
    "In a stack of `Dense` layers like what we were using, each layer can only access information present in the output of the previous layer. \n",
    "If one layer drops some information relevant to the classification problem, this information can never be recovered by later layers: each \n",
    "layer can potentially become an \"information bottleneck\". In our previous example, we were using 16-dimensional intermediate layers, but a \n",
    "16-dimensional space may be too limited to learn to separate 46 different classes: such small layers may act as information bottlenecks, \n",
    "permanently dropping relevant information.\n",
    "\n",
    "For this reason we will use larger layers. Let's go with 64 units:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras import models\n",
    "from keras import layers\n",
    "\n",
    "model = models.Sequential()\n",
    "model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))\n",
    "model.add(layers.Dense(64, activation='relu'))\n",
    "model.add(layers.Dense(46, activation='softmax'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "There are two other things you should note about this architecture:\n",
    "\n",
    "* We are ending the network with a `Dense` layer of size 46. This means that for each input sample, our network will output a \n",
    "46-dimensional vector. Each entry in this vector (each dimension) will encode a different output class.\n",
    "* The last layer uses a `softmax` activation. You have already seen this pattern in the MNIST example. It means that the network will \n",
    "output a _probability distribution_ over the 46 different output classes, i.e. for every input sample, the network will produce a \n",
    "46-dimensional output vector where `output[i]` is the probability that the sample belongs to class `i`. The 46 scores will sum to 1.\n",
    "\n",
    "The best loss function to use in this case is `categorical_crossentropy`. It measures the distance between two probability distributions: \n",
    "in our case, between the probability distribution output by our network, and the true distribution of the labels. By minimizing the \n",
    "distance between these two distributions, we train our network to output something as close as possible to the true labels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "model.compile(optimizer='rmsprop',\n",
    "              loss='categorical_crossentropy',\n",
    "              metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Validating our approach\n",
    "\n",
    "Let's set apart 1,000 samples in our training data to use as a validation set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x_val = x_train[:1000]\n",
    "partial_x_train = x_train[1000:]\n",
    "\n",
    "y_val = one_hot_train_labels[:1000]\n",
    "partial_y_train = one_hot_train_labels[1000:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's train our network for 20 epochs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 7982 samples, validate on 1000 samples\n",
      "Epoch 1/20\n",
      "7982/7982 [==============================] - 1s - loss: 2.5241 - acc: 0.4952 - val_loss: 1.7263 - val_acc: 0.6100\n",
      "Epoch 2/20\n",
      "7982/7982 [==============================] - 0s - loss: 1.4500 - acc: 0.6854 - val_loss: 1.3478 - val_acc: 0.7070\n",
      "Epoch 3/20\n",
      "7982/7982 [==============================] - 0s - loss: 1.0979 - acc: 0.7643 - val_loss: 1.1736 - val_acc: 0.7460\n",
      "Epoch 4/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.8723 - acc: 0.8178 - val_loss: 1.0880 - val_acc: 0.7490\n",
      "Epoch 5/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.7045 - acc: 0.8477 - val_loss: 0.9822 - val_acc: 0.7760\n",
      "Epoch 6/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.5660 - acc: 0.8792 - val_loss: 0.9379 - val_acc: 0.8030\n",
      "Epoch 7/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.4569 - acc: 0.9037 - val_loss: 0.9039 - val_acc: 0.8050\n",
      "Epoch 8/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.3668 - acc: 0.9238 - val_loss: 0.9279 - val_acc: 0.7890\n",
      "Epoch 9/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.3000 - acc: 0.9326 - val_loss: 0.8835 - val_acc: 0.8070\n",
      "Epoch 10/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.2505 - acc: 0.9434 - val_loss: 0.8967 - val_acc: 0.8150\n",
      "Epoch 11/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.2155 - acc: 0.9473 - val_loss: 0.9080 - val_acc: 0.8110\n",
      "Epoch 12/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1853 - acc: 0.9506 - val_loss: 0.9025 - val_acc: 0.8140\n",
      "Epoch 13/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1680 - acc: 0.9524 - val_loss: 0.9268 - val_acc: 0.8100\n",
      "Epoch 14/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1512 - acc: 0.9562 - val_loss: 0.9500 - val_acc: 0.8130\n",
      "Epoch 15/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1371 - acc: 0.9559 - val_loss: 0.9621 - val_acc: 0.8090\n",
      "Epoch 16/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1306 - acc: 0.9553 - val_loss: 1.0152 - val_acc: 0.8050\n",
      "Epoch 17/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1210 - acc: 0.9575 - val_loss: 1.0262 - val_acc: 0.8010\n",
      "Epoch 18/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1185 - acc: 0.9570 - val_loss: 1.0354 - val_acc: 0.8040\n",
      "Epoch 19/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1128 - acc: 0.9598 - val_loss: 1.0841 - val_acc: 0.8010\n",
      "Epoch 20/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.1097 - acc: 0.9594 - val_loss: 1.0707 - val_acc: 0.8040\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(partial_x_train,\n",
    "                    partial_y_train,\n",
    "                    epochs=20,\n",
    "                    batch_size=512,\n",
    "                    validation_data=(x_val, y_val))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's display its loss and accuracy curves:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nLy+PM844g1mzZnHggQfy0UcfcfnllzNjxgzat28PQL9+/TjzzDMpKiqq8cVntRVn7yMD/gQsdvc7\nKyizd1QOMxsSxbMurphEJF4VtdXF1YbXunXr5PySJUv47W9/yyuvvMLChQs5/vjj0/bX33333ZPz\nzZs3p6SkJO22W7RoUWWZmurUqRMLFy5k+PDh3HvvvVx44YUAzJgxg4suuog5c+YwZMgQduzYUafv\nm4k4zxSGAT8Gjk7pcnqimV1kZhdFZcYC75nZu8Bk4HTXreBEGqxstuFt2rSJtm3b0q5dO1auXMmM\nGTPq/D2GDRvGE088AcCiRYvSnomkGjp0KDNnzmTdunWUlJQwbdo0RowYQXFxMe7Oj370I2666Sbm\nz5/Pjh07WLFiBUcffTS33347a9euZWv5urh6EGfvozeASq+icPd7gHviikFE6lc22/AKCgro27cv\nvXv3pmfPngwbNqzO3+Oyyy7jrLPOom/fvskpUfWTTrdu3bj55psZOXIk7s5JJ53E97//febPn8/5\n55+Pu2Nm3HbbbZSUlHDGGWewefNmSktL+fnPf07btm3r/DNUpcHdo7mwsNB1kx2R+rN48WL69OmT\n7TByQklJCSUlJbRs2ZIlS5Zw3HHHsWTJEnbbLbcGh0j3NzOzee5eWMEqSbn1SUREctiWLVs45phj\nKCkpwd25//77cy4h1Fbj+jQiIjHq0KED8+bNy3YYsWoSo6SKiEhmlBRERCRJSUFERJKUFEREJElJ\nQURy2qhRo3a5EO3uu+/m4osvrnS9Nm3aAPDll18yduzYtGVGjhxJVV3c77777p0uIjvxxBPrZFyi\nG2+8kTvuuKPW26lrSgoiktPGjRvHtGnTdlo2bdo0xo0bl9H6++67L08++WSN3798Upg+fTodOnSo\n8fZynZKCiOS0sWPH8sILLyRvqLN06VK+/PJLhg8fnrxuoKCggAEDBvDss8/usv7SpUvp378/ANu2\nbeP000+nT58+jBkzhm3btiXLXXzxxclht3/1q18BMHnyZL788ktGjRrFqFGjAMjPz2ft2rUA3Hnn\nnfTv35/+/fsnh91eunQpffr04Sc/+Qn9+vXjuOOO2+l90lmwYAGHH344AwcOZMyYMaxfvz75/omh\ntBMD8b322mvJmwwNHjyYzZs313jfpqPrFEQkY1dcAXV9Q7FBgyA6nqa15557MmTIEF588UVGjx7N\ntGnTOO200zAzWrZsydNPP027du1Yu3Ythx9+OCeffHKF9ym+7777aNWqFYsXL2bhwoU7DX09adIk\n9txzT3bs2MExxxzDwoULufzyy7nzzjuZOXMmnTvvPIDzvHnzePDBB5k9ezbuztChQxkxYgQdO3Zk\nyZIlPP744/zhD3/gtNNO46mnnqr0/ghnnXUWU6ZMYcSIEdxwww38+te/5u677+bWW2/ls88+o0WL\nFskqqzvuuIN7772XYcOGsWXLFlq2bFmNvV01nSmISM5LrUJKrTpyd6655hoGDhzIscceyxdffMHq\n1asr3M6sWbOSB+eBAwcycODA5GtPPPEEBQUFDB48mPfff7/Kwe7eeOMNxowZQ+vWrWnTpg2nnHIK\nr7/+OgC9evVi0KBBQOXDc0O4v8OGDRsYMWIEAGeffTazZs1Kxjh+/HgeffTR5JXTw4YN46qrrmLy\n5Mls2LChzq+o1pmCiGSssl/0cRo9ejRXXnkl8+fPZ+vWrRx66KEAFBUVUVxczLx588jLyyM/Pz/t\ncNlV+eyzz7jjjjuYM2cOHTt25JxzzqnRdhISw25DGHq7quqjirzwwgvMmjWL559/nkmTJrFo0SIm\nTpzI97//faZPn86wYcOYMWMGvXv3rnGs5elMQURyXps2bRg1ahTnnXfeTg3MGzduZK+99iIvL4+Z\nM2eyLN0N2VMcddRRPPbYYwC89957LFy4EAjDbrdu3Zr27duzevVqXnzxxeQ6bdu2TVtvP3z4cJ55\n5hm2bt3K119/zdNPP83w4cOr/dnat29Px44dk2cZjzzyCCNGjKC0tJTPP/+cUaNGcdttt7Fx40a2\nbNnCJ598woABA7j66qs57LDD+PDDD6v9npXRmYKINAjjxo1jzJgxO/VEGj9+PCeddBIDBgygsLCw\nyl/MF198Meeeey59+vShT58+yTOOQw45hMGDB9O7d2+6d+++07DbEyZM4Pjjj2ffffdl5syZyeUF\nBQWcc845DBkyBIALLriAwYMHV1pVVJGHHnqIiy66iK1bt7L//vvz4IMPsmPHDs4880w2btyIu3P5\n5ZfToUMHrr/+embOnEmzZs3o169f8i5ydUVDZ4tIpTR0dsNTm6GzVX0kIiJJSgoiIpKkpCAiVWpo\n1cxNWW3/VkoKIlKpli1bsm7dOiWGBsDdWbduXa0uaFPvIxGpVLdu3VixYgXFxcXZDkUy0LJlS7p1\n61bj9ZUURKRSeXl59OrVK9thSD1R9ZGIiCQpKYiISJKSgoiIJCkpiIhIkpKCiIgkKSmIiEiSkoKI\niCQpKYiISJKSgoiIJCkpiIhIUmxJwcy6m9lMM/vAzN43s5+mKWNmNtnMPjazhWZWEFc8IiJStTjH\nPioBfubu882sLTDPzF529w9SypwAHBRNQ4H7okcREcmC2M4U3H2lu8+P5jcDi4H9yhUbDTzswdtA\nBzPbJ66YRESkcvXSpmBm+cBgYHa5l/YDPk95voJdEwdmNsHM5prZXA3fKyISn9iTgpm1AZ4CrnD3\nTTXZhrtPdfdCdy/s0qVL3QYoIiJJsSYFM8sjJIQid/9rmiJfAN1TnneLlomISBbE2fvIgD8Bi939\nzgqKPQecFfVCOhzY6O4r44pJREQqF2fvo2HAj4FFZrYgWnYN0APA3X8PTAdOBD4GtgLnxhiPiIhU\nIbak4O5vAFZFGQcuiSsGERGpHl3RLCIiSUoKIiKSpKQgIiJJSgoiIpKkpCAiIklKCiIikqSkICIi\nSUoKIiKSpKQgIiJJSgoiIpKkpCAiIklKCiIikqSkICIiSUoKIiKSpKQgIiJJTSYplJbCa69lOwoR\nkdzWZJLCAw/AyJHwz39mOxIRkdzVZJLCuHHQtStcdx24ZzsaEZHc1GSSQuvWcO218Oqr8Mor2Y5G\nRCQ3NZmkADBhAnTvrrMFEZGKNKmk0KIFXH89vP02vPBCtqMREck9TSopAJxzDhxwQEgOpaXZjkZE\nJLc0uaSQlwc33ggLFsBf/5rtaEREckuTSwoQeiL17Qs33AA7dmQ7GhGR3NEkk0Lz5nDTTbB4MTz2\nWLajERHJHU0yKQCMGQODB4eqpO3bsx2NiEhuaLJJoVkzuPlm+PRTePDBbEcjIpIbmmxSADjxRDji\niJAcvvkm29GIiGRfk04KZnDLLbBiBdx/f7ajERHJviadFACOPhpGjYLf/Aa+/jrb0YiIZFeTTwoQ\nzhbWrIF77sl2JCIi2aWkAHznO6F94bbbYOPGbEcjIpI9sSUFM3vAzNaY2XsVvD7SzDaa2YJouiGu\nWDJx882wfj3cdVc2oxARya44zxT+DBxfRZnX3X1QNN0UYyxVKiiAU0+FO++EdeuyGYmISPbElhTc\nfRbwVVzbj8Ovfw1btsDtt2c7EhGR7Mh2m8IRZvaumb1oZv0qKmRmE8xsrpnNLS4uji2Yfv1g/HiY\nMgVWrYrtbUREclY2k8J8oKe7HwJMAZ6pqKC7T3X3Qncv7NKlS6xB/epX8O23oYtqQlER5OeHq6Dz\n88NzEZHGKGtJwd03ufuWaH46kGdmnbMVT8KBB8K554aL2ZYvDwlgwgRYtizcrW3ZsvBciUFEGqOs\nJQUz29vMLJofEsWSE028118fHm+5JdzXeevWnV/fujUsFxFpbHaLa8Nm9jgwEuhsZiuAXwF5AO7+\ne2AscLGZlQDbgNPdc+POyT16wIUXwu9+V/H9FpYvr9+YRETqg+XIcThjhYWFPnfu3NjfZ9Uq2H//\n0I6QbviLnj1h6dLYwxARqRNmNs/dC6sql+3eRzlr773hsstCVVHLlju/1qoVTJqUnbhEROKUUVIw\nswPMrEU0P9LMLjezDvGGln2//CW0aQMDBoQzA7PwOHVq6LoqItLYZHqm8BSww8wOBKYC3YFGfyPL\nTp3gyithzhz461+htDRUGSkhiEhjlWlSKHX3EmAMMMXdfwHsE19YueOqq6BjR7ghqyMziYjUj0yT\nwnYzGwecDfwtWpYXT0i5pX37UI30wgvw1lvZjkZEJF6ZJoVzgSOASe7+mZn1Ah6JL6zcctllsNde\ncN112Y5ERCReGSUFd//A3S9398fNrCPQ1t1vizm2nNG6NVxzDbzySkgMDawXr4hIxjK6eM3MXgVO\njsrPA9aY2T/d/aoYY8spl1wC770XuqJ++WUYBiOvSVSgiUhTkmn1UXt33wScAjzs7kOBY+MLK/fs\ntlvoinrjjfDgg3DyyWGYbRGRxiTTpLCbme0DnEZZQ3OTYxZGUZ06FV56CUaOhNWrsx2ViEjdyTQp\n3ATMAD5x9zlmtj+wJL6wcttPfgLPPgsffBDu77ykye4JEWlsMm1o/l93H+juF0fPP3X3U+MNLbf9\n4AcwcyZs2hQSwzvvZDsiEZHay3SYi25m9rSZrYmmp8ysW9zB5bqhQ+HNN6FdOxg1KlzLICLSkGVa\nffQg8BywbzQ9Hy1r8g46KCSGPn1g9Gj44x+zHZGISM1lmhS6uPuD7l4STX8G4r0vZgPStSu8+ioc\ne2xob/j1r3Utg4g0TJkmhXVmdqaZNY+mM8mRu6TlijZt4Pnn4eyzQ7fVCROgpCTbUYmIVE+md147\nD5gC3AU48CZwTkwxNVh5eeEahm7dwkVuq1bBtGnhimgRkYYg095Hy9z9ZHfv4u57ufsPgSbd+6gi\nZuHezvfdB9Onw9FHQ3FxtqMSEclMbe681mSGuKiJiy4K92BYuDB0Wf3kk2xHJCJStdokBauzKBqp\n0aPhH/+Ar74KiaEebi0tIlIrtUkK6l+Tge98B/75T9hjDxgxAn72s3D3NhGRXFRpUjCzzWa2Kc20\nmXC9gmSgd+9wg57Ro2HyZDjgADj1VHj9dXVdFZHcUmlScPe27t4uzdTW3TPtuSTAPvvAY4/BZ5/B\n1VeH6xqOOgoKC+GRR+Dbb7MdoYhI7aqPpAa6dYPf/AY+/zzck2HbNjjrLOjZE26+GdasyXaEItKU\nKSnUg6IiyM+HZs3CY1ERtGoVLnB7/32YMQMGD4YbboAePeD880OvJRGR+qakELOionDwX7YstB8s\nWxaeFxWF183guOPCNQ2LF8N554UL3g45BI45JlwlXVqa3c8gIk2HkkLMrr0Wtm7dednWrWF5eb17\nw+9+F6qWbrsN/vWvcIe3gw+GKVNg8+b6iVlEmi7zBtb9pbCw0Oc2oA7/zZql72FkVvUZwPbt8PTT\ncPfdofdS+/bwi1/AFVdo6AyRhmLtWpg1K3RN37ED9twTOnbc+TEx37FjuPVvHMxsnrsXVllOSSFe\n+fmhyqi8nj2rd73CO++EBupnn4W99w6D7p1/fnxfIBGpmeLikARefRVeew0WLQrLW7YM46NVdcbf\ntm36xNGxY6hSPu64msWVaVLQISVmkyaFNoTUKqRWrcLy6hgyBJ55Jty74Ze/DMNo3HVXSBRjxoQz\nDxGpf2vWlCWBV18NnUcg/J8feSScfnq4n3thIey+e6gB2LAB1q8Pox1U9bh4cXj86quQVGqaFDKl\nM4V6UFQU2hCWLw+9iyZNgvHja74999AAPXFi+MIcfjjcfjsMH153MYtIeqtX75wEPvggLG/dOiSB\nESPKkkBeXt29r3uocm7evGbrq/qoCSgpgYceCl1Zv/wy3Df61luhX79sRyaS20pL4euvYcuWzKcN\nG2DOnPBDDMI9VI48MiSAESPg0EPrNgnUtawnBTN7APgBsMbd+6d53YDfAicCW4Fz3H1+VdtVUtjV\n1q1h+Ixbbw31lWefHe7+1r17tiMTqVvffgubNoVp48Zd59MtS51PHODL9wisTPPmoZ6/TRsYMKAs\nCRQU5HYSKC8XksJRwBbg4QqSwonAZYSkMBT4rbsPrWq7SgoVW7cutDHcc0/o9XT55aGKqWPHbEcm\nUjOffAIvvwwvvRSqatavr3qdvLzQU69duzClzicO7tWZdt+9cbTZZT0pREHkA3+rICncD7zq7o9H\nzz8CRrr7ysq2qaRQtWXLQpXSI49Ahw5wzTVw6aWh94NILtuwAV55JSSBl1+GTz8Ny3v0CPdA33//\nnQ/06Q7+LVo0joN4XWsIvY/2Az5Peb4iWrZLUjCzCcAEgB49etRLcA1Zz56hreGqq+A//zNc2zB5\ncujG+sMfhu5tIrlg+3aYPbvsbOCdd0J9f5s24a6FV14ZetscdJAO9PWlQXRJdfepwFQIZwpZDqfB\nOOSQMHzGzJlhZNbzzw9T797hPg+J6eCDQ3WTSNzcYcmSsiQwc2ZoB2vWDA47LPTS++53Q4+6hlRf\n35hkMyl8AaQ2hXaLlkkdGzUq/Bp7/fVwVeWbb4ZrHh54ILzesSMccURZkhgyRFdMS+ZKS8saczds\nKJvKPy8uDt/BxMWc+flwxhkhCRx9tNq+ckU2k8JzwKVmNo3Q0LyxqvYEqTmzcP+Go44Kz93D2Epv\nvlk2TZ8eXmvePJxlpJ5N9Oih0/emZNu2MAbX8uXhIL58eXi+bt2uB/xNm6q+WVSbNqF9q7AwnLV+\n97vhZlP6TuWeOHsfPQ6MBDoDq4FfAXkA7v77qEvqPcDxhC6p57p7lS3IamiOz/r18PbbZUli9uzQ\nlxtg333D2cSgQdC/f+ia16uXqp0aIvdwcE8c7FMP/InH8vf1aNYs3Ciqc+dwcE9M7dtX/bx9ew3H\nkgtyovdRHJQU6k9JSRi35a23QpJ4++3QRTChVatwoVwiSSQeu3at+S9A93BAWrYsjA21bFnZ/OrV\n0Ldv2dlL3741v7qzMSsthVWrwj5L7MPEfCIJlO+n36pV6KDQo0fZY+r8fvupjr+hU1KQWGzZEi7r\nX7QI3nuv7HH16rIynTrtnCT69w9Tu3ZhlMiVK9Mf9BPz33yz83t26BAOTl26hJsPJX7FtmsXGiQT\nSWLo0LAsl2zfXlbfvnlzSGItW4Zuk+WnTBNpYh+mO+gnDvzlb+/apUvYh4kp9YDfs2fokaaqnMZN\nSaERqeuxk+JQXFyWJBKJ4r33QhJJ2GuvUEW1ffvO6yYOWPn5ZQet1Pn27cvKuoe+66ltIYsWheXN\nmoUklNoW0qtXzQ92paXhYJ4YnGz9+vA89erZ8vPln2/blvn75eVVnDBatAhVMKtWhe9B+X3YtWvY\nZ4kpsQ/z88N3Rh0HREmhkUjcua38KKtTp+ZeYiivtDQcwBJJ4uOPQ2JIPejXxQFr48bQvz2RJN56\nq2x44q5gdAYfAAAMfElEQVRdyxLEYYeFX9mJA3zqwT51PvF848aqG1Dbti27cCr1gqp0z9u0Cfvk\n3/8O0zfflM2Xn9K9tn17GDY93UF/jz1qtw+l8VNSaCTq6n4MTcmOHWH44tSzidS2kFR5eWU3N0kd\nt778fGJKNJ4mhkxQm4Y0FEoKjURt7twmZVavhgULQjVM6sG+dWvVpUvT0BCGuZAM9OiR/kxBo31U\nT9eu8L3vZTsKkdynXuY5btKk0IaQqiZ3bhMRyYSSQo4bPz40KvfsGao5evZsGI3MItIwqfqoARg/\nXklAROqHzhRERCRJSUFERJKUFEREJElJQUREkpQUREQkSUlBRESSlBSagKKiMIZSs2bhsago2xGJ\nSK7SdQqNXPlRVpctC89B1z6IyK50ptDIXXvtrnfZ2ro1LBcRKU9JoZFbvrx6y0WkaVNSaOQqGk1V\no6yKSDpKCo2cRlkVkepQUmjkNMqqiFSHeh81ARplVUQypTMFERFJUlIQEZEkJQUREUlSUhARkSQl\nBcmIxk8SaRrU+0iqpPGTRJoOnSlIlTR+kkjToaQgVdL4SSJNh5KCVEnjJ4k0HbEmBTM73sw+MrOP\nzWximtfPMbNiM1sQTRfEGY/UjMZPEmk6YksKZtYcuBc4AegLjDOzvmmK/sXdB0XTH+OKR2pO4yeJ\nNB1x9j4aAnzs7p8CmNk0YDTwQYzvKTHR+EkiTUOc1Uf7AZ+nPF8RLSvvVDNbaGZPmln3dBsyswlm\nNtfM5hYXF8cRq8RM1zmINAzZbmh+Hsh394HAy8BD6Qq5+1R3L3T3wi5dutRrgFJ7iescli0D97Lr\nHJQYRHJPnEnhCyD1l3+3aFmSu69z939HT/8IHBpjPJIlus5BpOGIMynMAQ4ys15mtjtwOvBcagEz\n2yfl6cnA4hjjkSzRdQ4iDUdsDc3uXmJmlwIzgObAA+7+vpndBMx19+eAy83sZKAE+Ao4J654JHt6\n9AhVRumWi0huMXfPdgzVUlhY6HPnzs12GFIN5cdOgnCdg7q1itQfM5vn7oVVlct2Q7M0AbrOQaTh\n0CipUi90nYNIw6AzBWkQdJ2DSP3QmYLkPN3PQaT+6ExBcp6ucxCpP0oKkvN0nYNI/VFSkJyn+zmI\n1B8lBcl5dXE/BzVUi2RGSUFyXm2vc9CAfCKZ0xXN0ujl56cfZqNnT1i6tL6jEckOXdEsElFDtUjm\nlBSk0auLhmq1SUhToaQgjV5tG6rVJiFNiZKCNHq1bajWxXPSlCgpSJMwfnxoVC4tDY/VGR6jLtok\nVP0kDYWSgkgVatsmoeonaUiUFESqUNs2CVU/SUOipCBShdq2Saj6SRoSJQWRDNSmTSIXqp+UVCRT\nSgoiMct29ZPaNKQ6lBREYpbt6qe6aNPQmUbToaQgUg+yWf1U26SSC9VXSkr1R0lBJMfVtvqptkkl\n29VXSkr1zN0b1HTooYe6SFPz6KPuPXu6m4XHRx+t3rqtWrmHQ2qYWrXKfBtmO6+bmMwyW79nz/Tr\n9+xZP+vX9vPXdv3ENmr696uL9d3dgbmewTE26wf56k5KCiLVV5uDSm0PyrVNKkpKtU9K7pknBd1P\nQUQqlai+Sa1CatUq88by2t7PorbrN2sWDqXlmYU2nrjXz/bnT9D9FESkTtS291Rt20Sy3aaS7Yb+\n+r4fiJKCiFSpNr2naptUlJRqt361ZVLHlEuT2hREpLqy2dCrNoWYqU1BRBqaoqLQhXf58vALf9Kk\n6p1t1XZ9yLxNQUlBRKQJUEOziIhUW6xJwcyON7OPzOxjM5uY5vUWZvaX6PXZZpYfZzwiIlK52JKC\nmTUH7gVOAPoC48ysb7li5wPr3f1A4C7gtrjiERGRqsV5pjAE+NjdP3X3b4FpwOhyZUYDD0XzTwLH\nmJnFGJOIiFQizqSwH/B5yvMV0bK0Zdy9BNgIdCq/ITObYGZzzWxucXFxTOGKiMhu2Q4gE+4+FZgK\nYGbFZpbmou+c0BlYm+0gKpHr8UHux6j4akfx1U5t4uuZSaE4k8IXQPeU592iZenKrDCz3YD2wLrK\nNuruXeoyyLpkZnMz6fKVLbkeH+R+jIqvdhRf7dRHfHFWH80BDjKzXma2O3A68Fy5Ms8BZ0fzY4FX\nvKFdOCEi0ojEdqbg7iVmdikwA2gOPODu75vZTYTLrZ8D/gQ8YmYfA18REoeIiGRJrG0K7j4dmF5u\n2Q0p898AP4ozhno2NdsBVCHX44Pcj1Hx1Y7iq53Y42tww1yIiEh8NMyFiIgkKSmIiEiSkkI1mVl3\nM5tpZh+Y2ftm9tM0ZUaa2UYzWxBNN6TbVowxLjWzRdF77zKkrAWTozGnFppZQT3GdnDKfllgZpvM\n7IpyZep9/5nZA2a2xszeS1m2p5m9bGZLoseOFax7dlRmiZmdna5MTPH9t5l9GP0NnzazDhWsW+n3\nIcb4bjSzL1L+jidWsG6lY6TFGN9fUmJbamYLKlg31v1X0TEla9+/TG66oKlsAvYBCqL5tsC/gL7l\nyowE/pbFGJcCnSt5/UTgRcCAw4HZWYqzObAK6Jnt/QccBRQA76Usux2YGM1PBG5Ls96ewKfRY8do\nvmM9xXccsFs0f1u6+DL5PsQY343AzzP4DnwC7A/sDrxb/v8prvjKvf4/wA3Z2H8VHVOy9f3TmUI1\nuftKd58fzW8GFrPr8B25bjTwsAdvAx3MbJ8sxHEM8Im7Z/0KdXefRegWnSp1bK6HgB+mWfV7wMvu\n/pW7rwdeBo6vj/jc/SUPw8MAvE24QDQrKth/mchkjLRaqyy+aLy104DH6/p9M1HJMSUr3z8lhVqI\nhvoeDMxO8/IRZvaumb1oZv3qNTBw4CUzm2dmE9K8nsm4VPXhdCr+R8zm/kvo6u4ro/lVQNc0ZXJl\nX55HOPtLp6rvQ5wujaq3Hqig+iMX9t9wYLW7L6ng9Xrbf+WOKVn5/ikp1JCZtQGeAq5w903lXp5P\nqBI5BJgCPFPP4R3p7gWEYcsvMbOj6vn9qxRd5X4y8L9pXs72/tuFh3P1nOy/bWbXAiVAUQVFsvV9\nuA84ABgErCRU0eSicVR+llAv+6+yY0p9fv+UFGrAzPIIf7wid/9r+dfdfZO7b4nmpwN5Zta5vuJz\n9y+ixzXA04RT9FSZjEsVtxOA+e6+uvwL2d5/KVYnqtWixzVpymR1X5rZOcAPgPHRgWMXGXwfYuHu\nq919h7uXAn+o4H2zvf92A04B/lJRmfrYfxUcU7Ly/VNSqKao/vFPwGJ3v7OCMntH5TCzIYT9XOlA\nf3UYX2sza5uYJzRGvleu2HPAWVEvpMOBjSmnqfWlwl9n2dx/5aSOzXU28GyaMjOA48ysY1Q9cly0\nLHZmdjzwS+Bkd99aQZlMvg9xxZfaTjWmgvfNZIy0OB0LfOjuK9K9WB/7r5JjSna+f3G1qDfWCTiS\ncBq3EFgQTScCFwEXRWUuBd4n9KR4G/hOPca3f/S+70YxXBstT43PCHfF+wRYBBTW8z5sTTjIt09Z\nltX9R0hQK4HthHrZ8wn39vgHsAT4P2DPqGwh8MeUdc8DPo6mc+sxvo8J9cmJ7+Hvo7L7AtMr+z7U\nU3yPRN+vhYQD3D7l44uen0jocfNJfcYXLf9z4nuXUrZe918lx5SsfP80zIWIiCSp+khERJKUFERE\nJElJQUREkpQUREQkSUlBRESSlBREIma2w3YewbXORuw0s/zUETpFclWst+MUaWC2ufugbAchkk06\nUxCpQjSe/u3RmPrvmNmB0fJ8M3slGvDtH2bWI1re1cL9Dd6Npu9Em2puZn+Ixsx/ycz2iMpfHo2l\nv9DMpmXpY4oASgoiqfYoV330HymvbXT3AcA9wN3RsinAQ+4+kDAY3eRo+WTgNQ8D+hUQroQFOAi4\n1937ARuAU6PlE4HB0XYuiuvDiWRCVzSLRMxsi7u3SbN8KXC0u38aDVy2yt07mdlawtAN26PlK929\ns5kVA93c/d8p28gnjHt/UPT8aiDP3W8xs78DWwijwT7j0WCAItmgMwWRzHgF89Xx75T5HZS16X2f\nMBZVATAnGrlTJCuUFEQy8x8pj29F828SRvUEGA+8Hs3/A7gYwMyam1n7ijZqZs2A7u4+E7gaaA/s\ncrYiUl/0i0SkzB62883b/+7uiW6pHc1sIeHX/rho2WXAg2b2C6AYODda/lNgqpmdTzgjuJgwQmc6\nzYFHo8RhwGR331Bnn0ikmtSmIFKFqE2h0N3XZjsWkbip+khERJJ0piAiIkk6UxARkSQlBRERSVJS\nEBGRJCUFERFJUlIQEZGk/w+dYzD20tOTDwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fb6b8ab54a8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "loss = history.history['loss']\n",
    "val_loss = history.history['val_loss']\n",
    "\n",
    "epochs = range(1, len(loss) + 1)\n",
    "\n",
    "plt.plot(epochs, loss, 'bo', label='Training loss')\n",
    "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n",
    "plt.title('Training and validation loss')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nlbKyMr7++utchyLVaN26NT169KBly5a7tbySgohkpKysjA4dOlBYWIiZ5TocScPdWbNmDWVlZfTp\n02e31qHmIxHJyNdff02XLl2UEPKYmdGlS5c61eaUFEQkY0oI+a+ufyMlBRERSVBSEJFYFBdDYSE0\naxaei4vrtr41a9ZwxBFHcMQRR7DvvvvSvXv3xPstW7ZktI7LLruMjz76qNoy9913H8V1DbYBU0ez\niNS74mIYNQrKy8P7JUvCe4ARI3ZvnV26dGH27NkA3HrrrbRv354f//jHO5Vxd9ydZs3S/9596KGH\natzO1VdfvXsBNhKqKYhIvRs3bkdCqFReHqbXt4ULF9KvXz9GjBjBIYccwooVKxg1ahRFRUUccsgh\n3HbbbYmyxx13HLNnz6aiooJOnToxduxY+vfvzzHHHMOqVasAuOmmm7j77rsT5ceOHcugQYM46KCD\nePPNNwH46quvOP/88+nXrx/Dhg2jqKgokbCS3XLLLXzrW9/i0EMP5corr6Ty9scff/wxp5xyCv37\n92fAgAEsXrwYgF/+8pccdthh9O/fn3Fx7KwMKCmISL1burR20+vqww8/5Prrr2fevHl0796dX//6\n15SUlFBaWspLL73EvHnzdllm/fr1nHjiiZSWlnLMMccwadKktOt2d9555x1+97vfJRLMvffey777\n7su8efP4z//8T9577720y1577bXMnDmTuXPnsn79ep5//nkAhg8fzvXXX09paSlvvvkme++9N888\n8wzPPfcc77zzDqWlpdxwww31tHdqR0lBROpdr161m15X+++/P0VFO+5J//jjjzNgwAAGDBjA/Pnz\n0yaFNm3acOaZZwIwcODAxK/1VEOHDt2lzBtvvMFFF10EQP/+/TnkkEPSLjt9+nQGDRpE//79efXV\nV/nggw9Yt24dn3/+Oeeccw4QLjZr27YtL7/8Mpdffjlt2rQBYK+99qr9jqgHSgoiUu/Gj4e2bXee\n1rZtmB6Hdu3aJV4vWLCA3//+97zyyivMmTOHwYMHpz1vv1WrVonXzZs3p6KiIu2699hjjxrLpFNe\nXs7o0aOZOnUqc+bM4fLLL28QV4MrKYhIvRsxAiZMgN69wSw8T5iw+53MtbFhwwY6dOjAnnvuyYoV\nK3jhhRfqfRvHHnssTzzxBABz585NWxPZtGkTzZo1o6CggC+//JIpU6YA0LlzZ7p27cozzzwDhIsC\ny8vLOe2005g0aRKbNm0CYO3atfUedyZ09pGIxGLEiOwkgVQDBgygX79+9O3bl969e3PsscfW+zbG\njBnD978rmfjYAAANkklEQVT/ffr165d4dOzYcacyXbp04ZJLLqFfv35069aNo446KjGvuLiYH/7w\nh4wbN45WrVoxZcoUzj77bEpLSykqKqJly5acc8453H777fUee02ssje8oSgqKvKSkpJchyHS5Myf\nP5+DDz4412HkhYqKCioqKmjdujULFizg9NNPZ8GCBbRokR+/s9P9rcxslrsXVbFIQn58AhGRBmTj\nxo18+9vfpqKiAnfngQceyJuEUFeN41OIiGRRp06dmDVrVq7DiIU6mkVEJEFJQUREEpQUREQkQUlB\nREQSlBREpEE4+eSTd7kQ7e677+aqq66qdrn27dsDsHz5coYNG5a2zEknnURNp7rffffdlCeN8nfW\nWWfxxRdfZBJ6g6KkICINwvDhw5k8efJO0yZPnszw4cMzWn6//fbjqaee2u3tpyaFZ599lk6dOu32\n+vKVTkkVkVq77jpIM1J0nRxxBEQjVqc1bNgwbrrpJrZs2UKrVq1YvHgxy5cv5/jjj2fjxo0MGTKE\ndevWsXXrVu644w6GDBmy0/KLFy/m7LPP5v3332fTpk1cdtlllJaW0rdv38TQEgBXXXUVM2fOZNOm\nTQwbNoxf/OIX3HPPPSxfvpyTTz6ZgoICZsyYQWFhISUlJRQUFHDXXXclRlkdOXIk1113HYsXL+bM\nM8/kuOOO480336R79+785S9/SQx4V+mZZ57hjjvuYMuWLXTp0oXi4mL22WcfNm7cyJgxYygpKcHM\nuOWWWzj//PN5/vnnufHGG9m2bRsFBQVMnz69/v4IxFxTMLPBZvaRmS00s7Fp5l9qZqvNbHb0GBln\nPCLScO21114MGjSI5557Dgi1hAsuuAAzo3Xr1kydOpV3332XGTNmcMMNN1DdaA33338/bdu2Zf78\n+fziF7/Y6ZqD8ePHU1JSwpw5c3j11VeZM2cO11xzDfvttx8zZsxgxowZO61r1qxZPPTQQ7z99tu8\n9dZb/PGPf0wMpb1gwQKuvvpqPvjgAzp16pQY/yjZcccdx1tvvcV7773HRRddxG9/+1sAbr/9djp2\n7MjcuXOZM2cOp5xyCqtXr+YHP/gBU6ZMobS0lCeffLLO+zVVbDUFM2sO3AecBpQBM81smrunjhz1\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fb6b8ab51d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.clf()   # clear figure\n",
    "\n",
    "acc = history.history['acc']\n",
    "val_acc = history.history['val_acc']\n",
    "\n",
    "plt.plot(epochs, acc, 'bo', label='Training acc')\n",
    "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n",
    "plt.title('Training and validation accuracy')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It seems that the network starts overfitting after 8 epochs. Let's train a new network from scratch for 8 epochs, then let's evaluate it on \n",
    "the test set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 7982 samples, validate on 1000 samples\n",
      "Epoch 1/8\n",
      "7982/7982 [==============================] - 0s - loss: 2.6118 - acc: 0.4667 - val_loss: 1.7207 - val_acc: 0.6360\n",
      "Epoch 2/8\n",
      "7982/7982 [==============================] - 0s - loss: 1.3998 - acc: 0.7107 - val_loss: 1.2645 - val_acc: 0.7360\n",
      "Epoch 3/8\n",
      "7982/7982 [==============================] - 0s - loss: 1.0343 - acc: 0.7839 - val_loss: 1.0994 - val_acc: 0.7700\n",
      "Epoch 4/8\n",
      "7982/7982 [==============================] - 0s - loss: 0.8114 - acc: 0.8329 - val_loss: 1.0252 - val_acc: 0.7820\n",
      "Epoch 5/8\n",
      "7982/7982 [==============================] - 0s - loss: 0.6466 - acc: 0.8628 - val_loss: 0.9536 - val_acc: 0.8070\n",
      "Epoch 6/8\n",
      "7982/7982 [==============================] - 0s - loss: 0.5271 - acc: 0.8894 - val_loss: 0.9187 - val_acc: 0.8110\n",
      "Epoch 7/8\n",
      "7982/7982 [==============================] - 0s - loss: 0.4193 - acc: 0.9126 - val_loss: 0.9051 - val_acc: 0.8120\n",
      "Epoch 8/8\n",
      "7982/7982 [==============================] - 0s - loss: 0.3478 - acc: 0.9258 - val_loss: 0.8891 - val_acc: 0.8160\n",
      "1952/2246 [=========================>....] - ETA: 0s"
     ]
    }
   ],
   "source": [
    "model = models.Sequential()\n",
    "model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))\n",
    "model.add(layers.Dense(64, activation='relu'))\n",
    "model.add(layers.Dense(46, activation='softmax'))\n",
    "\n",
    "model.compile(optimizer='rmsprop',\n",
    "              loss='categorical_crossentropy',\n",
    "              metrics=['accuracy'])\n",
    "model.fit(partial_x_train,\n",
    "          partial_y_train,\n",
    "          epochs=8,\n",
    "          batch_size=512,\n",
    "          validation_data=(x_val, y_val))\n",
    "results = model.evaluate(x_test, one_hot_test_labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0.98764628548762257, 0.77693677651807869]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Our approach reaches an accuracy of ~78%. With a balanced binary classification problem, the accuracy reached by a purely random classifier \n",
    "would be 50%, but in our case it is closer to 19%, so our results seem pretty good, at least when compared to a random baseline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.18477292965271594"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import copy\n",
    "\n",
    "test_labels_copy = copy.copy(test_labels)\n",
    "np.random.shuffle(test_labels_copy)\n",
    "float(np.sum(np.array(test_labels) == np.array(test_labels_copy))) / len(test_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generating predictions on new data\n",
    "\n",
    "We can verify that the `predict` method of our model instance returns a probability distribution over all 46 topics. Let's generate topic \n",
    "predictions for all of the test data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "predictions = model.predict(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each entry in `predictions` is a vector of length 46:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(46,)"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predictions[0].shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The coefficients in this vector sum to 1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.99999994"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(predictions[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The largest entry is the predicted class, i.e. the class with the highest probability:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.argmax(predictions[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A different way to handle the labels and the loss\n",
    "\n",
    "We mentioned earlier that another way to encode the labels would be to cast them as an integer tensor, like such:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "y_train = np.array(train_labels)\n",
    "y_test = np.array(test_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "The only thing it would change is the choice of the loss function. Our previous loss, `categorical_crossentropy`, expects the labels to \n",
    "follow a categorical encoding. With integer labels, we should use `sparse_categorical_crossentropy`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['acc'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This new loss function is still mathematically the same as `categorical_crossentropy`; it just has a different interface."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## On the importance of having sufficiently large intermediate layers\n",
    "\n",
    "\n",
    "We mentioned earlier that since our final outputs were 46-dimensional, we should avoid intermediate layers with much less than 46 hidden \n",
    "units. Now let's try to see what happens when we introduce an information bottleneck by having intermediate layers significantly less than \n",
    "46-dimensional, e.g. 4-dimensional."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 7982 samples, validate on 1000 samples\n",
      "Epoch 1/20\n",
      "7982/7982 [==============================] - 0s - loss: 3.1620 - acc: 0.2295 - val_loss: 2.6750 - val_acc: 0.2740\n",
      "Epoch 2/20\n",
      "7982/7982 [==============================] - 0s - loss: 2.2009 - acc: 0.3829 - val_loss: 1.7626 - val_acc: 0.5990\n",
      "Epoch 3/20\n",
      "7982/7982 [==============================] - 0s - loss: 1.4490 - acc: 0.6486 - val_loss: 1.4738 - val_acc: 0.6390\n",
      "Epoch 4/20\n",
      "7982/7982 [==============================] - 0s - loss: 1.2258 - acc: 0.6776 - val_loss: 1.3961 - val_acc: 0.6570\n",
      "Epoch 5/20\n",
      "7982/7982 [==============================] - 0s - loss: 1.0886 - acc: 0.7032 - val_loss: 1.3727 - val_acc: 0.6700\n",
      "Epoch 6/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.9817 - acc: 0.7494 - val_loss: 1.3682 - val_acc: 0.6800\n",
      "Epoch 7/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.8937 - acc: 0.7757 - val_loss: 1.3587 - val_acc: 0.6810\n",
      "Epoch 8/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.8213 - acc: 0.7942 - val_loss: 1.3548 - val_acc: 0.6960\n",
      "Epoch 9/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.7595 - acc: 0.8088 - val_loss: 1.3883 - val_acc: 0.7050\n",
      "Epoch 10/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.7072 - acc: 0.8193 - val_loss: 1.4216 - val_acc: 0.7020\n",
      "Epoch 11/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.6642 - acc: 0.8254 - val_loss: 1.4405 - val_acc: 0.7020\n",
      "Epoch 12/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.6275 - acc: 0.8281 - val_loss: 1.4938 - val_acc: 0.7080\n",
      "Epoch 13/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.5915 - acc: 0.8353 - val_loss: 1.5301 - val_acc: 0.7110\n",
      "Epoch 14/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.5637 - acc: 0.8419 - val_loss: 1.5400 - val_acc: 0.7080\n",
      "Epoch 15/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.5389 - acc: 0.8523 - val_loss: 1.5826 - val_acc: 0.7090\n",
      "Epoch 16/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.5162 - acc: 0.8588 - val_loss: 1.6391 - val_acc: 0.7080\n",
      "Epoch 17/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.4950 - acc: 0.8623 - val_loss: 1.6469 - val_acc: 0.7060\n",
      "Epoch 18/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.4771 - acc: 0.8670 - val_loss: 1.7258 - val_acc: 0.6950\n",
      "Epoch 19/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.4562 - acc: 0.8718 - val_loss: 1.7667 - val_acc: 0.6930\n",
      "Epoch 20/20\n",
      "7982/7982 [==============================] - 0s - loss: 0.4428 - acc: 0.8742 - val_loss: 1.7785 - val_acc: 0.7060\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<keras.callbacks.History at 0x7f8ce7cdb9b0>"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = models.Sequential()\n",
    "model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))\n",
    "model.add(layers.Dense(4, activation='relu'))\n",
    "model.add(layers.Dense(46, activation='softmax'))\n",
    "\n",
    "model.compile(optimizer='rmsprop',\n",
    "              loss='categorical_crossentropy',\n",
    "              metrics=['accuracy'])\n",
    "model.fit(partial_x_train,\n",
    "          partial_y_train,\n",
    "          epochs=20,\n",
    "          batch_size=128,\n",
    "          validation_data=(x_val, y_val))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Our network now seems to peak at ~71% test accuracy, a 8% absolute drop. This drop is mostly due to the fact that we are now trying to \n",
    "compress a lot of information (enough information to recover the separation hyperplanes of 46 classes) into an intermediate space that is \n",
    "too low-dimensional. The network is able to cram _most_ of the necessary information into these 8-dimensional representations, but not all \n",
    "of it."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Further experiments\n",
    "\n",
    "* Try using larger or smaller layers: 32 units, 128 units...\n",
    "* We were using two hidden layers. Now try to use a single hidden layer, or three hidden layers."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Wrapping up\n",
    "\n",
    "\n",
    "Here's what you should take away from this example:\n",
    "\n",
    "* If you are trying to classify data points between N classes, your network should end with a `Dense` layer of size N.\n",
    "* In a single-label, multi-class classification problem, your network should end with a `softmax` activation, so that it will output a \n",
    "probability distribution over the N output classes.\n",
    "* _Categorical crossentropy_ is almost always the loss function you should use for such problems. It minimizes the distance between the \n",
    "probability distributions output by the network, and the true distribution of the targets.\n",
    "* There are two ways to handle labels in multi-class classification:\n",
    "    ** Encoding the labels via \"categorical encoding\" (also known as \"one-hot encoding\") and using `categorical_crossentropy` as your loss \n",
    "function.\n",
    "    ** Encoding the labels as integers and using the `sparse_categorical_crossentropy` loss function.\n",
    "* If you need to classify data into a large number of categories, then you should avoid creating information bottlenecks in your network by having \n",
    "intermediate layers that are too small."
   ]
  }
 ],
 "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.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
