Ubuntu
commited on
Commit
·
d695662
1
Parent(s):
f1fddae
Modified code for imagent datase
Browse files- requirements.txt +7 -0
- resnet_execute.py +33 -16
- resnet_model.py +9 -7
- validation.py +24 -0
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
torch
|
2 |
+
torchvision
|
3 |
+
albumentations
|
4 |
+
numpy
|
5 |
+
matplotlib
|
6 |
+
tqdm
|
7 |
+
torchsummary
|
resnet_execute.py
CHANGED
@@ -6,28 +6,37 @@ import torch.nn as nn
|
|
6 |
import torch.optim as optim
|
7 |
from resnet_model import ResNet50
|
8 |
from tqdm import tqdm
|
|
|
9 |
|
10 |
# Define transformations
|
11 |
transform = transforms.Compose([
|
|
|
|
|
12 |
transforms.ToTensor(),
|
13 |
-
transforms.Normalize(
|
14 |
])
|
15 |
|
16 |
-
#
|
17 |
-
trainset =
|
18 |
-
trainloader = DataLoader(trainset, batch_size=128, shuffle=True, num_workers=
|
19 |
|
20 |
-
testset =
|
21 |
-
testloader = DataLoader(testset, batch_size=1000, shuffle=False, num_workers=
|
22 |
|
23 |
# Initialize model, loss function, and optimizer
|
24 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
25 |
-
model = ResNet50()
|
|
|
|
|
|
|
26 |
criterion = nn.CrossEntropyLoss()
|
27 |
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4)
|
28 |
|
29 |
# Training function
|
30 |
-
|
|
|
|
|
|
|
31 |
model.train()
|
32 |
running_loss = 0.0
|
33 |
correct = 0
|
@@ -36,21 +45,29 @@ def train(model, device, train_loader, optimizer, criterion, epoch):
|
|
36 |
|
37 |
for batch_idx, (inputs, targets) in enumerate(pbar):
|
38 |
inputs, targets = inputs.to(device), targets.to(device)
|
39 |
-
optimizer.zero_grad()
|
40 |
|
41 |
-
|
42 |
-
|
|
|
|
|
43 |
loss.backward()
|
44 |
-
optimizer.step()
|
45 |
|
46 |
-
|
|
|
|
|
|
|
|
|
47 |
_, predicted = outputs.max(1)
|
48 |
total += targets.size(0)
|
49 |
correct += predicted.eq(targets).sum().item()
|
50 |
|
51 |
-
pbar.set_description(desc=f'Epoch {epoch} | Loss: {
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
-
return 100.*correct/total
|
54 |
|
55 |
# Testing function
|
56 |
def test(model, device, test_loader, criterion):
|
@@ -79,4 +96,4 @@ if __name__ == '__main__':
|
|
79 |
for epoch in range(1, 6): # 20 epochs
|
80 |
train_accuracy = train(model, device, trainloader, optimizer, criterion, epoch)
|
81 |
test_accuracy = test(model, device, testloader, criterion)
|
82 |
-
print(f'Epoch {epoch} | Train Accuracy: {train_accuracy:.2f}% | Test Accuracy: {test_accuracy:.2f}%')
|
|
|
6 |
import torch.optim as optim
|
7 |
from resnet_model import ResNet50
|
8 |
from tqdm import tqdm
|
9 |
+
from torchvision import datasets
|
10 |
|
11 |
# Define transformations
|
12 |
transform = transforms.Compose([
|
13 |
+
transforms.Resize(256), # Resize the smaller side to 256 pixels while keeping aspect ratio
|
14 |
+
transforms.CenterCrop(224), # Then crop to 224x224 pixels from the center
|
15 |
transforms.ToTensor(),
|
16 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # ImageNet normalization
|
17 |
])
|
18 |
|
19 |
+
# Train dataset and loader
|
20 |
+
trainset = datasets.ImageFolder(root='/mnt/imagenet/ILSVRC/Data/CLS-LOC/train', transform=transform)
|
21 |
+
trainloader = DataLoader(trainset, batch_size=128, shuffle=True, num_workers=16, pin_memory=True)
|
22 |
|
23 |
+
testset = datasets.ImageFolder(root='/mnt/imagenet/ILSVRC/Data/CLS-LOC/val', transform=transform )
|
24 |
+
testloader = DataLoader(testset, batch_size=1000, shuffle=False, num_workers=16, pin_memory=True)
|
25 |
|
26 |
# Initialize model, loss function, and optimizer
|
27 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
28 |
+
model = ResNet50()
|
29 |
+
model = torch.nn.DataParallel(model)
|
30 |
+
model = model.to(device)
|
31 |
+
|
32 |
criterion = nn.CrossEntropyLoss()
|
33 |
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4)
|
34 |
|
35 |
# Training function
|
36 |
+
from torch.amp import autocast
|
37 |
+
from tqdm import tqdm
|
38 |
+
|
39 |
+
def train(model, device, train_loader, optimizer, criterion, epoch, accumulation_steps=4):
|
40 |
model.train()
|
41 |
running_loss = 0.0
|
42 |
correct = 0
|
|
|
45 |
|
46 |
for batch_idx, (inputs, targets) in enumerate(pbar):
|
47 |
inputs, targets = inputs.to(device), targets.to(device)
|
|
|
48 |
|
49 |
+
with autocast(device_type='cuda'):
|
50 |
+
outputs = model(inputs)
|
51 |
+
loss = criterion(outputs, targets) / accumulation_steps
|
52 |
+
|
53 |
loss.backward()
|
|
|
54 |
|
55 |
+
if (batch_idx + 1) % accumulation_steps == 0 or (batch_idx + 1) == len(train_loader):
|
56 |
+
optimizer.step()
|
57 |
+
optimizer.zero_grad()
|
58 |
+
|
59 |
+
running_loss += loss.item() * accumulation_steps
|
60 |
_, predicted = outputs.max(1)
|
61 |
total += targets.size(0)
|
62 |
correct += predicted.eq(targets).sum().item()
|
63 |
|
64 |
+
pbar.set_description(desc=f'Epoch {epoch} | Loss: {running_loss / (batch_idx + 1):.4f} | Accuracy: {100. * correct / total:.2f}%')
|
65 |
+
|
66 |
+
if (batch_idx + 1) % 50 == 0:
|
67 |
+
torch.cuda.empty_cache()
|
68 |
+
|
69 |
+
return 100. * correct / total
|
70 |
|
|
|
71 |
|
72 |
# Testing function
|
73 |
def test(model, device, test_loader, criterion):
|
|
|
96 |
for epoch in range(1, 6): # 20 epochs
|
97 |
train_accuracy = train(model, device, trainloader, optimizer, criterion, epoch)
|
98 |
test_accuracy = test(model, device, testloader, criterion)
|
99 |
+
print(f'Epoch {epoch} | Train Accuracy: {train_accuracy:.2f}% | Test Accuracy: {test_accuracy:.2f}%')
|
resnet_model.py
CHANGED
@@ -3,7 +3,7 @@ import torch.nn as nn
|
|
3 |
from torchsummary import summary
|
4 |
|
5 |
class Bottleneck(nn.Module): # Bottleneck module as a single class which will be used to create the ResNet model. Each bottleneck as 3 convolutions.
|
6 |
-
expansion = 4 # sets how much the bottleneck will expand the output channels of the last block to. Used 4 as per original paper.
|
7 |
|
8 |
def __init__(self, in_channels, out_channels, stride=1, downsample=None):
|
9 |
super(Bottleneck, self).__init__()
|
@@ -13,8 +13,8 @@ class Bottleneck(nn.Module): # Bottleneck module as a single class which will be
|
|
13 |
self.bn2 = nn.BatchNorm2d(out_channels)
|
14 |
self.conv3 = nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size=1, bias=False) # this is the convolution where number of channels is expanded, as per the ResNet model.
|
15 |
self.bn3 = nn.BatchNorm2d(out_channels * self.expansion)
|
16 |
-
self.relu = nn.ReLU(inplace=True) # this will modify the original tensor rather than operating on a copy. Significant memory savings as this module is the fundamental repeating unit.
|
17 |
-
self.downsample = downsample # helps match the
|
18 |
|
19 |
def forward(self, x):
|
20 |
identity = x
|
@@ -40,16 +40,17 @@ class Bottleneck(nn.Module): # Bottleneck module as a single class which will be
|
|
40 |
return out
|
41 |
|
42 |
class ResNet50(nn.Module):
|
43 |
-
def __init__(self, num_classes=
|
44 |
super(ResNet50, self).__init__()
|
45 |
self.in_channels = 64 # only used for the initiation of the first bottleneck block in the first layer.
|
46 |
|
47 |
## See Excel sheet for Model Architecture
|
48 |
|
49 |
-
# Adjusted Initial Conv Layer for
|
50 |
-
self.conv1 = nn.Conv2d(3, 64, kernel_size=
|
51 |
self.bn1 = nn.BatchNorm2d(64)
|
52 |
self.relu = nn.ReLU(inplace=True) # as before, this will modify the input tensor. Good memory savings here as the input image will be large in size here.
|
|
|
53 |
|
54 |
# Layers with Bottleneck Blocks
|
55 |
self.layer1 = self._make_layer(Bottleneck, 64, 3) # stride is 1 here, so the downsampling will only adjust for the channel size in the first block of this layer
|
@@ -81,6 +82,7 @@ class ResNet50(nn.Module):
|
|
81 |
x = self.conv1(x)
|
82 |
x = self.bn1(x)
|
83 |
x = self.relu(x)
|
|
|
84 |
|
85 |
x = self.layer1(x)
|
86 |
x = self.layer2(x)
|
@@ -97,4 +99,4 @@ if __name__ == '__main__':
|
|
97 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
98 |
print(device)
|
99 |
model = ResNet50().to(device)
|
100 |
-
summary(model, input_size=(3,
|
|
|
3 |
from torchsummary import summary
|
4 |
|
5 |
class Bottleneck(nn.Module): # Bottleneck module as a single class which will be used to create the ResNet model. Each bottleneck as 3 convolutions.
|
6 |
+
expansion = 4 # sets how much the bottleneck will expand the output channels of the last layer in a bottleneck block to. Used 4 as per the original paper.
|
7 |
|
8 |
def __init__(self, in_channels, out_channels, stride=1, downsample=None):
|
9 |
super(Bottleneck, self).__init__()
|
|
|
13 |
self.bn2 = nn.BatchNorm2d(out_channels)
|
14 |
self.conv3 = nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size=1, bias=False) # this is the convolution where number of channels is expanded, as per the ResNet model.
|
15 |
self.bn3 = nn.BatchNorm2d(out_channels * self.expansion)
|
16 |
+
self.relu = nn.ReLU(inplace=True) # this will modify the original tensor rather than operating on a copy. Significant memory savings as this module is the fundamental repeating unit.
|
17 |
+
self.downsample = downsample # helps match the input dimensions to the dimensions after convolution for the special skip connection.
|
18 |
|
19 |
def forward(self, x):
|
20 |
identity = x
|
|
|
40 |
return out
|
41 |
|
42 |
class ResNet50(nn.Module):
|
43 |
+
def __init__(self, num_classes=1000): # num_classes to be set as per the dataset. 10 for CIFAR-10, 1000 for ImageNet 1k.
|
44 |
super(ResNet50, self).__init__()
|
45 |
self.in_channels = 64 # only used for the initiation of the first bottleneck block in the first layer.
|
46 |
|
47 |
## See Excel sheet for Model Architecture
|
48 |
|
49 |
+
# Adjusted Initial Conv Layer for ImageNet 1k
|
50 |
+
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) #kernel size is 7 here for ImageNet 1k.
|
51 |
self.bn1 = nn.BatchNorm2d(64)
|
52 |
self.relu = nn.ReLU(inplace=True) # as before, this will modify the input tensor. Good memory savings here as the input image will be large in size here.
|
53 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # Add max pooling layer
|
54 |
|
55 |
# Layers with Bottleneck Blocks
|
56 |
self.layer1 = self._make_layer(Bottleneck, 64, 3) # stride is 1 here, so the downsampling will only adjust for the channel size in the first block of this layer
|
|
|
82 |
x = self.conv1(x)
|
83 |
x = self.bn1(x)
|
84 |
x = self.relu(x)
|
85 |
+
x = self.maxpool(x) # Add max pooling layer in forward pass
|
86 |
|
87 |
x = self.layer1(x)
|
88 |
x = self.layer2(x)
|
|
|
99 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
100 |
print(device)
|
101 |
model = ResNet50().to(device)
|
102 |
+
summary(model, input_size=(3, 224, 224)) # size is (3, 224, 224) for ImageNet 1k.
|
validation.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import argparse
|
2 |
+
import os
|
3 |
+
import shutil
|
4 |
+
|
5 |
+
parser = argparse.ArgumentParser()
|
6 |
+
parser.add_argument("-d", "--dir", help="dir with the images", required=True)
|
7 |
+
parser.add_argument("-l", "--labels", help="file with image name to class label mapping", required=True)
|
8 |
+
|
9 |
+
args = parser.parse_args()
|
10 |
+
|
11 |
+
processed_classes = set()
|
12 |
+
|
13 |
+
with open(args.labels, "r") as file:
|
14 |
+
# skip header
|
15 |
+
next(file)
|
16 |
+
for line in file:
|
17 |
+
img_name, labels = line.split(",")
|
18 |
+
class_name = labels.split(" ")[0]
|
19 |
+
# create a dir for this classname
|
20 |
+
if class_name not in processed_classes:
|
21 |
+
dir_path = args.dir + "/" + class_name
|
22 |
+
if not os.path.exists(dir_path):
|
23 |
+
os.mkdir(dir_path)
|
24 |
+
shutil.move(args.dir + "/" + img_name + ".JPEG", args.dir + "/" + class_name+ "/" + img_name + ".JPEG")
|