Archived
final submission readyu...moved notebookks
This commit is contained in:
1 parent
6f854a9d62
commit
66e153ff4a
9 files changed
+711
-890
No files matched your search
@@ -0,0 +1,538 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "7a37220a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello, World!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello, World!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "d318d1f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Van: 1111 images\n",
|
||||
"Taxi: 748 images\n",
|
||||
"Bicycle: 1618 images\n",
|
||||
"Bus: 2133 images\n",
|
||||
"Car: 6781 images\n",
|
||||
"Motorcycle: 2986 images\n",
|
||||
"Truck: 2033 images\n",
|
||||
"NonVehicles: 8968 images\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"data_dir = '../../data/raw/vehicle_classification'\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if os.path.isdir(class_path):\n",
|
||||
" count = len(os.listdir(class_path))\n",
|
||||
" print(f\"{class_name}: {count} images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "64122ad4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out sample image from dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "5604ace3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Size: (64, 64)\n",
|
||||
"Mode: RGB\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 640x480 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from PIL import Image\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# first image in first folder\n",
|
||||
"first_class = os.listdir(data_dir)[0]\n",
|
||||
"first_image_path = os.path.join(data_dir, first_class, os.listdir(os.path.join(data_dir, first_class))[0])\n",
|
||||
"\n",
|
||||
"img = Image.open(first_image_path)\n",
|
||||
"print(f\"Size: {img.size}\")\n",
|
||||
"print(f\"Mode: {img.mode}\")\n",
|
||||
"plt.imshow(img)\n",
|
||||
"plt.title(first_class)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c19ec00a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that all images are RGB, all of same resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "3cedd586",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Unique sizes: {(64, 64)}\n",
|
||||
"Unique modes: {'RGB'}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sizes = set()\n",
|
||||
"modes = set()\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if not os.path.isdir(class_path):\n",
|
||||
" continue\n",
|
||||
" for img_name in os.listdir(class_path):\n",
|
||||
" img = Image.open(os.path.join(class_path, img_name))\n",
|
||||
" sizes.add(img.size)\n",
|
||||
" modes.add(img.mode)\n",
|
||||
"\n",
|
||||
"print(f\"Unique sizes: {sizes}\")\n",
|
||||
"print(f\"Unique modes: {modes}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "88ac961b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that torch works with GPU (5080) [Credit: Claude]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "8f556b22",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"True\n",
|
||||
"NVIDIA GeForce RTX 5080\n",
|
||||
"GPU works! torch.Size([1000, 1000])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"print(torch.cuda.is_available()) \n",
|
||||
"print(torch.cuda.get_device_name(0)) \n",
|
||||
"\n",
|
||||
"x = torch.randn(1000, 1000).cuda()\n",
|
||||
"y = x @ x\n",
|
||||
"print(\"GPU works!\", y.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "870fadbe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Transform image dataset into tensors. Normalized with 0.5 mean and 0.5 STD (can be differente to use pretrained weights)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "37793c77",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['Bicycle', 'Bus', 'Car', 'Motorcycle', 'NonVehicles', 'Taxi', 'Truck', 'Van']\n",
|
||||
"26378\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from torchvision import datasets, transforms\n",
|
||||
"from torch.utils.data import random_split, DataLoader\n",
|
||||
"\n",
|
||||
"transform = transforms.Compose([\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"dataset = datasets.ImageFolder(root=data_dir, transform=transform)\n",
|
||||
"\n",
|
||||
"print(dataset.classes) # should print 8 classes\n",
|
||||
"print(len(dataset)) # total image count"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac33bc21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Split 80-20 and save into 2 variables"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f68c1a25",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train: 21102, Test: 5276\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import math \n",
|
||||
"\n",
|
||||
"train_size = math.floor(len(dataset) * 0.8)\n",
|
||||
"test_size = len(dataset) - train_size\n",
|
||||
"\n",
|
||||
"train_dataset, test_dataset = random_split(dataset, [train_size, test_size])\n",
|
||||
"\n",
|
||||
"print(f\"Train: {len(train_dataset)}, Test: {len(test_dataset)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2eede814",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load the data into batches for faster loading...use 4 threads on CPU\n",
|
||||
"\n",
|
||||
"Shuffling train dataset so as to not overfit on one class"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "e1539eaa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"8\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4)\n",
|
||||
"test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=4)\n",
|
||||
"\n",
|
||||
"classes = ('bicycle', 'bus', 'car', 'motorcycle',\n",
|
||||
" 'nonvehicles', 'taxi', 'truck', 'van')\n",
|
||||
"\n",
|
||||
"print(len(classes))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "acebe98e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial CNN architecture implmenetation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "d1b7d9ca",
|
||||
"metadata": {},
|
||||
"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=2704, out_features=120, bias=True)\n",
|
||||
" (fc2): Linear(in_features=120, out_features=84, bias=True)\n",
|
||||
" (fc3): Linear(in_features=84, out_features=8, bias=True)\n",
|
||||
")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torch.nn as nn\n",
|
||||
"import torch.nn.functional as F\n",
|
||||
"\n",
|
||||
"class Net(nn.Module):\n",
|
||||
" def __init__(self):\n",
|
||||
" super().__init__()\n",
|
||||
" self.conv1 = nn.Conv2d(3, 6, 5) # 3 input channels for RGB, 6 filters [arbitrailly chosen ffrom PyTorch], 5x5 Kernel Size\n",
|
||||
" self.pool = nn.MaxPool2d(2, 2) # Halves spatial dimensions\n",
|
||||
" self.conv2 = nn.Conv2d(6, 16, 5) # 6 matches output of first conv layer, 16 arbitrary filter (more than earlier), 5x5 kernel\n",
|
||||
" self.fc1 = nn.Linear(16 * 13 * 13, 120) # 16 channels from conv layer 2; 13 *13 bc shape after 2 layers and 2 pools, 120 is arbitrary output size from tutorial\n",
|
||||
" self.fc2 = nn.Linear(120, 84) # 120 output from fc1 --> arbitrary lowering to 84\n",
|
||||
" self.fc3 = nn.Linear(84, len(classes)) # Bring down to number of classes \n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" x = self.pool(F.relu(self.conv1(x))) #Conv layer 1 --> relu --> pool \n",
|
||||
" x = self.pool(F.relu(self.conv2(x))) #Conv layer 1 --> relu --> pool \n",
|
||||
" x = torch.flatten(x, 1) # Flattedn to 1d\n",
|
||||
" x = F.relu(self.fc1(x)) #fully connected layer shriking 1\n",
|
||||
" x = F.relu(self.fc2(x)) # Shrink again\n",
|
||||
" x = self.fc3(x) # Shrink to number of classes\n",
|
||||
" return x\n",
|
||||
"\n",
|
||||
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||||
"model = Net().to(device)\n",
|
||||
"print(model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "22e71032",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Loss fn and optimizer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "54d11a04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch.optim as optim\n",
|
||||
"\n",
|
||||
"criterion = nn.CrossEntropyLoss() # Applied softmax to convert scores --> probabilities --> penalizes model \n",
|
||||
"\n",
|
||||
"# SGD is used on tutorial, but Adam optimizer is more popular as well\n",
|
||||
"optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # Defualt learning rate from tutorial: compromise btwn overshoot and training infinitely long, 0.9 --> 90% of update comes from prev. direction\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "572d80e3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial training cell adapted to use 10 epochs "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "374d0590",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1: Loss=1.518, Accuracy=51.03%\n",
|
||||
"Epoch 2: Loss=1.110, Accuracy=62.02%\n",
|
||||
"Epoch 3: Loss=0.976, Accuracy=66.24%\n",
|
||||
"Epoch 4: Loss=0.906, Accuracy=68.52%\n",
|
||||
"Epoch 5: Loss=0.838, Accuracy=70.77%\n",
|
||||
"Epoch 6: Loss=0.782, Accuracy=72.33%\n",
|
||||
"Epoch 7: Loss=0.735, Accuracy=74.25%\n",
|
||||
"Epoch 8: Loss=0.696, Accuracy=75.42%\n",
|
||||
"Epoch 9: Loss=0.665, Accuracy=76.53%\n",
|
||||
"Epoch 10: Loss=0.634, Accuracy=77.40%\n",
|
||||
"Finished Training\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for epoch in range(10): # 10 epochs\n",
|
||||
" running_loss = 0.0\n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
"\n",
|
||||
" for i, data in enumerate(train_loader, 0):\n",
|
||||
" inputs, labels = data\n",
|
||||
" inputs, labels = inputs.to(device), labels.to(device) # use GPU\n",
|
||||
"\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
"\n",
|
||||
" outputs = model(inputs)\n",
|
||||
" loss = criterion(outputs, labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
"\n",
|
||||
" # loss tracking\n",
|
||||
" running_loss += loss.item()\n",
|
||||
"\n",
|
||||
" # accuracy tracking\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
" accuracy = 100 * correct / total\n",
|
||||
" print(f'Epoch {epoch + 1}: Loss={running_loss / len(train_loader):.3f}, Accuracy={accuracy:.2f}%') # Credit: Claude for printing accuracy in good formatting\n",
|
||||
"\n",
|
||||
"print('Finished Training')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "2bf2b9a2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PATH = '../../models/tutorial-cnn.pth'\n",
|
||||
"torch.save(model.state_dict(), PATH)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a24dd4f0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Test on test set using Pytorch tutorial method for both total and per class (maybe spot overfitting as well)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "bc158602",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Test Accuracy: 75.66%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct = 0\n",
|
||||
"total = 0\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
"print(f'Test Accuracy: {100 * correct / total:.2f}%')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "8cc7ed40",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Accuracy for class: Bicycle is 73.6%\n",
|
||||
"Accuracy for class: Bus is 65.1%\n",
|
||||
"Accuracy for class: Car is 86.2%\n",
|
||||
"Accuracy for class: Motorcycle is 55.7%\n",
|
||||
"Accuracy for class: NonVehicles is 99.3%\n",
|
||||
"Accuracy for class: Taxi is 32.9%\n",
|
||||
"Accuracy for class: Truck is 24.8%\n",
|
||||
"Accuracy for class: Van is 22.9%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"total_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predictions = torch.max(outputs, 1)\n",
|
||||
" for label, prediction in zip(labels, predictions):\n",
|
||||
" if label == prediction:\n",
|
||||
" correct_pred[dataset.classes[label]] += 1\n",
|
||||
" total_pred[dataset.classes[label]] += 1\n",
|
||||
"\n",
|
||||
"for classname, correct_count in correct_pred.items():\n",
|
||||
" accuracy = 100 * float(correct_count) / total_pred[classname]\n",
|
||||
" print(f'Accuracy for class: {classname:10s} is {accuracy:.1f}%')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "7a37220a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello, World!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello, World!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "d318d1f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Van: 1111 images\n",
|
||||
"Taxi: 748 images\n",
|
||||
"Bicycle: 1618 images\n",
|
||||
"Bus: 2133 images\n",
|
||||
"Car: 6781 images\n",
|
||||
"Motorcycle: 2986 images\n",
|
||||
"Truck: 2033 images\n",
|
||||
"NonVehicles: 8968 images\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"data_dir = '../../data/raw/vehicle_classification'\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if os.path.isdir(class_path):\n",
|
||||
" count = len(os.listdir(class_path))\n",
|
||||
" print(f\"{class_name}: {count} images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "64122ad4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out sample image from dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "5604ace3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Size: (64, 64)\n",
|
||||
"Mode: RGB\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 640x480 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from PIL import Image\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# first image in first folder\n",
|
||||
"first_class = os.listdir(data_dir)[0]\n",
|
||||
"first_image_path = os.path.join(data_dir, first_class, os.listdir(os.path.join(data_dir, first_class))[0])\n",
|
||||
"\n",
|
||||
"img = Image.open(first_image_path)\n",
|
||||
"print(f\"Size: {img.size}\")\n",
|
||||
"print(f\"Mode: {img.mode}\")\n",
|
||||
"plt.imshow(img)\n",
|
||||
"plt.title(first_class)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c19ec00a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that all images are RGB, all of same resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "3cedd586",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Unique sizes: {(64, 64)}\n",
|
||||
"Unique modes: {'RGB'}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sizes = set()\n",
|
||||
"modes = set()\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if not os.path.isdir(class_path):\n",
|
||||
" continue\n",
|
||||
" for img_name in os.listdir(class_path):\n",
|
||||
" img = Image.open(os.path.join(class_path, img_name))\n",
|
||||
" sizes.add(img.size)\n",
|
||||
" modes.add(img.mode)\n",
|
||||
"\n",
|
||||
"print(f\"Unique sizes: {sizes}\")\n",
|
||||
"print(f\"Unique modes: {modes}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "88ac961b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that torch works with GPU (5080) [Credit: Claude]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "8f556b22",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"True\n",
|
||||
"NVIDIA GeForce RTX 5080\n",
|
||||
"GPU works! torch.Size([1000, 1000])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"print(torch.cuda.is_available()) \n",
|
||||
"print(torch.cuda.get_device_name(0)) \n",
|
||||
"\n",
|
||||
"x = torch.randn(1000, 1000).cuda()\n",
|
||||
"y = x @ x\n",
|
||||
"print(\"GPU works!\", y.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "870fadbe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Transform image dataset into tensors. Normalized with 0.5 mean and 0.5 STD (can be differente to use pretrained weights)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "37793c77",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['Bicycle', 'Bus', 'Car', 'Motorcycle', 'NonVehicles', 'Taxi', 'Truck', 'Van']\n",
|
||||
"26378\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from torchvision import datasets, transforms\n",
|
||||
"from torch.utils.data import random_split, DataLoader\n",
|
||||
"\n",
|
||||
"transform = transforms.Compose([\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"dataset = datasets.ImageFolder(root=data_dir, transform=transform)\n",
|
||||
"\n",
|
||||
"print(dataset.classes) # should print 8 classes\n",
|
||||
"print(len(dataset)) # total image count"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac33bc21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Split 80-20 and save into 2 variables"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f68c1a25",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train: 21102, Test: 5276\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import math \n",
|
||||
"\n",
|
||||
"train_size = math.floor(len(dataset) * 0.8)\n",
|
||||
"test_size = len(dataset) - train_size\n",
|
||||
"\n",
|
||||
"train_dataset, test_dataset = random_split(dataset, [train_size, test_size])\n",
|
||||
"\n",
|
||||
"print(f\"Train: {len(train_dataset)}, Test: {len(test_dataset)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2eede814",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load the data into batches for faster loading...use 4 threads on CPU\n",
|
||||
"\n",
|
||||
"Shuffling train dataset so as to not overfit on one class"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "e1539eaa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"8\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True, num_workers=16) # Increased from 4\n",
|
||||
"test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False, num_workers=16) # Increased from 4\n",
|
||||
"\n",
|
||||
"classes = ('bicycle', 'bus', 'car', 'motorcycle',\n",
|
||||
" 'nonvehicles', 'taxi', 'truck', 'van')\n",
|
||||
"\n",
|
||||
"print(len(classes))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "acebe98e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial CNN architecture implmenetation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "d1b7d9ca",
|
||||
"metadata": {},
|
||||
"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=2704, out_features=120, bias=True)\n",
|
||||
" (fc2): Linear(in_features=120, out_features=84, bias=True)\n",
|
||||
" (fc3): Linear(in_features=84, out_features=8, bias=True)\n",
|
||||
")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torch.nn as nn\n",
|
||||
"import torch.nn.functional as F\n",
|
||||
"\n",
|
||||
"class Net(nn.Module):\n",
|
||||
" def __init__(self):\n",
|
||||
" super().__init__()\n",
|
||||
" self.conv1 = nn.Conv2d(3, 6, 5) # 3 input channels for RGB, 6 filters [arbitrailly chosen ffrom PyTorch], 5x5 Kernel Size\n",
|
||||
" self.pool = nn.MaxPool2d(2, 2) # Halves spatial dimensions\n",
|
||||
" self.conv2 = nn.Conv2d(6, 16, 5) # 6 matches output of first conv layer, 16 arbitrary filter (more than earlier), 5x5 kernel\n",
|
||||
" self.fc1 = nn.Linear(16 * 13 * 13, 120) # 16 channels from conv layer 2; 13 *13 bc shape after 2 layers and 2 pools, 120 is arbitrary output size from tutorial\n",
|
||||
" self.fc2 = nn.Linear(120, 84) # 120 output from fc1 --> arbitrary lowering to 84\n",
|
||||
" self.fc3 = nn.Linear(84, len(classes)) # Bring down to number of classes \n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" x = self.pool(F.relu(self.conv1(x))) #Conv layer 1 --> relu --> pool \n",
|
||||
" x = self.pool(F.relu(self.conv2(x))) #Conv layer 1 --> relu --> pool \n",
|
||||
" x = torch.flatten(x, 1) # Flattedn to 1d\n",
|
||||
" x = F.relu(self.fc1(x)) #fully connected layer shriking 1\n",
|
||||
" x = F.relu(self.fc2(x)) # Shrink again\n",
|
||||
" x = self.fc3(x) # Shrink to number of classes\n",
|
||||
" return x\n",
|
||||
"\n",
|
||||
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||||
"model = Net().to(device)\n",
|
||||
"print(model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "22e71032",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Loss fn and optimizer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "54d11a04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch.optim as optim\n",
|
||||
"\n",
|
||||
"criterion = nn.CrossEntropyLoss() # Applied softmax to convert scores --> probabilities --> penalizes model \n",
|
||||
"\n",
|
||||
"# SGD is used on tutorial, but Adam optimizer is more popular as well\n",
|
||||
"optimizer = optim.SGD(model.parameters(), lr=0.008, momentum=0.9) # Defualt learning rate from tutorial: * 8 for larger batch size, 0.9 --> 90% of update comes from prev. direction\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "572d80e3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial training cell adapted to use 30 epochs "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "374d0590",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1: Loss=1.693, Accuracy=43.96%\n",
|
||||
"Epoch 2: Loss=1.285, Accuracy=56.70%\n",
|
||||
"Epoch 3: Loss=1.070, Accuracy=62.78%\n",
|
||||
"Epoch 4: Loss=0.964, Accuracy=66.34%\n",
|
||||
"Epoch 5: Loss=0.891, Accuracy=68.85%\n",
|
||||
"Epoch 6: Loss=0.826, Accuracy=70.92%\n",
|
||||
"Epoch 7: Loss=0.764, Accuracy=72.87%\n",
|
||||
"Epoch 8: Loss=0.734, Accuracy=74.25%\n",
|
||||
"Epoch 9: Loss=0.690, Accuracy=75.77%\n",
|
||||
"Epoch 10: Loss=0.636, Accuracy=77.49%\n",
|
||||
"Epoch 11: Loss=0.616, Accuracy=78.49%\n",
|
||||
"Epoch 12: Loss=0.581, Accuracy=79.67%\n",
|
||||
"Epoch 13: Loss=0.558, Accuracy=80.53%\n",
|
||||
"Epoch 14: Loss=0.526, Accuracy=81.44%\n",
|
||||
"Epoch 15: Loss=0.498, Accuracy=82.36%\n",
|
||||
"Epoch 16: Loss=0.458, Accuracy=83.76%\n",
|
||||
"Epoch 17: Loss=0.436, Accuracy=84.62%\n",
|
||||
"Epoch 18: Loss=0.398, Accuracy=86.02%\n",
|
||||
"Epoch 19: Loss=0.378, Accuracy=86.44%\n",
|
||||
"Epoch 20: Loss=0.327, Accuracy=88.69%\n",
|
||||
"Epoch 21: Loss=0.310, Accuracy=89.20%\n",
|
||||
"Epoch 22: Loss=0.265, Accuracy=90.82%\n",
|
||||
"Epoch 23: Loss=0.237, Accuracy=91.60%\n",
|
||||
"Epoch 24: Loss=0.212, Accuracy=92.75%\n",
|
||||
"Epoch 25: Loss=0.193, Accuracy=93.34%\n",
|
||||
"Epoch 26: Loss=0.161, Accuracy=94.54%\n",
|
||||
"Epoch 27: Loss=0.119, Accuracy=96.25%\n",
|
||||
"Epoch 28: Loss=0.095, Accuracy=96.94%\n",
|
||||
"Epoch 29: Loss=0.099, Accuracy=96.86%\n",
|
||||
"Epoch 30: Loss=0.078, Accuracy=97.66%\n",
|
||||
"Finished Training\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for epoch in range(30): # 10 --> 30 epochs\n",
|
||||
" running_loss = 0.0\n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
"\n",
|
||||
" for i, data in enumerate(train_loader, 0):\n",
|
||||
" inputs, labels = data\n",
|
||||
" inputs, labels = inputs.to(device), labels.to(device) # use GPU\n",
|
||||
"\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
"\n",
|
||||
" outputs = model(inputs)\n",
|
||||
" loss = criterion(outputs, labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
"\n",
|
||||
" # loss tracking\n",
|
||||
" running_loss += loss.item()\n",
|
||||
"\n",
|
||||
" # accuracy tracking\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
" accuracy = 100 * correct / total\n",
|
||||
" print(f'Epoch {epoch + 1}: Loss={running_loss / len(train_loader):.3f}, Accuracy={accuracy:.2f}%') # Credit: Claude for printing accuracy in good formatting\n",
|
||||
"\n",
|
||||
"print('Finished Training')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "2bf2b9a2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PATH = '../../models/tutorial-cnn-modified.pth'\n",
|
||||
"torch.save(model.state_dict(), PATH)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a24dd4f0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Test on test set using Pytorch tutorial method for both total and per class (maybe spot overfitting as well)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "bc158602",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Test Accuracy: 76.90%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct = 0\n",
|
||||
"total = 0\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
"print(f'Test Accuracy: {100 * correct / total:.2f}%')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "8cc7ed40",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Accuracy for class: Bicycle is 61.4%\n",
|
||||
"Accuracy for class: Bus is 75.6%\n",
|
||||
"Accuracy for class: Car is 76.0%\n",
|
||||
"Accuracy for class: Motorcycle is 76.0%\n",
|
||||
"Accuracy for class: NonVehicles is 98.5%\n",
|
||||
"Accuracy for class: Taxi is 45.6%\n",
|
||||
"Accuracy for class: Truck is 38.0%\n",
|
||||
"Accuracy for class: Van is 31.6%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"total_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predictions = torch.max(outputs, 1)\n",
|
||||
" for label, prediction in zip(labels, predictions):\n",
|
||||
" if label == prediction:\n",
|
||||
" correct_pred[dataset.classes[label]] += 1\n",
|
||||
" total_pred[dataset.classes[label]] += 1\n",
|
||||
"\n",
|
||||
"for classname, correct_count in correct_pred.items():\n",
|
||||
" accuracy = 100 * float(correct_count) / total_pred[classname]\n",
|
||||
" print(f'Accuracy for class: {classname:10s} is {accuracy:.1f}%')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "7a37220a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello, World!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello, World!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "d318d1f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Van: 1111 images\n",
|
||||
"Taxi: 748 images\n",
|
||||
"Bicycle: 1618 images\n",
|
||||
"Bus: 2133 images\n",
|
||||
"Car: 6781 images\n",
|
||||
"Motorcycle: 2986 images\n",
|
||||
"Truck: 2033 images\n",
|
||||
"NonVehicles: 8968 images\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"data_dir = '../../data/raw/vehicle_classification'\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if os.path.isdir(class_path):\n",
|
||||
" count = len(os.listdir(class_path))\n",
|
||||
" print(f\"{class_name}: {count} images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "64122ad4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out sample image from dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "5604ace3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Size: (64, 64)\n",
|
||||
"Mode: RGB\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 640x480 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from PIL import Image\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# first image in first folder\n",
|
||||
"first_class = os.listdir(data_dir)[0]\n",
|
||||
"first_image_path = os.path.join(data_dir, first_class, os.listdir(os.path.join(data_dir, first_class))[0])\n",
|
||||
"\n",
|
||||
"img = Image.open(first_image_path)\n",
|
||||
"print(f\"Size: {img.size}\")\n",
|
||||
"print(f\"Mode: {img.mode}\")\n",
|
||||
"plt.imshow(img)\n",
|
||||
"plt.title(first_class)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c19ec00a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that all images are RGB, all of same resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "3cedd586",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Unique sizes: {(64, 64)}\n",
|
||||
"Unique modes: {'RGB'}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sizes = set()\n",
|
||||
"modes = set()\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if not os.path.isdir(class_path):\n",
|
||||
" continue\n",
|
||||
" for img_name in os.listdir(class_path):\n",
|
||||
" img = Image.open(os.path.join(class_path, img_name))\n",
|
||||
" sizes.add(img.size)\n",
|
||||
" modes.add(img.mode)\n",
|
||||
"\n",
|
||||
"print(f\"Unique sizes: {sizes}\")\n",
|
||||
"print(f\"Unique modes: {modes}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "88ac961b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that torch works with GPU (5080) [Credit: Claude]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "8f556b22",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"True\n",
|
||||
"NVIDIA GeForce RTX 5080\n",
|
||||
"GPU works! torch.Size([1000, 1000])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"print(torch.cuda.is_available()) \n",
|
||||
"print(torch.cuda.get_device_name(0)) \n",
|
||||
"\n",
|
||||
"x = torch.randn(1000, 1000).cuda()\n",
|
||||
"y = x @ x\n",
|
||||
"print(\"GPU works!\", y.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "870fadbe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Transform image dataset into tensors. Normalized with 0.5 mean and 0.5 STD (can be differente to use pretrained weights)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "37793c77",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['Bicycle', 'Bus', 'Car', 'Motorcycle', 'NonVehicles', 'Taxi', 'Truck', 'Van']\n",
|
||||
"26378\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from torchvision import datasets, transforms\n",
|
||||
"from torch.utils.data import random_split, DataLoader\n",
|
||||
"\n",
|
||||
"transform = transforms.Compose([\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"dataset = datasets.ImageFolder(root=data_dir, transform=transform)\n",
|
||||
"\n",
|
||||
"print(dataset.classes) # should print 8 classes\n",
|
||||
"print(len(dataset)) # total image count"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac33bc21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Split 80-20 and save into 2 variables"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f68c1a25",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train: 21102, Test: 5276\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import math \n",
|
||||
"\n",
|
||||
"train_size = math.floor(len(dataset) * 0.8)\n",
|
||||
"test_size = len(dataset) - train_size\n",
|
||||
"\n",
|
||||
"train_dataset, test_dataset = random_split(dataset, [train_size, test_size])\n",
|
||||
"\n",
|
||||
"print(f\"Train: {len(train_dataset)}, Test: {len(test_dataset)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2eede814",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load the data into batches for faster loading...use 4 threads on CPU\n",
|
||||
"\n",
|
||||
"Shuffling train dataset so as to not overfit on one class"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "e1539eaa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"8\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True, num_workers=16) # Increased from 4\n",
|
||||
"test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False, num_workers=16) # Increased from 4\n",
|
||||
"\n",
|
||||
"classes = ('bicycle', 'bus', 'car', 'motorcycle',\n",
|
||||
" 'nonvehicles', 'taxi', 'truck', 'van')\n",
|
||||
"\n",
|
||||
"print(len(classes))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "acebe98e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial CNN architecture implmenetation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "d1b7d9ca",
|
||||
"metadata": {},
|
||||
"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=2704, out_features=120, bias=True)\n",
|
||||
" (fc2): Linear(in_features=120, out_features=84, bias=True)\n",
|
||||
" (fc3): Linear(in_features=84, out_features=8, bias=True)\n",
|
||||
")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torch.nn as nn\n",
|
||||
"import torch.nn.functional as F\n",
|
||||
"\n",
|
||||
"class Net(nn.Module):\n",
|
||||
" def __init__(self):\n",
|
||||
" super().__init__()\n",
|
||||
" self.conv1 = nn.Conv2d(3, 6, 5) # 3 input channels for RGB, 6 filters [arbitrailly chosen ffrom PyTorch], 5x5 Kernel Size\n",
|
||||
" self.pool = nn.MaxPool2d(2, 2) # Halves spatial dimensions\n",
|
||||
" self.conv2 = nn.Conv2d(6, 16, 5) # 6 matches output of first conv layer, 16 arbitrary filter (more than earlier), 5x5 kernel\n",
|
||||
" self.fc1 = nn.Linear(16 * 13 * 13, 120) # 16 channels from conv layer 2; 13 *13 bc shape after 2 layers and 2 pools, 120 is arbitrary output size from tutorial\n",
|
||||
" self.fc2 = nn.Linear(120, 84) # 120 output from fc1 --> arbitrary lowering to 84\n",
|
||||
" self.fc3 = nn.Linear(84, len(classes)) # Bring down to number of classes \n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" x = self.pool(F.relu(self.conv1(x))) #Conv layer 1 --> relu --> pool \n",
|
||||
" x = self.pool(F.relu(self.conv2(x))) #Conv layer 1 --> relu --> pool \n",
|
||||
" x = torch.flatten(x, 1) # Flattedn to 1d\n",
|
||||
" x = F.relu(self.fc1(x)) #fully connected layer shriking 1\n",
|
||||
" x = F.relu(self.fc2(x)) # Shrink again\n",
|
||||
" x = self.fc3(x) # Shrink to number of classes\n",
|
||||
" return x\n",
|
||||
"\n",
|
||||
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||||
"model = Net().to(device)\n",
|
||||
"print(model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "22e71032",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Loss fn and optimizer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "54d11a04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch.optim as optim\n",
|
||||
"\n",
|
||||
"criterion = nn.CrossEntropyLoss() # Applied softmax to convert scores --> probabilities --> penalizes model \n",
|
||||
"\n",
|
||||
"# Changed to adam optimizer (internal momentumn calculation)\n",
|
||||
"optimizer = optim.Adam(model.parameters(), lr=0.001)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "572d80e3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial training cell adapted to use 30 epochs "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "374d0590",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1: Loss=1.301, Accuracy=56.17%\n",
|
||||
"Epoch 2: Loss=1.014, Accuracy=64.94%\n",
|
||||
"Epoch 3: Loss=0.933, Accuracy=67.15%\n",
|
||||
"Epoch 4: Loss=0.858, Accuracy=69.63%\n",
|
||||
"Epoch 5: Loss=0.783, Accuracy=72.12%\n",
|
||||
"Epoch 6: Loss=0.749, Accuracy=73.16%\n",
|
||||
"Epoch 7: Loss=0.695, Accuracy=75.33%\n",
|
||||
"Epoch 8: Loss=0.662, Accuracy=76.60%\n",
|
||||
"Epoch 9: Loss=0.634, Accuracy=77.59%\n",
|
||||
"Epoch 10: Loss=0.618, Accuracy=78.29%\n",
|
||||
"Epoch 11: Loss=0.586, Accuracy=79.37%\n",
|
||||
"Epoch 12: Loss=0.554, Accuracy=80.26%\n",
|
||||
"Epoch 13: Loss=0.538, Accuracy=81.11%\n",
|
||||
"Epoch 14: Loss=0.507, Accuracy=82.21%\n",
|
||||
"Epoch 15: Loss=0.489, Accuracy=82.95%\n",
|
||||
"Epoch 16: Loss=0.462, Accuracy=83.55%\n",
|
||||
"Epoch 17: Loss=0.435, Accuracy=84.84%\n",
|
||||
"Epoch 18: Loss=0.412, Accuracy=85.40%\n",
|
||||
"Epoch 19: Loss=0.390, Accuracy=86.27%\n",
|
||||
"Epoch 20: Loss=0.364, Accuracy=87.19%\n",
|
||||
"Epoch 21: Loss=0.348, Accuracy=87.81%\n",
|
||||
"Epoch 22: Loss=0.305, Accuracy=89.40%\n",
|
||||
"Epoch 23: Loss=0.279, Accuracy=90.41%\n",
|
||||
"Epoch 24: Loss=0.268, Accuracy=90.56%\n",
|
||||
"Epoch 25: Loss=0.227, Accuracy=92.43%\n",
|
||||
"Epoch 26: Loss=0.209, Accuracy=92.78%\n",
|
||||
"Epoch 27: Loss=0.181, Accuracy=94.17%\n",
|
||||
"Epoch 28: Loss=0.173, Accuracy=94.14%\n",
|
||||
"Epoch 29: Loss=0.140, Accuracy=95.50%\n",
|
||||
"Epoch 30: Loss=0.126, Accuracy=95.96%\n",
|
||||
"Finished Training\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for epoch in range(30): # 10 --> 30 epochs\n",
|
||||
" running_loss = 0.0\n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
"\n",
|
||||
" for i, data in enumerate(train_loader, 0):\n",
|
||||
" inputs, labels = data\n",
|
||||
" inputs, labels = inputs.to(device), labels.to(device) # use GPU\n",
|
||||
"\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
"\n",
|
||||
" outputs = model(inputs)\n",
|
||||
" loss = criterion(outputs, labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
"\n",
|
||||
" # loss tracking\n",
|
||||
" running_loss += loss.item()\n",
|
||||
"\n",
|
||||
" # accuracy tracking\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
" accuracy = 100 * correct / total\n",
|
||||
" print(f'Epoch {epoch + 1}: Loss={running_loss / len(train_loader):.3f}, Accuracy={accuracy:.2f}%') # Credit: Claude for printing accuracy in good formatting\n",
|
||||
"\n",
|
||||
"print('Finished Training')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "2bf2b9a2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PATH = '../../models/adam-optimized.pth'\n",
|
||||
"torch.save(model.state_dict(), PATH)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a24dd4f0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Test on test set using Pytorch tutorial method for both total and per class (maybe spot overfitting as well)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "bc158602",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Test Accuracy: 76.50%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct = 0\n",
|
||||
"total = 0\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
"print(f'Test Accuracy: {100 * correct / total:.2f}%')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "8cc7ed40",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Accuracy for class: Bicycle is 66.5%\n",
|
||||
"Accuracy for class: Bus is 68.0%\n",
|
||||
"Accuracy for class: Car is 73.8%\n",
|
||||
"Accuracy for class: Motorcycle is 77.1%\n",
|
||||
"Accuracy for class: NonVehicles is 99.0%\n",
|
||||
"Accuracy for class: Taxi is 41.3%\n",
|
||||
"Accuracy for class: Truck is 43.8%\n",
|
||||
"Accuracy for class: Van is 23.6%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"total_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predictions = torch.max(outputs, 1)\n",
|
||||
" for label, prediction in zip(labels, predictions):\n",
|
||||
" if label == prediction:\n",
|
||||
" correct_pred[dataset.classes[label]] += 1\n",
|
||||
" total_pred[dataset.classes[label]] += 1\n",
|
||||
"\n",
|
||||
"for classname, correct_count in correct_pred.items():\n",
|
||||
" accuracy = 100 * float(correct_count) / total_pred[classname]\n",
|
||||
" print(f'Accuracy for class: {classname:10s} is {accuracy:.1f}%')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "7a37220a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello, World!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello, World!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "d318d1f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Van: 1111 images\n",
|
||||
"Taxi: 748 images\n",
|
||||
"Bicycle: 1618 images\n",
|
||||
"Bus: 2133 images\n",
|
||||
"Car: 6781 images\n",
|
||||
"Motorcycle: 2986 images\n",
|
||||
"Truck: 2033 images\n",
|
||||
"NonVehicles: 8968 images\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"data_dir = '../../data/raw/vehicle_classification'\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if os.path.isdir(class_path):\n",
|
||||
" count = len(os.listdir(class_path))\n",
|
||||
" print(f\"{class_name}: {count} images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "64122ad4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out sample image from dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "5604ace3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Size: (64, 64)\n",
|
||||
"Mode: RGB\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 640x480 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from PIL import Image\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# first image in first folder\n",
|
||||
"first_class = os.listdir(data_dir)[0]\n",
|
||||
"first_image_path = os.path.join(data_dir, first_class, os.listdir(os.path.join(data_dir, first_class))[0])\n",
|
||||
"\n",
|
||||
"img = Image.open(first_image_path)\n",
|
||||
"print(f\"Size: {img.size}\")\n",
|
||||
"print(f\"Mode: {img.mode}\")\n",
|
||||
"plt.imshow(img)\n",
|
||||
"plt.title(first_class)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c19ec00a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that all images are RGB, all of same resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "3cedd586",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Unique sizes: {(64, 64)}\n",
|
||||
"Unique modes: {'RGB'}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sizes = set()\n",
|
||||
"modes = set()\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if not os.path.isdir(class_path):\n",
|
||||
" continue\n",
|
||||
" for img_name in os.listdir(class_path):\n",
|
||||
" img = Image.open(os.path.join(class_path, img_name))\n",
|
||||
" sizes.add(img.size)\n",
|
||||
" modes.add(img.mode)\n",
|
||||
"\n",
|
||||
"print(f\"Unique sizes: {sizes}\")\n",
|
||||
"print(f\"Unique modes: {modes}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "88ac961b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that torch works with GPU (5080) [Credit: Claude]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "8f556b22",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"True\n",
|
||||
"NVIDIA GeForce RTX 5080\n",
|
||||
"GPU works! torch.Size([1000, 1000])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"print(torch.cuda.is_available()) \n",
|
||||
"print(torch.cuda.get_device_name(0)) \n",
|
||||
"\n",
|
||||
"x = torch.randn(1000, 1000).cuda()\n",
|
||||
"y = x @ x\n",
|
||||
"print(\"GPU works!\", y.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "870fadbe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Transform image dataset into tensors. Normalized with 0.5 mean and 0.5 STD (can be differente to use pretrained weights)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "37793c77",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['Bicycle', 'Bus', 'Car', 'Motorcycle', 'NonVehicles', 'Taxi', 'Truck', 'Van']\n",
|
||||
"26378\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from torchvision import datasets, transforms\n",
|
||||
"from torch.utils.data import random_split, DataLoader\n",
|
||||
"\n",
|
||||
"transform = transforms.Compose([\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"dataset = datasets.ImageFolder(root=data_dir, transform=transform)\n",
|
||||
"\n",
|
||||
"print(dataset.classes) # should print 8 classes\n",
|
||||
"print(len(dataset)) # total image count"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac33bc21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Split 80-20 and save into 2 variables"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f68c1a25",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train: 21102, Test: 5276\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import math \n",
|
||||
"\n",
|
||||
"train_size = math.floor(len(dataset) * 0.8)\n",
|
||||
"test_size = len(dataset) - train_size\n",
|
||||
"\n",
|
||||
"train_dataset, test_dataset = random_split(dataset, [train_size, test_size])\n",
|
||||
"\n",
|
||||
"print(f\"Train: {len(train_dataset)}, Test: {len(test_dataset)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2eede814",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load the data into batches for faster loading...use 4 threads on CPU\n",
|
||||
"\n",
|
||||
"Shuffling train dataset so as to not overfit on one class"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "e1539eaa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"8\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True, num_workers=16) # Increased from 4\n",
|
||||
"test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False, num_workers=16) # Increased from 4\n",
|
||||
"\n",
|
||||
"classes = ('bicycle', 'bus', 'car', 'motorcycle',\n",
|
||||
" 'nonvehicles', 'taxi', 'truck', 'van')\n",
|
||||
"\n",
|
||||
"print(len(classes))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "acebe98e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"MOdified tutorial arch to handle BathhNorm and Dropout (to try to prevent overfitting)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "d1b7d9ca",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Net(\n",
|
||||
" (features): Sequential(\n",
|
||||
" (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
|
||||
" (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
||||
" (2): ReLU()\n",
|
||||
" (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
|
||||
" (4): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
|
||||
" (5): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
||||
" (6): ReLU()\n",
|
||||
" (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
|
||||
" (8): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
|
||||
" (9): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
||||
" (10): ReLU()\n",
|
||||
" (11): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
|
||||
" )\n",
|
||||
" (classifier): Sequential(\n",
|
||||
" (0): Flatten(start_dim=1, end_dim=-1)\n",
|
||||
" (1): Linear(in_features=8192, out_features=512, bias=True)\n",
|
||||
" (2): ReLU()\n",
|
||||
" (3): Dropout(p=0.5, inplace=False)\n",
|
||||
" (4): Linear(in_features=512, out_features=8, bias=True)\n",
|
||||
" )\n",
|
||||
")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torch.nn as nn\n",
|
||||
"import torch.nn.functional as F\n",
|
||||
"\n",
|
||||
"class Net(nn.Module):\n",
|
||||
" def __init__(self):\n",
|
||||
" super(Net, self).__init__()\n",
|
||||
"\n",
|
||||
" self.features = nn.Sequential(\n",
|
||||
" # go from 64x64 to 32x32\n",
|
||||
"\n",
|
||||
" # kernel size = 3x3 filter patch\n",
|
||||
" #padding = 1, so 64x64 stays 64x64 after conv\n",
|
||||
" nn.Conv2d(3, 32, kernel_size=3, padding=1), # 3 channel RRGB, 32 filters (recommended)\n",
|
||||
"\n",
|
||||
" nn.BatchNorm2d(32), # mirrors conv2d output\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.MaxPool2d(2,2), # 2x2 window so halved --> 32x32\n",
|
||||
"\n",
|
||||
" #Go from 32x32 --> 16x16\n",
|
||||
"\n",
|
||||
" nn.Conv2d(32, 64, kernel_size=3, padding=1),\n",
|
||||
" nn.BatchNorm2d(64),\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.MaxPool2d(2,2), \n",
|
||||
" \n",
|
||||
" # Go from 16x 16 0 --> 8x8\n",
|
||||
"\n",
|
||||
" nn.Conv2d(64, 128, kernel_size=3, padding=1),\n",
|
||||
" nn.BatchNorm2d(128),\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.MaxPool2d(2,2), \n",
|
||||
"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" self.classifier = nn.Sequential(\n",
|
||||
" nn.Flatten(),\n",
|
||||
" nn.Linear (128 * 8 *8, 512), # flattened size of 8x8 * 128, 512 is arbitrary output (recommended by sources)\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.Dropout(0.5),#Randomly zero 50% of neurons --> prevent moeoorization\n",
|
||||
" nn.Linear(512, len(classes)) # one score per vehicle class\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" x = self.features(x)\n",
|
||||
" x = self.classifier(x)\n",
|
||||
" return x\n",
|
||||
"\n",
|
||||
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||||
"model = Net().to(device)\n",
|
||||
"print(model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "22e71032",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Loss fn and optimizer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "54d11a04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch.optim as optim\n",
|
||||
"\n",
|
||||
"criterion = nn.CrossEntropyLoss() # Applied softmax to convert scores --> probabilities --> penalizes model \n",
|
||||
"\n",
|
||||
"# Changed to adam optimizer (internal momentumn calculation)\n",
|
||||
"optimizer = optim.Adam(model.parameters(), lr=0.001)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "572d80e3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial training cell adapted to use 30 epochs "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "374d0590",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1: Loss=1.427, Accuracy=61.06%\n",
|
||||
"Epoch 2: Loss=0.814, Accuracy=71.39%\n",
|
||||
"Epoch 3: Loss=0.709, Accuracy=74.82%\n",
|
||||
"Epoch 4: Loss=0.645, Accuracy=77.18%\n",
|
||||
"Epoch 5: Loss=0.597, Accuracy=79.08%\n",
|
||||
"Epoch 6: Loss=0.564, Accuracy=80.10%\n",
|
||||
"Epoch 7: Loss=0.529, Accuracy=81.31%\n",
|
||||
"Epoch 8: Loss=0.500, Accuracy=82.12%\n",
|
||||
"Epoch 9: Loss=0.468, Accuracy=83.23%\n",
|
||||
"Epoch 10: Loss=0.451, Accuracy=84.01%\n",
|
||||
"Epoch 11: Loss=0.430, Accuracy=84.79%\n",
|
||||
"Epoch 12: Loss=0.423, Accuracy=84.93%\n",
|
||||
"Epoch 13: Loss=0.385, Accuracy=86.47%\n",
|
||||
"Epoch 14: Loss=0.368, Accuracy=86.48%\n",
|
||||
"Epoch 15: Loss=0.346, Accuracy=87.31%\n",
|
||||
"Epoch 16: Loss=0.341, Accuracy=87.57%\n",
|
||||
"Epoch 17: Loss=0.310, Accuracy=89.02%\n",
|
||||
"Epoch 18: Loss=0.303, Accuracy=89.11%\n",
|
||||
"Epoch 19: Loss=0.274, Accuracy=89.85%\n",
|
||||
"Epoch 20: Loss=0.258, Accuracy=90.29%\n",
|
||||
"Epoch 21: Loss=0.246, Accuracy=90.69%\n",
|
||||
"Epoch 22: Loss=0.239, Accuracy=91.06%\n",
|
||||
"Epoch 23: Loss=0.227, Accuracy=91.57%\n",
|
||||
"Epoch 24: Loss=0.207, Accuracy=92.31%\n",
|
||||
"Epoch 25: Loss=0.191, Accuracy=92.94%\n",
|
||||
"Epoch 26: Loss=0.178, Accuracy=93.24%\n",
|
||||
"Epoch 27: Loss=0.163, Accuracy=93.96%\n",
|
||||
"Epoch 28: Loss=0.147, Accuracy=94.67%\n",
|
||||
"Epoch 29: Loss=0.150, Accuracy=94.36%\n",
|
||||
"Epoch 30: Loss=0.130, Accuracy=95.14%\n",
|
||||
"Finished Training\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for epoch in range(30): # 10 --> 30 epochs\n",
|
||||
" running_loss = 0.0\n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
"\n",
|
||||
" for i, data in enumerate(train_loader, 0):\n",
|
||||
" inputs, labels = data\n",
|
||||
" inputs, labels = inputs.to(device), labels.to(device) # use GPU\n",
|
||||
"\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
"\n",
|
||||
" outputs = model(inputs)\n",
|
||||
" loss = criterion(outputs, labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
"\n",
|
||||
" # loss tracking\n",
|
||||
" running_loss += loss.item()\n",
|
||||
"\n",
|
||||
" # accuracy tracking\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
" accuracy = 100 * correct / total\n",
|
||||
" print(f'Epoch {epoch + 1}: Loss={running_loss / len(train_loader):.3f}, Accuracy={accuracy:.2f}%') # Credit: Claude for printing accuracy in good formatting\n",
|
||||
"\n",
|
||||
"print('Finished Training')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "2bf2b9a2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PATH = '../../models/batch-norm-dropout.pth'\n",
|
||||
"torch.save(model.state_dict(), PATH)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a24dd4f0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Test on test set using Pytorch tutorial method for both total and per class (maybe spot overfitting as well)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "bc158602",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Test Accuracy: 82.26%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct = 0\n",
|
||||
"total = 0\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
"print(f'Test Accuracy: {100 * correct / total:.2f}%')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "8cc7ed40",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Accuracy for class: Bicycle is 72.7%\n",
|
||||
"Accuracy for class: Bus is 81.5%\n",
|
||||
"Accuracy for class: Car is 78.2%\n",
|
||||
"Accuracy for class: Motorcycle is 82.6%\n",
|
||||
"Accuracy for class: NonVehicles is 100.0%\n",
|
||||
"Accuracy for class: Taxi is 53.2%\n",
|
||||
"Accuracy for class: Truck is 52.9%\n",
|
||||
"Accuracy for class: Van is 41.8%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"total_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predictions = torch.max(outputs, 1)\n",
|
||||
" for label, prediction in zip(labels, predictions):\n",
|
||||
" if label == prediction:\n",
|
||||
" correct_pred[dataset.classes[label]] += 1\n",
|
||||
" total_pred[dataset.classes[label]] += 1\n",
|
||||
"\n",
|
||||
"for classname, correct_count in correct_pred.items():\n",
|
||||
" accuracy = 100 * float(correct_count) / total_pred[classname] \n",
|
||||
" print(f'Accuracy for class: {classname:10s} is {accuracy:.1f}%')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,620 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "7a37220a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello, World!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello, World!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "d318d1f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Van: 1111 images\n",
|
||||
"Taxi: 748 images\n",
|
||||
"Bicycle: 1618 images\n",
|
||||
"Bus: 2133 images\n",
|
||||
"Car: 6781 images\n",
|
||||
"Motorcycle: 2986 images\n",
|
||||
"Truck: 2033 images\n",
|
||||
"NonVehicles: 8968 images\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"data_dir = '../../data/raw/vehicle_classification'\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if os.path.isdir(class_path):\n",
|
||||
" count = len(os.listdir(class_path))\n",
|
||||
" print(f\"{class_name}: {count} images\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "64122ad4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Check out sample image from dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "5604ace3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Size: (64, 64)\n",
|
||||
"Mode: RGB\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 640x480 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from PIL import Image\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# first image in first folder\n",
|
||||
"first_class = os.listdir(data_dir)[0]\n",
|
||||
"first_image_path = os.path.join(data_dir, first_class, os.listdir(os.path.join(data_dir, first_class))[0])\n",
|
||||
"\n",
|
||||
"img = Image.open(first_image_path)\n",
|
||||
"print(f\"Size: {img.size}\")\n",
|
||||
"print(f\"Mode: {img.mode}\")\n",
|
||||
"plt.imshow(img)\n",
|
||||
"plt.title(first_class)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c19ec00a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that all images are RGB, all of same resolution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "3cedd586",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Unique sizes: {(64, 64)}\n",
|
||||
"Unique modes: {'RGB'}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sizes = set()\n",
|
||||
"modes = set()\n",
|
||||
"\n",
|
||||
"for class_name in os.listdir(data_dir):\n",
|
||||
" class_path = os.path.join(data_dir, class_name)\n",
|
||||
" if not os.path.isdir(class_path):\n",
|
||||
" continue\n",
|
||||
" for img_name in os.listdir(class_path):\n",
|
||||
" img = Image.open(os.path.join(class_path, img_name))\n",
|
||||
" sizes.add(img.size)\n",
|
||||
" modes.add(img.mode)\n",
|
||||
"\n",
|
||||
"print(f\"Unique sizes: {sizes}\")\n",
|
||||
"print(f\"Unique modes: {modes}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "88ac961b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Ensure that torch works with GPU (5080) [Credit: Claude]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "8f556b22",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"True\n",
|
||||
"NVIDIA GeForce RTX 5080\n",
|
||||
"GPU works! torch.Size([1000, 1000])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"print(torch.cuda.is_available()) \n",
|
||||
"print(torch.cuda.get_device_name(0)) \n",
|
||||
"\n",
|
||||
"x = torch.randn(1000, 1000).cuda()\n",
|
||||
"y = x @ x\n",
|
||||
"print(\"GPU works!\", y.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "870fadbe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Transform image dataset into tensors. Normalized with 0.5 mean and 0.5 STD (can be differente to use pretrained weights)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "37793c77",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['Bicycle', 'Bus', 'Car', 'Motorcycle', 'NonVehicles', 'Taxi', 'Truck', 'Van']\n",
|
||||
"26378\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from torchvision import datasets, transforms\n",
|
||||
"from torch.utils.data import random_split, DataLoader\n",
|
||||
"\n",
|
||||
"transform = transforms.Compose([\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"dataset = datasets.ImageFolder(root=data_dir, transform=transform)\n",
|
||||
"\n",
|
||||
"print(dataset.classes) # should print 8 classes\n",
|
||||
"print(len(dataset)) # total image count"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ac33bc21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Split 80-20 and save into 2 variables....augments the test set by rotating and lighting it differently to add training nuance."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "f68c1a25",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train: 21102, Test: 5276\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import math \n",
|
||||
"\n",
|
||||
"train_transform = transforms.Compose([\n",
|
||||
" transforms.RandomHorizontalFlip(), # randomly mirror image\n",
|
||||
" transforms.RandomRotation(10), # rotate up to 10 degrees\n",
|
||||
" transforms.ColorJitter(brightness=0.2, contrast=0.2), # vary lighting\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"test_transform = transforms.Compose([\n",
|
||||
" transforms.ToTensor(),\n",
|
||||
" transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"train_full = datasets.ImageFolder(root=data_dir, transform=train_transform)\n",
|
||||
"test_full = datasets.ImageFolder(root=data_dir, transform=test_transform)\n",
|
||||
"\n",
|
||||
"train_size = math.floor(len(train_full) * 0.8)\n",
|
||||
"test_size = len(train_full) - train_size\n",
|
||||
"\n",
|
||||
"# Use same indices for both so split is consistent\n",
|
||||
"indices = torch.randperm(len(train_full)).tolist()\n",
|
||||
"train_indices = indices[:train_size]\n",
|
||||
"test_indices = indices[train_size:]\n",
|
||||
"\n",
|
||||
"train_dataset = torch.utils.data.Subset(train_full, train_indices)\n",
|
||||
"test_dataset = torch.utils.data.Subset(test_full, test_indices)\n",
|
||||
"\n",
|
||||
"print(f\"Train: {len(train_dataset)}, Test: {len(test_dataset)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2eede814",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Load the data into batches for faster loading...use 4 threads on CPU\n",
|
||||
"\n",
|
||||
"Shuffling train dataset so as to not overfit on one class"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "e1539eaa",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['Bicycle', 'Bus', 'Car', 'Motorcycle', 'NonVehicles', 'Taxi', 'Truck', 'Van']\n",
|
||||
"8\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True, num_workers=16)\n",
|
||||
"test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False, num_workers=16)\n",
|
||||
"\n",
|
||||
"classes = train_full.classes \n",
|
||||
"print(classes)\n",
|
||||
"print(len(classes))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "acebe98e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"MOdified tutorial arch to handle BathhNorm and Dropout (to try to prevent overfitting)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "d1b7d9ca",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Net(\n",
|
||||
" (features): Sequential(\n",
|
||||
" (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
|
||||
" (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
||||
" (2): ReLU()\n",
|
||||
" (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
|
||||
" (4): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
|
||||
" (5): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
||||
" (6): ReLU()\n",
|
||||
" (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
|
||||
" (8): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
|
||||
" (9): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
||||
" (10): ReLU()\n",
|
||||
" (11): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
|
||||
" )\n",
|
||||
" (classifier): Sequential(\n",
|
||||
" (0): Flatten(start_dim=1, end_dim=-1)\n",
|
||||
" (1): Linear(in_features=8192, out_features=512, bias=True)\n",
|
||||
" (2): ReLU()\n",
|
||||
" (3): Dropout(p=0.5, inplace=False)\n",
|
||||
" (4): Linear(in_features=512, out_features=8, bias=True)\n",
|
||||
" )\n",
|
||||
")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torch.nn as nn\n",
|
||||
"import torch.nn.functional as F\n",
|
||||
"\n",
|
||||
"class Net(nn.Module):\n",
|
||||
" def __init__(self):\n",
|
||||
" super(Net, self).__init__()\n",
|
||||
"\n",
|
||||
" self.features = nn.Sequential(\n",
|
||||
" # go from 64x64 to 32x32\n",
|
||||
"\n",
|
||||
" # kernel size = 3x3 filter patch\n",
|
||||
" #padding = 1, so 64x64 stays 64x64 after conv\n",
|
||||
" nn.Conv2d(3, 32, kernel_size=3, padding=1), # 3 channel RRGB, 32 filters (recommended)\n",
|
||||
"\n",
|
||||
" nn.BatchNorm2d(32), # mirrors conv2d output\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.MaxPool2d(2,2), # 2x2 window so halved --> 32x32\n",
|
||||
"\n",
|
||||
" #Go from 32x32 --> 16x16\n",
|
||||
"\n",
|
||||
" nn.Conv2d(32, 64, kernel_size=3, padding=1),\n",
|
||||
" nn.BatchNorm2d(64),\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.MaxPool2d(2,2), \n",
|
||||
" \n",
|
||||
" # Go from 16x 16 0 --> 8x8\n",
|
||||
"\n",
|
||||
" nn.Conv2d(64, 128, kernel_size=3, padding=1),\n",
|
||||
" nn.BatchNorm2d(128),\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.MaxPool2d(2,2), \n",
|
||||
"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" self.classifier = nn.Sequential(\n",
|
||||
" nn.Flatten(),\n",
|
||||
" nn.Linear (128 * 8 *8, 512), # flattened size of 8x8 * 128, 512 is arbitrary output (recommended by sources)\n",
|
||||
" nn.ReLU(),\n",
|
||||
" nn.Dropout(0.5),#Randomly zero 50% of neurons --> prevent moeoorization\n",
|
||||
" nn.Linear(512, len(classes)) # one score per vehicle class\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" x = self.features(x)\n",
|
||||
" x = self.classifier(x)\n",
|
||||
" return x\n",
|
||||
"\n",
|
||||
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||||
"model = Net().to(device)\n",
|
||||
"print(model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "22e71032",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Loss fn and optimizer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "54d11a04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch.optim as optim\n",
|
||||
"\n",
|
||||
"criterion = nn.CrossEntropyLoss() # Applied softmax to convert scores --> probabilities --> penalizes model \n",
|
||||
"\n",
|
||||
"# Changed to adam optimizer (internal momentumn calculation)\n",
|
||||
"optimizer = optim.Adam(model.parameters(), lr=0.001)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "572d80e3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Tutorial training cell adapted to use 30 epochs "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "374d0590",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1: Loss=1.515, Accuracy=58.09%\n",
|
||||
"Epoch 2: Loss=0.901, Accuracy=68.38%\n",
|
||||
"Epoch 3: Loss=0.786, Accuracy=72.40%\n",
|
||||
"Epoch 4: Loss=0.723, Accuracy=74.59%\n",
|
||||
"Epoch 5: Loss=0.665, Accuracy=76.57%\n",
|
||||
"Epoch 6: Loss=0.634, Accuracy=77.73%\n",
|
||||
"Epoch 7: Loss=0.602, Accuracy=78.87%\n",
|
||||
"Epoch 8: Loss=0.593, Accuracy=79.30%\n",
|
||||
"Epoch 9: Loss=0.580, Accuracy=79.91%\n",
|
||||
"Epoch 10: Loss=0.548, Accuracy=80.70%\n",
|
||||
"Epoch 11: Loss=0.535, Accuracy=80.95%\n",
|
||||
"Epoch 12: Loss=0.517, Accuracy=81.76%\n",
|
||||
"Epoch 13: Loss=0.521, Accuracy=81.70%\n",
|
||||
"Epoch 14: Loss=0.499, Accuracy=82.46%\n",
|
||||
"Epoch 15: Loss=0.488, Accuracy=82.87%\n",
|
||||
"Epoch 16: Loss=0.483, Accuracy=83.06%\n",
|
||||
"Epoch 17: Loss=0.480, Accuracy=82.94%\n",
|
||||
"Epoch 18: Loss=0.465, Accuracy=83.34%\n",
|
||||
"Epoch 19: Loss=0.464, Accuracy=83.39%\n",
|
||||
"Epoch 20: Loss=0.443, Accuracy=84.22%\n",
|
||||
"Epoch 21: Loss=0.445, Accuracy=84.40%\n",
|
||||
"Epoch 22: Loss=0.436, Accuracy=84.60%\n",
|
||||
"Epoch 23: Loss=0.417, Accuracy=85.12%\n",
|
||||
"Epoch 24: Loss=0.412, Accuracy=85.36%\n",
|
||||
"Epoch 25: Loss=0.410, Accuracy=85.11%\n",
|
||||
"Epoch 26: Loss=0.405, Accuracy=85.42%\n",
|
||||
"Epoch 27: Loss=0.402, Accuracy=85.44%\n",
|
||||
"Epoch 28: Loss=0.388, Accuracy=85.96%\n",
|
||||
"Epoch 29: Loss=0.383, Accuracy=86.22%\n",
|
||||
"Epoch 30: Loss=0.377, Accuracy=86.23%\n",
|
||||
"Finished Training\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for epoch in range(30): # 10 --> 30 epochs\n",
|
||||
" running_loss = 0.0\n",
|
||||
" correct = 0\n",
|
||||
" total = 0\n",
|
||||
"\n",
|
||||
" for i, data in enumerate(train_loader, 0):\n",
|
||||
" inputs, labels = data\n",
|
||||
" inputs, labels = inputs.to(device), labels.to(device) # use GPU\n",
|
||||
"\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
"\n",
|
||||
" outputs = model(inputs)\n",
|
||||
" loss = criterion(outputs, labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
"\n",
|
||||
" # loss tracking\n",
|
||||
" running_loss += loss.item()\n",
|
||||
"\n",
|
||||
" # accuracy tracking\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
" accuracy = 100 * correct / total\n",
|
||||
" print(f'Epoch {epoch + 1}: Loss={running_loss / len(train_loader):.3f}, Accuracy={accuracy:.2f}%') # Credit: Claude for printing accuracy in good formatting\n",
|
||||
"\n",
|
||||
"print('Finished Training')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "2bf2b9a2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PATH = '../../models/data-augmented.pth'\n",
|
||||
"torch.save(model.state_dict(), PATH)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a24dd4f0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Test on test set using Pytorch tutorial method for both total and per class (maybe spot overfitting as well)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "bc158602",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Test Accuracy: 84.17%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct = 0\n",
|
||||
"total = 0\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predicted = torch.max(outputs, 1)\n",
|
||||
" total += labels.size(0)\n",
|
||||
" correct += (predicted == labels).sum().item()\n",
|
||||
"\n",
|
||||
"print(f'Test Accuracy: {100 * correct / total:.2f}%')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "8cc7ed40",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Accuracy for class: Bicycle is 77.4%\n",
|
||||
"Accuracy for class: Bus is 85.3%\n",
|
||||
"Accuracy for class: Car is 86.3%\n",
|
||||
"Accuracy for class: Motorcycle is 81.8%\n",
|
||||
"Accuracy for class: NonVehicles is 99.8%\n",
|
||||
"Accuracy for class: Taxi is 60.8%\n",
|
||||
"Accuracy for class: Truck is 52.8%\n",
|
||||
"Accuracy for class: Van is 41.3%\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"correct_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"total_pred = {classname: 0 for classname in dataset.classes}\n",
|
||||
"\n",
|
||||
"with torch.no_grad():\n",
|
||||
" for data in test_loader:\n",
|
||||
" images, labels = data\n",
|
||||
" images, labels = images.to(device), labels.to(device)\n",
|
||||
" outputs = model(images)\n",
|
||||
" _, predictions = torch.max(outputs, 1)\n",
|
||||
" for label, prediction in zip(labels, predictions):\n",
|
||||
" if label == prediction:\n",
|
||||
" correct_pred[dataset.classes[label]] += 1\n",
|
||||
" total_pred[dataset.classes[label]] += 1\n",
|
||||
"\n",
|
||||
"for classname, correct_count in correct_pred.items():\n",
|
||||
" accuracy = 100 * float(correct_count) / total_pred[classname]\n",
|
||||
" print(f'Accuracy for class: {classname:10s} is {accuracy:.1f}%')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in new issue
Block a user