From 58dab060dca87b44440ef7623a313a200897f62f Mon Sep 17 00:00:00 2001
From: HeberArteagaJ <heberarteagajimenez@gmail.com>
Date: Thu, 21 Nov 2024 10:39:43 +0100
Subject: [PATCH] Exercise 1

---
 TD2 Deep Learning.ipynb | 842 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 813 insertions(+), 29 deletions(-)

diff --git a/TD2 Deep Learning.ipynb b/TD2 Deep Learning.ipynb
index 00e4fdc..68483a7 100644
--- a/TD2 Deep Learning.ipynb	
+++ b/TD2 Deep Learning.ipynb	
@@ -33,10 +33,54 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "id": "330a42f5",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Requirement already satisfied: torch in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (2.2.0)\n",
+      "Collecting torchvision\n",
+      "  Downloading torchvision-0.20.1-cp311-cp311-macosx_11_0_arm64.whl.metadata (6.1 kB)\n",
+      "Requirement already satisfied: filelock in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torch) (3.13.1)\n",
+      "Requirement already satisfied: typing-extensions>=4.8.0 in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torch) (4.9.0)\n",
+      "Requirement already satisfied: sympy in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torch) (1.12)\n",
+      "Requirement already satisfied: networkx in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torch) (3.2.1)\n",
+      "Requirement already satisfied: jinja2 in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torch) (3.1.3)\n",
+      "Requirement already satisfied: fsspec in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torch) (2024.2.0)\n",
+      "Requirement already satisfied: numpy in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torchvision) (1.26.3)\n",
+      "Collecting torch\n",
+      "  Downloading torch-2.5.1-cp311-none-macosx_11_0_arm64.whl.metadata (28 kB)\n",
+      "Requirement already satisfied: pillow!=8.3.*,>=5.3.0 in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from torchvision) (10.2.0)\n",
+      "Collecting sympy==1.13.1 (from torch)\n",
+      "  Downloading sympy-1.13.1-py3-none-any.whl.metadata (12 kB)\n",
+      "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from sympy==1.13.1->torch) (1.3.0)\n",
+      "Requirement already satisfied: MarkupSafe>=2.0 in /Users/heber/.pyenv/versions/3.11.7/lib/python3.11/site-packages (from jinja2->torch) (2.1.5)\n",
+      "Downloading torchvision-0.20.1-cp311-cp311-macosx_11_0_arm64.whl (1.8 MB)\n",
+      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.8/1.8 MB\u001b[0m \u001b[31m827.0 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n",
+      "\u001b[?25hDownloading torch-2.5.1-cp311-none-macosx_11_0_arm64.whl (63.9 MB)\n",
+      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m63.9/63.9 MB\u001b[0m \u001b[31m4.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0mm\n",
+      "\u001b[?25hDownloading sympy-1.13.1-py3-none-any.whl (6.2 MB)\n",
+      "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.2/6.2 MB\u001b[0m \u001b[31m4.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0mm\n",
+      "\u001b[?25hInstalling collected packages: sympy, torch, torchvision\n",
+      "  Attempting uninstall: sympy\n",
+      "    Found existing installation: sympy 1.12\n",
+      "    Uninstalling sympy-1.12:\n",
+      "      Successfully uninstalled sympy-1.12\n",
+      "  Attempting uninstall: torch\n",
+      "    Found existing installation: torch 2.2.0\n",
+      "    Uninstalling torch-2.2.0:\n",
+      "      Successfully uninstalled torch-2.2.0\n",
+      "Successfully installed sympy-1.13.1 torch-2.5.1 torchvision-0.20.1\n",
+      "\n",
+      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
+      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
+      "Note: you may need to restart the kernel to use updated packages.\n"
+     ]
+    }
+   ],
    "source": [
     "%pip install torch torchvision"
    ]
@@ -52,10 +96,72 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 7,
    "id": "b1950f0a",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "tensor([[-0.4614,  0.2167,  1.3662,  0.5457,  2.7665,  0.8728, -0.1837,  0.0607,\n",
+      "          1.5946, -0.7726],\n",
+      "        [-0.8952,  0.7103, -0.7606,  0.9257, -0.1401,  0.5907,  0.7204,  1.3177,\n",
+      "         -0.4342,  0.4527],\n",
+      "        [ 0.7967,  0.1907, -0.5346,  1.4139, -0.5380, -2.1966,  0.4751,  1.4743,\n",
+      "          1.2449, -0.8389],\n",
+      "        [ 0.0833,  0.5977, -0.7399, -0.4702, -0.6887,  1.1328, -1.1584,  0.3544,\n",
+      "          1.0611, -0.0325],\n",
+      "        [ 0.5764, -0.5985, -1.0803, -0.7565, -1.0020,  1.7249, -0.6647,  0.7847,\n",
+      "          1.7402,  0.8243],\n",
+      "        [-0.9695,  0.5117,  1.9237,  1.7299,  1.0193,  0.3211, -0.5839,  0.5866,\n",
+      "          1.0019, -0.2681],\n",
+      "        [-0.4172, -2.3619, -1.1206, -0.7292,  0.9231, -0.3644,  0.6110,  1.3185,\n",
+      "          1.2674, -1.5235],\n",
+      "        [ 0.2213, -0.5554, -0.4785,  0.9106,  0.1333,  1.1237,  0.2859, -1.6737,\n",
+      "         -0.8616, -2.5445],\n",
+      "        [ 0.2351,  1.3325,  0.1848,  0.1473,  1.3133, -0.7523,  0.6736,  1.8610,\n",
+      "         -0.1847,  1.0223],\n",
+      "        [-0.6824, -0.0298, -0.1910,  1.4017, -1.9937,  0.4087,  0.0165,  1.7551,\n",
+      "         -0.6690, -0.7425],\n",
+      "        [-1.3005, -0.5498, -1.3494, -1.2090,  0.3210,  0.7386,  0.5926, -0.6941,\n",
+      "         -0.1688, -0.6065],\n",
+      "        [ 0.4044,  0.6994, -0.9141, -0.3529,  1.0734, -0.9639,  0.0657, -0.2253,\n",
+      "          0.3391,  0.5039],\n",
+      "        [-2.1911,  1.6130, -0.7344, -1.0796, -0.3465, -0.9285, -0.5405, -0.0072,\n",
+      "         -0.1058, -1.7597],\n",
+      "        [-1.4770,  0.3449,  0.6489,  1.7304, -0.0802, -0.0332, -0.2949,  0.2265,\n",
+      "         -0.7456,  0.8549]])\n",
+      "AlexNet(\n",
+      "  (features): Sequential(\n",
+      "    (0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))\n",
+      "    (1): ReLU(inplace=True)\n",
+      "    (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
+      "    (3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))\n",
+      "    (4): ReLU(inplace=True)\n",
+      "    (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
+      "    (6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
+      "    (7): ReLU(inplace=True)\n",
+      "    (8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
+      "    (9): ReLU(inplace=True)\n",
+      "    (10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
+      "    (11): ReLU(inplace=True)\n",
+      "    (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
+      "  )\n",
+      "  (avgpool): AdaptiveAvgPool2d(output_size=(6, 6))\n",
+      "  (classifier): Sequential(\n",
+      "    (0): Dropout(p=0.5, inplace=False)\n",
+      "    (1): Linear(in_features=9216, out_features=4096, bias=True)\n",
+      "    (2): ReLU(inplace=True)\n",
+      "    (3): Dropout(p=0.5, inplace=False)\n",
+      "    (4): Linear(in_features=4096, out_features=4096, bias=True)\n",
+      "    (5): ReLU(inplace=True)\n",
+      "    (6): Linear(in_features=4096, out_features=1000, bias=True)\n",
+      "  )\n",
+      ")\n"
+     ]
+    }
+   ],
    "source": [
     "import torch\n",
     "\n",
@@ -95,10 +201,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 9,
    "id": "6e18f2fd",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CUDA is not available.  Training on CPU ...\n"
+     ]
+    }
+   ],
    "source": [
     "import torch\n",
     "\n",
@@ -111,6 +225,29 @@
     "    print(\"CUDA is available!  Training on GPU ...\")"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "abb4553c",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "tensor([1.], device='mps:0')\n"
+     ]
+    }
+   ],
+   "source": [
+    "if torch.backends.mps.is_available():\n",
+    "    mps_device = torch.device(\"mps\")\n",
+    "    x = torch.ones(1, device=mps_device)\n",
+    "    print (x)\n",
+    "else:\n",
+    "    print (\"MPS device not found.\")"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "5cf214eb",
@@ -121,10 +258,31 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "id": "462666a2",
+   "execution_count": 5,
+   "id": "711b0b8e",
    "metadata": {},
    "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "from torchvision import datasets, transforms\n",
+    "from torch.utils.data.sampler import SubsetRandomSampler"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "462666a2",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Files already downloaded and verified\n",
+      "Files already downloaded and verified\n"
+     ]
+    }
+   ],
    "source": [
     "import numpy as np\n",
     "from torchvision import datasets, transforms\n",
@@ -193,10 +351,25 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 32,
    "id": "317bf070",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Net(\n",
+      "  (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))\n",
+      "  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
+      "  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))\n",
+      "  (fc1): Linear(in_features=400, out_features=120, bias=True)\n",
+      "  (fc2): Linear(in_features=120, out_features=84, bias=True)\n",
+      "  (fc3): Linear(in_features=84, out_features=10, bias=True)\n",
+      ")\n"
+     ]
+    }
+   ],
    "source": [
     "import torch.nn as nn\n",
     "import torch.nn.functional as F\n",
@@ -242,10 +415,58 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 34,
    "id": "4b53f229",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Epoch: 0 \tTraining Loss: 28.707199 \tValidation Loss: 28.363214\n",
+      "Validation loss decreased (inf --> 28.363214).  Saving model ...\n",
+      "Epoch: 1 \tTraining Loss: 27.053440 \tValidation Loss: 26.921309\n",
+      "Validation loss decreased (28.363214 --> 26.921309).  Saving model ...\n",
+      "Epoch: 2 \tTraining Loss: 25.798181 \tValidation Loss: 25.484369\n",
+      "Validation loss decreased (26.921309 --> 25.484369).  Saving model ...\n",
+      "Epoch: 3 \tTraining Loss: 24.616021 \tValidation Loss: 25.825257\n",
+      "Epoch: 4 \tTraining Loss: 23.607140 \tValidation Loss: 24.406983\n",
+      "Validation loss decreased (25.484369 --> 24.406983).  Saving model ...\n",
+      "Epoch: 5 \tTraining Loss: 22.641223 \tValidation Loss: 23.463277\n",
+      "Validation loss decreased (24.406983 --> 23.463277).  Saving model ...\n",
+      "Epoch: 6 \tTraining Loss: 21.727461 \tValidation Loss: 23.323754\n",
+      "Validation loss decreased (23.463277 --> 23.323754).  Saving model ...\n",
+      "Epoch: 7 \tTraining Loss: 20.908013 \tValidation Loss: 22.815489\n",
+      "Validation loss decreased (23.323754 --> 22.815489).  Saving model ...\n",
+      "Epoch: 8 \tTraining Loss: 20.072570 \tValidation Loss: 22.468899\n",
+      "Validation loss decreased (22.815489 --> 22.468899).  Saving model ...\n",
+      "Epoch: 9 \tTraining Loss: 19.337123 \tValidation Loss: 23.307148\n",
+      "Epoch: 10 \tTraining Loss: 18.578279 \tValidation Loss: 22.322720\n",
+      "Validation loss decreased (22.468899 --> 22.322720).  Saving model ...\n",
+      "Epoch: 11 \tTraining Loss: 17.925301 \tValidation Loss: 22.491466\n",
+      "Epoch: 12 \tTraining Loss: 17.266396 \tValidation Loss: 22.145613\n",
+      "Validation loss decreased (22.322720 --> 22.145613).  Saving model ...\n",
+      "Epoch: 13 \tTraining Loss: 16.644972 \tValidation Loss: 21.923327\n",
+      "Validation loss decreased (22.145613 --> 21.923327).  Saving model ...\n",
+      "Epoch: 14 \tTraining Loss: 16.097757 \tValidation Loss: 22.242258\n",
+      "Epoch: 15 \tTraining Loss: 15.522903 \tValidation Loss: 22.269535\n",
+      "Epoch: 16 \tTraining Loss: 14.930308 \tValidation Loss: 23.073589\n",
+      "Epoch: 17 \tTraining Loss: 14.374154 \tValidation Loss: 23.190186\n",
+      "Epoch: 18 \tTraining Loss: 13.829007 \tValidation Loss: 23.638800\n",
+      "Epoch: 19 \tTraining Loss: 13.414001 \tValidation Loss: 25.147587\n",
+      "Epoch: 20 \tTraining Loss: 12.890743 \tValidation Loss: 24.385583\n",
+      "Epoch: 21 \tTraining Loss: 12.456227 \tValidation Loss: 24.933902\n",
+      "Epoch: 22 \tTraining Loss: 11.993389 \tValidation Loss: 25.289021\n",
+      "Epoch: 23 \tTraining Loss: 11.565563 \tValidation Loss: 26.004760\n",
+      "Epoch: 24 \tTraining Loss: 11.188692 \tValidation Loss: 26.451757\n",
+      "Epoch: 25 \tTraining Loss: 10.716678 \tValidation Loss: 27.236794\n",
+      "Epoch: 26 \tTraining Loss: 10.315807 \tValidation Loss: 27.493770\n",
+      "Epoch: 27 \tTraining Loss: 9.975283 \tValidation Loss: 27.571290\n",
+      "Epoch: 28 \tTraining Loss: 9.440035 \tValidation Loss: 29.006522\n",
+      "Epoch: 29 \tTraining Loss: 9.220511 \tValidation Loss: 29.190469\n"
+     ]
+    }
+   ],
    "source": [
     "import torch.optim as optim\n",
     "\n",
@@ -324,18 +545,179 @@
     "Does overfit occur? If so, do an early stopping."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "4e567158",
+   "metadata": {},
+   "source": [
+    "Yes, overfitting occurs. This is evident starting around Epoch 15, where the Validation Loss stops decreasing and begins to oscillate or increase, while the Training Loss continues to decrease. \n",
+    "This indicates the model is fitting too closely to the training data and failling to generalize well to the validation data.\n",
+    "By doing an early stopping, the training should stop around Epoch 15, where the Validation Loss reaches its minimum value of 21.882406. Continuing beyond this point does not improve validation performance and increases the risk of overfitting."
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 35,
+   "id": "11952c52",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Epoch: 0 \tTraining Loss: 8.891932 \tValidation Loss: 30.875338\n",
+      "Validation loss decreased (inf --> 30.875338).  Saving model ...\n"
+     ]
+    },
+    {
+     "ename": "KeyboardInterrupt",
+     "evalue": "",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
+      "Cell \u001b[0;32mIn[35], line 35\u001b[0m\n\u001b[1;32m     33\u001b[0m loss \u001b[38;5;241m=\u001b[39m criterion(output, target)\n\u001b[1;32m     34\u001b[0m \u001b[38;5;66;03m# Backward pass: compute gradient of the loss with respect to model parameters\u001b[39;00m\n\u001b[0;32m---> 35\u001b[0m \u001b[43mloss\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     36\u001b[0m \u001b[38;5;66;03m# Perform a single optimization step (parameter update)\u001b[39;00m\n\u001b[1;32m     37\u001b[0m optimizer\u001b[38;5;241m.\u001b[39mstep()\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/_tensor.py:581\u001b[0m, in \u001b[0;36mTensor.backward\u001b[0;34m(self, gradient, retain_graph, create_graph, inputs)\u001b[0m\n\u001b[1;32m    571\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m has_torch_function_unary(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m    572\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m handle_torch_function(\n\u001b[1;32m    573\u001b[0m         Tensor\u001b[38;5;241m.\u001b[39mbackward,\n\u001b[1;32m    574\u001b[0m         (\u001b[38;5;28mself\u001b[39m,),\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    579\u001b[0m         inputs\u001b[38;5;241m=\u001b[39minputs,\n\u001b[1;32m    580\u001b[0m     )\n\u001b[0;32m--> 581\u001b[0m \u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mautograd\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    582\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgradient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minputs\u001b[49m\n\u001b[1;32m    583\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/autograd/__init__.py:347\u001b[0m, in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[0m\n\u001b[1;32m    342\u001b[0m     retain_graph \u001b[38;5;241m=\u001b[39m create_graph\n\u001b[1;32m    344\u001b[0m \u001b[38;5;66;03m# The reason we repeat the same comment below is that\u001b[39;00m\n\u001b[1;32m    345\u001b[0m \u001b[38;5;66;03m# some Python versions print out the first line of a multi-line function\u001b[39;00m\n\u001b[1;32m    346\u001b[0m \u001b[38;5;66;03m# calls in the traceback and some print out the last line\u001b[39;00m\n\u001b[0;32m--> 347\u001b[0m \u001b[43m_engine_run_backward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    348\u001b[0m \u001b[43m    \u001b[49m\u001b[43mtensors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    349\u001b[0m \u001b[43m    \u001b[49m\u001b[43mgrad_tensors_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    350\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    351\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    352\u001b[0m \u001b[43m    \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    353\u001b[0m \u001b[43m    \u001b[49m\u001b[43mallow_unreachable\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m    354\u001b[0m \u001b[43m    \u001b[49m\u001b[43maccumulate_grad\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m    355\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/autograd/graph.py:825\u001b[0m, in \u001b[0;36m_engine_run_backward\u001b[0;34m(t_outputs, *args, **kwargs)\u001b[0m\n\u001b[1;32m    823\u001b[0m     unregister_hooks \u001b[38;5;241m=\u001b[39m _register_logging_hooks_on_whole_graph(t_outputs)\n\u001b[1;32m    824\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 825\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mVariable\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execution_engine\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_backward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m  \u001b[49m\u001b[38;5;66;43;03m# Calls into the C++ engine to run the backward pass\u001b[39;49;00m\n\u001b[1;32m    826\u001b[0m \u001b[43m        \u001b[49m\u001b[43mt_outputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m    827\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m  \u001b[38;5;66;03m# Calls into the C++ engine to run the backward pass\u001b[39;00m\n\u001b[1;32m    828\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m    829\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m attach_logging_hooks:\n",
+      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
+     ]
+    }
+   ],
+   "source": [
+    "# EARLY STOP\n",
+    "import torch.optim as optim\n",
+    "\n",
+    "min_epochs = 10\n",
+    "patience = 3 # Nb of epochs to wait after no improvement\n",
+    "epochs_no_improve = 0\n",
+    "\n",
+    "\n",
+    "criterion = nn.CrossEntropyLoss()  # specify loss function\n",
+    "optimizer = optim.SGD(model.parameters(), lr=0.01)  # specify optimizer\n",
+    "\n",
+    "n_epochs = 30  # number of epochs to train the model\n",
+    "valid_loss_list = []  # list to store validation loss to visualize\n",
+    "train_loss_list = []  # list to store trainloss to visualize\n",
+    "valid_loss_min = np.Inf  # track change in validation loss\n",
+    "\n",
+    "for epoch in range(n_epochs):\n",
+    "    # Keep track of training and validation loss\n",
+    "    train_loss = 0.0\n",
+    "    valid_loss = 0.0\n",
+    "\n",
+    "    # Train the model\n",
+    "    model.train()\n",
+    "    for data, target in train_loader:\n",
+    "        # Move tensors to GPU if CUDA is available\n",
+    "        if train_on_gpu:\n",
+    "            data, target = data.cuda(), target.cuda()\n",
+    "        # Clear the gradients of all optimized variables\n",
+    "        optimizer.zero_grad()\n",
+    "        # Forward pass: compute predicted outputs by passing inputs to the model\n",
+    "        output = model(data)\n",
+    "        # Calculate the batch loss\n",
+    "        loss = criterion(output, target)\n",
+    "        # Backward pass: compute gradient of the loss with respect to model parameters\n",
+    "        loss.backward()\n",
+    "        # Perform a single optimization step (parameter update)\n",
+    "        optimizer.step()\n",
+    "        # Update training loss\n",
+    "        train_loss += loss.item() * data.size(0)\n",
+    "\n",
+    "    # Validate the model\n",
+    "    model.eval()\n",
+    "    for data, target in valid_loader:\n",
+    "        # Move tensors to GPU if CUDA is available\n",
+    "        if train_on_gpu:\n",
+    "            data, target = data.cuda(), target.cuda()\n",
+    "        # Forward pass: compute predicted outputs by passing inputs to the model\n",
+    "        output = model(data)\n",
+    "        # Calculate the batch loss\n",
+    "        loss = criterion(output, target)\n",
+    "        # Update average validation loss\n",
+    "        valid_loss += loss.item() * data.size(0)\n",
+    "\n",
+    "    # Calculate average losses\n",
+    "    train_loss = train_loss / len(train_loader)\n",
+    "    valid_loss = valid_loss / len(valid_loader)\n",
+    "    train_loss_list.append(train_loss)\n",
+    "    valid_loss_list.append(valid_loss)\n",
+    "\n",
+    "    # Print training/validation statistics\n",
+    "    print(\n",
+    "        \"Epoch: {} \\tTraining Loss: {:.6f} \\tValidation Loss: {:.6f}\".format(\n",
+    "            epoch, train_loss, valid_loss\n",
+    "        )\n",
+    "    )\n",
+    "\n",
+    "    # Save model if validation loss has decreased\n",
+    "    if valid_loss <= valid_loss_min:\n",
+    "        print(\n",
+    "            \"Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...\".format(\n",
+    "                valid_loss_min, valid_loss\n",
+    "            )\n",
+    "        )\n",
+    "        torch.save(model.state_dict(), \"model_cifar_1_early_stop.pt\")\n",
+    "        valid_loss_min = valid_loss\n",
+    "        epochs_no_improve = 0\n",
+    "    elif epoch >= min_epochs:\n",
+    "        epochs_no_improve += 1\n",
+    "        if epochs_no_improve >= patience:\n",
+    "            print(f\"Validation loss increased for {patience} times consecutives. Applying Early Stop.\")\n",
+    "            break\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
    "id": "d39df818",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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",
+      "text/plain": [
+       "<Figure size 640x480 with 1 Axes>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "import matplotlib.pyplot as plt\n",
+    "\n",
+    "plt.plot(range(len(train_loss_list)), train_loss_list)\n",
+    "plt.xlabel(\"Epoch\")\n",
+    "plt.ylabel(\"Train Loss\")\n",
+    "plt.title(\"Performance of Model 1\")\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "id": "2111dfe9",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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",
+      "text/plain": [
+       "<Figure size 640x480 with 1 Axes>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
    "source": [
     "import matplotlib.pyplot as plt\n",
     "\n",
-    "plt.plot(range(n_epochs), train_loss_list)\n",
+    "plt.plot(range(len(valid_loss_list)), valid_loss_list)\n",
     "plt.xlabel(\"Epoch\")\n",
-    "plt.ylabel(\"Loss\")\n",
+    "plt.ylabel(\"Validation Loss\")\n",
     "plt.title(\"Performance of Model 1\")\n",
     "plt.show()"
    ]
@@ -353,9 +735,39 @@
    "execution_count": null,
    "id": "e93efdfc",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/var/folders/qb/94v41qkx157gvjjjv1rchcr00000gn/T/ipykernel_25820/3291884398.py:1: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
+      "  model.load_state_dict(torch.load(\"./model_cifar.pt\"))\n"
+     ]
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Test Loss: 21.811477\n",
+      "\n",
+      "Test Accuracy of airplane: 71% (716/1000)\n",
+      "Test Accuracy of automobile: 75% (750/1000)\n",
+      "Test Accuracy of  bird: 55% (558/1000)\n",
+      "Test Accuracy of   cat: 44% (442/1000)\n",
+      "Test Accuracy of  deer: 60% (604/1000)\n",
+      "Test Accuracy of   dog: 52% (521/1000)\n",
+      "Test Accuracy of  frog: 64% (644/1000)\n",
+      "Test Accuracy of horse: 58% (588/1000)\n",
+      "Test Accuracy of  ship: 74% (746/1000)\n",
+      "Test Accuracy of truck: 68% (681/1000)\n",
+      "\n",
+      "Test Accuracy (Overall): 62% (6250/10000)\n"
+     ]
+    }
+   ],
    "source": [
-    "model.load_state_dict(torch.load(\"./model_cifar.pt\"))\n",
+    "# model.load_state_dict(torch.load(\"./model_cifar.pt\"))\n",
+    "model.load_state_dict(torch.load(\"./model_cifar_1_early_stop.pt\"))\n",
     "\n",
     "# track test loss\n",
     "test_loss = 0.0\n",
@@ -434,6 +846,337 @@
     "Compare the results obtained with this new network to those obtained previously."
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "id": "8b67c2c6",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import torch.nn as nn\n",
+    "import torch.nn.functional as F\n",
+    "\n",
+    "# define the CNN architecture\n",
+    "\n",
+    "class NewNet(nn.Module):\n",
+    "    def __init__(self, dropout_value=0.5):\n",
+    "        super(NewNet, self).__init__()\n",
+    "        # Convolutional layers\n",
+    "        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, padding=1)\n",
+    "        self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, padding=1)\n",
+    "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n",
+    "        \n",
+    "        # MaxPool\n",
+    "        self.pool = nn.MaxPool2d(kernel_size=2)\n",
+    "\n",
+    "        # Dropout\n",
+    "        self.dropout = nn.Dropout(p=dropout_value)\n",
+    "        \n",
+    "        # Fully connected layers\n",
+    "        # self.fc1 = nn.Linear(in_features=64 * (input_size // 8) * (input_size // 8), out_features=512)\n",
+    "        self.fc1 = nn.Linear(in_features=64 * 4 * 4, out_features=512)\n",
+    "        self.fc2 = nn.Linear(in_features=512, out_features=64)\n",
+    "        self.fc3 = nn.Linear(64, 10)\n",
+    "\n",
+    "    def forward(self, x):\n",
+    "        # Convolutional layers with ReLU and MaxPool\n",
+    "        x = self.pool(F.relu(self.conv1(x)))\n",
+    "        x = self.pool(F.relu(self.conv2(x)))\n",
+    "        x = self.pool(F.relu(self.conv3(x)))\n",
+    "\n",
+    "        x = x.view(x.size(0), -1)\n",
+    "        x = self.dropout(F.relu(self.fc1(x)))\n",
+    "        x = self.dropout(F.relu(self.fc2(x)))\n",
+    "        x = self.fc3(x)\n",
+    "        return x\n",
+    "\n",
+    "\n",
+    "# # create a complete CNN\n",
+    "# new_model = NewNet()\n",
+    "# print(new_model)\n",
+    "# # move tensors to GPU if CUDA is available\n",
+    "# if train_on_gpu:\n",
+    "#     new_model.cuda()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3cc6cc8a",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "NewNet(\n",
+      "  (conv1): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
+      "  (conv2): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
+      "  (conv3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
+      "  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
+      "  (dropout): Dropout(p=0.5, inplace=False)\n",
+      "  (fc1): Linear(in_features=1024, out_features=512, bias=True)\n",
+      "  (fc2): Linear(in_features=512, out_features=64, bias=True)\n",
+      "  (fc3): Linear(in_features=64, out_features=10, bias=True)\n",
+      ")\n",
+      "Epoch: 0 \tTraining Loss: 44.934554 \tValidation Loss: 40.292926\n",
+      "Validation loss decreased (inf --> 40.292926).  Saving model ...\n",
+      "Epoch: 1 \tTraining Loss: 38.547384 \tValidation Loss: 34.307230\n",
+      "Validation loss decreased (40.292926 --> 34.307230).  Saving model ...\n",
+      "Epoch: 2 \tTraining Loss: 34.167031 \tValidation Loss: 30.783441\n",
+      "Validation loss decreased (34.307230 --> 30.783441).  Saving model ...\n",
+      "Epoch: 3 \tTraining Loss: 31.514744 \tValidation Loss: 29.177271\n",
+      "Validation loss decreased (30.783441 --> 29.177271).  Saving model ...\n",
+      "Epoch: 4 \tTraining Loss: 29.490232 \tValidation Loss: 26.770098\n",
+      "Validation loss decreased (29.177271 --> 26.770098).  Saving model ...\n",
+      "Epoch: 5 \tTraining Loss: 27.982251 \tValidation Loss: 25.774428\n",
+      "Validation loss decreased (26.770098 --> 25.774428).  Saving model ...\n",
+      "Epoch: 6 \tTraining Loss: 26.515079 \tValidation Loss: 24.038370\n",
+      "Validation loss decreased (25.774428 --> 24.038370).  Saving model ...\n",
+      "Epoch: 7 \tTraining Loss: 25.259680 \tValidation Loss: 23.620053\n",
+      "Validation loss decreased (24.038370 --> 23.620053).  Saving model ...\n",
+      "Epoch: 8 \tTraining Loss: 23.969766 \tValidation Loss: 22.249926\n",
+      "Validation loss decreased (23.620053 --> 22.249926).  Saving model ...\n",
+      "Epoch: 9 \tTraining Loss: 23.044149 \tValidation Loss: 21.061266\n",
+      "Validation loss decreased (22.249926 --> 21.061266).  Saving model ...\n",
+      "Epoch: 10 \tTraining Loss: 21.929328 \tValidation Loss: 20.193573\n",
+      "Validation loss decreased (21.061266 --> 20.193573).  Saving model ...\n",
+      "Epoch: 11 \tTraining Loss: 21.162510 \tValidation Loss: 19.769918\n",
+      "Validation loss decreased (20.193573 --> 19.769918).  Saving model ...\n",
+      "Epoch: 12 \tTraining Loss: 20.163602 \tValidation Loss: 19.290062\n",
+      "Validation loss decreased (19.769918 --> 19.290062).  Saving model ...\n",
+      "Epoch: 13 \tTraining Loss: 19.370121 \tValidation Loss: 18.626375\n",
+      "Validation loss decreased (19.290062 --> 18.626375).  Saving model ...\n",
+      "Epoch: 14 \tTraining Loss: 18.558041 \tValidation Loss: 18.075628\n",
+      "Validation loss decreased (18.626375 --> 18.075628).  Saving model ...\n"
+     ]
+    }
+   ],
+   "source": [
+    "\n",
+    "# create a complete CNN\n",
+    "new_model = NewNet()\n",
+    "print(new_model)\n",
+    "# move tensors to GPU if CUDA is available\n",
+    "if train_on_gpu:\n",
+    "    new_model.cuda()\n",
+    "\n",
+    "\n",
+    "import torch.optim as optim\n",
+    "\n",
+    "min_epochs = 10\n",
+    "patience = 3 # Nb of epochs to wait after no improvement\n",
+    "epochs_no_improve = 0\n",
+    "\n",
+    "\n",
+    "criterion = nn.CrossEntropyLoss()  # specify loss function\n",
+    "optimizer = optim.SGD(new_model.parameters(), lr=0.01)  # specify optimizer\n",
+    "\n",
+    "n_epochs = 30  # number of epochs to train the model\n",
+    "valid_loss_list = []  # list to store validation loss to visualize\n",
+    "train_loss_list = []  # list to store trainloss to visualize\n",
+    "valid_loss_min = np.Inf  # track change in validation loss\n",
+    "\n",
+    "for epoch in range(n_epochs):\n",
+    "    # Keep track of training and validation loss\n",
+    "    train_loss = 0.0\n",
+    "    valid_loss = 0.0\n",
+    "\n",
+    "    # Train the model\n",
+    "    new_model.train()\n",
+    "    for data, target in train_loader:\n",
+    "        # Move tensors to GPU if CUDA is available\n",
+    "        if train_on_gpu:\n",
+    "            data, target = data.cuda(), target.cuda()\n",
+    "        # Clear the gradients of all optimized variables\n",
+    "        optimizer.zero_grad()\n",
+    "        # Forward pass: compute predicted outputs by passing inputs to the model\n",
+    "        output = new_model(data)\n",
+    "        # Calculate the batch loss\n",
+    "        loss = criterion(output, target)\n",
+    "        # Backward pass: compute gradient of the loss with respect to model parameters\n",
+    "        loss.backward()\n",
+    "        # Perform a single optimization step (parameter update)\n",
+    "        optimizer.step()\n",
+    "        # Update training loss\n",
+    "        train_loss += loss.item() * data.size(0)\n",
+    "\n",
+    "    # Validate the model\n",
+    "    new_model.eval()\n",
+    "    for data, target in valid_loader:\n",
+    "        # Move tensors to GPU if CUDA is available\n",
+    "        if train_on_gpu:\n",
+    "            data, target = data.cuda(), target.cuda()\n",
+    "        # Forward pass: compute predicted outputs by passing inputs to the model\n",
+    "        output = new_model(data)\n",
+    "        # Calculate the batch loss\n",
+    "        loss = criterion(output, target)\n",
+    "        # Update average validation loss\n",
+    "        valid_loss += loss.item() * data.size(0)\n",
+    "\n",
+    "    # Calculate average losses\n",
+    "    train_loss = train_loss / len(train_loader)\n",
+    "    valid_loss = valid_loss / len(valid_loader)\n",
+    "    train_loss_list.append(train_loss)\n",
+    "    valid_loss_list.append(valid_loss)\n",
+    "\n",
+    "    # Print training/validation statistics\n",
+    "    print(\n",
+    "        \"Epoch: {} \\tTraining Loss: {:.6f} \\tValidation Loss: {:.6f}\".format(\n",
+    "            epoch, train_loss, valid_loss\n",
+    "        )\n",
+    "    )\n",
+    "\n",
+    "    # Save model if validation loss has decreased\n",
+    "    if valid_loss <= valid_loss_min:\n",
+    "        print(\n",
+    "            \"Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...\".format(\n",
+    "                valid_loss_min, valid_loss\n",
+    "            )\n",
+    "        )\n",
+    "        torch.save(new_model.state_dict(), \"model_cifar_2.pt\")\n",
+    "        valid_loss_min = valid_loss\n",
+    "        epochs_no_improve = 0\n",
+    "    elif epoch >= min_epochs:\n",
+    "        epochs_no_improve += 1\n",
+    "        if epochs_no_improve >= patience:\n",
+    "            print(f\"Validation loss increased for {patience} times consecutives. Applying Early Stop.\")\n",
+    "            break\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "id": "97355006",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/var/folders/qb/94v41qkx157gvjjjv1rchcr00000gn/T/ipykernel_32008/3634208260.py:1: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
+      "  model.load_state_dict(torch.load(\"./model_cifar_2.pt\"))\n"
+     ]
+    },
+    {
+     "ename": "FileNotFoundError",
+     "evalue": "[Errno 2] No such file or directory: './model_cifar_2.pt'",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
+      "Cell \u001b[0;32mIn[31], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m model\u001b[38;5;241m.\u001b[39mload_state_dict(\u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mload\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m./model_cifar_2.pt\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[1;32m      3\u001b[0m \u001b[38;5;66;03m# track test loss\u001b[39;00m\n\u001b[1;32m      4\u001b[0m test_loss \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0.0\u001b[39m\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/serialization.py:1319\u001b[0m, in \u001b[0;36mload\u001b[0;34m(f, map_location, pickle_module, weights_only, mmap, **pickle_load_args)\u001b[0m\n\u001b[1;32m   1316\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mencoding\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m pickle_load_args\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m   1317\u001b[0m     pickle_load_args[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mencoding\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m-> 1319\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[43m_open_file_like\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m opened_file:\n\u001b[1;32m   1320\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m _is_zipfile(opened_file):\n\u001b[1;32m   1321\u001b[0m         \u001b[38;5;66;03m# The zipfile reader is going to advance the current file position.\u001b[39;00m\n\u001b[1;32m   1322\u001b[0m         \u001b[38;5;66;03m# If we want to actually tail call to torch.jit.load, we need to\u001b[39;00m\n\u001b[1;32m   1323\u001b[0m         \u001b[38;5;66;03m# reset back to the original position.\u001b[39;00m\n\u001b[1;32m   1324\u001b[0m         orig_position \u001b[38;5;241m=\u001b[39m opened_file\u001b[38;5;241m.\u001b[39mtell()\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/serialization.py:659\u001b[0m, in \u001b[0;36m_open_file_like\u001b[0;34m(name_or_buffer, mode)\u001b[0m\n\u001b[1;32m    657\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_open_file_like\u001b[39m(name_or_buffer, mode):\n\u001b[1;32m    658\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m _is_path(name_or_buffer):\n\u001b[0;32m--> 659\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_open_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43mname_or_buffer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    660\u001b[0m     \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    661\u001b[0m         \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mw\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m mode:\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/serialization.py:640\u001b[0m, in \u001b[0;36m_open_file.__init__\u001b[0;34m(self, name, mode)\u001b[0m\n\u001b[1;32m    639\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, name, mode):\n\u001b[0;32m--> 640\u001b[0m     \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmode\u001b[49m\u001b[43m)\u001b[49m)\n",
+      "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: './model_cifar_2.pt'"
+     ]
+    }
+   ],
+   "source": [
+    "model.load_state_dict(torch.load(\"./model_cifar_2.pt\"))\n",
+    "\n",
+    "# track test loss\n",
+    "test_loss = 0.0\n",
+    "class_correct = list(0.0 for i in range(10))\n",
+    "class_total = list(0.0 for i in range(10))\n",
+    "\n",
+    "model.eval()\n",
+    "# iterate over test data\n",
+    "for data, target in test_loader:\n",
+    "    # move tensors to GPU if CUDA is available\n",
+    "    if train_on_gpu:\n",
+    "        data, target = data.cuda(), target.cuda()\n",
+    "    # forward pass: compute predicted outputs by passing inputs to the model\n",
+    "    output = model(data)\n",
+    "    # calculate the batch loss\n",
+    "    loss = criterion(output, target)\n",
+    "    # update test loss\n",
+    "    test_loss += loss.item() * data.size(0)\n",
+    "    # convert output probabilities to predicted class\n",
+    "    _, pred = torch.max(output, 1)\n",
+    "    # compare predictions to true label\n",
+    "    correct_tensor = pred.eq(target.data.view_as(pred))\n",
+    "    correct = (\n",
+    "        np.squeeze(correct_tensor.numpy())\n",
+    "        if not train_on_gpu\n",
+    "        else np.squeeze(correct_tensor.cpu().numpy())\n",
+    "    )\n",
+    "    # calculate test accuracy for each object class\n",
+    "    for i in range(batch_size):\n",
+    "        label = target.data[i]\n",
+    "        class_correct[label] += correct[i].item()\n",
+    "        class_total[label] += 1\n",
+    "\n",
+    "# average test loss\n",
+    "test_loss = test_loss / len(test_loader)\n",
+    "print(\"Test Loss: {:.6f}\\n\".format(test_loss))\n",
+    "\n",
+    "for i in range(10):\n",
+    "    if class_total[i] > 0:\n",
+    "        print(\n",
+    "            \"Test Accuracy of %5s: %2d%% (%2d/%2d)\"\n",
+    "            % (\n",
+    "                classes[i],\n",
+    "                100 * class_correct[i] / class_total[i],\n",
+    "                np.sum(class_correct[i]),\n",
+    "                np.sum(class_total[i]),\n",
+    "            )\n",
+    "        )\n",
+    "    else:\n",
+    "        print(\"Test Accuracy of %5s: N/A (no training examples)\" % (classes[i]))\n",
+    "\n",
+    "print(\n",
+    "    \"\\nTest Accuracy (Overall): %2d%% (%2d/%2d)\"\n",
+    "    % (\n",
+    "        100.0 * np.sum(class_correct) / np.sum(class_total),\n",
+    "        np.sum(class_correct),\n",
+    "        np.sum(class_total),\n",
+    "    )\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6245b27f",
+   "metadata": {},
+   "source": [
+    "# Test Accuracy: Model 1 v/s Model 2\n",
+    "\n",
+    "## Test Accuracy Model 1: \n",
+    "* Test Loss: 21.811477\n",
+    "\n",
+    "* Test Accuracy of airplane: 71% (716/1000)\n",
+    "* Test Accuracy of automobile: 75% (750/1000)\n",
+    "* Test Accuracy of  bird: 55% (558/1000)\n",
+    "* Test Accuracy of   cat: 44% (442/1000)\n",
+    "* Test Accuracy of  deer: 60% (604/1000)\n",
+    "* Test Accuracy of   dog: 52% (521/1000)\n",
+    "* Test Accuracy of  frog: 64% (644/1000)\n",
+    "* Test Accuracy of horse: 58% (588/1000)\n",
+    "* Test Accuracy of  ship: 74% (746/1000)\n",
+    "* Test Accuracy of truck: 68% (681/1000)\n",
+    "\n",
+    "* Test Accuracy (Overall): 62% (6250/10000)\n",
+    "\n",
+    "\n",
+    "## Test Accuracy Model 2:\n",
+    "* Test Loss: 16.239906\n",
+    "\n",
+    "* Test Accuracy of airplane: 78% (784/1000)\n",
+    "* Test Accuracy of automobile: 88% (889/1000)\n",
+    "* Test Accuracy of  bird: 61% (618/1000)\n",
+    "* Test Accuracy of   cat: 61% (615/1000)\n",
+    "* Test Accuracy of  deer: 66% (662/1000)\n",
+    "* Test Accuracy of   dog: 50% (509/1000)\n",
+    "* Test Accuracy of  frog: 82% (823/1000)\n",
+    "* Test Accuracy of horse: 73% (732/1000)\n",
+    "* Test Accuracy of  ship: 86% (862/1000)\n",
+    "* Test Accuracy of truck: 75% (751/1000)\n",
+    "\n",
+    "* Test Accuracy (Overall): 72% (7245/10000)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "bc381cf4",
@@ -451,10 +1194,28 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 29,
    "id": "ef623c26",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "model:  fp32  \t Size (KB): 2330.946\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "2330946"
+      ]
+     },
+     "execution_count": 29,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "import os\n",
     "\n",
