From 2492b8140af6445e21ae0716497a2638ddde5735 Mon Sep 17 00:00:00 2001 From: Jeanne Caty <jeanne.caty@ecl22.ec-lyon.fr> Date: Thu, 5 Dec 2024 22:59:08 +0100 Subject: [PATCH] Update TD2 Deep Learning.ipynb --- TD2 Deep Learning.ipynb | 1007 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 944 insertions(+), 63 deletions(-) diff --git a/TD2 Deep Learning.ipynb b/TD2 Deep Learning.ipynb index 65bffee..dbad099 100644 --- a/TD2 Deep Learning.ipynb +++ b/TD2 Deep Learning.ipynb @@ -33,10 +33,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "330a42f5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: torch in /opt/conda/envs/pytorch/lib/python3.8/site-packages (1.7.0)\n", + "Requirement already satisfied: torchvision in /opt/conda/envs/pytorch/lib/python3.8/site-packages (0.8.1)\n", + "Requirement already satisfied: future in /home/jcaty/.local/lib/python3.8/site-packages (from torch) (1.0.0)\n", + "Requirement already satisfied: typing_extensions in /opt/conda/envs/pytorch/lib/python3.8/site-packages (from torch) (3.7.4.3)\n", + "Requirement already satisfied: dataclasses in /home/jcaty/.local/lib/python3.8/site-packages (from torch) (0.6)\n", + "Requirement already satisfied: numpy in /opt/conda/envs/pytorch/lib/python3.8/site-packages (from torch) (1.19.2)\n", + "Requirement already satisfied: numpy in /opt/conda/envs/pytorch/lib/python3.8/site-packages (from torch) (1.19.2)\n", + "Requirement already satisfied: torch in /opt/conda/envs/pytorch/lib/python3.8/site-packages (1.7.0)\n", + "Requirement already satisfied: pillow>=4.1.1 in /opt/conda/envs/pytorch/lib/python3.8/site-packages (from torchvision) (8.0.1)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], "source": [ "%pip install torch torchvision\n" ] @@ -52,7 +70,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 2, "id": "b1950f0a", "metadata": {}, "outputs": [ @@ -60,34 +78,34 @@ "name": "stdout", "output_type": "stream", "text": [ - "tensor([[ 0.6418, -0.1700, 0.6304, 0.3113, 0.1379, 1.0753, -0.3344, 1.4741,\n", - " -0.8532, 0.3268],\n", - " [ 0.0866, 2.1869, -1.1434, 0.6156, -0.4647, -0.5759, -0.6658, 0.3761,\n", - " -1.1228, 0.1785],\n", - " [-0.4243, -1.0760, 0.3367, 1.0454, 0.4841, 0.8989, 0.4678, -0.1791,\n", - " 2.0395, -0.9181],\n", - " [-0.8864, 0.1093, -0.5295, 0.4361, 0.7182, 0.2051, 0.0169, -0.8813,\n", - " 0.3688, 1.3760],\n", - " [-0.5702, -0.1380, 1.2921, 1.0146, 0.1518, 1.2922, 0.6758, -0.1043,\n", - " 1.1502, 0.4387],\n", - " [ 0.5554, 0.5230, 0.8402, -1.3635, -0.3173, 0.3087, 0.7613, -2.0258,\n", - " 0.1627, 0.9464],\n", - " [ 0.7489, -0.2015, 0.8231, 1.2146, 0.9384, 0.6064, 0.6412, 0.0341,\n", - " 1.0204, -0.0650],\n", - " [ 0.2682, 1.0076, -1.5576, -0.3533, -0.1235, 2.4824, 1.1433, 0.5006,\n", - " 0.0619, 0.2076],\n", - " [ 1.1212, 1.7072, -0.4859, 0.3846, -0.6056, 1.4281, 0.5899, 0.1623,\n", - " 1.9957, 0.3370],\n", - " [ 0.9890, -1.0958, -0.5957, -0.4671, -0.2902, -0.0344, -0.8092, -0.5606,\n", - " -0.5501, -1.3897],\n", - " [ 0.8188, -0.6413, 0.2777, 0.7005, -0.2147, -0.0083, 0.6479, -1.1746,\n", - " -1.2938, 2.7529],\n", - " [-0.2992, 1.4363, 0.4173, 2.1196, 0.1661, -0.6726, 1.1396, -0.0788,\n", - " -0.9447, 0.5560],\n", - " [ 1.8925, -1.7427, -1.7261, -0.3420, 1.3588, -0.4280, -0.2186, -0.1555,\n", - " -0.4196, -0.6740],\n", - " [-0.9354, -0.1141, 0.3492, 0.5924, -1.0574, -1.2510, 0.9382, 0.2804,\n", - " -1.0396, -1.6244]])\n", + "tensor([[-0.5495, 0.3131, 0.3228, 1.3840, 0.1736, -0.5699, -0.5469, -0.1029,\n", + " 0.4430, -0.1313],\n", + " [ 1.6023, -0.0431, 0.1905, -1.2841, -0.6138, 1.2643, -2.4099, -1.4513,\n", + " 0.6383, -0.2009],\n", + " [-0.8383, -0.6552, 1.0406, -1.5920, 0.4239, 0.4551, -0.2714, 0.5442,\n", + " -1.3059, -2.6276],\n", + " [-0.3160, 1.8964, -0.2690, -0.8876, 0.7519, -0.9116, -0.5473, -0.3028,\n", + " -0.3744, 0.6574],\n", + " [-0.6114, 0.4902, -0.4574, -0.7184, -1.7202, 0.1125, 0.7156, 2.1900,\n", + " 0.2498, -0.7416],\n", + " [-1.2556, -1.1005, 0.5423, -0.4725, -0.1278, -1.4557, -0.3942, 0.4152,\n", + " -0.4224, -0.2448],\n", + " [-1.1054, -1.8207, -0.6234, 1.0362, -0.7470, -1.2543, -0.1024, -3.1436,\n", + " 0.5020, 0.1043],\n", + " [-0.4296, 0.7543, -1.0903, 0.5653, 0.5865, -0.1357, 1.0432, -2.1515,\n", + " 0.9859, 0.1869],\n", + " [ 1.2781, -0.0249, -2.4766, -0.6888, 2.4392, -0.3861, 1.1405, -0.3728,\n", + " -0.3757, 0.5114],\n", + " [-1.3493, 0.4288, 0.1204, -0.5416, -0.6372, 0.0356, 1.5299, 3.0577,\n", + " -0.0736, -0.6135],\n", + " [ 0.3671, -0.6893, -1.8283, 0.0539, -1.1218, 0.6083, 1.0533, -1.0715,\n", + " 0.9260, 0.2665],\n", + " [ 0.3744, 0.5221, -0.5731, -1.8915, -1.7480, -1.1056, -0.9834, -2.1452,\n", + " 0.2002, -0.1863],\n", + " [ 0.4086, -0.0441, 1.4076, 0.3931, -0.2699, 0.1125, -0.2010, 0.5686,\n", + " 1.4326, 0.6409],\n", + " [-0.3705, 0.4681, -0.8285, -2.4345, -0.8233, 1.6192, 1.0805, -1.1024,\n", + " -0.8309, -0.8049]])\n", "AlexNet(\n", " (features): Sequential(\n", " (0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))\n", @@ -157,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "id": "6e18f2fd", "metadata": {}, "outputs": [ @@ -165,7 +183,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "CUDA is not available. Training on CPU ...\n" + "CUDA is available! Training on GPU ...\n" ] } ], @@ -191,7 +209,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "id": "462666a2", "metadata": {}, "outputs": [ @@ -199,21 +217,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to data\\cifar-10-python.tar.gz\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 170M/170M [00:48<00:00, 3.52MB/s] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Extracting data\\cifar-10-python.tar.gz to data\n", + "Files already downloaded and verified\n", "Files already downloaded and verified\n" ] } @@ -286,7 +290,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "id": "317bf070", "metadata": {}, "outputs": [ @@ -350,10 +354,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "4b53f229", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch: 0 \tTraining Loss: 43.364632 \tValidation Loss: 38.230504\n", + "Validation loss decreased (inf --> 38.230504). Saving model ...\n", + "Epoch: 1 \tTraining Loss: 34.865927 \tValidation Loss: 33.684661\n", + "Validation loss decreased (38.230504 --> 33.684661). Saving model ...\n", + "Epoch: 2 \tTraining Loss: 30.692502 \tValidation Loss: 29.130355\n", + "Validation loss decreased (33.684661 --> 29.130355). Saving model ...\n", + "Epoch: 3 \tTraining Loss: 28.641049 \tValidation Loss: 28.378742\n", + "Validation loss decreased (29.130355 --> 28.378742). Saving model ...\n", + "Epoch: 4 \tTraining Loss: 27.138877 \tValidation Loss: 26.230459\n", + "Validation loss decreased (28.378742 --> 26.230459). Saving model ...\n", + "Epoch: 5 \tTraining Loss: 25.893545 \tValidation Loss: 25.292490\n", + "Validation loss decreased (26.230459 --> 25.292490). Saving model ...\n", + "Epoch: 6 \tTraining Loss: 24.842854 \tValidation Loss: 24.485009\n", + "Validation loss decreased (25.292490 --> 24.485009). Saving model ...\n", + "Epoch: 7 \tTraining Loss: 23.926138 \tValidation Loss: 24.722069\n", + "Epoch: 8 \tTraining Loss: 23.031194 \tValidation Loss: 23.820090\n", + "Validation loss decreased (24.485009 --> 23.820090). Saving model ...\n", + "Epoch: 9 \tTraining Loss: 22.231426 \tValidation Loss: 22.906112\n", + "Validation loss decreased (23.820090 --> 22.906112). Saving model ...\n", + "Epoch: 10 \tTraining Loss: 21.461538 \tValidation Loss: 22.387376\n", + "Validation loss decreased (22.906112 --> 22.387376). Saving model ...\n", + "Epoch: 11 \tTraining Loss: 20.719964 \tValidation Loss: 21.980570\n", + "Validation loss decreased (22.387376 --> 21.980570). Saving model ...\n", + "Epoch: 12 \tTraining Loss: 20.001674 \tValidation Loss: 21.879977\n", + "Validation loss decreased (21.980570 --> 21.879977). Saving model ...\n", + "Epoch: 13 \tTraining Loss: 19.320499 \tValidation Loss: 21.871980\n", + "Validation loss decreased (21.879977 --> 21.871980). Saving model ...\n", + "Epoch: 14 \tTraining Loss: 18.759199 \tValidation Loss: 21.631741\n", + "Validation loss decreased (21.871980 --> 21.631741). Saving model ...\n", + "Epoch: 15 \tTraining Loss: 18.190729 \tValidation Loss: 21.728372\n", + "Epoch: 16 \tTraining Loss: 17.555403 \tValidation Loss: 21.431064\n", + "Validation loss decreased (21.631741 --> 21.431064). Saving model ...\n", + "Epoch: 17 \tTraining Loss: 17.052087 \tValidation Loss: 21.122314\n", + "Validation loss decreased (21.431064 --> 21.122314). Saving model ...\n", + "Epoch: 18 \tTraining Loss: 16.523702 \tValidation Loss: 21.020531\n", + "Validation loss decreased (21.122314 --> 21.020531). Saving model ...\n", + "Epoch: 19 \tTraining Loss: 15.990338 \tValidation Loss: 21.419434\n", + "Epoch: 20 \tTraining Loss: 15.540147 \tValidation Loss: 22.715216\n", + "Epoch: 21 \tTraining Loss: 15.053955 \tValidation Loss: 21.344116\n", + "Epoch: 22 \tTraining Loss: 14.620870 \tValidation Loss: 21.724516\n", + "Epoch: 23 \tTraining Loss: 14.099778 \tValidation Loss: 23.172254\n", + "Epoch: 24 \tTraining Loss: 13.740382 \tValidation Loss: 22.565675\n", + "Epoch: 25 \tTraining Loss: 13.261968 \tValidation Loss: 23.687115\n", + "Epoch: 26 \tTraining Loss: 12.842589 \tValidation Loss: 23.510745\n", + "Epoch: 27 \tTraining Loss: 12.429801 \tValidation Loss: 22.843567\n", + "Epoch: 28 \tTraining Loss: 12.002632 \tValidation Loss: 24.907918\n", + "Epoch: 29 \tTraining Loss: 11.709404 \tValidation Loss: 25.430181\n" + ] + } + ], "source": [ "import torch.optim as optim\n", "\n", @@ -434,10 +492,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "d39df818", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -458,10 +529,31 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "e93efdfc", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Loss: 21.750090\n", + "\n", + "Test Accuracy of airplane: 68% (685/1000)\n", + "Test Accuracy of automobile: 75% (752/1000)\n", + "Test Accuracy of bird: 51% (512/1000)\n", + "Test Accuracy of cat: 37% (374/1000)\n", + "Test Accuracy of deer: 61% (615/1000)\n", + "Test Accuracy of dog: 57% (570/1000)\n", + "Test Accuracy of frog: 68% (688/1000)\n", + "Test Accuracy of horse: 64% (646/1000)\n", + "Test Accuracy of ship: 73% (736/1000)\n", + "Test Accuracy of truck: 73% (739/1000)\n", + "\n", + "Test Accuracy (Overall): 63% (6317/10000)\n" + ] + } + ], "source": [ "model.load_state_dict(torch.load(\"./model_cifar.pt\"))\n", "\n", @@ -542,6 +634,355 @@ "Compare the results obtained with this new network to those obtained previously." ] }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CustomCNN(\n", + " (conv1): Conv2d(3, 16, kernel_size=(3, 3), stride=(1, 1))\n", + " (conv2): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1))\n", + " (conv3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1))\n", + " (fc1): Linear(in_features=256, 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", + " (dropout): Dropout(p=0.3, inplace=False)\n", + " (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + ")\n" + ] + } + ], + "source": [ + "\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "class CustomCNN(nn.Module):\n", + " def __init__(self, dropout_rate=0.3): # Suggested dropout rate\n", + " super(CustomCNN, self).__init__()\n", + " \n", + " # Convolutional layers\n", + " self.conv1 = nn.Conv2d(3, 16, 3, 1) \n", + " self.conv2 = nn.Conv2d(16, 32, 3, 1)\n", + " self.conv3 = nn.Conv2d(32, 64, 3, 1)\n", + " \n", + " # Fully connected layers\n", + " \n", + " self.fc1 = nn.Linear(64 * 2 * 2, 512) # Corrigé en fonction de la forme réelle\n", + " # Adjust input size based on input dimensions after pooling\n", + " self.fc2 = nn.Linear(512, 64)\n", + " self.fc3 = nn.Linear(64, 10) # Assume 10 output classes\n", + " \n", + " # Dropout\n", + " self.dropout = nn.Dropout(p=dropout_rate)\n", + " \n", + " # Pooling\n", + " self.pool = nn.MaxPool2d(kernel_size=2)\n", + "\n", + " def forward(self, x):\n", + " # Convolutional layers with ReLU and pooling\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", + " # Flatten for fully connected layers\n", + " x = x.view(x.size(0), -1) # Flatten the tensor\n", + " \n", + " # Fully connected layers with dropout and ReLU\n", + " x = self.dropout(F.relu(self.fc1(x)))\n", + " x = self.dropout(F.relu(self.fc2(x)))\n", + " x = self.fc3(x)\n", + " \n", + " return x\n", + " \n", + " # create a complete CNN\n", + "model2 = CustomCNN()\n", + "print(model2)\n", + "# move tensors to GPU if CUDA is available\n", + "if train_on_gpu:\n", + " model2.cuda()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data type: torch.cuda.FloatTensor\n", + "Target type: torch.cuda.LongTensor\n", + "Model weight type: torch.cuda.FloatTensor\n" + ] + } + ], + "source": [ + "print(f\"Data type: {data.type()}\")\n", + "print(f\"Target type: {target.type()}\")\n", + "print(f\"Model weight type: {next(model2.parameters()).type()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch: 0 \tTraining Loss: 46.035675 \tValidation Loss: 45.975821\n", + "Validation loss decreased (inf --> 45.975821). Saving model ...\n", + "Epoch: 1 \tTraining Loss: 44.774520 \tValidation Loss: 39.919449\n", + "Validation loss decreased (45.975821 --> 39.919449). Saving model ...\n", + "Epoch: 2 \tTraining Loss: 37.829997 \tValidation Loss: 34.754118\n", + "Validation loss decreased (39.919449 --> 34.754118). Saving model ...\n", + "Epoch: 3 \tTraining Loss: 34.342419 \tValidation Loss: 31.707682\n", + "Validation loss decreased (34.754118 --> 31.707682). Saving model ...\n", + "Epoch: 4 \tTraining Loss: 31.886869 \tValidation Loss: 29.301074\n", + "Validation loss decreased (31.707682 --> 29.301074). Saving model ...\n", + "Epoch: 5 \tTraining Loss: 29.959563 \tValidation Loss: 27.393316\n", + "Validation loss decreased (29.301074 --> 27.393316). Saving model ...\n", + "Epoch: 6 \tTraining Loss: 28.335243 \tValidation Loss: 26.011092\n", + "Validation loss decreased (27.393316 --> 26.011092). Saving model ...\n", + "Epoch: 7 \tTraining Loss: 26.877296 \tValidation Loss: 25.462102\n", + "Validation loss decreased (26.011092 --> 25.462102). Saving model ...\n", + "Epoch: 8 \tTraining Loss: 25.688337 \tValidation Loss: 23.929076\n", + "Validation loss decreased (25.462102 --> 23.929076). Saving model ...\n", + "Epoch: 9 \tTraining Loss: 24.511723 \tValidation Loss: 23.005517\n", + "Validation loss decreased (23.929076 --> 23.005517). Saving model ...\n", + "Epoch: 10 \tTraining Loss: 23.442500 \tValidation Loss: 21.846585\n", + "Validation loss decreased (23.005517 --> 21.846585). Saving model ...\n", + "Epoch: 11 \tTraining Loss: 22.508246 \tValidation Loss: 21.496349\n", + "Validation loss decreased (21.846585 --> 21.496349). Saving model ...\n", + "Epoch: 12 \tTraining Loss: 21.597253 \tValidation Loss: 20.689961\n", + "Validation loss decreased (21.496349 --> 20.689961). Saving model ...\n", + "Epoch: 13 \tTraining Loss: 20.734205 \tValidation Loss: 20.371868\n", + "Validation loss decreased (20.689961 --> 20.371868). Saving model ...\n", + "Epoch: 14 \tTraining Loss: 19.955687 \tValidation Loss: 19.954705\n", + "Validation loss decreased (20.371868 --> 19.954705). Saving model ...\n", + "Epoch: 15 \tTraining Loss: 19.160272 \tValidation Loss: 19.218016\n", + "Validation loss decreased (19.954705 --> 19.218016). Saving model ...\n", + "Epoch: 16 \tTraining Loss: 18.510708 \tValidation Loss: 18.665482\n", + "Validation loss decreased (19.218016 --> 18.665482). Saving model ...\n", + "Epoch: 17 \tTraining Loss: 17.827416 \tValidation Loss: 18.159658\n", + "Validation loss decreased (18.665482 --> 18.159658). Saving model ...\n", + "Epoch: 18 \tTraining Loss: 17.263423 \tValidation Loss: 18.637613\n", + "Epoch: 19 \tTraining Loss: 16.628417 \tValidation Loss: 18.351383\n", + "Epoch: 20 \tTraining Loss: 16.099328 \tValidation Loss: 18.048815\n", + "Validation loss decreased (18.159658 --> 18.048815). Saving model ...\n", + "Epoch: 21 \tTraining Loss: 15.631801 \tValidation Loss: 17.525234\n", + "Validation loss decreased (18.048815 --> 17.525234). Saving model ...\n", + "Epoch: 22 \tTraining Loss: 15.120226 \tValidation Loss: 17.490126\n", + "Validation loss decreased (17.525234 --> 17.490126). Saving model ...\n", + "Epoch: 23 \tTraining Loss: 14.537110 \tValidation Loss: 17.395222\n", + "Validation loss decreased (17.490126 --> 17.395222). Saving model ...\n", + "Epoch: 24 \tTraining Loss: 14.215695 \tValidation Loss: 18.225738\n", + "Epoch: 25 \tTraining Loss: 13.695718 \tValidation Loss: 17.967939\n", + "Epoch: 26 \tTraining Loss: 13.258958 \tValidation Loss: 18.192724\n", + "Epoch: 27 \tTraining Loss: 12.916577 \tValidation Loss: 17.067720\n", + "Validation loss decreased (17.395222 --> 17.067720). Saving model ...\n", + "Epoch: 28 \tTraining Loss: 12.522411 \tValidation Loss: 16.906484\n", + "Validation loss decreased (17.067720 --> 16.906484). Saving model ...\n", + "Epoch: 29 \tTraining Loss: 12.141524 \tValidation Loss: 17.213408\n" + ] + } + ], + "source": [ + "import torch.optim as optim\n", + "\n", + "criterion = nn.CrossEntropyLoss() # specify loss function\n", + "optimizer = optim.SGD(model2.parameters(), lr=0.01) # specify optimizer\n", + "\n", + "n_epochs = 30 # number of epochs to train the model\n", + "train_loss_list2 = [] # list to store loss to visualize\n", + "valid_loss_min2 = 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_loss2 = 0.0\n", + " valid_loss2 = 0.0\n", + "\n", + " # Train the model\n", + " model2.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", + " output2 = model2(data)\n", + " # Calculate the batch loss\n", + " loss2 = criterion(output2, target)\n", + " # Backward pass: compute gradient of the loss with respect to model parameters\n", + " loss2.backward()\n", + " # Perform a single optimization step (parameter update)\n", + " optimizer.step()\n", + " # Update training loss\n", + " train_loss2 += loss2.item() * data.size(0)\n", + "\n", + " # Validate the model\n", + " model2.eval()\n", + " with torch.no_grad():\n", + " for data, target in valid_loader:\n", + " if train_on_gpu:\n", + " data, target = data.cuda(), target.cuda()\n", + " else:\n", + " data, target = data.cpu(), target.cpu()\n", + " output2 = model2(data)\n", + " loss2 = criterion(output2, target)\n", + " valid_loss2 += loss2.item() * data.size(0)\n", + "\n", + " \n", + " # Calculate average losses\n", + " train_loss2 = train_loss2 / len(train_loader)\n", + " valid_loss2 = valid_loss2 / len(valid_loader)\n", + " train_loss_list2.append(train_loss2)\n", + "\n", + " # Print training/validation statistics\n", + " print(\n", + " \"Epoch: {} \\tTraining Loss: {:.6f} \\tValidation Loss: {:.6f}\".format(\n", + " epoch, train_loss2, valid_loss2\n", + " )\n", + " )\n", + "\n", + " # Save model if validation loss has decreased\n", + " if valid_loss2 <= valid_loss_min2:\n", + " print(\n", + " \"Validation loss decreased ({:.6f} --> {:.6f}). Saving model ...\".format(\n", + " valid_loss_min2, valid_loss2\n", + " )\n", + " )\n", + " torch.save(model2.state_dict(), \"model_cifar2.pt\")\n", + " valid_loss_min2 = valid_loss2" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plt.plot(range(n_epochs), train_loss_list2)\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.title(\"Performance of Model 2\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Loss: 17.564571\n", + "\n", + "Test Accuracy of airplane: 77% (776/1000)\n", + "Test Accuracy of automobile: 79% (793/1000)\n", + "Test Accuracy of bird: 63% (639/1000)\n", + "Test Accuracy of cat: 50% (506/1000)\n", + "Test Accuracy of deer: 65% (655/1000)\n", + "Test Accuracy of dog: 53% (536/1000)\n", + "Test Accuracy of frog: 83% (835/1000)\n", + "Test Accuracy of horse: 70% (709/1000)\n", + "Test Accuracy of ship: 80% (801/1000)\n", + "Test Accuracy of truck: 84% (841/1000)\n", + "\n", + "Test Accuracy (Overall): 70% (7091/10000)\n" + ] + } + ], + "source": [ + "model2.load_state_dict(torch.load(\"./model_cifar2.pt\"))\n", + "\n", + "# track test loss\n", + "test_loss2 = 0.0\n", + "class_correct2 = list(0.0 for i in range(10))\n", + "class_total2 = list(0.0 for i in range(10))\n", + "\n", + "model2.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 = model2(data)\n", + " # calculate the batch loss\n", + " loss2 = criterion(output, target)\n", + " # update test loss\n", + " test_loss2 += loss2.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_correct2[label] += correct[i].item()\n", + " class_total2[label] += 1\n", + "\n", + "# average test loss\n", + "test_loss2 = test_loss2 / len(test_loader)\n", + "print(\"Test Loss: {:.6f}\\n\".format(test_loss2))\n", + "\n", + "for i in range(10):\n", + " if class_total2[i] > 0:\n", + " print(\n", + " \"Test Accuracy of %5s: %2d%% (%2d/%2d)\"\n", + " % (\n", + " classes[i],\n", + " 100 * class_correct2[i] / class_total2[i],\n", + " np.sum(class_correct2[i]),\n", + " np.sum(class_total2[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_correct2) / np.sum(class_total2),\n", + " np.sum(class_correct2),\n", + " np.sum(class_total2),\n", + " )\n", + ")" + ] + }, { "cell_type": "markdown", "id": "bc381cf4", @@ -559,10 +1000,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "ef623c26", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "model: fp32 \t Size (KB): 251.529\n" + ] + }, + { + "data": { + "text/plain": [ + "251529" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import os\n", "\n", @@ -588,10 +1047,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "c4c65d4b", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "RuntimeError", + "evalue": "iter.device(arg).is_cuda() INTERNAL ASSERT FAILED at \"/opt/conda/conda-bld/pytorch_1603729096996/work/aten/src/ATen/native/cuda/Loops.cuh\":94, please report a bug to PyTorch. ", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-22-b8bd12179156>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mquantized_model\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantization\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize_dynamic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mqint8\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mprint_size_of_model\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquantized_model\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"int8\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/quantization/quantize.py\u001b[0m in \u001b[0;36mquantize_dynamic\u001b[0;34m(model, qconfig_spec, dtype, mapping, inplace)\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 348\u001b[0m \u001b[0mpropagate_qconfig_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mqconfig_spec\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 349\u001b[0;31m \u001b[0mconvert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 350\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 351\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/quantization/quantize.py\u001b[0m in \u001b[0;36mconvert\u001b[0;34m(module, mapping, inplace, remove_qconfig)\u001b[0m\n\u001b[1;32m 412\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0minplace\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 413\u001b[0m \u001b[0mmodule\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdeepcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodule\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 414\u001b[0;31m \u001b[0m_convert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodule\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 415\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mremove_qconfig\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 416\u001b[0m \u001b[0m_remove_qconfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodule\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/quantization/quantize.py\u001b[0m in \u001b[0;36m_convert\u001b[0;34m(module, mapping, inplace)\u001b[0m\n\u001b[1;32m 457\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mis_observed_custom_module\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 458\u001b[0m \u001b[0m_convert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minplace\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 459\u001b[0;31m \u001b[0mreassign\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mswap_module\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 460\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 461\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mreassign\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/quantization/quantize.py\u001b[0m in \u001b[0;36mswap_module\u001b[0;34m(mod, mapping)\u001b[0m\n\u001b[1;32m 483\u001b[0m \u001b[0mswapped\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 484\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 485\u001b[0;31m \u001b[0mnew_mod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_float\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 486\u001b[0m \u001b[0mswapped\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 487\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/nn/quantized/dynamic/modules/linear.py\u001b[0m in \u001b[0;36mfrom_float\u001b[0;34m(cls, mod)\u001b[0m\n\u001b[1;32m 93\u001b[0m \u001b[0mdtype\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mweight_observer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mdtype\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mqint8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat16\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'The only supported dtypes for dynamic quantized linear are qint8 and float16'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 95\u001b[0;31m \u001b[0mweight_observer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweight\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 96\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdtype\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mqint8\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0mqweight\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_quantize_weight\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmod\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweight\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mweight_observer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[0;34m(self, *input, **kwargs)\u001b[0m\n\u001b[1;32m 725\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_slow_forward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 726\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 727\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mforward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 728\u001b[0m for hook in itertools.chain(\n\u001b[1;32m 729\u001b[0m \u001b[0m_global_forward_hooks\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/opt/conda/envs/pytorch/lib/python3.8/site-packages/torch/quantization/observer.py\u001b[0m in \u001b[0;36mforward\u001b[0;34m(self, x_orig)\u001b[0m\n\u001b[1;32m 391\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmin_val\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 392\u001b[0m \u001b[0mmin_val_cur\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmax_val_cur\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_aminmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 393\u001b[0;31m \u001b[0mmin_val\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_val_cur\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmin_val\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 394\u001b[0m \u001b[0mmax_val\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmax_val_cur\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmax_val\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 395\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmin_val\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmin_val\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mRuntimeError\u001b[0m: iter.device(arg).is_cuda() INTERNAL ASSERT FAILED at \"/opt/conda/conda-bld/pytorch_1603729096996/work/aten/src/ATen/native/cuda/Loops.cuh\":94, please report a bug to PyTorch. " + ] + } + ], "source": [ "import torch.quantization\n", "\n", @@ -608,6 +1086,249 @@ "For each class, compare the classification test accuracy of the initial model and the quantized model. Also give the overall test accuracy for both models." ] }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "model: int8 \t Size (KB): 267.281\n" + ] + }, + { + "data": { + "text/plain": [ + "267281" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model2.cpu()\n", + "quantized_model2 = torch.quantization.quantize_dynamic(model2, dtype=torch.qint8)\n", + "print_size_of_model(quantized_model2, \"int8\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Après la quantification du modèle (avant d'enregistrer)\n", + "quantized_model2 = quantized_model2.to('cpu') # Déplace le modèle quantifié sur le CPU\n", + "\n", + "# Sauvegarder le modèle quantifié\n", + "torch.save(quantized_model2.state_dict(), \"quantized_model_cifar2.pt\")\n", + "\n", + "# track test loss\n", + "test_loss2q = 0.0\n", + "class_correct2q = list(0.0 for i in range(10))\n", + "class_total2q = list(0.0 for i in range(10))\n", + "\n", + "quantized_model2.eval() # Modèle en mode évaluation\n", + "# itérer sur les données de test\n", + "for data, target in test_loader:\n", + " # Déplacer les données sur le CPU si elles sont sur le GPU\n", + " data, target = data.to('cpu'), target.to('cpu') # Déplace les données et les cibles sur le CPU\n", + " \n", + " # forward pass: calcul des sorties prédites en passant les entrées dans le modèle\n", + " output = quantized_model2(data)\n", + " \n", + " # Calculer la perte par lot\n", + " loss2q = criterion(output, target)\n", + " test_loss2q += loss2q.item() * data.size(0)\n", + " \n", + " # Convertir les probabilités de sortie en classes prédites\n", + " _, pred = torch.max(output, 1)\n", + " \n", + " # Comparer les prédictions aux étiquettes vraies\n", + " correct_tensor = pred.eq(target.data.view_as(pred))\n", + " correct = (\n", + " np.squeeze(correct_tensor.numpy()) # Si non-CPU, utilisez CPU\n", + " if not train_on_gpu\n", + " else np.squeeze(correct_tensor.cpu().numpy())\n", + " )\n", + " \n", + " # Calculer la précision de test pour chaque classe d'objet\n", + " for i in range(batch_size):\n", + " label = target.data[i]\n", + " class_correct2q[label] += correct[i].item()\n", + " class_total2q[label] += 1\n", + "\n", + "# Calcul de la perte moyenne de test\n", + "test_loss2q = test_loss2q / len(test_loader)\n", + "print(\"Test Loss: {:.6f}\\n\".format(test_loss2q))\n", + "\n", + "# Affichage de la précision pour chaque classe\n", + "for i in range(10):\n", + " if class_total2q[i] > 0:\n", + " print(\n", + " \"Test Accuracy of %5s: %2d%% (%2d/%2d)\"\n", + " % (\n", + " classes[i],\n", + " 100 * class_correct2q[i] / class_total2q[i],\n", + " np.sum(class_correct2q[i]),\n", + " np.sum(class_total2q[i]),\n", + " )\n", + " )\n", + " else:\n", + " print(\"Test Accuracy of %5s: N/A (no training examples)\" % (classes[i]))\n", + "\n", + "# Affichage de la précision globale de test\n", + "print(\n", + " \"\\nTest Accuracy (Overall): %2d%% (%2d/%2d)\"\n", + " % (\n", + " 100.0 * np.sum(class_correct2q) / np.sum(class_total2q),\n", + " np.sum(class_correct2q),\n", + " np.sum(class_total2q),\n", + " )\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "\n", + "torch.save(quantized_model2.state_dict(), \"quantized_model_cifar2.pt\")\n", + "\n", + "# track test loss\n", + "test_loss2q = 0.0\n", + "class_correct2q = list(0.0 for i in range(10))\n", + "class_total2q = list(0.0 for i in range(10))\n", + "\n", + "quantized_model2.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 = quantized_model2(data)\n", + " # calculate the batch loss\n", + " loss2q = criterion(output, target)\n", + " # update test loss\n", + " test_loss2q += loss2q.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_correct2q[label] += correct[i].item()\n", + " class_total2q[label] += 1\n", + "\n", + "# average test loss\n", + "test_loss2q = test_loss2q / len(test_loader)\n", + "print(\"Test Loss: {:.6f}\\n\".format(test_loss2q))\n", + "\n", + "for i in range(10):\n", + " if class_total2q[i] > 0:\n", + " print(\n", + " \"Test Accuracy of %5s: %2d%% (%2d/%2d)\"\n", + " % (\n", + " classes[i],\n", + " 100 * class_correct2q[i] / class_total2q[i],\n", + " np.sum(class_correct2q[i]),\n", + " np.sum(class_total2q[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_correct2q) / np.sum(class_total2q),\n", + " np.sum(class_correct2q),\n", + " np.sum(class_total2q),\n", + " )\n", + ")" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "quantized_model.load_state_dict(torch.load(\"./model_cifarq.pt\"))\n", + "\n", + "# track test loss\n", + "test_lossq = 0.0\n", + "class_correctq = list(0.0 for i in range(10))\n", + "class_totalq = list(0.0 for i in range(10))\n", + "\n", + "quantized_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 = quantized_model(data)\n", + " # calculate the batch loss\n", + " lossq = criterion(output, target)\n", + " # update test loss\n", + " test_lossq += lossq.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", + " correctq = (\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_correctq[label] += correctq[i].item()\n", + " class_totalq[label] += 1\n", + "\n", + "# average test loss\n", + "test_lossq = test_lossq / len(test_loader)\n", + "print(\"Test Loss: {:.6f}\\n\".format(test_lossq))\n", + "\n", + "for i in range(10):\n", + " if class_totalq[i] > 0:\n", + " print(\n", + " \"Test Accuracy of %5s: %2d%% (%2d/%2d)\"\n", + " % (\n", + " classes[i],\n", + " 100 * class_correctq[i] / class_totalq[i],\n", + " np.sum(class_correctq[i]),\n", + " np.sum(class_totalq[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_correctq) / np.sum(class_totalq),\n", + " np.sum(class_correctq),\n", + " np.sum(class_totalq),\n", + " )\n", + ")" + ] + }, { "cell_type": "markdown", "id": "a0a34b90", @@ -694,6 +1415,148 @@ " \n" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original model size (ResNet50): 25.56 million parameters\n", + "Quantized model size (ResNet50): 23.51 million parameters\n", + "Original Model Prediction (ResNet50): Golden Retriever\n", + "Quantized Model Prediction (ResNet50): Golden Retriever\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Downloading: \"https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth\" to /home/jcaty/.cache/torch/hub/checkpoints/alexnet-owt-4df8aa71.pth\n", + "70.5%IOPub message rate exceeded.\n", + "The notebook server will temporarily stop sending output\n", + "to the client in order to avoid crashing it.\n", + "To change this limit, set the config variable\n", + "`--NotebookApp.iopub_msg_rate_limit`.\n", + "\n", + "Current values:\n", + "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", + "NotebookApp.rate_limit_window=3.0 (secs)\n", + "\n", + "8.3%IOPub message rate exceeded.\n", + "The notebook server will temporarily stop sending output\n", + "to the client in order to avoid crashing it.\n", + "To change this limit, set the config variable\n", + "`--NotebookApp.iopub_msg_rate_limit`.\n", + "\n", + "Current values:\n", + "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", + "NotebookApp.rate_limit_window=3.0 (secs)\n", + "\n", + "33.0%IOPub message rate exceeded.\n", + "The notebook server will temporarily stop sending output\n", + "to the client in order to avoid crashing it.\n", + "To change this limit, set the config variable\n", + "`--NotebookApp.iopub_msg_rate_limit`.\n", + "\n", + "Current values:\n", + "NotebookApp.iopub_msg_rate_limit=1000.0 (msgs/sec)\n", + "NotebookApp.rate_limit_window=3.0 (secs)\n", + "\n" + ] + } + ], + "source": [ + "import torch\n", + "import torch.quantization\n", + "from torchvision import models, transforms\n", + "from PIL import Image\n", + "import json\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Prepare the transformations for the input image\n", + "data_transform = transforms.Compose(\n", + " [\n", + " transforms.Resize((224, 224)),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n", + " ]\n", + ")\n", + "\n", + "# Load the labels for ImageNet (a JSON file with label mappings)\n", + "with open(\"imagenet-simple-labels.json\") as f:\n", + " labels = json.load(f)\n", + "\n", + "# Choose an image to pass through the model\n", + "test_image = \"dog.png\"\n", + "\n", + "# Load the image and apply transformations\n", + "image = Image.open(test_image)\n", + "plt.imshow(image)\n", + "plt.xticks([]), plt.yticks([]) # Hide axis ticks\n", + "plt.show()\n", + "\n", + "# Apply the data transformation and add a batch dimension\n", + "image_tensor = data_transform(image).unsqueeze(0)\n", + "\n", + "# Load the pre-trained ResNet50 model\n", + "model_resnet50 = models.resnet50(pretrained=True)\n", + "model_resnet50.eval() # Set model to evaluation mode\n", + "\n", + "# Quantize the model dynamically (8-bit integer)\n", + "quantized_model_resnet50 = torch.quantization.quantize_dynamic(model_resnet50, dtype=torch.qint8)\n", + "\n", + "# Check the original and quantized model sizes\n", + "def model_size(model):\n", + " return sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e6 # In million parameters\n", + "\n", + "original_size = model_size(model_resnet50)\n", + "quantized_size = model_size(quantized_model_resnet50)\n", + "\n", + "print(f\"Original model size (ResNet50): {original_size:.2f} million parameters\")\n", + "print(f\"Quantized model size (ResNet50): {quantized_size:.2f} million parameters\")\n", + "\n", + "# Function to predict using the model\n", + "def predict(model, image_tensor):\n", + " with torch.no_grad(): # No need to track gradients for inference\n", + " output = model(image_tensor)\n", + " _, predicted_class = torch.max(output, 1)\n", + " return labels[predicted_class.item()]\n", + "\n", + "# Test the original model (ResNet50)\n", + "print(\"Original Model Prediction (ResNet50):\", predict(model_resnet50, image_tensor))\n", + "\n", + "# Test the quantized model (ResNet50)\n", + "quantized_model_resnet50.eval() # Set to eval mode\n", + "print(\"Quantized Model Prediction (ResNet50):\", predict(quantized_model_resnet50, image_tensor))\n", + "\n", + "# Experiment with another model, e.g., AlexNet\n", + "model_alexnet = models.alexnet(pretrained=True)\n", + "model_alexnet.eval()\n", + "print(\"AlexNet Prediction:\", predict(model_alexnet, image_tensor))\n", + "\n", + "# Experiment with another model, e.g., VGG16\n", + "model_vgg16 = models.vgg16(pretrained=True)\n", + "model_vgg16.eval()\n", + "print(\"VGG16 Prediction:\", predict(model_vgg16, image_tensor))\n", + "\n", + "# You can add more models like ResNet18, DenseNet, etc., in a similar way.\n", + "\n" + ] + }, { "cell_type": "markdown", "id": "5d57da4b", @@ -1030,13 +1893,31 @@ "\n", "Alberto BOSIO - Ph. D." ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Author\n", + "\n", + "Alberto BOSIO - Ph. D." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Author\n", + "\n", + "Alberto BOSIO - Ph. D." + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python PyTorch 1.7.0", "language": "python", - "name": "python3" + "name": "pytorch-1.7.0" }, "language_info": { "codemirror_mode": { @@ -1048,7 +1929,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.8.5" }, "vscode": { "interpreter": { -- GitLab