{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "34cd05b0-a7a9-4053-853c-2bc7f43ec0af",
   "metadata": {},
   "source": [
    "# Notebook 8: Binary Classifier Exercise [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mattsankner/micrograd/blob/main/mg8_binary_classifer.ipynb) [![View in nbviewer](https://img.shields.io/badge/view-nbviewer-orange)](https://nbviewer.jupyter.org/github/mattsankner/micrograd/blob/main/mg8_binary_classifier.ipynb)\n",
    "\n",
    "## Let's build a binary classifier with our micrograd engine! This will be able to tell whether random points on a graph are red or blue.\n",
    "\n",
    "Below, I take Andrej's example and expound on it with my notes about the code. You won't understand all of it just from the series of lectures, but it runs on what we already went through, although the code is slightly different, you should be able to understand what is going on, especially if you test with different outputs along the way."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a13b84ac-cf6a-48da-b67d-a36f0bcdde58",
   "metadata": {},
   "outputs": [],
   "source": [
    "# This code is adapted from [Andrei Karpathy]\n",
    "# Copyright (c) [2020] [Andrei Karpathy]\n",
    "\n",
    "import random\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plot\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0ccaba28-e1c0-4921-a800-cf7ca415cf10",
   "metadata": {},
   "outputs": [],
   "source": [
    "#import the micrograd we made\n",
    "from micrograd.engine import Value \n",
    "from micrograd.mlp import Neuron, Layer, MLP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "ee213fe0-e4b6-4247-81f1-359dd8d26483",
   "metadata": {},
   "outputs": [],
   "source": [
    "#set random seeds for reproducibility. Numpy and random both have seeds set.\n",
    "#every time the code is run, it will generate the same random numbers, \n",
    "#which means that the dataset and any other random operations produce the same results\n",
    "np.random.seed(1337)\n",
    "random.seed(1337)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "668fa78c-f60a-4b75-9b03-9631db52c113",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x12ab56ed0>"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 500x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.datasets import make_moons, make_blobs\n",
    "\n",
    "# create dataset using 'make_moons' from sklearn\n",
    "# generates synthetic dataset with two interleaving half circles shape\n",
    "X, y = make_moons(n_samples=100, noise=0.1) #adds gaussian noise to the data\n",
    "\n",
    "# make y axis to be -1 to 1 instead of 0 to 1 (default)\n",
    "y = y * 2 - 1 \n",
    "\n",
    "# print dataset in 2D\n",
    "plot.figure(figsize=(5,5)) #figure width/height in inches\n",
    "\n",
    "# creates scatter plot of data with x and y coordinates. c=y sets color based on labels; s is size of the points, cmap=jet is colormap\n",
    "plot.scatter(X[:,0], X[:,1], c=y, s=20, cmap='jet')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87f068df-c06e-4dc7-aaa6-25684e84479e",
   "metadata": {},
   "source": [
    "### ReLU"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a64da87-28c4-4417-91f7-92e0deb5454a",
   "metadata": {},
   "source": [
    "Below, we initialize the neural network, which will be made with the micrograd I created in this repository. It is made with a ```ReLU``` function instead of a ```tanh()``` activation function, as Andrei coded his this way in his final product. \n",
    "\n",
    "Recall from the notebooks that without an activation functions to introduce non-linearity into the network, the network would not be able to learn complex patterns and functions; it would simply be equivalent to a single-layer lienar model, which would limit its modeling power. Activation functions enable stacking of multiple layers, where each layer learns different levels of abstraction. Different activation functions can control the range of the output values. \n",
    "\n",
    "After some research, here is the theory behind why we implemented ```ReLU()``` in micrograd and are using it here:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "373e1584-ec70-4973-8795-4552ff60f3ef",
   "metadata": {},
   "source": [
    "### ```tanh(): Hyperbolic Tangent```\n",
    "\n",
    "$$tanh(x)={\\frac {\\sinh x}{\\cosh x}}={\\frac {e^{x}-e^{-x}}{e^{x}+e^{-x}}}={\\frac {e^{2x}-1}{e^{2x}+1}}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40920a02-9b7c-4b47-a7c4-5c4f1e68a676",
   "metadata": {},
   "source": [
    "### ```ReLU(): Rectified Linear Unit```\n",
    "\n",
    "$$ReLU(x)={max(0, x)}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "daadd844-9230-4a1b-96cc-2797ff93898b",
   "metadata": {},
   "source": [
    "- While ```tanh()``` outputs between -1 and 1, ```ReLU``` outputs $0$ for any negative input and the input itself for any positive input\n",
    "\n",
    "- In the backward pass, ```tanh()```'s derivativce is $1-tanh(x)^2$, which can lead to small gradients for large values of $x$, potentially causing ```vanishing gradients```. The ```ReLU```'s derivative is $1$ for positive outputs and $0$ for negative inputs, making gradient calculation straightforward.\n",
    "\n",
    "- ```vanishing gradients```: when the gradients of the loss function with respect to the model parameters become very small, effectively approaching zero. This can severely slow down or even stop the training process becasue the updates to the model parameters become insignificant.\n",
    "  \n",
    "- For ```tanh()```, when the input values are are very large or very small, ```tanh(x)``` saturates to $-1$ or $1$, making the gradient close to $0$. Since these gradients get mutliplied through each layer, it results in exponentially smaller gradients continually, which makes it tough for the model to learn.\n",
    "\n",
    "- For ```ReLU()```, it doesn't squash the gradients into a small range, so it doesn't have this problem. It suffers from a different problem, however, known as the ```dying ReLU``` porblem, where neurons get stuck with a gradient of $0$ if they always output $0$.\n",
    "\n",
    "- Note: another common activation function is ```sigmoid(x)```, which outputs values between $0$ and $1$, and is useful for binary classification, although they can also suffer from the vanishing gradient problem. Its function is \n",
    "$${\\frac {1}{1 + {e^{-x}}}}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "6b517b85-43be-427d-b8a4-9f7e5c64c0c9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "parameter count:  337\n"
     ]
    }
   ],
   "source": [
    "model = MLP(2, [16, 16, 1]) # 2-layer neural network: 2 inputs, 2 hidden layers with 16 \n",
    "\n",
    "print(\"parameter count: \", len(model.parameters()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc916bfb-ea8d-4ff2-8ab9-b631f23bf759",
   "metadata": {},
   "source": [
    "## Observe the loss function below. Use comments and explanations below the code block to aod your understanding."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "76595edd-91ca-4d47-875d-86a9cfe6fbc9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total loss:  0.8958441028683222 , Accuracy:  50.0 %\n"
     ]
    }
   ],
   "source": [
    "# loss function\n",
    "def loss(batch_size=None): #compare predictions to labels. Xb = data; yb = labels\n",
    "    \n",
    "    #batch selection\n",
    "    if batch_size is None:\n",
    "        Xb, yb = X, y # Use the entire dataset\n",
    "    else:\n",
    "        ri = np.random.permutation(X.shape[0])[:batch_size] # randomly select a batch of batch_size, if specified\n",
    "        Xb, yb = X[ri], y[ri] #randomly select a subset of the data\n",
    "        \n",
    "    inputs = [list(map(Value, xrow)) for xrow in Xb] # Convert input rows to Value objects for automatic differentiation\n",
    "    \n",
    "    #passing the inputs through the model to compute the scores (predictions)\n",
    "    #map function applies a given function (model; an mlp instance) to each item of an iterable(inputs; list of input vectors) and returns an iterator (object)\n",
    "    scores = list(map(model, inputs))\n",
    "    \n",
    "    # calculates \"max-margin\" loss for each prediction and applies ReLU(). \n",
    "    #for each y and score for the given x input to mlp, calculate loss\n",
    "    losses = [(1 + -yi*scorei).relu() for yi, scorei in zip(yb, scores)]\n",
    "    data_loss = sum(losses) * (1.0 / len(losses)) #dataloss = average of these individual losses\n",
    "    \n",
    "    #adds an L2 regularization (weight decay) term to the loss to prevent overfitting\n",
    "    alpha = 1e-4\n",
    "    reg_loss = alpha * sum((p*p for p in model.parameters())) #regularization term: alpha * sum of p^2 for all params p in model\n",
    "    total_loss = data_loss + reg_loss #total loss = sum of data loss and regularization loss\n",
    "    \n",
    "    #calculate accuracy... measures proportion of correct predictions, determines if predictions are correct\n",
    "    #checks if sign of true label yi and predictions scorei are both positive -> then the prediction is correct\n",
    "    #results in a list of boolean values for correct and incorrect predictions inside accuracy\n",
    "    accuracy = [(yi > 0) == (scorei.data > 0) for yi, scorei in zip(yb, scores)] \n",
    "\n",
    "    #sum(accuracy) = number of true values in accuracy list (num of correct predictions)\n",
    "    #len(accuracy) = total number of predictions\n",
    "    #sum/len = accuracy as a fraction of correct predictions\n",
    "    return total_loss, sum(accuracy) / len(accuracy) #return loss and accuracy\n",
    "\n",
    "total_loss, acc = loss() #compute total loss and accuracy for current model state\n",
    "print(\"Total loss: \", total_loss.data, \", Accuracy: \", acc*100, \"%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e41bd0cb-a9f4-4bb6-83b6-8d6548a98375",
   "metadata": {},
   "source": [
    "As you can see, the initial loss is high accuracy is low."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35f97082-5027-443e-b8fd-11e5388c2814",
   "metadata": {},
   "source": [
    "## Explanation:\n",
    "This is the code that gives us the losses using ```ReLU``` for each pair of ```yi``` and ```scorei```:\n",
    "``` python\n",
    "losses = [(1 + -yi*scorei).relu() for yi, scorei in zip(yb, scores)]\n",
    "```\n",
    "You could write its main function as:\n",
    "```python\n",
    "loss = ReLU(1 + -y[i] * score[i]\n",
    "```\n",
    "\n",
    "### ReLU():\n",
    "```ReLU(x) = max(0,x)```\n",
    "- it outputs x if x is positive, and 0 if x is negative. This non-linearity allows the net to learn complex functions.\n",
    "\n",
    "\n",
    "### Hinge Loss:\n",
    "The hinge loss function is commonly used for ```max-margin``` classification, such as in support vector machines(svm's). The hinge loss for a single prediction is defined as:\n",
    "\n",
    "```loss = max(0,1 - y[i] * score[i]```\n",
    "\n",
    "Here, ```y[i]``` is the true label, either $1$ or $-1$, and ```score[i]``` is the predicted score. The hinge loss penalizes the predictions if it does not exceed the margin (set to $1$ here) in the correct direction. \n",
    "\n",
    "### Margin-Based Loss:\n",
    "In the code, the ```ReLU``` function is used as a part of caluclating the hinge loss to get the ```margin-based loss```. The ```ReLU``` is taking the ```max()``` of $0$ and ```1 - y[i] * score[i]```. \n",
    "\n",
    "This ensures that the hinge loss will be $0$ or $>0$, because it's taking the ```max()``` of $0$ and the ```hinge loss value```. \n",
    "\n",
    "The hinge loss penalizes predictions that are correct but not confident enough (within the margin) or incorrect. If the product of ```y[i] * score[i] < 1```, the prediction is either incorrect or not within a safe margin. For ```y[i]>0```, the score should be greater than $1$, \n",
    "and for ```y[i]<0```, the score should be less than $-1$. \n",
    "\n",
    "### True Labels ($y[i]$)):\n",
    "\n",
    "For binary classification, the true labels are typically either $+1$ or $-1$. In this example, $( yb[0] = 1 ) and ( yb[1] = -1 )$.\n",
    "\n",
    "### Predicted Scores ($score[i]$)):\n",
    "\n",
    "These are the outputs of the model. In the example, we have $score[0] = 0.8$ and $score[1] = -0.6$.\n",
    "\n",
    "Adding $1$ to this product shifts the hinge loss so that it is zero or positive:\n",
    "\n",
    "- For correctly classified examples with a sufficient margin,$1- -y[i] * score[i]$ will be less than $0$, and ReLU will make it $0$.\n",
    "- For incorrectly classified examples or those within the margin, $1 - -y[i] * score[i]$ will be positive, and ReLU will keep the positive value.\n",
    "\n",
    "\n",
    "### Examples:\n",
    "\n",
    "Prediction is correct but not confident enough (within margin), resulting in small loss.\n",
    "- ```y[i] = 1; score[i] = 0.8``` -> $1 - y[i] * score[i] = 1 - 1 * 0.8 = 0.2$   -> ```ReLU(0.2) = 0.2)```\n",
    "  \n",
    "Prediction is incorrect, resulting in a significant loss:\n",
    "- ```y[i] = 1; score[i] = -0.5``` -> $1 - y[i] * score[i] = 1 - 1 * (-0.5) = 1 + 0.5 = 1.5$   ->   ```ReLU(1.5) = 1.5```\n",
    "\n",
    "Prediction is incorrect:\n",
    "- ```y[i] = -1; score[i] = 0.3```  -> $1 - y[i] * score[i] = 1- (-1) * 0.3 = 1 + 0.3 = 1.3$    -> ```ReLU(1.3) = 1.3```\n",
    "\n",
    "Prediction is correct but not condient enough:\n",
    "- ```y[i] = -1; score[i] = 0.7```   -> $1 -y[i] * score[i] = 1 - (-1) * (-0.7) = 1 - 0.7 = 0.3$   ->   ```ReLU(0.3) = 0.3```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c72e8ce-17de-4e34-8e47-99f1dfe75c72",
   "metadata": {},
   "source": [
    "### L2 Regularization:\n",
    "\n",
    "```python\n",
    "alpha = 1e-4 #a hyperparameter; the regularization strength set to a small value\n",
    "reg_loss = alpha * sum((p*p for p in model.parameters())) #scales the sum of squared parameters by the regularization strength\n",
    "total_loss = data_loss + reg_loss \n",
    "#total loss = sum of data loss(loss computed from predictions and true labels using hinge loss and relu) \n",
    "#reg_loss = regularization loss (l2)\n",
    "```\n",
    "\n",
    "Penalizes large weights in the model to prevent overfitting. This encourages the model to keep the weigths smaller, which often leads to better generalization. \n",
    "\n",
    "Combining the two losses ```data_loss``` and ```reg_loss``` gives the total loss used for backpropogation. This ensures that the optimization process considers both the fit to the data and complexity of the model through regularization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b038d110-2424-4e15-9715-09684428c6b1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step 1 loss 0.8958441028683222, accuracy 50.0%\n",
      "step 2 loss 1.723590533697202, accuracy 81.0%\n",
      "step 3 loss 0.7429006313851131, accuracy 77.0%\n",
      "step 4 loss 0.7705641260584201, accuracy 82.0%\n",
      "step 5 loss 0.3692793385976537, accuracy 84.0%\n",
      "step 6 loss 0.313545481918522, accuracy 86.0%\n",
      "step 7 loss 0.28142343497724337, accuracy 89.0%\n",
      "step 8 loss 0.26888733313983904, accuracy 91.0%\n",
      "step 9 loss 0.2567147286057417, accuracy 91.0%\n",
      "step 10 loss 0.27048625516379227, accuracy 91.0%\n",
      "step 11 loss 0.24507023853658041, accuracy 91.0%\n",
      "step 12 loss 0.25099055297915024, accuracy 92.0%\n",
      "step 13 loss 0.21560951851922952, accuracy 91.0%\n",
      "step 14 loss 0.2309037844640272, accuracy 93.0%\n",
      "step 15 loss 0.20152151227899456, accuracy 92.0%\n",
      "step 16 loss 0.2257450627928221, accuracy 93.0%\n",
      "step 17 loss 0.19447987596204114, accuracy 92.0%\n",
      "step 18 loss 0.21089496199246363, accuracy 93.0%\n",
      "step 19 loss 0.15983077356303607, accuracy 94.0%\n",
      "step 20 loss 0.1845374874688391, accuracy 93.0%\n",
      "step 21 loss 0.18977522856087642, accuracy 91.0%\n",
      "step 22 loss 0.19072704042579647, accuracy 93.0%\n",
      "step 23 loss 0.11733695088756486, accuracy 97.0%\n",
      "step 24 loss 0.12173524408232453, accuracy 95.0%\n",
      "step 25 loss 0.12615712612770455, accuracy 95.0%\n",
      "step 26 loss 0.1604909778080166, accuracy 95.0%\n",
      "step 27 loss 0.18747197705245813, accuracy 92.0%\n",
      "step 28 loss 0.16741837891059413, accuracy 95.0%\n",
      "step 29 loss 0.0958658349145541, accuracy 97.0%\n",
      "step 30 loss 0.08778783707420913, accuracy 96.0%\n",
      "step 31 loss 0.11731297569011852, accuracy 95.0%\n",
      "step 32 loss 0.09340146460619832, accuracy 97.0%\n",
      "step 33 loss 0.12454454903103455, accuracy 95.0%\n",
      "step 34 loss 0.07984002652777279, accuracy 97.0%\n",
      "step 35 loss 0.07727519232921685, accuracy 97.0%\n",
      "step 36 loss 0.07661250143094467, accuracy 98.0%\n",
      "step 37 loss 0.10610492379198366, accuracy 96.0%\n",
      "step 38 loss 0.09062808429265971, accuracy 99.0%\n",
      "step 39 loss 0.10671887043036933, accuracy 95.0%\n",
      "step 40 loss 0.052256599219758566, accuracy 98.0%\n",
      "step 41 loss 0.060160098952344594, accuracy 100.0%\n",
      "step 42 loss 0.08596724533333948, accuracy 96.0%\n",
      "step 43 loss 0.051121079431796, accuracy 99.0%\n",
      "step 44 loss 0.05240142401642842, accuracy 97.0%\n",
      "step 45 loss 0.04530684179001555, accuracy 100.0%\n",
      "step 46 loss 0.07211073370655112, accuracy 97.0%\n",
      "step 47 loss 0.03334238651310243, accuracy 99.0%\n",
      "step 48 loss 0.03143222795751121, accuracy 100.0%\n",
      "step 49 loss 0.036585367471115224, accuracy 99.0%\n",
      "step 50 loss 0.048291393823902774, accuracy 99.0%\n",
      "step 51 loss 0.09875114765619629, accuracy 96.0%\n",
      "step 52 loss 0.05449063965875469, accuracy 99.0%\n",
      "step 53 loss 0.033926794357082984, accuracy 100.0%\n",
      "step 54 loss 0.0526151726356845, accuracy 97.0%\n",
      "step 55 loss 0.03250295251424938, accuracy 99.0%\n",
      "step 56 loss 0.028883273872078112, accuracy 100.0%\n",
      "step 57 loss 0.041391511040272486, accuracy 98.0%\n",
      "step 58 loss 0.0189874074261285, accuracy 100.0%\n",
      "step 59 loss 0.02523833523883749, accuracy 100.0%\n",
      "step 60 loss 0.02079656521341884, accuracy 100.0%\n",
      "step 61 loss 0.03259711157810239, accuracy 99.0%\n",
      "step 62 loss 0.017863351693480307, accuracy 100.0%\n",
      "step 63 loss 0.023008717832211728, accuracy 100.0%\n",
      "step 64 loss 0.02207932546358146, accuracy 100.0%\n",
      "step 65 loss 0.029432917853529764, accuracy 99.0%\n",
      "step 66 loss 0.016251514644091865, accuracy 100.0%\n",
      "step 67 loss 0.028468534483264557, accuracy 99.0%\n",
      "step 68 loss 0.01399436554620875, accuracy 100.0%\n",
      "step 69 loss 0.015552344843651311, accuracy 100.0%\n",
      "step 70 loss 0.0338911994616018, accuracy 99.0%\n",
      "step 71 loss 0.01422987006592695, accuracy 100.0%\n",
      "step 72 loss 0.013255281583285525, accuracy 100.0%\n",
      "step 73 loss 0.012300277590022099, accuracy 100.0%\n",
      "step 74 loss 0.01267605249835594, accuracy 100.0%\n",
      "step 75 loss 0.02059381195595479, accuracy 100.0%\n",
      "step 76 loss 0.011845398205364475, accuracy 100.0%\n",
      "step 77 loss 0.016012697472883017, accuracy 100.0%\n",
      "step 78 loss 0.02545836023922218, accuracy 100.0%\n",
      "step 79 loss 0.014382930289661939, accuracy 100.0%\n",
      "step 80 loss 0.011698962425818023, accuracy 100.0%\n",
      "step 81 loss 0.012318500800515733, accuracy 100.0%\n",
      "step 82 loss 0.014121117031464264, accuracy 100.0%\n",
      "step 83 loss 0.011664591962446257, accuracy 100.0%\n",
      "step 84 loss 0.011589314549188712, accuracy 100.0%\n",
      "step 85 loss 0.010990299347735225, accuracy 100.0%\n",
      "step 86 loss 0.01098922672069161, accuracy 100.0%\n",
      "step 87 loss 0.010988193757655071, accuracy 100.0%\n",
      "step 88 loss 0.010987200447388702, accuracy 100.0%\n",
      "step 89 loss 0.010986246779084923, accuracy 100.0%\n",
      "step 90 loss 0.010985332742365272, accuracy 100.0%\n",
      "step 91 loss 0.010984458327280172, accuracy 100.0%\n",
      "step 92 loss 0.010983623524308863, accuracy 100.0%\n",
      "step 93 loss 0.010982828324359071, accuracy 100.0%\n",
      "step 94 loss 0.010982072718767003, accuracy 100.0%\n",
      "step 95 loss 0.010981356699297043, accuracy 100.0%\n",
      "step 96 loss 0.010980680258141725, accuracy 100.0%\n",
      "step 97 loss 0.010980043387921506, accuracy 100.0%\n",
      "step 98 loss 0.010979446081684675, accuracy 100.0%\n",
      "step 99 loss 0.010978888332907227, accuracy 100.0%\n",
      "step 100 loss 0.010978370135492717, accuracy 100.0%\n"
     ]
    }
   ],
   "source": [
    "# optimization\n",
    "for k in range(100):\n",
    "    \n",
    "    # forward, compute total loss and accuracy\n",
    "    total_loss, acc = loss()\n",
    "    \n",
    "    # backward\n",
    "    model.zero_grad() #reset grads\n",
    "    total_loss.backward() \n",
    "    \n",
    "    # update params of model (sgd)\n",
    "    learning_rate = 1.0 - 0.9*k/100 #learning rate goes from 1.0 - .9 *0/100 -> 1.0 - .9 * 99/100 = 1.0 -> .109\n",
    "    for p in model.parameters():\n",
    "        p.data -= learning_rate * p.grad #updates data of each p by subtracting product of learning rate and gradient\n",
    "        #moves p in direction of reduced loss\n",
    "    \n",
    "    if k % 1 == 0:\n",
    "        print(f\"step {k + 1} loss {total_loss.data}, accuracy {acc*100}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66ea69ba-33c4-4b54-82b8-be5e9b27e01a",
   "metadata": {},
   "source": [
    "The model improves significantly, achieving $100%$ ```accuracy``` with very low ```loss.```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3044c64-2cf9-4dad-a825-9d802607688c",
   "metadata": {},
   "source": [
    "### Below we visualize the decision boundary of the trained model. \n",
    "\n",
    "It creates a grid of points over the input space, predicts the class for each point using the model, and then plots the decision boundary along with the original data points. \n",
    "\n",
    "The ```h``` parameter controls the resolution of the grid, and the use of ```X[:, 0]``` and ```X[:, 1]``` extracts the ```x``` and ```y``` coordinates of the data points. The mesh grid creation and prediction steps ensure that the decision boundary is accurately plotted based on the model's predictions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "312e23f3-a082-4324-9a3f-decb40ac2260",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-1.548639298268643, 1.951360701731357)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGdCAYAAADaPpOnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABmaklEQVR4nO3dd3hc5Z02/vuc6SNpRr0XS7bcG+42zQYHYwgJKQTSKCGQ5AdpZt8szpsNIWX9ZpNsSAgJIQUCGxLCEkogMcW4gG1s3DBuslVsyZJGXRpNL+f8/hgVC02Vpx3p/lzXXODRmZlHmnLuecr3EWRZlkFERESkEGKqG0BEREQUC4YXIiIiUhSGFyIiIlIUhhciIiJSFIYXIiIiUhSGFyIiIlIUhhciIiJSFIYXIiIiUhR1qhsQb5Ikoa2tDVlZWRAEIdXNISIioijIsozBwUGUlpZCFMP3rUy68NLW1oaKiopUN4OIiIgmoKWlBeXl5WGPmXThJSsrCwDQ9IsvIcugS3FriIiIKBqDTjeqv/7bkfN4OJMuvAwPFWUZdDAZGV6IiIiUJJopH5ywS0RERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIrC8EJERESKwvBCREREisLwQkRERIqS0PCya9cu3HDDDSgtLYUgCHjhhRfCHr9jxw4IgjDuYrFYEtlMIiIiUpCEhhe73Y5FixbhkUceiel2dXV1aG9vH7kUFhYmqIVERESkNOpE3vnGjRuxcePGmG9XWFiI7Ozs+DeIiIiIFC8t57wsXrwYJSUl+NCHPoTdu3eHPdbtdsNqtY65EBER0eSVVuGlpKQEjz76KJ577jk899xzqKiowNq1a3Ho0KGQt9myZQvMZvPIpaKiIoktJiIiomQTZFmW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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# visualize decision boundary\n",
    "\n",
    "h = 0.25 #step size for mesh grid. Defines resolution of grid for plotting decision boundary. Smaller vals = finer grid\n",
    "\n",
    "#x_min, x_max; y_min, y_max are the minimum and maximum values of the x and y-coordinates of the data points, \n",
    "#extended by 1 unit on each side for better visualization.\n",
    "x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1\n",
    "y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1\n",
    "\n",
    "#np.arrange() generates  values from x_min to x_max and y_min to y_max with a step size of h.\n",
    "#np.meshgrid takes these ranges and creates two 2D arrays xx and yy representing the grid coordinates. \n",
    "#xx contains the x-coordinates and yy contains the y-coordinates of the grid point\n",
    "xx, yy = np.meshgrid(np.arange(x_min, x_max, h),\n",
    "                     np.arange(y_min, y_max, h))\n",
    "\n",
    "#xx.ravel() and yy.ravel() flatten the 2D arrays xx and yy into 1D arrays\n",
    "#np.c_[...] combines these 1D arrays column-wise to form a 2D array Xmesh where each row is a point on the grid.\n",
    "Xmesh = np.c_[xx.ravel(), yy.ravel()]\n",
    "\n",
    "#each row of Xmesh is converted into a list of Value objects (used for automatic differentiation by the micrograd library)\n",
    "#this step is necessary for the model to process the inputs and make predictions\n",
    "inputs = [list(map(Value, xrow)) for xrow in Xmesh]\n",
    "\n",
    "#map(model, inputs) applies the model to each point in inputs, producing a list of scores (predictions)\n",
    "scores = list(map(model, inputs))\n",
    "\n",
    "#s.data > 0 checks if the score is positive, indicating that the model predicts the positive class for that point\n",
    "#np.array([...]) converts the list of boolean values into a NumPy array.\n",
    "Z = np.array([s.data > 0 for s in scores])\n",
    "\n",
    "#reshapes this 1D array back into the 2D shape of the grid, matching xx and yy.\n",
    "Z = Z.reshape(xx.shape)\n",
    "\n",
    "fig = plot.figure() #new figure for plotting\n",
    "plot.contourf(xx, yy, Z, cmap=plot.cm.Spectral, alpha=0.8) #plot decision boundary\n",
    "plot.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plot.cm.Spectral) #plot original data points\n",
    "plot.xlim(xx.min(), xx.max()) #sets the limits of the x-axis to the range of the grid\n",
    "plot.ylim(yy.min(), yy.max()) #sets the limits of the y-axis to the range of the grid"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c0f29f3-6750-4cc4-a219-b6751d9723d0",
   "metadata": {},
   "source": [
    "### Above, our neural network has  learned to separate the two classes well, as indicated by the clear boundary between the orange and green regions. The red and blue points correspond to the two classes, and they are on the correct side of the boundary.\n",
    "\n",
    "# Summary:\n",
    "\n",
    "- The model starts with an initial ```accuracy = 50%``` and ```loss = 0.896```.\n",
    "- Through 100 iterations of training with Stochastic Gradient Descent, the model improves significantly, achieving final ```accuracy = 100%``` and a very low ```loss=0.011```.\n",
    "- The decision boundary visualization confirms that the model has learned to separate the two classes effectively\n",
    "- This process demonstrates the effectiveness of the neural netwrok in learning a non-linear decision boundary to classify the ```make_moons``` dataset accurately."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}