@@ -483,15 +1244,43 @@
    "execution_count": null,
    "id": "c4c65d4b",
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "ename": "RuntimeError",
+     "evalue": "Didn't find engine for operation quantized::linear_prepack NoQEngine",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
+      "Cell \u001b[0;32mIn[30], line 4\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mquantization\u001b[39;00m\n\u001b[0;32m----> 4\u001b[0m quantized_model \u001b[38;5;241m=\u001b[39m \u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquantization\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquantize_dynamic\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdtype\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mqint8\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m      5\u001b[0m print_size_of_model(quantized_model, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mint8\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/quantization/quantize.py:564\u001b[0m, in \u001b[0;36mquantize_dynamic\u001b[0;34m(model, qconfig_spec, dtype, mapping, inplace)\u001b[0m\n\u001b[1;32m    562\u001b[0m model\u001b[38;5;241m.\u001b[39meval()\n\u001b[1;32m    563\u001b[0m propagate_qconfig_(model, qconfig_spec)\n\u001b[0;32m--> 564\u001b[0m \u001b[43mconvert\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmapping\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minplace\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m    565\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m model\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/quantization/quantize.py:659\u001b[0m, in \u001b[0;36mconvert\u001b[0;34m(module, mapping, inplace, remove_qconfig, is_reference, convert_custom_config_dict, use_precomputed_fake_quant)\u001b[0m\n\u001b[1;32m    657\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m inplace:\n\u001b[1;32m    658\u001b[0m     module \u001b[38;5;241m=\u001b[39m copy\u001b[38;5;241m.\u001b[39mdeepcopy(module)\n\u001b[0;32m--> 659\u001b[0m \u001b[43m_convert\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    660\u001b[0m \u001b[43m    \u001b[49m\u001b[43mmodule\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    661\u001b[0m \u001b[43m    \u001b[49m\u001b[43mmapping\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    662\u001b[0m \u001b[43m    \u001b[49m\u001b[43minplace\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m    663\u001b[0m \u001b[43m    \u001b[49m\u001b[43mis_reference\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mis_reference\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    664\u001b[0m \u001b[43m    \u001b[49m\u001b[43mconvert_custom_config_dict\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconvert_custom_config_dict\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    665\u001b[0m \u001b[43m    \u001b[49m\u001b[43muse_precomputed_fake_quant\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_precomputed_fake_quant\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    666\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    667\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m remove_qconfig:\n\u001b[1;32m    668\u001b[0m     _remove_qconfig(module)\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/quantization/quantize.py:724\u001b[0m, in \u001b[0;36m_convert\u001b[0;34m(module, mapping, inplace, is_reference, convert_custom_config_dict, use_precomputed_fake_quant)\u001b[0m\n\u001b[1;32m    712\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m    713\u001b[0m         \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(mod, _FusedModule)\n\u001b[1;32m    714\u001b[0m         \u001b[38;5;129;01mand\u001b[39;00m type_before_parametrizations(mod) \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m custom_module_class_mapping\n\u001b[1;32m    715\u001b[0m     ):\n\u001b[1;32m    716\u001b[0m         _convert(\n\u001b[1;32m    717\u001b[0m             mod,\n\u001b[1;32m    718\u001b[0m             mapping,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    722\u001b[0m             use_precomputed_fake_quant\u001b[38;5;241m=\u001b[39muse_precomputed_fake_quant,\n\u001b[1;32m    723\u001b[0m         )\n\u001b[0;32m--> 724\u001b[0m     reassign[name] \u001b[38;5;241m=\u001b[39m \u001b[43mswap_module\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    725\u001b[0m \u001b[43m        \u001b[49m\u001b[43mmod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmapping\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcustom_module_class_mapping\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muse_precomputed_fake_quant\u001b[49m\n\u001b[1;32m    726\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    728\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, value \u001b[38;5;129;01min\u001b[39;00m reassign\u001b[38;5;241m.\u001b[39mitems():\n\u001b[1;32m    729\u001b[0m     module\u001b[38;5;241m.\u001b[39m_modules[key] \u001b[38;5;241m=\u001b[39m value\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/quantization/quantize.py:766\u001b[0m, in \u001b[0;36mswap_module\u001b[0;34m(mod, mapping, custom_module_class_mapping, use_precomputed_fake_quant)\u001b[0m\n\u001b[1;32m    764\u001b[0m sig \u001b[38;5;241m=\u001b[39m inspect\u001b[38;5;241m.\u001b[39msignature(qmod\u001b[38;5;241m.\u001b[39mfrom_float)\n\u001b[1;32m    765\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muse_precomputed_fake_quant\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m sig\u001b[38;5;241m.\u001b[39mparameters:\n\u001b[0;32m--> 766\u001b[0m     new_mod \u001b[38;5;241m=\u001b[39m \u001b[43mqmod\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfrom_float\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    767\u001b[0m \u001b[43m        \u001b[49m\u001b[43mmod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muse_precomputed_fake_quant\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_precomputed_fake_quant\u001b[49m\n\u001b[1;32m    768\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    769\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    770\u001b[0m     new_mod \u001b[38;5;241m=\u001b[39m qmod\u001b[38;5;241m.\u001b[39mfrom_float(mod)\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/nn/quantized/dynamic/modules/linear.py:145\u001b[0m, in \u001b[0;36mLinear.from_float\u001b[0;34m(cls, mod, use_precomputed_fake_quant)\u001b[0m\n\u001b[1;32m    141\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    142\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[1;32m    143\u001b[0m         \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUnsupported dtype specified for dynamic quantized Linear!\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m    144\u001b[0m     )\n\u001b[0;32m--> 145\u001b[0m qlinear \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mmod\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43min_features\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmod\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mout_features\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdtype\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdtype\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    146\u001b[0m qlinear\u001b[38;5;241m.\u001b[39mset_weight_bias(qweight, mod\u001b[38;5;241m.\u001b[39mbias)\n\u001b[1;32m    147\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m qlinear\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/nn/quantized/dynamic/modules/linear.py:42\u001b[0m, in \u001b[0;36mLinear.__init__\u001b[0;34m(self, in_features, out_features, bias_, dtype)\u001b[0m\n\u001b[1;32m     41\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, in_features, out_features, bias_\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, dtype\u001b[38;5;241m=\u001b[39mtorch\u001b[38;5;241m.\u001b[39mqint8):\n\u001b[0;32m---> 42\u001b[0m     \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43min_features\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mout_features\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbias_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdtype\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdtype\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     43\u001b[0m     \u001b[38;5;66;03m# We don't muck around with buffers or attributes or anything here\u001b[39;00m\n\u001b[1;32m     44\u001b[0m     \u001b[38;5;66;03m# to keep the module simple. *everything* is simply a Python attribute.\u001b[39;00m\n\u001b[1;32m     45\u001b[0m     \u001b[38;5;66;03m# Serialization logic is explicitly handled in the below serialization and\u001b[39;00m\n\u001b[1;32m     46\u001b[0m     \u001b[38;5;66;03m# deserialization modules\u001b[39;00m\n\u001b[1;32m     47\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mversion \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m4\u001b[39m\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/nn/quantized/modules/linear.py:172\u001b[0m, in \u001b[0;36mLinear.__init__\u001b[0;34m(self, in_features, out_features, bias_, dtype)\u001b[0m\n\u001b[1;32m    169\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    170\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUnsupported dtype specified for quantized Linear!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m--> 172\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_packed_params \u001b[38;5;241m=\u001b[39m \u001b[43mLinearPackedParams\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdtype\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    173\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_packed_params\u001b[38;5;241m.\u001b[39mset_weight_bias(qweight, bias)\n\u001b[1;32m    174\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mscale \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1.0\u001b[39m\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/nn/quantized/modules/linear.py:31\u001b[0m, in \u001b[0;36mLinearPackedParams.__init__\u001b[0;34m(self, dtype)\u001b[0m\n\u001b[1;32m     29\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdtype \u001b[38;5;241m==\u001b[39m torch\u001b[38;5;241m.\u001b[39mfloat16:\n\u001b[1;32m     30\u001b[0m     wq \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mzeros([\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m1\u001b[39m], dtype\u001b[38;5;241m=\u001b[39mtorch\u001b[38;5;241m.\u001b[39mfloat)\n\u001b[0;32m---> 31\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset_weight_bias\u001b[49m\u001b[43m(\u001b[49m\u001b[43mwq\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/ao/nn/quantized/modules/linear.py:38\u001b[0m, in \u001b[0;36mLinearPackedParams.set_weight_bias\u001b[0;34m(self, weight, bias)\u001b[0m\n\u001b[1;32m     33\u001b[0m \u001b[38;5;129m@torch\u001b[39m\u001b[38;5;241m.\u001b[39mjit\u001b[38;5;241m.\u001b[39mexport\n\u001b[1;32m     34\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mset_weight_bias\u001b[39m(\n\u001b[1;32m     35\u001b[0m     \u001b[38;5;28mself\u001b[39m, weight: torch\u001b[38;5;241m.\u001b[39mTensor, bias: Optional[torch\u001b[38;5;241m.\u001b[39mTensor]\n\u001b[1;32m     36\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m     37\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdtype \u001b[38;5;241m==\u001b[39m torch\u001b[38;5;241m.\u001b[39mqint8:\n\u001b[0;32m---> 38\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_packed_params \u001b[38;5;241m=\u001b[39m \u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mops\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquantized\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlinear_prepack\u001b[49m\u001b[43m(\u001b[49m\u001b[43mweight\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbias\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     39\u001b[0m     \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdtype \u001b[38;5;241m==\u001b[39m torch\u001b[38;5;241m.\u001b[39mfloat16:\n\u001b[1;32m     40\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_packed_params \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mops\u001b[38;5;241m.\u001b[39mquantized\u001b[38;5;241m.\u001b[39mlinear_prepack_fp16(weight, bias)\n",
+      "File \u001b[0;32m~/.pyenv/versions/3.11.7/lib/python3.11/site-packages/torch/_ops.py:1116\u001b[0m, in \u001b[0;36mOpOverloadPacket.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m   1114\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_has_torchbind_op_overload \u001b[38;5;129;01mand\u001b[39;00m _must_dispatch_in_python(args, kwargs):\n\u001b[1;32m   1115\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m _call_overload_packet_from_python(\u001b[38;5;28mself\u001b[39m, args, kwargs)\n\u001b[0;32m-> 1116\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_op\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n",
+      "\u001b[0;31mRuntimeError\u001b[0m: Didn't find engine for operation quantized::linear_prepack NoQEngine"
+     ]
+    }
+   ],
    "source": [
     "import torch.quantization\n",
     "\n",
-    "\n",
+    "torch.backends.quantized.engine = 'qnnpack'\n",
     "quantized_model = torch.quantization.quantize_dynamic(model, dtype=torch.qint8)\n",
     "print_size_of_model(quantized_model, \"int8\")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "063d405c",
+   "metadata": {},
+   "source": []
+  },
   {
    "cell_type": "markdown",
    "id": "7b108e17",
@@ -926,7 +1715,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3.8.5 ('base')",
+   "display_name": "Python 3",
    "language": "python",
    "name": "python3"
   },
@@ -940,12 +1729,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.8.5"
-  },
-  "vscode": {
-   "interpreter": {
-    "hash": "9e3efbebb05da2d4a1968abe9a0645745f54b63feb7a85a514e4da0495be97eb"
-   }
+   "version": "3.11.7"
   }
  },
  "nbformat": 4,
-- 
GitLab