Skip to content
Snippets Groups Projects
neural_nets_intro.ipynb 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • chadhat's avatar
    chadhat committed
    {
     "cells": [
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "code",
    
       "execution_count": 4,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [
        {
         "data": {
          "text/html": [
           "<style>\n",
           "    \n",
           "    @import url('http://fonts.googleapis.com/css?family=Source+Code+Pro');\n",
           "    \n",
           "    @import url('http://fonts.googleapis.com/css?family=Kameron');\n",
           "    @import url('http://fonts.googleapis.com/css?family=Crimson+Text');\n",
           "    \n",
           "    @import url('http://fonts.googleapis.com/css?family=Lato');\n",
           "    @import url('http://fonts.googleapis.com/css?family=Source+Sans+Pro');\n",
           "    \n",
           "    @import url('http://fonts.googleapis.com/css?family=Lora'); \n",
           "\n",
           "    \n",
           "    body {\n",
           "        font-family: 'Lora', Consolas, sans-serif;\n",
           "       \n",
           "        -webkit-print-color-adjust: exact important !;\n",
           "        \n",
           "      \n",
           "       \n",
           "    }\n",
           "    \n",
           "    .alert-block {\n",
           "        width: 95%;\n",
           "        margin: auto;\n",
           "    }\n",
           "    \n",
           "    .rendered_html code\n",
           "    {\n",
           "        color: black;\n",
           "        background: #eaf0ff;\n",
           "        background: #f5f5f5; \n",
           "        padding: 1pt;\n",
           "        font-family:  'Source Code Pro', Consolas, monocco, monospace;\n",
           "    }\n",
           "    \n",
           "    p {\n",
           "      line-height: 140%;\n",
           "    }\n",
           "    \n",
           "    strong code {\n",
           "        background: red;\n",
           "    }\n",
           "    \n",
           "    .rendered_html strong code\n",
           "    {\n",
           "        background: #f5f5f5;\n",
           "    }\n",
           "    \n",
           "    .CodeMirror pre {\n",
           "    font-family: 'Source Code Pro', monocco, Consolas, monocco, monospace;\n",
           "    }\n",
           "    \n",
           "    .cm-s-ipython span.cm-keyword {\n",
           "        font-weight: normal;\n",
           "     }\n",
           "     \n",
           "     strong {\n",
           "         background: #f5f5f5;\n",
           "         margin-top: 4pt;\n",
           "         margin-bottom: 4pt;\n",
           "         padding: 2pt;\n",
           "         border: 0.5px solid #a0a0a0;\n",
           "         font-weight: bold;\n",
           "         color: darkred;\n",
           "     }\n",
           "     \n",
           "    \n",
           "    div #notebook {\n",
           "        # font-size: 10pt; \n",
           "        line-height: 145%;\n",
           "        }\n",
           "        \n",
           "    li {\n",
           "        line-height: 145%;\n",
           "    }\n",
           "\n",
           "    div.output_area pre {\n",
           "        background: #fff9d8 !important;\n",
           "        padding: 5pt;\n",
           "       \n",
           "       -webkit-print-color-adjust: exact; \n",
           "        \n",
           "    }\n",
           " \n",
           "    \n",
           " \n",
           "    h1, h2, h3, h4 {\n",
           "        font-family: Kameron, arial;\n",
           "\n",
           "\n",
           "    }\n",
           "    \n",
           "    div#maintoolbar {display: none !important;}\n",
           "</style>\n"
          ],
          "text/plain": [
           "<IPython.core.display.HTML object>"
          ]
         },
    
         "execution_count": 4,
    
         "metadata": {},
         "output_type": "execute_result"
        }
       ],
    
    chadhat's avatar
    chadhat committed
       "source": [
        "# IGNORE THIS CELL WHICH CUSTOMIZES LAYOUT AND STYLING OF THE NOTEBOOK !\n",
        "import matplotlib.pyplot as plt\n",
    
    chadhat's avatar
    chadhat committed
        "import matplotlib as mpl\n",
        "mpl.rcParams['lines.linewidth'] = 3\n",
    
    chadhat's avatar
    chadhat committed
        "%matplotlib inline\n",
        "%config InlineBackend.figure_format = 'retina'\n",
    
    chadhat's avatar
    chadhat committed
        "%config IPCompleter.greedy=True\n",
    
    chadhat's avatar
    chadhat committed
        "import warnings\n",
        "warnings.filterwarnings('ignore', category=FutureWarning)\n",
    
        "from IPython.core.display import HTML; HTML(open(\"custom.html\", \"r\").read())"
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "# Introduction to Neural Networks\n",
        "\n",
        "\n",
        "## History of Neural networks\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "<div class=\"alert alert-block alert-danger\"><p>\n",
        "    <strong>TODO</strong>: Make it more complete and format properly\n",
        "</p></div>\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "1943 - Threshold Logic\n",
        "\n",
        "1940s - Hebbian Learning\n",
        "\n",
        "1958 - Perceptron\n",
        "\n",
        "1975 - Backpropagation\n",
        "\n",
        "1980s - Neocognitron\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "1982 - Hopfield Network\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
    chadhat's avatar
    chadhat committed
        "1986 - Convolutional Neural Networks\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
    chadhat's avatar
    chadhat committed
        "1997 - Long-short term memory (LSTM) model\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
    chadhat's avatar
    chadhat committed
        "2014 - Gated Recurrent Units, Generative Adversarial Networks(Check)?"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## Feed-Forward neural network\n",
        "<center>\n",
        "<figure>\n",
        "<img src=\"./images/neuralnets/neural_net_ex.svg\" width=\"700\"/>\n",
    
        "<figcaption>A 3 layer densely connected Neural Network (By convention the input layer is not counted).</figcaption>\n",
    
    chadhat's avatar
    chadhat committed
        "</figure>\n",
        "</center>"
    
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## Why the boom now?\n",
        "* Data\n",
        "* Data\n",
        "* Data\n",
        "* Availability of GPUs\n",
    
        "* Algorithmic developments which allow for efficient training and making networks networks\n",
        "* Development of high-level libraries/APIs have made the field much more accessible than it was a decade ago"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## Building blocks\n",
        "### Perceptron\n",
        "\n",
    
        "The smallest unit of a neural network is a **perceptron** like node.\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "**What is a Perceptron?**\n",
        "\n",
    
        "It is a simple function which can have multiple inputs and has a single output.\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "<center>\n",
        "<figure>\n",
        "<img src=\"./images/neuralnets/perceptron_ex.svg\" width=\"400\"/>\n",
        "<figcaption>A simple perceptron with 3 inputs and 1 output.</figcaption>\n",
        "</figure>\n",
        "</center>\n",
        "\n",
        "\n",
        "It works as follows: \n",
        "\n",
        "Step 1: A **weighted sum** of the inputs is calculated\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "\\begin{equation*}\n",
    
    chadhat's avatar
    chadhat committed
        "weighted\\_sum = w_{1} x_{1} + w_{2} x_{2} + w_{3} x_{3} + ...\n",
    
    chadhat's avatar
    chadhat committed
        "\\end{equation*}\n",
        "\n",
    
        "Step 2: A **step** activation function is applied\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "$$\n",
        "f(weighted\\_sum) = \\left\\{\n",
        "        \\begin{array}{ll}\n",
    
        "            0 & \\quad weighted\\_sum < threshold \\\\\n",
        "            1 & \\quad weighted\\_sum \\geq threshold\n",
    
    chadhat's avatar
    chadhat committed
        "        \\end{array}\n",
        "    \\right.\n",
        "$$\n",
    
        "You can see that this is also a linear classifier as the ones we introduced in script 02."
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {
        "tags": [
         "hidecode"
        ]
       },
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
    
    chadhat's avatar
    chadhat committed
        "# Plotting the step function\n",
        "import matplotlib.pyplot as plt\n",
        "import seaborn as sns\n",
        "import numpy as np\n",
        "x = np.arange(-2,2.1,0.01)\n",
        "y = np.zeros(len(x))\n",
        "threshold = 0.\n",
        "y[x>threshold] = 1.\n",
        "step_plot = sns.lineplot(x, y).set_title('Step function') ;\n",
        "plt.xlabel('weighted_sum') ;\n",
        "plt.ylabel('f(weighted_sum)') ;"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
    chadhat's avatar
    chadhat committed
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
        "import numpy as np\n",
    
        "import matplotlib.pyplot as plt\n",
    
    chadhat's avatar
    chadhat committed
        "def perceptron(X, w, threshold=1):\n",
    
        "    # This function computes sum(w_i*x_i) and\n",
    
    chadhat's avatar
    chadhat committed
        "    # applies a perceptron activation\n",
    
    chadhat's avatar
    chadhat committed
        "    linear_sum = np.dot(np.asarray(X).T, w)\n",
        "    output = np.zeros(len(linear_sum), dtype=np.int8)\n",
        "    output[linear_sum >= threshold] = 1\n",
    
    chadhat's avatar
    chadhat committed
        "    return output"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "#### Boolean AND\n",
        "\n",
        "| x$_1$ | x$_2$ | output |\n",
        "| --- | --- | --- |\n",
        "| 0 | 0 | 0 |\n",
        "| 1 | 0 | 0 |\n",
        "| 0 | 1 | 0 |\n",
        "| 1 | 1 | 1 |"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
        "# Calculating Boolean AND using a perceptron\n",
    
        "threshold = 1.5\n",
    
        "# (w1, w2)\n",
    
        "w = [1, 1]\n",
    
    chadhat's avatar
    chadhat committed
        "# (x1, x2) pairs\n",
        "x1 = [0, 1, 0, 1]\n",
        "x2 = [0, 0, 1, 1]\n",
        "# Calling the perceptron function\n",
        "output = perceptron([x1, x2], w, threshold)\n",
        "for i in range(len(output)):\n",
        "    print(\"Perceptron output for x1, x2 = \", x1[i], \",\", x2[i],\n",
        "          \" is \", output[i])"
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "In this simple case we can rewrite our equation to $x_2 = ...... $ which describes a line in 2D:"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
       "metadata": {},
       "outputs": [],
       "source": [
    
    chadhat's avatar
    chadhat committed
        "def perceptron_DB(x1, x2, w, threshold):\n",
        "    # Plotting the decision boundary of the perceptron\n",
        "    sns.scatterplot(x1, x2)\n",
        "    plt.xlim(-1,2)\n",
        "    plt.ylim(-1,2)\n",
    
        "    # The decision boundary is a line given by\n",
        "    # w_1*x_1+w_2*x_2-threshold=0\n",
        "    x1 = np.arange(-3, 4)\n",
        "    x2 = (threshold - x1*w[0])/w[1]\n",
    
    chadhat's avatar
    chadhat committed
        "    sns.lineplot(x1, x2, **{\"color\": \"black\"})\n",
    
        "    plt.xlabel(\"x$_1$\", fontsize=16)\n",
    
    chadhat's avatar
    chadhat committed
        "    plt.ylabel(\"x$_2$\", fontsize=16)\n",
        "    # Coloring the regions\n",
        "    pts_tmp = np.arange(-2, 2.1, 0.02)\n",
        "    points = np.array(np.meshgrid(pts_tmp, pts_tmp)).T.reshape(-1, 2)\n",
        "    outputs = perceptron(points.T, w, threshold)\n",
        "    plt.plot(points[:, 0][outputs == 0], points[:, 1][outputs == 0],\n",
        "             \"o\",\n",
        "             color=\"steelblue\",\n",
        "             markersize=1,\n",
        "             alpha=0.04,\n",
        "             )\n",
        "    plt.plot(points[:, 0][outputs == 1], points[:, 1][outputs == 1],\n",
        "             \"o\",\n",
        "             color=\"chocolate\",\n",
        "             markersize=1,\n",
        "             alpha=0.04,\n",
        "             )\n",
        "    plt.title(\"Blue color = 0 and Chocolate = 1\")"
    
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
    
    chadhat's avatar
    chadhat committed
        "# Plotting the perceptron decision boundary\n",
        "perceptron_DB(x1, x2, w, threshold)"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "**Exercise 1 : Compute a Boolean \"OR\" using a perceptron?**\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "Hint: copy the code from the \"AND\" example and edit the weights and/or threshold"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "#### Boolean OR\n",
        "\n",
        "| x$_1$ | x$_2$ | output |\n",
        "| --- | --- | --- |\n",
        "| 0 | 0 | 0 |\n",
        "| 1 | 0 | 1 |\n",
        "| 0 | 1 | 1 |\n",
        "| 1 | 1 | 1 |"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
       "outputs": [],
       "source": [
        "# Calculating Boolean OR using a perceptron\n",
        "# Edit the code below"
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
        "# Solution\n",
        "# Calculating Boolean OR using a perceptron\n",
        "threshold=0.6\n",
    
        "# (w1, w2)\n",
    
    chadhat's avatar
    chadhat committed
        "w=[1,1]\n",
    
    chadhat's avatar
    chadhat committed
        "# (x1, x2) pairs\n",
        "x1 = [0, 1, 0, 1]\n",
        "x2 = [0, 0, 1, 1]\n",
        "output = perceptron([x1, x2], w, threshold)\n",
        "for i in range(len(output)):\n",
        "    print(\"Perceptron output for x1, x2 = \", x1[i], \",\", x2[i],\n",
        "          \" is \", output[i])\n",
        "perceptron_DB(x1, x2, w, threshold)"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "**Exercise 2 : Create a NAND gate using a perceptron**\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "#### Boolean NAND\n",
        "\n",
        "| x$_1$ | x$_2$ | output |\n",
        "| --- | --- | --- |\n",
        "| 0 | 0 | 1 |\n",
        "| 1 | 0 | 1 |\n",
        "| 0 | 1 | 1 |\n",
        "| 1 | 1 | 0 |"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
       "outputs": [],
       "source": [
    
        "# Calculating Boolean NAND using a perceptron\n"
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
        "# Solution\n",
    
        "# Calculating Boolean NAND using a perceptron\n",
    
    chadhat's avatar
    chadhat committed
        "import matplotlib.pyplot as plt\n",
        "threshold=-1.5\n",
    
        "# (w1, w2)\n",
    
    chadhat's avatar
    chadhat committed
        "w=[-1,-1]\n",
    
    chadhat's avatar
    chadhat committed
        "# (x1, x2) pairs\n",
        "x1 = [0, 1, 0, 1]\n",
        "x2 = [0, 0, 1, 1]\n",
        "output = perceptron([x1, x2], w, threshold)\n",
        "for i in range(len(output)):\n",
        "    print(\"Perceptron output for x1, x2 = \", x1[i], \",\", x2[i],\n",
        "          \" is \", output[i])\n",
        "perceptron_DB(x1, x2, w, threshold)"
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "In fact, a single perceptron can compute \"AND\", \"OR\" and \"NOT\" boolean functions.\n",
    
        "\n",
        "However, it cannot compute some other boolean functions such as \"XOR\".\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "**WHAT CAN WE DO?**\n",
        "\n",
        "\n",
    
        "Hint: Think about what is the significance of the NAND gate we have created above?\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "Answer: We said a single perceptron can't compute a \"XOR\" function. We didn't say that about **multiple Perceptrons** put together."
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "**XOR function using multiple perceptrons**\n",
        "\n",
        "<center>\n",
        "<figure>\n",
        "<img src=\"./images/neuralnets/perceptron_XOR.svg\" width=\"400\"/>\n",
    
        "<figcaption>Multiple perceptrons connected together to output a XOR function.</figcaption>\n",
    
        "</figure>\n",
        "</center>"
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
    
       "cell_type": "markdown",
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
       "source": [
    
        "### Multi-layer perceptrons\n",
        "\n",
        "The normal densely connected neural network is sometimes also called \"Multi-layer\" perceptron."
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "### Google Playground\n",
        "\n",
    
        "A great tool from Google to develop a feeling about neural networks.\n",
        "\n",
    
        "https://playground.tensorflow.org/\n",
        "\n",
    
        "<img src=\"./images/neuralnets/google_playground.png\"/>\n",
        "\n",
        "Some concepts to look at:\n",
        "\n",
        "* Effect of activation functions\n",
        "* Effect of network size"
    
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "## Learning\n",
        "\n",
    
        "Now we know that we can compute complex functions by combining a number of perceptrons.\n",
    
        "In the perceptron examples we had set the model parameters (weights and thresholds) by hand.\n",
    
        "This is something we definitely **DO NOT** want to do or even can do for big networks.\n",
    
        "We want some algorithm to set the weights for us!\n",
        "\n",
        "This is achieved by choosing an appropriate loss function for the problem at hand and solving an optimization problem.\n",
        "We will explain below what this means.\n",
    
        "\n",
        "\n",
        "### Loss function\n",
    
        "\n",
        "To learn using an algorithm we need to define a quantity/function which allows us to measure how close or far are the predictions of our network/setup from reality or the supplied labels. This is done by choosing a so-called \"Loss function\" (as in the case for other machine learning algorithms).\n",
        "\n",
        "Once we have this function, we need an algorithm to update the weights of the network such that this loss function decreases. \n",
        "As one can already imagine the choice of an appropriate loss function is critical to the success of the model. \n",
        "\n",
        "Fortunately, for classification and regression (which cover a large variety of problems) these loss functions are well known. \n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "Generally **crossentropy** and **mean squared error** loss functions are used for classification and regression problems, respectively.\n",
    
    chadhat's avatar
    chadhat committed
        "<div class=\"alert alert-block alert-warning\">\n",
        "    <i class=\"fa fa-info-circle\"></i>&nbsp; <strong>mean squared error</strong> is defined as \n",
        "\n",
        "\n",
        "$$\n",
        "\\frac{1}{n} \\left((y_1 - \\hat{y}_1)^2 + (y_2 - \\hat{y}_2)^2 + ... + (y_n - \\hat{y}_n)^2 \\right)\n",
        "$$\n",
        "\n",
        "\n",
        "</div>\n",
        "\n",
    
        "### Gradient based learning\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "As mentioned above, once we have chosen a loss function, we want to solve an **optimization problem** which minimizes this loss by updating the weights of the network. This is how the learning takes in a NN, and the \"knowledge\" is stored in the weights.\n",
        "\n",
        "The most popular optimization methods used in Neural Network training are **Gradient-descent (GD)** type methods, such as gradient-descent itself, RMSprop and Adam. \n",
        "\n",
        "**Gradient-descent** uses partial derivatives of the loss function with respect to the network weights and a learning rate to updates the weights such that the loss function decreases and after some iterations reaches its (Global) minimum value.\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
    chadhat's avatar
    chadhat committed
        "First, the loss function and its derivative are computed at the output node, and this signal is propagated backwards, using the chain rule, in the network to compute the partial derivatives. Hence, this method is called **Backpropagation**.\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "One way to perform a single GD pass is to compute the partial derivatives using **all the samples** in our data, computing average derivatives and using them to update the weights. This is called **Batch gradient descent**. However, in deep learning we mostly work with massive datasets and using batch gradient descent can make the training very slow!\n",
        "\n",
        "The other extreme is to randomly shuffle the dataset and advance a pass of GD with the gradients computed using only **one sample** at a time. This is called **Stochastic gradient descent**.\n",
        "\n",
        "<center>\n",
        "<figure>\n",
        "<img src=\"stochastic-vs-batch-gradient-descent.png\" width=\"600\"/>\n",
        "<figcaption>Source: <a href=\"https://wikidocs.net/3413\">https://wikidocs.net/3413</a></figcaption>\n",
        "</figure>\n",
        "</center>\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "In practice, an approach in-between these two is used. The entire dataset is divided into **m batches** and these are used one by one to compute the derivatives and apply GD. This technique is called **Mini-batch gradient descent**. \n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "<div class=\"alert alert-block alert-warning\">\n",
        "<p><i class=\"fa fa-warning\"></i>&nbsp;\n",
        "One pass through the entire training dataset is called 1 epoch of training.\n",
        "</p>\n",
        "</div>"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 2,
       "metadata": {},
       "outputs": [],
       "source": [
        "import matplotlib.pyplot as plt\n",
        "import seaborn as sns\n",
        "import numpy as np\n",
    
        "plt.figure(figsize=(10, 4)) ;\n",
    
        "pts=np.arange(-20,20, 0.1) ;"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
    chadhat's avatar
    chadhat committed
        "### Activation Functions\n",
        "\n",
    
        "In order to train the network we need to move away from Perceptron's **step** activation function because it does not allow training using the gradient-descent and back-propagation algorithms among other drawbacks.\n",
    
        "\n",
        "Non-Linear functions such as:\n",
        "\n",
        "* Sigmoid\n",
        "\n",
        "\\begin{equation*}\n",
        "f(z) = \\frac{1}{1+e^{-z}}\n",
    
        "\\end{equation*}"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 5,
       "metadata": {},
       "outputs": [
        {
         "data": {
          "image/png": "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\n",
          "text/plain": [
           "<Figure size 432x288 with 1 Axes>"
          ]
         },
         "metadata": {
          "image/png": {
           "height": 250,
           "width": 373
          },
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ],
       "source": [
        "sns.lineplot(pts, 1/(1+np.exp(-pts))) ;"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "* tanh\n",
        "\n",
        "\\begin{equation*}\n",
        "f(z) = \\frac{e^{z} - e^{-z}}{e^{z} + e^{-z}}\n",
    
        "\\end{equation*}"
       ]
      },
      {
       "cell_type": "code",
       "execution_count": 6,
       "metadata": {},
       "outputs": [
        {
         "data": {
          "image/png": "iVBORw0KGgoAAAANSUhEUgAAAwkAAAH0CAYAAAB2CGFiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3XmYZVV96P3vr+duoLshoiCoCNpAjGgAhcANMuQlaJzCkPjeSBSjN04Rp3vjjRrBV42+N1FeHBMnohjRNDcY34ghVwYVggntVYm2gEAr0KgM0kAPVT387h97l1RVn11Vp/rU3qu6vp/nOc/us/aw1tnP7nXqd9YUmYkkSZIkjZjXdQEkSZIklcUgQZIkSdIYBgmSJEmSxjBIkCRJkjSGQYIkSZKkMQwSJEmSJI1hkCBJkiRpDIMESZIkSWMYJEiSJEkawyBBkiRJ0hgGCZIkSZLGMEiQJEmSNIZBgiRJkqQxDBIkSZIkjWGQIEmSJGkMgwRJkiRJYyzougBzQUTcDiwH1nVcFEmSJO3eDgIezMwn7spFDBLasXzp0qX7HH744ft0XRBJkiTtvtauXcvmzZt3+ToGCe1Yd/jhh++zZs2arsshSZKk3dhRRx3Ft7/97XW7eh3HJEiSJEkawyBBkiRJ0hgGCZIkSZLGMEiQJEmSNIZBgiRJkqQxDBIkSZIkjWGQIEmSJGkMgwRJkiRJYxgkSJIkSRrDIEGSJEnSGAYJkiRJksYoIkiIiDMj4oMR8Y2IeDAiMiIunua1DoyIT0XE+ogYioh1EXFBROw9wTm/GhFfjIifR8SWiLgpIs6PiKXT/1SSJEnS7LSg6wLU3gY8DXgYuBM4bDoXiYhDgOuARwNfAn4IPBM4FzgtIo7PzPvGnXMMcCWwEFgN3AGcDPw5cEpEnJKZQ9MpjyRJkjQbFdGSALwBWAUsB161C9f5CFWA8LrMfGFmviUzTwY+ABwKvHv0wRExH/g0sAw4MzP/c2b+KXAMcClwfF02SZIkac4ooiUhM68a+XdETOsaEXEwcCqwDvjwuN3vAP4LcHZEvCkzN9bpzwIOB76emf84qjw7IuK/AWcAr4yI92VmTqtgkqTObB7ezl0PbOaeh4a45+EhHty8lW3bd7BtR7J9R7JtR7Jte7J9x46B5+2XhqQRrznpSSxZOL/rYvSliCBhQE6ut1dk5pjaPjMfiohrqYKIY4GvjTvnq+Mvlpm3RcTNVC0cBwO3zkipJUkDtf6BzXz+337ClT/8OWvvfpAd/rUuqWMv/82DDRI6dGi9vblh/y1UQcIqHgkSpnLOqvo1aZAQEWsadk1rjIUkaeq2bN3Oe76ylouv/7GBgSTtot0pSFhRbzc07B9JX7mL50iSCrP+gc284jM38P31D3ZdFEnaLexOQcJkRgY79PP7Ul/nZOZRPS9StTAc2Ue+kqQp2ji0jRd/8lvcds/GyQ+WJE1JKbMbDcLIr/4rGvYvH3fcdM+RJBXkLy5fa4AgSQO2O7Uk3FRvVzXsf3K9HT3+YDrnSJIKce2P7uXi638y4TGP2nMxj9tnKfvuuZh99ljEogXzmD8vWDi/2i6YF8yfFwTTm11vItOcsE/Sbmbxgtn3u/zuFCSMTKN6akTMGz3DUUTsRbXmwWbg+lHnXAm8FTgN+IvRF6unVF0F/Bi4bQbLLUmapgv+V/NvOKc9ZT/edOoqnvToPac9vbYkzVWzLqyJiIURcVi9uvIvZeatwBXAQcBrxp12PrAH8JlRayQAXAOsBU6IiOePymMe8L767cdcI0GSyrPu3o38+7pf9Nz3O0/dn4+++Eie/Ji9DBAkaRqKaEmIiBcCL6zf7ldvfyMiLqr/fW9mvrn+9wFUf9j/mCogGO3VwHXAhRFxSn3cMcBJVF2G3jr64MzcHhHnULUorI6I1cBPgFOAo4FrqVZrliQV5tJv39kzfd+9FvOe059qcCBJu6CIIAF4OvCScWkH1y+oAoI3M4nMvDUijgbeSdWF6DnA3cCFwPmZeX+Pc74VEc+gam04Fdirzu+dwHszc2han0iSNGN27Ej+57fv6rnv944+kBVLF7ZcIknavRQRJGTmecB5Uzx2HTSPLsvMO4Bz+sz/B8BZ/ZwjSerOmp/8grse2Nxz3+lHHthyaSRp9zPrxiRIkvTv63ZqGAbg1x+/kkP23bPl0kjS7scgQZI063zvjt7L15z2lP16pkuS+mOQIEmadW68q3eQ8LTHrWy5JJK0ezJIkCTNKvc+PNRzPEIE/NoBKzookSTtfgwSJEmzyo139m5FOGTfPdlzcRHzcUjSrGeQIEmaVb7XECQcYSuCJA2MQYIkaVa58a4HeqY/9UCDBEkaFIMESdKs8oP1D/ZMP+JABy1L0qAYJEiSZo2t23fw0we39Nx3+P57tVwaSdp9GSRIkmaNnz24hR25c/reyxaybJGDliVpUAwSJEmzxt0berciPHbl0pZLIkm7N4MESdKssb7H+ggA+68wSJCkQTJIkCTNGusf6N2ScMDKJS2XRJJ2bwYJkqRZo7Elwe5GkjRQBgmSpFnj7g29gwTHJEjSYBkkSJJmjbsauhs9doXdjSRpkAwSJEmzRlNLgt2NJGmwDBIkSbPCpuFtPLBp607p8wIes9fiDkokSbsvgwRJ0qzQNLPRY5YvYcF8v84kaZCsVSVJs4KDliWpPQYJkqRZoXkhNQctS9KgGSRIkmaF+zYO90zfb7lBgiQNmkGCJGlW2NBj0DLA3nssarkkkrT7M0iQJM0KGzb3DhKWL13YckkkafdnkCBJmhWagoQVBgmSNHAGCZKkWaHXGgkAKw0SJGngDBIkSbOCLQmS1B6DBEnSrGCQIEntMUiQJM0KTUHCymUGCZI0aAYJkqTibd2+g4eHtvXct9cSgwRJGjSDBElS8R5smv50yQLmz4uWSyNJuz+DBElS8RrHI9jVSJJmhEGCJKl4DzhoWZJaZZAgSSpe46DlpYtaLokkzQ0GCZKk4jWNSbAlQZJmRlFBQkQcGBGfioj1ETEUEesi4oKI2HuK558YETmF1+PGnTfRsdfPzKeVJE1V02rLyw0SJGlGLOi6ACMi4hDgOuDRwJeAHwLPBM4FTouI4zPzvkkusw44v2HfU4HTge9n5h099v8YuKhH+p2TFl6SNKNcI0GS2lVMkAB8hCpAeF1mfnAkMSLeD7wBeDfwyokukJnrgPN67YuIz9f//JuG09dlZs9zJUndampJsLuRJM2MIrobRcTBwKlULQEfHrf7HcBG4OyI2GOa1/8V4HeBzcBnp19SSVIXmgcuGyRI0kwopSXh5Hp7RWbuGL0jMx+KiGupgohjga9N4/ovBRYDn8nMXzQcszIiXgbsB2wA1mSm4xEkqQCN6yQYJEjSjCglSDi03t7csP8WqiBhFdMLEl5eb/96gmOeBnxydEJEfBc4OzNvnEomEbGmYddhUzlfktTbhs3DPdMNEiRpZhTR3QhYUW83NOwfSV/Z74Uj4llUf6R/PzOvazjs/cDxwL7AXsAzgNVUgcOVEXFAv/lKkgbHFZclqV2ltCRMJuptTuPc/1JvG1sRMvNN45JuAM6KiNXAGcCbqQZPTygzj+qVXrcwHDml0kqSduLAZUlqVyktCSMtBSsa9i8fd9yURMQ+VH/kT3fA8sfq7QnTOFeSNCAPbjFIkKQ2lRIk3FRvVzXsf3K9bRqz0OQlVAOWv5iZD0yjXPfU22nNqiRJ2nXbdyRbtu7ouW+PRbOlQVySZpdSgoSr6u2pETGmTBGxF9V4gc1Av7MNvaLeNq2NMJlj6+1t0zxfkrSLNm/d3jN9ycJ5zJsXPfdJknZNEUFCZt4KXAEcBLxm3O7zqX7J/0xmbhxJjIjDIqJx1qCI+E3gcOA/JhiwTEQc2Wv9hYg4gmoBN4CLp/hRJEkDtml4W8/0ZbYiSNKMKamGfTVwHXBhRJwCrAWOAU6i6mb01nHHr623TT8jjQxYnqwV4XXA6RFxJXAHMEQ1G9JpwHzg48Dnm0+XJM2kzcO9WxKWLpzfckkkae4oJkjIzFsj4mjgnVR/oD8HuBu4EDg/M++f6rUiYm/gTKY2YPkyqoHRR1At6rYEuA+4HPh4Zv5jnx9FkjRAmxqChGWLDBIkaaYUEyQAZOYdwDlTPLaxI2q9qvLSKV7nMqpAQZJUIIMESWpfEWMSJElq0tjdyCBBkmaMQYIkqWgOXJak9hkkSJKK1jQFqi0JkjRzDBIkSUVrHJPg7EaSNGMMEiRJRXPgsiS1zyBBklS0zQ1jEpY6JkGSZoxBgiSpaLYkSFL7DBIkSUUzSJCk9hkkSJKK5joJktQ+gwRJUtE2NUyBakuCJM0cgwRJUtEaBy4vdOCyJM0UgwRJUtEckyBJ7TNIkCQVzSBBktpnkCBJKpoDlyWpfQYJkqSibdrae0zCMhdTk6QZY5AgSSpaU0uC3Y0kaeYYJEiSitY0JsHuRpI0cwwSJEnFykw2N62TsNAgQZJmikGCJKlYW7buIHPn9EXz57Fgvl9hkjRTrGElScXa1LSQml2NJGlGGSRIkorlGgmS1A2DBElSsZrGI9iSIEkzyyBBklQsWxIkqRsGCZKkYjWNSVi20IXUJGkmGSRIkorVtJCa3Y0kaWYZJEiSimV3I0nqhkGCJKlYtiRIUjcMEiRJxWock2CQIEkzyiBBklSsTQ1ToC5b5MBlSZpJBgmSpGJt2bqjZ/qShbYkSNJMMkiQJBVraFvvloQlC/36kqSZZC0rSSrW8LbeLQmL5vv1JUkzyVpWklSsoYYgYbHdjSRpRhkkSJKKNdQwJmHxAr++JGkmFVXLRsSBEfGpiFgfEUMRsS4iLoiIvfu4xtURkRO8ljSc96sR8cWI+HlEbImImyLi/IhYOrhPKEnqR9OYBIMESZpZxcwhFxGHANcBjwa+BPwQeCZwLnBaRByfmff1ccnzG9J3mnQ7Io4BrgQWAquBO4CTgT8HTomIUzJzqI+8JUkD0NjdaIHdjSRpJhUTJAAfoQoQXpeZHxxJjIj3A28A3g28cqoXy8zzpnJcRMwHPg0sA16Qmf9Yp88DvgicUef/3qnmLUkajOYxCbYkSNJMKqKWjYiDgVOBdcCHx+1+B7ARODsi9piB7J8FHA58fSRAAMjMHcB/q9++MiJiBvKWJE1guKm7kbMbSdKMKqWWPbneXlH/cf5LmfkQcC3VL/3HTvWCEfH7EfGWiHhjRDw7IhZPkvdXx+/IzNuAm4EnAAdPNW9J0mDYkiBJ3Silu9Gh9fbmhv23ULU0rAK+NsVrXjLu/c8j4jWZuXoaea+qX7dOlGFErGnYddhE50mSemue3cgxCZI0k0r5KWZFvd3QsH8kfeUUrvUl4HnAgcBSqj/Q/6I+9wsR8ewZzFuSNEDObiRJ3SilJWEyI+MBcrIDM/MD45JuAv4sItYDHwTeA1w+Q3kf1fMCVQvDkX3kKUnC2Y0kqSul/BQz8mv9iob9y8cdNx2foJr+9OkRsVfLeUuSpmG4IUhYZEuCJM2oUmrZm+rtqob9T663TeMGJpWZW4CH6rejZ0ma8bwlSdPT3JJQyteXJO2eSqllr6q3p9brE/xS/av/8cBm4PrpZhARhwJ7UwUK947adWW9Pa3HOQdTBQ8/Bm6bbt6SpOlpHJPg7EaSNKOKqGUz81bgCuAg4DXjdp9P9cv/ZzJz40hiRBwWEWNmDYqIgyPigPHXj4hHUS2YBnBJZo5edfkaYC1wQkQ8f9Q584D31W8/lpmTjkmQJA1OZja2JCxynQRJmlElDVx+NXAdcGFEnEL1h/sxwElUXX3eOu74tfV29CJnJwCfiIhrqKYrvR94PPAcqjEHN/DIAmkAZOb2iDiHqkVhdUSsBn4CnAIcTbVGw/jB0JKkGbZ1e9Lr55kF84IFBgmSNKOKCRIy89aIOBp4J1XXn+cAdwMXAudn5v1TuMwa4GLgKODpVIOOHwJuBL4I/HVmDvfI+1sR8QyqVotTgb2ouhi9E3hvZg7t4seTJPWpqauRg5YlaeYVEyQAZOYdwDlTPDZ6pN0IvHSaef8AOGs650qSBq9pZiMHLUvSzLOmlSQVyTUSJKk7BgmSpCI1BgnObCRJM86aVpJUpMbpT+1uJEkzzppWklSkoa12N5KkrhgkSJKK1LhGgi0JkjTjrGklSUVydiNJ6o41rSSpSI5JkKTuWNNKkorkFKiS1B2DBElSkRpbEpwCVZJmnDWtJKlITbMbLZrvV5ckzTRrWklSkYa3u5iaJHXFmlaSVCTXSZCk7hgkSJKK5OxGktQda1pJUpGc3UiSumOQIEkqkisuS1J3rGklSUUa2mp3I0nqijWtJKlIzm4kSd2xppUkFcnZjSSpOwYJkqQiNQ9c9qtLkmaaNa0kqUhOgSpJ3bGmlSQVydmNJKk71rSSpCI5JkGSumOQIEkq0pCzG0lSZ6xpJUlFcp0ESeqONa0kqUjDjbMb2d1IkmaaQYIkqUhOgSpJ3bGmlSQVySlQJak71rSSpCI1tyTY3UiSZppBgiSpSI1BgrMbSdKMs6aVJBUnMxsHLi+a71eXJM00a1pJUnGGG9ZIWDAvmDcvWi6NJM09BgmSpOI0tiI4aFmSWmFtK0kqztbt2TPdIEGS2mFtK0kqTlNLwkLHI0hSK4qqbSPiwIj4VESsj4ihiFgXERdExN5TPH+PiPiDiPi7iPhhRGyMiIci4oaIeFNELGo4Lyd4XT/YTylJmszWhjEJDlqWpHYs6LoAIyLiEOA64NHAl4AfAs8EzgVOi4jjM/O+SS7zm8DFwP3AVcBlwD7A84C/BE6PiFMyc0uPc38MXNQj/c7+P40kaVc0TX9qdyNJakcxQQLwEaoA4XWZ+cGRxIh4P/AG4N3AKye5xk+BFwN/n5nDo66xF3A1cBzwGuCvepy7LjPP24XyS5IGxJYESepWEbVtRBwMnAqsAz48bvc7gI3A2RGxx0TXyczvZObnRgcIdfpDPBIYnDiIMkuSZk7jmIQFTn8qSW0opSXh5Hp7RWaO+WbIzIci4lqqIOJY4GvTzGNrvd3WsH9lRLwM2A/YAKzJTMcjSFIHbEmQpG6VEiQcWm9vbth/C1WQsIrpBwkvq7dfbdj/NOCToxMi4rvA2Zl54zTzlCRNg7MbSVK3SgkSVtTbDQ37R9JXTufiEfFa4DTgO8CnehzyfuBSqiBlC3AY8KfAmcCVEfH0zLxrCvmsadh12HTKLUlzVdOKyw5clqR2zJbadqQTau/VdSY6MeJ04AKqQc1nZObW8cdk5psy87rMvDczH87MGzLzLKrA4VHAm3eh7JKkPjWuuGxLgiS1opSWhJGWghUN+5ePO25KIuKFwCXAz4GTMvO2Psv1MeAM4ISpHJyZRzWUYw1wZJ95S9Kc5YrLktStUmrbm+rtqob9T663TWMWdhIRZwF/D/wMeFZm3jTJKb3cU28nnFVJkjRYw9u390x3TIIktaOU2vaqentqRIwpU73GwfHAZmBKsw1FxH8GPg+spwoQbplmuY6tt/22QEiSdsHWbbYkSFKXiqhtM/NW4ArgIKrFzkY7n+qX/M9k5saRxIg4LCJ2GhAcES8BPgv8BDhhsi5GEXFkr/UXIuIIqgXcoFrFWZLUkqGGgcu2JEhSO0oZkwDwauA64MKIOAVYCxwDnETVzeit445fW29/ubJORJxENXvRPKrWiXMidlp454HMvGDU+9cBp0fElcAdwBDVbESnAfOBj1O1SkiSWrK1YeDyYlsSJKkVxQQJmXlrRBwNvJPqD/TnAHcDFwLnZ+b9U7jME3ikdeRlDcf8mGq2oxGXUQ2MPoJqUbclwH3A5cDHM/Mf+/wokqRd1DQF6sL5rrgsSW0oJkgAyMw7gHOmeOxO3xSZeRFwUZ95XkYVKEiSCtHUkuCYBElqh7WtJKk4zS0Jfm1JUhusbSVJxXHFZUnqlrWtJKk4rrgsSd2ytpUkFWerLQmS1ClrW0lScZpaEhyTIEntsLaVJBVn6/aGFZcNEiSpFda2kqTiNLYk2N1IklphbStJKk7j7Ea2JEhSK6xtJUnFaZzdaIErLktSGwwSJEnFaZzdaP78lksiSXOTQYIkqTjNsxvZkiBJbTBIkCQVx3USJKlb1raSpOIMuU6CJHXK2laSVJymloTFtiRIUiusbSVJxWmaAtWWBElqh7WtJKk4W7c1rLhsS4IktcLaVpJUHFsSJKlb1raSpOJsbVxMza8tSWqDta0kqThDjYup+bUlSW2wtpUkFSUzXSdBkjpmbStJKsq2HUn2GLc8L2D+PFdclqQ2GCRIkopiK4Ikdc8aV5JUlGFXW5akzlnjSpKK0jT9qastS1J7rHElSUWxJUGSumeNK0kqytbtrrYsSV2zxpUkFcWWBEnqnjWuJKkojbMbGSRIUmuscSVJRRlqakmwu5EktcYaV5JUlOaWBBdSk6S2GCRIkorSNCbBgcuS1B5rXElSUZpaEhy4LEntscaVJBWlsSXBIEGSWmONK0kqStOKyw5clqT2WONKkorS1JKw2JYESWpNMTVuRBwYEZ+KiPURMRQR6yLigojYu8/r7FOft66+zvr6ugfOdN6SpF3XtOKyYxIkqT0Lui4AQEQcAlwHPBr4EvBD4JnAucBpEXF8Zt43hev8Sn2dVcCVwCXAYcA5wO9ExG9k5m0zkbckaTCGt23vme7sRpLUnlJq3I9Q/ZH+usx8YWa+JTNPBj4AHAq8e4rXeQ9VgPCBzDylvs4Lqf7gf3Sdz0zlLUkaAFsSJKl7nde4EXEwcCqwDvjwuN3vADYCZ0fEHpNcZw/g7Pr4d4zb/aH6+r9d5zfQvCVJg9M0cNmWBElqTwk17sn19orMHPPNkJkPAdcCy4BjJ7nObwBLgWvr80ZfZwdwRf32pBnIW5I0IM1ToLrisiS1pYQg4dB6e3PD/lvq7aoZuM6g8gYgItb0elGNi5AkTYEtCZLUvRJq3BX1dkPD/pH0lTNwnUHlLUkakK0NLQmOSZCk9hQxu9EkRtqXe49km9nr9HVOZh7V8yJVa8KRfeQrSXPWVlsSJKlzJdS4I7/Wr2jYv3zccYO8zqDyliQNSOOKy7YkSFJrSqhxb6q3Tf3+n1xvm8YN7Mp1BpW3JGlAhrf1bry1JUGS2lNCjXtVvT01IsaUJyL2Ao4HNgPXT3Kd6+vjjq/PG32deVRTnY7Ob5B5S5IGpHHgsi0JktSazmvczLyVanrSg4DXjNt9PrAH8JnM3DiSGBGHRcSYGYMy82Hgs/Xx5427zmvr6//z6BWXp5O3JGlmNQ1ctiVBktpTysDlVwPXARdGxCnAWuAYqjUNbgbeOu74tfV2/KTZfwacCLwxIp4O/BtwOPAC4OfsHAhMJ29J0gxyTIIkda+IGrf+Rf9o4CKqP9DfBBwCXAj8RmbeN8Xr3Ee1qNqFwJPq6xwDfBo4qs5nRvKWJA2GsxtJUvdKaUkgM+8AzpnisY3Lbmbm/cC59WvgeUuSZtZQ4zoJrrgsSW3xZxlJUlGaWhIW25IgSa2xxpUkFWXYFZclqXPWuJKkojgmQZK6Z40rSSqKLQmS1D1rXElSUbZub1hx2SBBklpjjStJKkrT7EZ2N5Kk9ljjSpKK0jgmwZYESWqNNa4kqSiNYxJsSZCk1ljjSpKKYkuCJHXPGleSVIwdO5JtO3oPXHbFZUlqj0GCJKkYwxO0IkQYJEhSWwwSJEnFaAoSbEWQpHYZJEiSirHV6U8lqQjWupKkYjS3JPh1JUltstaVJBVj67aG1ZZtSZCkVlnrSpKKMbx9e890pz+VpHZZ60qSijFsS4IkFcFaV5JUDMckSFIZrHUlScVoXG3ZlgRJapW1riSpGMMNU6C6ToIktcsgQZJUjMYVlxfMb7kkkjS3GSRIkorR1JKwyJYESWqVQYIkqRiOSZCkMljrSpKK0Twmwa8rSWqTta4kqRiNLQkGCZLUKmtdSVIxGlsS7G4kSa2y1pUkFWN4e8OKy7YkSFKrrHUlScVonN3IlgRJapW1riSpGI5JkKQyWOtKkorh7EaSVAZrXUlSMVwnQZLKYK0rSSrGUGNLgisuS1KbDBIkScVoaklYbEuCJLXKWleSVAzHJEhSGYqpdSPiuIj4SkTcHxGbIuJ7EfH6iJjfxzUOiIg/iYjLI2JdRAxFxH0R8S8RcXrDOSdGRE7weu/gPqUkaSKOSZCkMizougAAEfEC4FJgC/AF4H7gecAHgOOBs6Z4qT8B/hS4HbgK+CnwBOB04Lci4gOZ+caGc68Bru6R/s0p5i1J2kXDDUGCLQmS1K7Og4SIWA58HNgOnJiZN9TpbweuBM6MiBdl5iVTuNy/1de4ZlwehwPXA2+IiM9l5poe516dmeftwkeRJO2i4W0NKy7bkiBJrSqh1j0T2Be4ZCRAAMjMLcDb6revmsqFMvN/jg8Q6vS1VC0UACfuUmklSTOmqSXBxdQkqV2dtyQAJ9fbr/bY93VgE3BcRCzOzKFdyGdrvd3WsP9JEfFaYDlVN6VvZOYtu5CfJKlPWxsGLtuSIEntKiFIOLTe3jx+R2Zui4jbgacABwNrp5NB3aXpDCCBKxoO+4P6Nfq8S4FXZOYvppOvJKk/jkmQpDKUECSsqLcbGvaPpK+czsUjIoBPAI8BPlJ3PRrtHuAtwD8B64AlwNHAe6gCi/0i4oTM7P3NNTavXmMdAA6bTtklaa5xdiNJKsNAat16utGJphEd/7q4n8vX296j2Sb3V1SzI30D2Glmo8z8fma+LzP/IzMfzsx7M/OrVGMXbqeaXel508xbktSH5nUSXHFZkto0qJaEW6mmL52q9aP+PdJSsKLXgVRjBEYfN2UR8T+AN1CNbfidfsY0ZOaDEfF3wFuBE4AvTeGcoxrKsQY4cqp5S9Jc1dTdyBWXJaldAwkSMvOUXTj9JqruPauAMd11ImIB8ESqwca39XPRiPgA8Hqq9RKem5mbplG2e+rtHtM4V5LUJ1dclqQylFDrXllvT+ux7wRgGXDdVFsBovJhqgDhX6haEKYTIAAcW2/7ClAkSdPjmARJKkMJte5q4F7gRRFx9EhiRCwB3lW//ejoEyJiWUQcFhGPH5cewN8ArwYuB56fmZsnyjwijo+Ine5DRLwY+H1gGPgbrlpUAAAc/0lEQVRi359KktQ3WxIkqQydz25U9/1/BVWwcHVEXALcDzyfanrU1TyyENqIZ1J1I7qGsYuj/TnwcmAz8B3gLVXcMMZ3MvOyUe8/B8yLiOuAO6lmN3pGncc24I8zc92ufUpJ0lRs3e6Ky5JUgs6DBIDMvCwinkU1SPgMqj/Uf0Q1G9GFmTnVmY2eWG+XAv+94Zi/BUYHCR8FfotqFqNHUc2mdBdwEXBBZn536p9EkrQrmloSXHFZktpVRJAAkJnXAs+Z4rFX88jUqKPTXwq8tM983we8r59zJEmDl5kupiZJhbDWlSQVoamr0fx5wfx5rpMgSW0ySJAkFaFxZiNbESSpdda8kqQiuNqyJJXDIEGSVIShhiBh8cL5LZdEkmSQIEkqQlNLwmKnP5Wk1lnzSpKKMLRte890gwRJap81rySpCI3djRbY3UiS2maQIEkqQlNLgqstS1L7rHklSUUY2uqYBEkqhTWvJKkIzm4kSeUwSJAkFaF5TIJfVZLUNmteSVIRnN1IksphzStJKoKzG0lSOQwSJElFaAoSnN1IktpnzStJKsLQVrsbSVIprHklSUUY3t40u5FfVZLUNmteSVIRmtdJcEyCJLXNIEGSVASnQJWkcljzSpKK4BSoklQOa15JUhFsSZCkcljzSpKKMOw6CZJUDIMESVIRGlsSnN1IklpnzStJKoLrJEhSOax5JUlFaB6TYHcjSWqbQYIkqQhNsxstsiVBklpnzStJKoKzG0lSOax5JUlFcHYjSSqHQYIkqQjObiRJ5bDmlSQVwRWXJakc1rySpCIMbbW7kSSVwiBBklSEpu5Gzm4kSe2z5pUkFaF54LJfVZLUNmteSVLnMtMxCZJUEGteSVLntu1IduTO6fPnBQvm+1UlSW2z5pUkdc6F1CSpLMXUvhFxXER8JSLuj4hNEfG9iHh9RPQ1rUVE5ASv6yc477kRcXVEbIiIhyPiWxHxkl3/ZJKkyQxttauRJJVkQdcFAIiIFwCXAluALwD3A88DPgAcD5zV5yV/DFzUI/3OhvxfC3wQuA+4GBgGzgQuioinZuab+8xfktSH4e3ObCRJJek8SIiI5cDHge3AiZl5Q53+duBK4MyIeFFmXtLHZddl5nlTzP8g4C+pApOjM3Ndnf5O4N+BN0XEpZn5r33kL0nqg2skSFJZSviJ5kxgX+CSkQABIDO3AG+r375qBvN/GbAY+NBIgFDn/wvgPfXbV85g/pI05zkmQZLK0nlLAnByvf1qj31fBzYBx0XE4swcmuI1V0bEy4D9gA3AmsxsGo8wUf6XjztGkjQDGqc/XWiQIEldKCFIOLTe3jx+R2Zui4jbgacABwNrp3jNpwGfHJ0QEd8Fzs7MG/vI/+6I2AgcGBHLMnPTRJlGxJqGXYdNrdiSNDc1tyTY3UiSulDCTzQr6u2Ghv0j6SuneL33Uw123hfYC3gGsJoqcLgyIg6YZv4rGvZLknZR85iEEr6mJGnuGUhLQkSsA57Qxymfy8wXT/Xy9bbHMjs7y8w3jUu6ATgrIlYDZwBvBt4wxbz7yj8zj+p5gaqF4cg+8pSkOWV4e+/uRs5uJEndGFR3o1uppi+dqvWj/j3ZL/XLxx03XR+jChJOGJe+AXhUnf99E+T/4C7mL0lqYEuCJJVlIEFCZp6yC6ffBBwNrALG9OmPiAXAE4FtwG27kAfAPfV2jx75P6rOf8w0pxGxf338nZONR5AkTZ9jEiSpLCX8RHNlvT2tx74TgGXAdX3MbNTk2Ho7PtiYKP9njztGkjQDGmc3siVBkjpRQu27GrgXeFFEHD2SGBFLgHfVbz86+oSIWBYRh0XE48elHxkR41sKiIgjgHfXby8et/vTwBDw2nphtZFz9gb+rH77sT4/kySpD40tCU6BKkmd6HwK1Mx8MCJeQRUsXB0Rl1Ctfvx8qulJVwNfGHfaM4GrgGuAE0elvw44PSKuBO6g+uP/MKpWgvlUKzt/flz+t0fEfwUuBG6IiC8Aw1SLvB0I/JWrLUvSzBpuCBIWzbe7kSR1ofMgASAzL4uIZwFvpRpcvAT4EfBG4MLMnNLMRsBlVAONj6BaAG0J1WDky4GPZ+Y/NuT/wXqGpjcDf0jVwvID4G2Z+bfT/VySpKmxJUGSylJEkACQmdcCz5nisVfzyNSko9MvowoUppP/l4EvT+dcSdKuGdrqmARJKom1rySpc85uJEllMUiQJHWuOUjwa0qSumDtK0nq3Jam7kaOSZCkTlj7SpI6t2m4d5CwbJHdjSSpCwYJkqTONQUJSxcWM7+GJM0pBgmSpM5t3rqtZ7otCZLUDYMESVLn7G4kSWUxSJAkdW5zU3cjgwRJ6oRBgiSpc80tCY5JkKQuGCRIkjpndyNJKotBgiSpc5uHew9ctruRJHXDIEGS1KnMZFPDYmrLFhokSFIXDBIkSZ0a2raDzJ3TF82fx4L5fk1JUhesfSVJnWpcSM2uRpLUGYMESVKnNjWMR3DQsiR1xyBBktQp10iQpPIYJEiSOuX0p5JUHoMESVKnGoOEhS6kJkldMUiQJHVq81bXSJCk0hgkSJI6ZXcjSSqPQYIkqVNOgSpJ5TFIkCR1qml2I1sSJKk7BgmSpE41dzdy4LIkdcUgQZLUqc0Ni6ktXWhLgiR1xSBBktQpBy5LUnkMEiRJndq01SBBkkpjkCBJ6lTTwOWljkmQpM4YJEiSOrWpYUyCLQmS1B2DBElSp1wnQZLKY5AgSepU4zoJzm4kSZ0xSJAkdcp1EiSpPAYJkqRObW6Y3cjuRpLUHYMESVKnHLgsSeUxSJAkdcrF1CSpPMUECRFxXER8JSLuj4hNEfG9iHh9REz5WyIizouInOR167hzTpzk+PcO/tNKkkY0r5NgkCBJXSliVFhEvAC4FNgCfAG4H3ge8AHgeOCsKV7q6gn2PQ84Eri8Yf81Ded/c4p5S5L6NLxtB9t25E7p8+cFi+YX8zuWJM05nQcJEbEc+DiwHTgxM2+o098OXAmcGREvysxLJrtWZl5Njz/069aIP6rf/k3D6Vdn5nn9ll+SNH0TTX8aES2XRpI0ooSfac4E9gUuGQkQADJzC/C2+u2rdjGP5wAHAtdn5vd28VqSpAHZ2DBo2a5GktStzlsSgJPr7Vd77Ps6sAk4LiIWZ+bQNPP4L/W2qRUB4EkR8VpgOfBT4BuZecs085MkTcGDW7b2TN9rSQlfT5I0d5VQCx9ab28evyMzt0XE7cBTgIOBtf1ePCIOAJ4NbKAa79DkD+rX6HMvBV6Rmb+YYl5rGnYdNpXzJWmueWBT7yBhxdKFLZdEkjRaCd2NVtTbDQ37R9JXTvP6LwfmAxdn5qYe++8B3gI8FdiLquvTs4H/DZwBfDkiSrhPkrTb2bC5d5CwctmilksiSRptIC0JEbEOeEIfp3wuM1881cvX252nv5jsxOqP+5fVb3t2NcrM7wPfH5X0MPDViLgO+A7V7ErPA740WX6ZeVRDOdZQzawkSRqlKUiwJUGSujWo7ka3Uk1fOlXrR/17pKVgRa8DqcYIjD6uH88GHs80Bixn5oMR8XfAW4ETmEKQIEnqzwa7G0lSkQYSJGTmKbtw+k3A0cAqYEyf/ohYADwR2AbcNo1rjwxY/utplu2eervHNM+XJE3AlgRJKlMJfe2vrLen9dh3ArAMuK7fmY0i4rHA71C1QHxxmmU7tt5OJ0CRJE3igc3DPdMNEiSpWyUECauBe4EXRcTRI4kRsQR4V/32o6NPiIhlEXFYRDx+guv+EdWA5c82DFgeudbxvQYmR8SLgd8Hhpl+kCFJmsCGzb3XSVi5zCBBkrrU+RSodd//V1AFC1dHxCXA/cDzqaZHXc3OU5c+E7gKuAY4cfw16z/6J1thecTngHn1QOU7gSXAM+o8tgF/nJnr+v5gkqRJ2d1IksrUeZAAkJmXRcSzqAYJn0H1h/qPgDcCF2ZmvzMb/TbVbEvXZ+aNkxz7UeC3qGYxehTVbEp3ARcBF2Tmd/vMW5I0RRs22d1IkkpURJAAkJnXAs+Z4rFX88jUqL32Xz7R/nHHvg9431SOlSQNVvM6CQYJktSlEsYkSJLmqKYgYbktCZLUKYMESVInduxIxyRIUqEMEiRJnXh4eBs7eow4W7pwPosXzG+/QJKkXzJIkCR1wtWWJalcBgmSpE44aFmSymWQIEnqhIOWJalcBgmSpE480NDdaKVBgiR1ziBBktQJZzaSpHIZJEiSOvHAZldblqRSGSRIkjrhwGVJKpdBgiSpEw/a3UiSimWQIEnqxD0PDfVMX7lsUcslkSSNZ5AgSerEXQ9s6Zm+/4olLZdEkjSeQYIkqRN3b9jcM/2xK5e2XBJJ0ngGCZKk1m0a3tZznYR5AY/ea3EHJZIkjWaQIElq3fqGrkb7LV/Cgvl+NUlS16yJJUmtW/9A765G+9vVSJKKYJAgSWpd03gEBy1LUhkMEiRJrWvqbnSALQmSVASDBElS6xq7G9mSIElFMEiQJLXu7g29WxKc/lSSymCQIElqXVNLgkGCJJXBIEGS1KrMZL0LqUlS0QwSJEmt+sWmrWzZumOn9MUL5rH3soUdlEiSNJ5BgiSpVT/6+cM90x+7cikR0XJpJEm9GCRIklr1vTsf6Jn+5Efv2XJJJElNDBIkSa268a4NPdOPOHBFyyWRJDUxSJAktep7dzYFCStbLokkqYlBgiSpNRs2b+X2ezf23PfUA2xJkKRSGCRIklrz/YauRo/bZyl777Go5dJIkpoYJEiSWvO9xvEIdjWSpJIYJEiSWvONW+7pmX6EXY0kqSgGCZKkVtz1wGauu/W+nvue9jhbEiSpJAYJkqRW/MO37yRz5/RH7bmYo56wd/sFkiQ1MkiQJM24rdt3sHrNnT33vfDpj2XhfL+OJKkkndfKEbEwIs6NiE9HxHciYjgiMiJevgvXPC4ivhIR90fEpoj4XkS8PiLmT3DOcyPi6ojYEBEPR8S3IuIl0y2DJOkRH77qR6y7b1PPfWccdWDLpZEkTWZB1wUA9gAuqP/9M+CnwOOme7GIeAFwKbAF+AJwP/A84APA8cBZPc55LfBB4D7gYmAYOBO4KCKemplvnm55JGku27Ej+dy3fswF/+uWnvt/df/lHL7/8pZLJUmaTAlBwibgOcB3MvPuiDgPeMd0LhQRy4GPA9uBEzPzhjr97cCVwJkR8aLMvGTUOQcBf0kVTBydmevq9HcC/w68KSIuzcx/ndan68D6Bzbz0JZtA7lW0qMD8XSvNbhLDfZahX7GQZsL92yQtz8HWLDBlmuAFxtoyWBo2w42DW1n4/A27nloiFvv2cg3f3QPd9y/ufGcP/pPTxxoGSRJg9F5kJCZw8DlA7rcmcC+wGdGAoQ6jy0R8Tbga8CrgEtGnfMyYDHwvpEAoT7nFxHxHuCTwCuBWRMkvPuf1vJPN97ddTEkaUK/+eRHcfqRB3RdDElSD52PSRiwk+vtV3vs+zpVq8VxEbF4iudcPu4YSdIALF+ygP/3zCOIiK6LIknqofOWhAE7tN7ePH5HZm6LiNuBpwAHA2uncM7dEbERODAilmVm71F3tYhY07DrsKkUXpLmgj0XL+Cvzz6a/Vcs7bookqQGu1uQMLJk54aG/SPpo1ftmco5e9THTRgkSJImtuoxe/LRFx/FIfvu2XVRJEkTGEiQEBHrgCf0ccrnMvPFg8i7TyPt2v2M1pvyOZl5VM8LVC0MR/aRpyTtVn7tgOWcfewT+N1fP5BFC3a3nq6StPsZVEvCrVRTjk7V+gHlO95Ia8CKhv3Lxx038u9H1efcN8E5D+5y6Vqy34olrHrM4H6lCwbXZ7jU7seD7Bc96I84yHs20GsV+lwM9P4X+lwM+v/RoC63YN489lg8nz0WL2CvJQs5YOUSDt53T458/N7st2LJgHKRJLVhIEFCZp4yiOsMwE3A0cAqYMz4gIhYADwR2AbcNu6cR9Xn/Ou4c/an6mp052TjEUry9uf+atdFkCRJ0iy2u7X5XllvT+ux7wRgGXBdZg5N8ZxnjztGkiRJ2u3NyiAhIlZExGH1L/2jrQbuBV4UEUePOn4J8K767UfHnfNpYAh4bb2w2sg5ewN/Vr/92OBKL0mSJJWtiNmNIuItPDJN6NPr7TkR8Z/qf38zMz8x6pTfpfrj/m+Bl44kZuaDEfEKqmDh6oi4hGol5edTTXW6GvjC6Lwz8/aI+K/AhcANEfEFYJhqYbYDgb+aTastS5IkSbuqiCCBqqvPs8alHVe/RnyCKcjMyyLiWcBbgTOAJcCPgDcCF2bmTrMUZeYH6xma3gz8IVULyw+At2Xm3/b3USRJkqTZrYggITNP7PP4i4CLJth/LfCcPq/5ZeDL/ZwjSZIk7Y5m5ZgESZIkSTPHIEGSJEnSGAYJkiRJksYwSJAkSZI0hkGCJEmSpDEMEiRJkiSNYZAgSZIkaQyDBEmSJEljGCRIkiRJGsMgQZIkSdIYkZldl2G3FxH3LV26dJ/DDz+866JIkiRpN7Z27Vo2b958f2b+yq5cxyChBRFxO7AcWNdy1ofV2x+2nO9s5j3rj/erP96v/ni/+uc964/3qz/er/51cc8OAh7MzCfuykUMEnZjEbEGIDOP6ross4X3rD/er/54v/rj/eqf96w/3q/+eL/6N5vvmWMSJEmSJI1hkCBJkiRpDIMESZIkSWMYJEiSJEkawyBBkiRJ0hjObiRJkiRpDFsSJEmSJI1hkCBJkiRpDIMESZIkSWMYJEiSJEkawyBBkiRJ0hgGCZIkSZLGMEiQJEmSNIZBwm4mIp4cEX8aEVdGxB0RMRwRP4uIL0XESZOc+5KI+LeIeDgiNkTE1RHx3LbK3pWIWBgR50bEpyPiO/U9y4h4+QTnvLQ+pun1yjY/Q5umc79GnTsnn7EmEXHQJM/RJV2XsQsRcWBEfCoi1kfEUESsi4gLImLvrstWovr+ND1DP+26fF2IiDMj4oMR8Y2IeLC+FxdPcs5xEfGViLg/IjZFxPci4vURMb+tcnepn3s21+uuiPiViHh5RPxDRPwoIjbX32nfjIg/ioief1/PtmdsQdcF0MD9P8DvAz8AvgLcDxwKPB94fkScm5kXjj8pIv4SeBNwJ/BxYBHwIuDLEfEnmfmhlsrfhT2AC+p//wz4KfC4KZ77JeA7PdJvGEC5SjWt+zXHn7HJfBe4rEf6f7RdkK5FxCHAdcCjqf5//RB4JnAucFpEHJ+Z93VYxFJt4JH/l6M93HZBCvE24GlUn/9O4LCJDo6IFwCXAluAL1B9dz4P+ABwPHDWTBa2EH3ds9pcrbvOAj4K3A1cBfwEeAxwOvAJ4NkRcVaOWrF4Vj5jmelrN3oBLwV+vUf6s4BhYAjYf9y+44AEfgTsPSr9IOA+qgf6oK4/2wzes0XAs0fuC3BefT9ePsl9TuClXZd/ltyvOf2MTXBfDqrvy0Vdl6WUF/DP9T35k3Hp76/TP9Z1GUt7AeuAdV2Xo6QXcBLwZCCAE+tn5+KGY5cDP6+/H48elb6EKmBN4EVdf6bC7tmcrruAk6n+wJ83Ln0/qoAhgTNGpc/KZ8zuRruZzLwoM/93j/RrgKup/sA7btzuka4x787MX4w6Zx3wYWAxcM5MlLcEmTmcmZdn5t1dl2U2mOb9mtPPmKYmIg4GTqX6o/fD43a/A9gInB0Re7RcNM0ymXlVZt6S9V9ikzgT2Be4JDN/2QqcmVuofl0HeNUMFLMofd6zOS0zr8zML2fmjnHpPwU+Vr89cdSuWfmMGSTMLVvr7bZx6SfX26/2OOfyccdorKfX/QnfEhFnR8SBXReoUD5jE3tsRPxxRPxZvT2i6wJ1ZOQZuKLHl+9DwLXAMuDYtgs2CyyOiBfXz9C5EXFSqf2cCzRR/fR1YBNwXEQsbq9Is4Z11856/a01K58xxyTMERHxBOAUqgfx66PS9wAOAB5u+GX4lnq7asYLOTudO+799oj4BPD6+heCOc9nbEr+r/r1SxFxNfCSzPxJJyXqxqH19uaG/bdQtTSsAr7WSolmj/2Az45Luz0izqlbktWs8bnLzG0RcTvwFOBgYG2bBZsFrLtGiYgFwB/Wb0cHBLPyGbMlYQ6oI9PPUXXpOG90dw9gRb3d0HD6SPrKGSrebHU78CdU//H3AB4L/B5VN4k/Bj7VWcnK4zPWbBPVZANHAXvXr2dRDYQ7EfjaHOta47MyPZ+m+hFoP6r66KnAX1P1G788Ip7WXdFmBZ+7/ll39fZe4NeAr2TmP49Kn5XPmEFCgSaZzq7Xq3Fat7q5+bNUI+e/APzlNItVdB/FQd6zqcjMazLzQ5l5c2Zuysy7M/PvqQZ+/QL4v0v+Ym77fk1R0c9Yk125l5n588z888z8dmY+UL++TvVr+beAJwGTTi07h0S9nZXPykzJzPPrPtI/q+uj/8jMV1IN9l5KNbmAps/nbhzrrp1FxOuoZvD7IXB2v6fX26KeMbsblelWqtlepmp9r8Q6QLiYalqtLwIv7jEgaSR6XUFvk0W/pRjIPdtVmXlHRHwF+APgBKrp4UrU5v3aXZ6xJgO/l3Xz8yeAY6ieo/9vmmWbbSZ7VpaPO04T+xjVHy0ndF2QwvncDchcrbsi4jVUn/UHwCmZef+4Q2blM2aQUKDMPGVXr1H3i/s7qgDh74A/zMztPfLaGBF3AQdExP49+ow/ud429REuwiDu2QDdU2+LbWpt837tLs9Ykxm8l8U/RzPgpnrbND5lVj8rHfh5vZ1Lz9B03AQcTfXcrRm9o/4ufSLVINTb2i/arDSn6q6IeD3VWgf/QRUg/LzHYbPyGbO70W4oIhYBq6kChM8AZ/cKEEa5st6e1mPfs8cdo8kdU2+L+s/eMZ+x/o3M4DOXnqOr6u2p41csjYi9qLpNbgaub7tgs9Rv1Nu59AxNx0T10wlUM2pdl5lD7RVpVpszdVdE/ClVgPAd4KSGAAFm6TNmkLCbqQcp/wPwAuCTwDnjpxLsYWRO37dGxN6jrnUQ8BqqxT8+PfDCzmIR8Zs90iIi/jvVF/O99J7qbK7yGeshIo6pg/rx6ScDb6jftjEepAiZeStwBdWA29eM230+1S+Tn8nMjS0XrVgR8ZSI2KdH+hOAkVXM58wzNE2rqersF0XE0SOJEbEEeFf99qNdFKxU1l0QEW+nGqi8hqoF4d4JDp+Vz1i4ZsbuJSI+TbUa8L3AR+g9CObqzLx63Hl/BbyRain21VSLrv0+8CtUK59+aPxFdicR8RYeWYL+6VRL01/HI9NzfjMzPzHq+KTq8vDvwF1U/QyPp5rVYBPwu5l5RTulb1+/96s+Z04/Y73UUwU+hWqhwzvr5CN4ZE7tt2fmu3Y+c/cVEYdQPUuPBr5ENR3gMVSTAtwMHJeZ93VXwrJExHnAW6haYW4HHgIOAX6HajXXr1DVR8NdlbELEfFC4IX12/2A36b6Zfsbddq9mfnmccevphpfdAlwP/B8qhnsVgO/t7svMtbPPZvrdVdEvAS4CNgOfJDeYwnWZeZFo86Zfc/YIJZt9lXOi+o/bE7yOq/h3JdQ/dG7keqL5hrguV1/pkLu20Xjjv8f9f1ZT/UffhPVjAYfAg7u+vOUdr98xhrv4x8B/z/V1LkPU7Wo/IRqJrLf7Lp8Hd6Xx1G1LN0NDAM/phoUuE/XZSvtRTXt5Ofr+ucBqoWc7gH+hWq+9ui6jB3dl/MmqaPW9TjneKqg6hdU3dpupPpVfH7Xn6e0ezbX664p3Kuk+kF2Vj9jtiRIkiRJGsMxCZIkSZLGMEiQJEmSNIZBgiRJkqQxDBIkSZIkjWGQIEmSJGkMgwRJkiRJYxgkSJIkSRrDIEGSJEnSGAYJkiRJksYwSJAkSfo/7dexAAAAAMAgf+vdcyiLgJEEAABgJAEAABhJAAAARhIAAICRBAAAYCQBAAAYSQAAACZlSOBcrInF5AAAAABJRU5ErkJggg==\n",
          "text/plain": [
           "<Figure size 432x288 with 1 Axes>"
          ]
         },
         "metadata": {
          "image/png": {
           "height": 250,
           "width": 388
          },
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ],
       "source": [
        "sns.lineplot(pts, np.tanh(pts*np.pi)) ;"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "* **ReLU (Rectified linear unit)**\n",
    
        "\\begin{equation*}\n",
        "f(z) = \\mathrm{max}(0,z)\n",
        "\\end{equation*}"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": 9,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
       "outputs": [
        {
         "data": {
    
          "image/png": "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\n",
    
    chadhat's avatar
    chadhat committed
          "text/plain": [
    
           "<Figure size 432x288 with 1 Axes>"
    
    chadhat's avatar
    chadhat committed
          ]
         },
         "metadata": {
    
          "image/png": {
           "height": 250,
           "width": 380
          },
    
    chadhat's avatar
    chadhat committed
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ],
    
    chadhat's avatar
    chadhat committed
       "source": [
    
        "pts_relu=[max(0,i) for i in pts];\n",
        "plt.plot(pts, pts_relu) ;"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "are some of the commonly used as activation functions."
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "<div class=\"alert alert-block alert-warning\">\n",
        "<p><i class=\"fa fa-warning\"></i>&nbsp;\n",
        "ReLU is very popular and is widely used nowadays. There also exist other variations of ReLU, e.g. \"leaky ReLU\".\n",
        "</p>\n",
        "</div>"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "<div class=\"alert alert-block alert-info\">\n",
        "<p><i class=\"fa fa-warning\"></i>&nbsp;\n",
        "Why don't we just use a simple linear activation function?\n",
        "    \n",
        "Linear activations are **NOT** used because it can be mathematically shown that if they are used then the output is just a linear function of the input. So we cannot learn interesting and complex functions by adding any number of hidden layers.\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "The only exception when we do want to use a linear activation is for the output layer of a network when solving a regression problem.\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "</p>\n",
        "</div>\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "Non-linear activation functions allow the network to learn complex representations."
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "# Introduction to Keras"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "### What is Keras?\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
        "* It is a high level API to create and work with neural networks\n",
    
        "* Supports multiple backends such as TensorFlow from Google, Theano (Although Theano is dead now) and CNTK (Microsoft Cognitive Toolkit)\n",
        "* Very good for creating neural nets very quickly and hides away a lot of tedious work\n",
        "* Has been incorporated into official TensorFlow (which obviously only works with tensforflow) and as of TensorFlow 2.0 this will the main api to use TensorFlow (check reference)\n"
    
    chadhat's avatar
    chadhat committed
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
        "# Say hello to keras\n",
        "\n",
        "from keras.models import Sequential\n",
        "from keras.layers import Dense, Activation\n",
        "\n",
        "# Creating a model\n",
        "model = Sequential()\n",
        "\n",
        "# Adding layers to this model\n",
        "# 1st Hidden layer\n",
    
        "# A Dense/fully-connected layer which takes as input a \n",
        "# feature array of shape (samples, num_features)\n",
        "# Here input_shape = (8,) means that the layer expects an input with num_features = 8 \n",
        "# and the sample size could be anything\n",
        "# Then we specify an activation function\n",
        "model.add(Dense(units=4, input_shape=(8,)))\n",
    
    chadhat's avatar
    chadhat committed
        "model.add(Activation(\"relu\"))\n",
        "\n",
    
        "# 2nd Hidden layer\n",
        "# This is also a fully-connected layer and we do not need to specify the\n",
        "# shape of the input anymore (We need to do that only for the first layer)\n",
    
    chadhat's avatar
    chadhat committed
        "# NOTE: Now we didn't add the activation seperately. Instead we just added it\n",
    
        "# while calling Dense(). This and the way used for the first layer are Equivalent!\n",
        "model.add(Dense(units=4, activation=\"relu\"))\n",
        "\n",
        "          \n",
    
    chadhat's avatar
    chadhat committed
        "# The output layer\n",
        "model.add(Dense(units=1))\n",
        "model.add(Activation(\"sigmoid\"))\n",
        "\n",
        "model.summary()"
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "### XOR using neural networks"
       ]
      },
    
    chadhat's avatar
    chadhat committed
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
    
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "from sklearn.model_selection import train_test_split\n",
        "from keras.models import Sequential\n",
        "from keras.layers import Dense\n",
        "import numpy as np"
    
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
    chadhat's avatar
    chadhat committed
       "source": [
    
        "# Creating a network to solve the XOR problem\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "# Loading and plotting the data\n",
        "xor = pd.read_csv(\"xor.csv\")\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "# Using x and y coordinates as featues\n",
        "features = xor.iloc[:, :-1]\n",
        "# Convert boolean to integer values (True->1 and False->0)\n",
        "labels = xor.iloc[:, -1].astype(int)\n",
    
    chadhat's avatar
    chadhat committed
        "\n",
    
        "colors = [[\"steelblue\", \"chocolate\"][i] for i in xor[\"label\"]]\n",
        "plt.figure(figsize=(5, 5))\n",
        "plt.xlim([-2, 2])\n",
        "plt.ylim([-2, 2])\n",
        "plt.title(\"Blue points are False\")\n",
    
        "\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "plt.scatter(features[\"x\"], features[\"y\"], color=colors, marker=\"o\") ;"
    
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
       "metadata": {},
       "outputs": [],
       "source": [
    
        "# Building a Keras model\n",
    
        "def a_simple_NN():\n",
        "    \n",
        "    model = Sequential()\n",
    
        "    model.add(Dense(4, input_shape = (2,), activation = \"relu\"))\n",
        "\n",
        "    model.add(Dense(4, activation = \"relu\"))\n",
        "\n",
        "    model.add(Dense(1, activation = \"sigmoid\"))\n",
        "\n",
        "    model.compile(loss=\"binary_crossentropy\", optimizer=\"rmsprop\", metrics=[\"accuracy\"])\n",
        "    \n",
        "    return model"
    
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
       "source": [
    
    chadhat's avatar
    chadhat committed
        "# Instantiating the model\n",
    
        "model = a_simple_NN()\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "# Splitting the dataset into training (70%) and validation sets (30%)\n",
    
        "X_train, X_test, y_train, y_test = train_test_split(\n",
        "    features, labels, test_size=0.3)\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "# Setting the number of passes through the entire training set\n",
        "num_epochs = 300\n",
    
        "\n",
        "# We can pass validation data while training\n",
        "model_run = model.fit(X_train, y_train, epochs=num_epochs,\n",
        "                      validation_data=(X_test, y_test))"
    
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
    chadhat's avatar
    chadhat committed
       "metadata": {},
    
       "outputs": [],
    
       "source": [
    
    chadhat's avatar
    chadhat committed
        "# Looking at the loss and accuracy on the training and validation sets during the training\n",
        "# This can be done by using Keras callback \"history\" which is applied by default\n",
    
        "history_model = model_run.history\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "print(\"The history has the following data: \", history_model.keys())\n",
        "\n",
        "# Plotting the training and validation accuracy during the training\n",
        "plt.plot(np.arange(1, num_epochs+1), history_model[\"acc\"], \"blue\") ;\n",
    
    chadhat's avatar
    chadhat committed
        "plt.plot(np.arange(1, num_epochs+1), history_model[\"val_acc\"], \"red\") ;"
    
       "cell_type": "markdown",
    
       "metadata": {},
       "source": [
    
    chadhat's avatar
    chadhat committed
        "**Here we dont't really see a big difference between the training and validation data because the function we are trying to fit is quiet simple and there is not too much noise. We will come back to these curves in a later example**"
       ]
      },
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
        "In the example above we splitted our dataset into a 70-30 train-validation set. We know from previous chapters that to more robustly calculate accuracy we can use **K-fold crossvalidation**.\n",
    
        "This is even more important when we have small datasets and cannot afford to reserve a validation set!\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "One way to do the cross validation here would be to write our own function to do this. However, we also know that **SciKit learn** provides several handy functions to evaluate and tune the models. So the question is:\n",
    
    chadhat's avatar
    chadhat committed
        "Can we somehow use these **Scikit learn** functions or ones we wrote ourselves for **Scikit learn** models to evaluate and tune our Keras models?\n",
    
        "\n",
        "The Answer is **YES !**\n",
        "\n",
        "We show how to do this in the following section."
    
    chadhat's avatar
    chadhat committed
       ]
      },
    
      {
       "cell_type": "markdown",
       "metadata": {},
       "source": [
    
        "## Using SciKit learn functions on Keras models\n",
        "\n",
    
    chadhat's avatar
    chadhat committed
        "Keras offers 2 wrappers which allow its Sequential models to be used with SciKit learn. \n",
    
    chadhat's avatar
    chadhat committed
        "There are: **KerasClassifier** and **KerasRegressor**.\n",
    
        "\n",
        "For more information:\n",
        "https://keras.io/scikit-learn-api/\n",
        "\n",
        "**Now lets see how this works!**"
    
       ]
      },
      {
       "cell_type": "code",
    
       "execution_count": null,
    
       "metadata": {},
    
    chadhat's avatar
    chadhat committed
       "outputs": [],
    
       "source": [
    
        "# We wrap the Keras model we created above with KerasClassifier\n",
    
    chadhat's avatar
    chadhat committed
        "from keras.wrappers.scikit_learn import KerasClassifier\n",
    
        "from sklearn.model_selection import cross_val_score\n",
    
    chadhat's avatar
    chadhat committed
        "# Wrapping Keras model\n",
        "# NOTE: We pass verbose=0 to suppress the model output\n",
        "num_epochs = 400\n",
        "model_scikit = KerasClassifier(\n",
        "    build_fn=a_simple_NN, **{\"epochs\": num_epochs, \"verbose\": 0})"
    
       "cell_type": "code",
    
       "execution_count": null,
    
       "metadata": {},
    
       "outputs": [],
    
       "source": [
    
    chadhat's avatar
    chadhat committed
        "# Let's reuse the function to visualize the decision boundary which we saw in chapter 2 with minimal change\n",
        "\n",
    
        "def list_flatten(list_of_list):\n",
        "    flattened_list = [i for j in list_of_list for i in j]\n",
        "    return flattened_list\n",
    
    chadhat's avatar
    chadhat committed
        "def plot_points(plt=plt, marker='o'):\n",
        "    colors = [[\"steelblue\", \"chocolate\"][i] for i in labels]\n",
        "    plt.scatter(features.iloc[:, 0], features.iloc[:, 1], color=colors, marker=marker);\n",
        "\n",
    
        "def train_and_plot_decision_surface(\n",
        "    name, classifier, features_2d, labels, preproc=None, plt=plt, marker='o', N=400\n",
        "):\n",
    
        "    features_2d = np.array(features_2d)\n",