├── PyTorch_Image_Inference.ipynb ├── PyTorch_Image_Training.ipynb └── README.md /PyTorch_Image_Training.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "%matplotlib inline\n", 10 | "%config InlineBackend.figure_format = 'retina'\n", 11 | "\n", 12 | "import matplotlib.pyplot as plt\n", 13 | "\n", 14 | "import numpy as np\n", 15 | "import torch\n", 16 | "from torch import nn\n", 17 | "from torch import optim\n", 18 | "import torch.nn.functional as F\n", 19 | "from torchvision import datasets, transforms, models" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 2, 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "name": "stdout", 29 | "output_type": "stream", 30 | "text": [ 31 | "['brush', 'commercial', 'field', 'grass', 'parking', 'play', 'residential', 'road', 'trees', 'water']\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "data_dir = '/data/train'\n", 37 | "\n", 38 | "def load_split_train_test(datadir, valid_size = .2):\n", 39 | " train_transforms = transforms.Compose([#transforms.RandomRotation(30), # data augmentations are great\n", 40 | " #transforms.RandomResizedCrop(224), # but not in this case of map tiles\n", 41 | " #transforms.RandomHorizontalFlip(),\n", 42 | " transforms.Resize(224),\n", 43 | " transforms.ToTensor(),\n", 44 | " #transforms.Normalize([0.485, 0.456, 0.406], # PyTorch recommends these but in this\n", 45 | " # [0.229, 0.224, 0.225]) # case I didn't get good results\n", 46 | " ])\n", 47 | "\n", 48 | " test_transforms = transforms.Compose([transforms.Resize(224),\n", 49 | " transforms.ToTensor(),\n", 50 | " #transforms.Normalize([0.485, 0.456, 0.406],\n", 51 | " # [0.229, 0.224, 0.225])\n", 52 | " ])\n", 53 | "\n", 54 | " train_data = datasets.ImageFolder(datadir, transform=train_transforms)\n", 55 | " test_data = datasets.ImageFolder(datadir, transform=test_transforms)\n", 56 | "\n", 57 | " num_train = len(train_data)\n", 58 | " indices = list(range(num_train))\n", 59 | " split = int(np.floor(valid_size * num_train))\n", 60 | " np.random.shuffle(indices)\n", 61 | " from torch.utils.data.sampler import SubsetRandomSampler\n", 62 | " train_idx, test_idx = indices[split:], indices[:split]\n", 63 | " train_sampler = SubsetRandomSampler(train_idx)\n", 64 | " test_sampler = SubsetRandomSampler(test_idx)\n", 65 | " trainloader = torch.utils.data.DataLoader(train_data, sampler=train_sampler, batch_size=64)\n", 66 | " testloader = torch.utils.data.DataLoader(test_data, sampler=test_sampler, batch_size=64)\n", 67 | " return trainloader, testloader\n", 68 | "\n", 69 | "trainloader, testloader = load_split_train_test(data_dir, .2)\n", 70 | "print(trainloader.dataset.classes)\n" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 3, 76 | "metadata": { 77 | "scrolled": true 78 | }, 79 | "outputs": [ 80 | { 81 | "data": { 82 | "text/plain": [ 83 | "ResNet(\n", 84 | " (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)\n", 85 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 86 | " (relu): ReLU(inplace)\n", 87 | " (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)\n", 88 | " (layer1): Sequential(\n", 89 | " (0): Bottleneck(\n", 90 | " (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 91 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 92 | " (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 93 | " (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 94 | " (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 95 | " (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 96 | " (relu): ReLU(inplace)\n", 97 | " (downsample): Sequential(\n", 98 | " (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 99 | " (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 100 | " )\n", 101 | " )\n", 102 | " (1): Bottleneck(\n", 103 | " (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 104 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 105 | " (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 106 | " (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 107 | " (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 108 | " (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 109 | " (relu): ReLU(inplace)\n", 110 | " )\n", 111 | " (2): Bottleneck(\n", 112 | " (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 113 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 114 | " (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 115 | " (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 116 | " (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 117 | " (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 118 | " (relu): ReLU(inplace)\n", 119 | " )\n", 120 | " )\n", 121 | " (layer2): Sequential(\n", 122 | " (0): Bottleneck(\n", 123 | " (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 124 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 125 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", 126 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 127 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 128 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 129 | " (relu): ReLU(inplace)\n", 130 | " (downsample): Sequential(\n", 131 | " (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)\n", 132 | " (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 133 | " )\n", 134 | " )\n", 135 | " (1): Bottleneck(\n", 136 | " (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 137 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 138 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 139 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 140 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 141 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 142 | " (relu): ReLU(inplace)\n", 143 | " )\n", 144 | " (2): Bottleneck(\n", 145 | " (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 146 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 147 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 148 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 149 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 150 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 151 | " (relu): ReLU(inplace)\n", 152 | " )\n", 153 | " (3): Bottleneck(\n", 154 | " (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 155 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 156 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 157 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 158 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 159 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 160 | " (relu): ReLU(inplace)\n", 161 | " )\n", 162 | " )\n", 163 | " (layer3): Sequential(\n", 164 | " (0): Bottleneck(\n", 165 | " (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 166 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 167 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", 168 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 169 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 170 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 171 | " (relu): ReLU(inplace)\n", 172 | " (downsample): Sequential(\n", 173 | " (0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False)\n", 174 | " (1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 175 | " )\n", 176 | " )\n", 177 | " (1): Bottleneck(\n", 178 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 179 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 180 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 181 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 182 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 183 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 184 | " (relu): ReLU(inplace)\n", 185 | " )\n", 186 | " (2): Bottleneck(\n", 187 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 188 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 189 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 190 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 191 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 192 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 193 | " (relu): ReLU(inplace)\n", 194 | " )\n", 195 | " (3): Bottleneck(\n", 196 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 197 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 198 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 199 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 200 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 201 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 202 | " (relu): ReLU(inplace)\n", 203 | " )\n", 204 | " (4): Bottleneck(\n", 205 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 206 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 207 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 208 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 209 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 210 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 211 | " (relu): ReLU(inplace)\n", 212 | " )\n", 213 | " (5): Bottleneck(\n", 214 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 215 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 216 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 217 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 218 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 219 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 220 | " (relu): ReLU(inplace)\n", 221 | " )\n", 222 | " )\n", 223 | " (layer4): Sequential(\n", 224 | " (0): Bottleneck(\n", 225 | " (conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 226 | " (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 227 | " (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", 228 | " (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 229 | " (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 230 | " (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 231 | " (relu): ReLU(inplace)\n", 232 | " (downsample): Sequential(\n", 233 | " (0): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2), bias=False)\n", 234 | " (1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 235 | " )\n", 236 | " )\n", 237 | " (1): Bottleneck(\n", 238 | " (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 239 | " (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 240 | " (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 241 | " (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 242 | " (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 243 | " (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 244 | " (relu): ReLU(inplace)\n", 245 | " )\n", 246 | " (2): Bottleneck(\n", 247 | " (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 248 | " (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 249 | " (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 250 | " (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 251 | " (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 252 | " (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 253 | " (relu): ReLU(inplace)\n", 254 | " )\n", 255 | " )\n", 256 | " (avgpool): AvgPool2d(kernel_size=7, stride=1, padding=0)\n", 257 | " (fc): Linear(in_features=2048, out_features=1000, bias=True)\n", 258 | ")" 259 | ] 260 | }, 261 | "execution_count": 3, 262 | "metadata": {}, 263 | "output_type": "execute_result" 264 | } 265 | ], 266 | "source": [ 267 | "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", 268 | "model = models.resnet50(pretrained=True)\n", 269 | "model" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 4, 275 | "metadata": {}, 276 | "outputs": [ 277 | { 278 | "data": { 279 | "text/plain": [ 280 | "ResNet(\n", 281 | " (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)\n", 282 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 283 | " (relu): ReLU(inplace)\n", 284 | " (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)\n", 285 | " (layer1): Sequential(\n", 286 | " (0): Bottleneck(\n", 287 | " (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 288 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 289 | " (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 290 | " (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 291 | " (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 292 | " (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 293 | " (relu): ReLU(inplace)\n", 294 | " (downsample): Sequential(\n", 295 | " (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 296 | " (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 297 | " )\n", 298 | " )\n", 299 | " (1): Bottleneck(\n", 300 | " (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 301 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 302 | " (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 303 | " (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 304 | " (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 305 | " (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 306 | " (relu): ReLU(inplace)\n", 307 | " )\n", 308 | " (2): Bottleneck(\n", 309 | " (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 310 | " (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 311 | " (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 312 | " (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 313 | " (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 314 | " (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 315 | " (relu): ReLU(inplace)\n", 316 | " )\n", 317 | " )\n", 318 | " (layer2): Sequential(\n", 319 | " (0): Bottleneck(\n", 320 | " (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 321 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 322 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", 323 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 324 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 325 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 326 | " (relu): ReLU(inplace)\n", 327 | " (downsample): Sequential(\n", 328 | " (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)\n", 329 | " (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 330 | " )\n", 331 | " )\n", 332 | " (1): Bottleneck(\n", 333 | " (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 334 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 335 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 336 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 337 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 338 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 339 | " (relu): ReLU(inplace)\n", 340 | " )\n", 341 | " (2): Bottleneck(\n", 342 | " (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 343 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 344 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 345 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 346 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 347 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 348 | " (relu): ReLU(inplace)\n", 349 | " )\n", 350 | " (3): Bottleneck(\n", 351 | " (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 352 | " (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 353 | " (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 354 | " (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 355 | " (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 356 | " (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 357 | " (relu): ReLU(inplace)\n", 358 | " )\n", 359 | " )\n", 360 | " (layer3): Sequential(\n", 361 | " (0): Bottleneck(\n", 362 | " (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 363 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 364 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", 365 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 366 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 367 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 368 | " (relu): ReLU(inplace)\n", 369 | " (downsample): Sequential(\n", 370 | " (0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False)\n", 371 | " (1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 372 | " )\n", 373 | " )\n", 374 | " (1): Bottleneck(\n", 375 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 376 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 377 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 378 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 379 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 380 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 381 | " (relu): ReLU(inplace)\n", 382 | " )\n", 383 | " (2): Bottleneck(\n", 384 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 385 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 386 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 387 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 388 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 389 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 390 | " (relu): ReLU(inplace)\n", 391 | " )\n", 392 | " (3): Bottleneck(\n", 393 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 394 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 395 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 396 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 397 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 398 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 399 | " (relu): ReLU(inplace)\n", 400 | " )\n", 401 | " (4): Bottleneck(\n", 402 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 403 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 404 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 405 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 406 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 407 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 408 | " (relu): ReLU(inplace)\n", 409 | " )\n", 410 | " (5): Bottleneck(\n", 411 | " (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 412 | " (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 413 | " (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 414 | " (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 415 | " (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 416 | " (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 417 | " (relu): ReLU(inplace)\n", 418 | " )\n", 419 | " )\n", 420 | " (layer4): Sequential(\n", 421 | " (0): Bottleneck(\n", 422 | " (conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 423 | " (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 424 | " (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", 425 | " (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 426 | " (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 427 | " (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 428 | " (relu): ReLU(inplace)\n", 429 | " (downsample): Sequential(\n", 430 | " (0): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2), bias=False)\n", 431 | " (1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 432 | " )\n", 433 | " )\n", 434 | " (1): Bottleneck(\n", 435 | " (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 436 | " (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 437 | " (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 438 | " (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 439 | " (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 440 | " (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 441 | " (relu): ReLU(inplace)\n", 442 | " )\n", 443 | " (2): Bottleneck(\n", 444 | " (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 445 | " (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 446 | " (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", 447 | " (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 448 | " (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", 449 | " (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", 450 | " (relu): ReLU(inplace)\n", 451 | " )\n", 452 | " )\n", 453 | " (avgpool): AvgPool2d(kernel_size=7, stride=1, padding=0)\n", 454 | " (fc): Sequential(\n", 455 | " (0): Linear(in_features=2048, out_features=512, bias=True)\n", 456 | " (1): ReLU()\n", 457 | " (2): Dropout(p=0.2)\n", 458 | " (3): Linear(in_features=512, out_features=10, bias=True)\n", 459 | " (4): LogSoftmax()\n", 460 | " )\n", 461 | ")" 462 | ] 463 | }, 464 | "execution_count": 4, 465 | "metadata": {}, 466 | "output_type": "execute_result" 467 | } 468 | ], 469 | "source": [ 470 | "# Freeze parameters so we don't backprop through them\n", 471 | "for param in model.parameters():\n", 472 | " param.requires_grad = False\n", 473 | " \n", 474 | "model.fc = nn.Sequential(nn.Linear(2048, 512),\n", 475 | " nn.ReLU(),\n", 476 | " nn.Dropout(0.2),\n", 477 | " nn.Linear(512, 10),\n", 478 | " nn.LogSoftmax(dim=1))\n", 479 | "criterion = nn.NLLLoss()\n", 480 | "optimizer = optim.Adam(model.fc.parameters(), lr=0.003)\n", 481 | "model.to(device)" 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": 5, 487 | "metadata": {}, 488 | "outputs": [ 489 | { 490 | "name": "stdout", 491 | "output_type": "stream", 492 | "text": [ 493 | "Epoch 1/1.. Train loss: 2.624.. Test loss: 1.574.. Test accuracy: 0.538\n", 494 | "Epoch 1/1.. Train loss: 0.946.. Test loss: 1.245.. Test accuracy: 0.649\n", 495 | "Epoch 1/1.. Train loss: 0.710.. Test loss: 1.130.. Test accuracy: 0.686\n", 496 | "Epoch 1/1.. Train loss: 0.553.. Test loss: 0.542.. Test accuracy: 0.838\n", 497 | "Epoch 1/1.. Train loss: 0.521.. Test loss: 0.447.. Test accuracy: 0.865\n", 498 | "Epoch 1/1.. Train loss: 0.513.. Test loss: 0.404.. Test accuracy: 0.877\n", 499 | "Epoch 1/1.. Train loss: 0.431.. Test loss: 0.399.. Test accuracy: 0.872\n", 500 | "Epoch 1/1.. Train loss: 0.582.. Test loss: 0.370.. Test accuracy: 0.879\n", 501 | "Epoch 1/1.. Train loss: 0.463.. Test loss: 0.419.. Test accuracy: 0.879\n", 502 | "Epoch 1/1.. Train loss: 0.452.. Test loss: 0.367.. Test accuracy: 0.887\n", 503 | "Epoch 1/1.. Train loss: 0.426.. Test loss: 0.377.. Test accuracy: 0.878\n", 504 | "Epoch 1/1.. Train loss: 0.396.. Test loss: 0.414.. Test accuracy: 0.864\n", 505 | "Epoch 1/1.. Train loss: 0.432.. Test loss: 0.335.. Test accuracy: 0.889\n", 506 | "Epoch 1/1.. Train loss: 0.460.. Test loss: 0.357.. Test accuracy: 0.885\n", 507 | "Epoch 1/1.. Train loss: 0.421.. Test loss: 0.349.. Test accuracy: 0.881\n", 508 | "Epoch 1/1.. Train loss: 0.352.. Test loss: 0.320.. Test accuracy: 0.892\n", 509 | "Epoch 1/1.. Train loss: 0.405.. Test loss: 0.310.. Test accuracy: 0.899\n", 510 | "Epoch 1/1.. Train loss: 0.339.. Test loss: 0.325.. Test accuracy: 0.897\n", 511 | "Epoch 1/1.. Train loss: 0.392.. Test loss: 0.353.. Test accuracy: 0.891\n", 512 | "Epoch 1/1.. Train loss: 0.419.. Test loss: 0.338.. Test accuracy: 0.884\n", 513 | "Epoch 1/1.. Train loss: 0.381.. Test loss: 0.456.. Test accuracy: 0.871\n", 514 | "Epoch 1/1.. Train loss: 0.378.. Test loss: 0.301.. Test accuracy: 0.897\n", 515 | "Epoch 1/1.. Train loss: 0.316.. Test loss: 0.331.. Test accuracy: 0.899\n", 516 | "Epoch 1/1.. Train loss: 0.351.. Test loss: 0.327.. Test accuracy: 0.890\n", 517 | "Epoch 1/1.. Train loss: 0.319.. Test loss: 0.319.. Test accuracy: 0.892\n", 518 | "Epoch 1/1.. Train loss: 0.423.. Test loss: 0.341.. Test accuracy: 0.890\n" 519 | ] 520 | } 521 | ], 522 | "source": [ 523 | "epochs = 1\n", 524 | "steps = 0\n", 525 | "running_loss = 0\n", 526 | "print_every = 10\n", 527 | "train_losses, test_losses = [], []\n", 528 | "\n", 529 | "for epoch in range(epochs):\n", 530 | " for inputs, labels in trainloader:\n", 531 | " steps += 1\n", 532 | " inputs, labels = inputs.to(device), labels.to(device)\n", 533 | " optimizer.zero_grad()\n", 534 | " logps = model.forward(inputs)\n", 535 | " loss = criterion(logps, labels)\n", 536 | " loss.backward()\n", 537 | " optimizer.step()\n", 538 | " running_loss += loss.item()\n", 539 | " \n", 540 | " if steps % print_every == 0:\n", 541 | " test_loss = 0\n", 542 | " accuracy = 0\n", 543 | " model.eval()\n", 544 | " with torch.no_grad():\n", 545 | " for inputs, labels in testloader:\n", 546 | " inputs, labels = inputs.to(device), labels.to(device)\n", 547 | " logps = model.forward(inputs)\n", 548 | " batch_loss = criterion(logps, labels)\n", 549 | " test_loss += batch_loss.item()\n", 550 | " \n", 551 | " ps = torch.exp(logps)\n", 552 | " top_p, top_class = ps.topk(1, dim=1)\n", 553 | " equals = top_class == labels.view(*top_class.shape)\n", 554 | " accuracy += torch.mean(equals.type(torch.FloatTensor)).item()\n", 555 | "\n", 556 | " train_losses.append(running_loss/len(trainloader))\n", 557 | " test_losses.append(test_loss/len(testloader)) \n", 558 | " print(f\"Epoch {epoch+1}/{epochs}.. \"\n", 559 | " f\"Train loss: {running_loss/print_every:.3f}.. \"\n", 560 | " f\"Test loss: {test_loss/len(testloader):.3f}.. \"\n", 561 | " f\"Test accuracy: {accuracy/len(testloader):.3f}\")\n", 562 | " running_loss = 0\n", 563 | " model.train()" 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "execution_count": 7, 569 | "metadata": {}, 570 | "outputs": [ 571 | { 572 | "data": { 573 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAusAAAH0CAYAAACEkWPuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4wLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvqOYd8AAAIABJREFUeJzs3XeYVdXdt/F70XsHFRFBilgjoGIHUTEqItbYsDyWPCbGEokmUSwxib5G89gTY0ETTYxYwGDDEFRAUAQ1wdBEQRERkI6IwKz3jz2VmYGBOZx9Zrg/13Wus/de+6z5DRa+s2attUOMEUmSJEm5p0baBUiSJEkqm2FdkiRJylGGdUmSJClHGdYlSZKkHGVYlyRJknKUYV2SJEnKUYZ1SZIkKUcZ1iVJkqQcZViXJEmScpRhXZIkScpRhnVJkiQpRxnWJUmSpBxlWJckSZJylGFdkiRJylGGdUmSJClHGdYlSZKkHFUr7QKyKYTwKdAEmJNyKZIkSareOgArYowdK9PJdhXWgSb169dvsccee7RIuxBJkiRVX9OmTWPNmjWV7md7C+tz9thjjxaTJ09Ouw5JkiRVYz179mTKlClzKtuPc9YlSZKkHGVYlyRJknKUYV2SJEnKUYZ1SZIkKUcZ1iVJkqQclZGwHkI4LYRwXwhhbAhhRQghhhCerER/R4UQXgghLAghrA0hzA8hvBZCOD4T9UqSJElVQaa2brwB+B6wCpgHdNvajkIIdwA/y+/nRWAx0BroCfQBXq5krZIkSVKVkKmwfjVJuP4Y6A2M2ZpOQgiXkAT1J4BLY4zfbdReu5J1SpIkSVVGRsJ6jLEwnIcQtqqPEEJd4DfAZ5QR1PO/zrqtrVGSJEmqanLpCabHkEx3uRvICyGcAOwNfAu8G2OckGZxkiRJUrblUlg/IP/9W+B9kqBeKITwFnBajHHR5joKIUwup2mr59JLkiRJ2ZZLWze2yX//GRCBw4HGwL7AKOAIYFg6pUmSJEnZl0sj6wU/OKwHBsQY5+Sf/yeEcDIwA+gdQjh4c1NiYow9y7qeP+LeI0P1SpIkSdtULo2sL8t/f79YUAcgxvgN8Fr+6YHZLEqSJElKSy6F9Rn578vKaV+a/14/C7VIkiRJqculsD6aZK76niGEsuoqWHD6afZKyqD1a2GuG9pIkiSp4rIe1kMItUMI3UIInYpfjzHOBf4BtAeu3Ogz/YBjSUbdX81WrRkRIwy7AP5fRxj6fVg+L+2KJElSFqxatYoQAv379690X/vvvz+NGjXKQFWZc//99xNC4Nlnn027lGotI2E9hDAwhPB4COFx4Of5lw8uuBZCuLPY7TsD00hG0jf2Y+Bz4PchhH+GEH4XQngWeBnYAFwcY1yeiZqzJgRYswzWrU7OZ41Ktx5Jkqq5EMIWvR5//PG0S5bKlandYPYDzt/o2m75L4C5wODNdRJjnBdC6AncCAwg2a5xBcmI+20xxnczVG92dT0WPsl/yOvMUbD//6RbjyRJ1dhNN91U6trdd9/N8uXLufLKK2nWrFmJtv3222+b1NGwYUOmTZuWkRHx5557jrVr12agKlU1GQnrMcabgZsreO8cIGyifRHwk/xX9dClH7ya/wuHT9+Edd9C7Xrp1iRJUjV18803l7r2+OOPs3z5cq666io6dOiQlTpCCHTrlpnnMe66664Z6UdVTy4tMK2+WnaClp2T43XfwNxx6dYjSZJKKZgXvmbNGm644QY6d+5MnTp1uPzyywH4+uuvuf322+nduzdt27alTp067LDDDpx66qlMnlz64enlzVkfPHgwIQTee+89nnrqKXr27En9+vVp1aoVgwYNYuHCheXWVtzIkSMJIXDnnXfy7rvvcuyxx9KkSRMaNWrE0UcfXWZNAJ999hnnnnsurVq1okGDBvTs2ZO///3vJfqrrAkTJnDSSSfRqlUr6taty2677cZVV13FokWlH0Q/f/58rrzySrp27UqDBg1o3rw5e+yxBxdddBGff/554X15eXk8/PDD9OrVi1atWlG/fn3at2/P8ccfz/Dhwytdc67KpYciVW9d+sHXHyfHM0dB56PTrUeSJJWSl5dH//79mTFjBsceeywtW7YsHNV+//33uemmm+jTpw8nnXQSTZs25dNPP+XFF19k5MiRvP766xxxxBEV/lp33HEHI0eO5KSTTuLII49k/PjxPPnkk0ydOpX33nuPmjVrVqifcePGccMNN9CnTx8uvfRSPvnkE4YPH06fPn2YOnVqiVH5efPmcfDBBzN//nyOOuooDjjgAL744gvOP/98jjvuuC37wyrHM888wznnnEPNmjU5/fTTadeuHRMnTuSee+5hxIgRjB8/nrZt2wKwYsUKevXqxfz58+nXrx8DBw5k3bp1zJ07l2effZZBgwaxyy67AHDVVVdx33330aVLF8466ywaNWrE/Pnzeeeddxg+fDgDBw7MSP25xrCeLV36wcQHk+NZr0H8f8niU0mSlDPWrFnDypUrmTp1aqm57T169GDBggU0b968xPXZs2fTq1cvrrnmGiZNmlThrzV69Gg++OADunbtCkCMkYEDB/Liiy/y2muvcfzxx1eonxEjRjBs2DBOO+20wmt33XUXgwcP5oEHHuCOO+4ovH7NNdcwf/58fvWrXzFkyJDC6z/60Y847LDDKlx7eZYsWcLFF19MCIFx48ax//77F7YNGTKEX//611x++eU8//zzALz00kvMmzePG264gVtvvbVEX99++y3r168HikbVO3XqxH/+8x/q1q1b4t7FixdXuvZcZVjPll0PhTqN4LtVsHQOLJ4FrbumXZUkaTvT4ecvpV1Chc25/YRUvu5tt91WKqgDtGjRosz7O3XqxIABAxg6dChLliwp976N/exnPysM6pDMcb/44ot58cUXeffddysc1o899tgSQR3g0ksvZfDgwbz7btHeHCtXruT555+nTZs2/OxnPytx/0EHHcTpp5/O008/XaGvWZ5hw4axcuVKLrnkkhJBHeD666/nkUceYcSIESxevJhWrVoVttWvX/qZl/XqlVzfF0KgTp06Zf7GoXhf1Y1z1rOlVh3YrU/R+azX0qpEkiRtwoEHHlhu25gxYzjllFNo164dderUKdz+cejQoQB88cUXFf46G4dZoHDKx9KlS0u1bUk/jRs3pmnTpiX6mTp1KuvXr6dnz56lgjCQkZH1KVOmANC3b99SbfXq1eOQQw4hLy+PDz/8EIBjjjmG1q1bM2TIEPr3788DDzzABx98QF5eXonP1qhRgzPPPJNp06ax9957M2TIEEaNGsXKlSsrXXOuc2Q9m7r0g+kjk+NZo+CQ6rPhjSRJ1UGDBg1o3LhxmW1PPvkk5513Ho0aNeKYY46hY8eONGzYkBACo0aNYsKECVu0vWJZo/e1aiXRbMOGDZXqp6Cv4v0sX548qmaHHXYo8/7yrm+Jgq+x0047ldlecH3ZsmVAMiL+zjvvcPPNNzNy5EheeumlwlquuOIKrrvuusKR9Iceeohu3brxxBNP8Otf/xqA2rVrM2DAAO66665qu2OOYT2buvQrOp77Nny7Auo1Sa8eSdJ2J62pJVVF2MR6shtuuIHGjRvz/vvvs9tuu5VomzVrFhMmTNjW5VVKkyZJ5vjqq6/KbC/v+pZo2rQpAAsWLCiz/csvvyxxH0DHjh154oknyMvLY+rUqYwePZr777+f66+/npo1a3LdddcBSTC/9tprufbaa1mwYAFjx47lySef5LnnnmP69Ol8+OGHFV6UW5U4DSabmuwEO+6bHOetL3pQkiRJymnr169n7ty57LfffqWC+rp163I+qAPss88+1KpVi8mTJ/Ptt9+Wah83rvJbS3fv3h2AN954o1Tb2rVrmTBhAiGEMh9EVaNGDfbdd1+uvvpqRo5MZiKUtyXjjjvuyOmnn86IESM48MAD+eijj/j4448rXX8uMqxnW9dji45njkqvDkmSVGG1atVi55135qOPPiqx80heXh6/+MUv+PTTT1OsrmIaN27MwIEDWbhwIb/73e9KtL3zzjsMGzas0l/jjDPOoFGjRgwdOrRwXnqB2267jS+//LJw/3WAf//732Xu5FIwyt+gQQMg2bO++GLZAmvXri2celPWItXqwGkw2dblWHgr/z+QWaMgLw9q+DOTJEm57uqrr2bw4MHsu+++nHLKKdSoUYM333yTOXPmcNxxx/HKK6+kXeJm3XXXXYwbN44bb7yRt956iwMOOIB58+bxzDPPcOKJJzJ8+HBqVCKXtGjRgj/96U8MGjSIgw8+mNNPP52dd96ZiRMnMmbMGNq3b8/9999feP+LL77Ir371Kw499FC6dOlCq1atmDt3LiNGjKBmzZoMHjwYSOa49+rVi27dutG9e3fat2/PN998w6uvvsqsWbM4++yzad++faX/fHKRYT3bdu4BDVrCN1/D6oWw4ENo2z3tqiRJ0mb89Kc/pVGjRtx///089thjNGzYkD59+vDMM8/w8MMPV4mw3r59eyZOnMgvfvELXnvtNcaNG8eee+7JE088wZo1axg+fHjh3PatddZZZ9G+fXtuv/12Ro4cycqVK2nbti0/+clPuOGGG2jTpk3hvQMGDGDRokWMHTuW559/nlWrVrHTTjtx4okncs011xTudNOyZUt++9vfMmbMGMaOHcuiRYto0qQJXbp04brrruP888+vVM25LMQY064ha0IIk3v06NGjvMfvZs3zl8K//54c9/kl9Lku3XokSdJ278orr+Tee+9l3LhxHHrooWmX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OeUU/vCHPzB//nzatm27xfUUmDNnDv/617/o2LEj11xzTYm2ww8/nDPOOIOnn36a4cOHc/bZZ5doP+ecc+jTp0+Ja5deeil33nkn77777lbXNHbsWGbNmsXhhx/OD37wg1Jf8/7772fixIlMmDCBQw45pLCtfv36pfqqWbNmiT9vSObuF0wZKq5Vq1ZbXfO24pz1XFJiv/XXIS8vvVokSari+vbtS6dOnRg/fjzTpk0rvD506FDWr1/PBRdcUGZge/HFFznhhBPYcccdqV27duG871deeYU1a9awZMmSraqnYLFr7969S7UdccQR1KhRdiwbO3Ysp59+Orvssgt169YtrOcPf/gDkMyzr4yC+dxHHHFEmQti+/btW+K+4vbff/9S13bZZRcAli5dutU1FfxZFXztzdW0zz77sM8++/CXv/yFww8/nN/97ndMmDCBdevWlfrsOeecw8qVK9lzzz356U9/yogRI1i8ePFW17qtGdZzSdvu0LB1cvzN4qJRdkmStMWKL6Qs2MYxxsijjz5KCKFwEWpxd911FyeddBITJ06kd+/eXH311dx4443cdNNN7LPPPgCsXbt2q+pZvjz5TUFZo/d16tQpNfoLMGzYMPr06cMrr7zC/vvvz+WXX86QIUO46aabOPzwwytVz8Z17bTTTmW2F1xftmxZqbayRv4LAv+GDRuyVlOtWrUYM2YMV1xxBZ9++inXXnsthxxyCK1ateLKK69k9erVhZ+99tprGTp0KO3atePuu+9m4MCBtGnThqOOOmqTuwelJSPTYEIIpwG9gf2A7wGNgadijOdWst9zgb/kn14SY9z0hqlVXY0ayULTD/+anM96Ddr1TLcmSVL1UsmpJVXNhRdeyI033sif//xnbrvtNsaOHcsnn3xC3759Sz0tdN26ddxyyy20bduWKVOmlArVY8eOrVQtTZsmU5C++uor2rdvX6Ltu+++Y+nSpaXC75AhQ6hXrx6TJ09m9913L9H2+eefV7qm4nUtWLCgzPYvv/yyxH3ZsDU1tWzZknvuuYd77rmHWbNm8cYbb/DQQw9x7733smLFisJpSAAXXHABF1xwAcuWLWP8+PE8//zzDB06lGOPPZbp06fTsmXLbfjdbZlMjazfAFxOEtYr97uYfCGEXYD7gVWZ6K/K6FpsKoz7rUuSVCk77LADAwYMYPHixQwfPrxw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\n", 574 | "text/plain": [ 575 | "
" 576 | ] 577 | }, 578 | "metadata": { 579 | "image/png": { 580 | "height": 250, 581 | "width": 373 582 | }, 583 | "needs_background": "light" 584 | }, 585 | "output_type": "display_data" 586 | } 587 | ], 588 | "source": [ 589 | "plt.plot(train_losses, label='Training loss')\n", 590 | "plt.plot(test_losses, label='Validation loss')\n", 591 | "plt.legend(frameon=False)\n", 592 | "plt.show()" 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "execution_count": null, 598 | "metadata": {}, 599 | "outputs": [], 600 | "source": [] 601 | } 602 | ], 603 | "metadata": { 604 | "kernelspec": { 605 | "display_name": "Python 3", 606 | "language": "python", 607 | "name": "python3" 608 | }, 609 | "language_info": { 610 | "codemirror_mode": { 611 | "name": "ipython", 612 | "version": 3 613 | }, 614 | "file_extension": ".py", 615 | "mimetype": "text/x-python", 616 | "name": "python", 617 | "nbconvert_exporter": "python", 618 | "pygments_lexer": "ipython3", 619 | "version": "3.6.5" 620 | } 621 | }, 622 | "nbformat": 4, 623 | "nbformat_minor": 2 624 | } 625 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Full story: 2 | https://towardsdatascience.com/how-to-train-an-image-classifier-in-pytorch-and-use-it-to-perform-basic-inference-on-single-images-99465a1e9bf5 3 | 4 | 5 | --------------------------------------------------------------------------------