32 KiB
32 KiB
None
<html>
<head>
</head>
</html>
Question 7 - ResNet¶
In [1]:
import os
import glob
import torch
import numpy as np
from PIL import Image
from tqdm import tqdm
from collections import Counter
from xml.etree import ElementTree as ET
from torchvision import transforms, models
from torch.utils.data import Dataset, DataLoader
Load the dataset¶
In [2]:
val_labels = "./data/MaskedFace/val/labels"
val_imgs = "./data/MaskedFace/val/images"
y_true = glob.glob(os.path.join(val_labels,"*.txt"))
images = glob.glob(os.path.join(val_imgs,"*.png"))
In [3]:
test_dataset = {
'images': images, # list of image paths
'y_true': y_true, # list of label paths
}
In [4]:
def count_obj(txt_file, n_class):
with open(txt_file, 'r') as file:
lines = file.readlines()
# Extracting the class identifiers from each line
class_ids = [int(line.split()[0]) for line in lines]
# Counting the occurrences of each class
class_counts = Counter(class_ids)
# Sorting the dictionary by class id and converting it to a list of counts
sorted_counts = [class_counts[i] if i in class_counts else 0 for i in range(n_class)]
return sorted_counts
In [5]:
gt_counts = []
for idx , (img , txt) in enumerate(tqdm(zip(test_dataset['images'], test_dataset['y_true']))):
# get ground truth
obj_count = count_obj(txt, 3)
gt_counts.append(obj_count)
Load the model¶
In [6]:
class ImageDataset(Dataset):
def __init__(self, directory, transformations=None):
self.directory = directory
self.transformations = transformations
self.filenames = [file for file in os.listdir(directory) if file.endswith('.png')]
self.labels_array = np.zeros((len(self.filenames), 3), dtype=np.int64)
def __len__(self):
return len(self.filenames)
def __getitem__(self, index):
file_path = os.path.join(self.directory, self.filenames[index])
img = Image.open(file_path).convert('RGB')
labels = self.extract_labels(file_path.replace('.png', '.xml'))
if self.transformations:
img = self.transformations(img)
self.labels_array[index] = labels
return img, torch.tensor(labels, dtype=torch.float32)
def extract_labels(self, xml_path):
xml_data = ET.parse(xml_path)
categories = {'with_mask': 0, 'without_mask': 0, 'mask_weared_incorrect': 0}
for item in xml_data.getroot().findall('object'):
categories[item.find('name').text] += 1
return list(categories.values())
# Define image transformations
image_transforms = {
'train': transforms.Compose([
transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)),
transforms.RandomRotation(degrees=15),
transforms.ColorJitter(),
transforms.RandomHorizontalFlip(),
transforms.CenterCrop(size=224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'val': transforms.Compose([
transforms.Resize(size=256),
transforms.CenterCrop(size=224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
}
# Initialize the pretrained ResNet18 model and modify the fully connected layer
pretrained_model = models.resnet18(pretrained=True)
pretrained_model.fc = torch.nn.Linear(pretrained_model.fc.in_features, 3)
# Create the dataset and dataloaders
training_data = ImageDataset('data/MaskedFace/train', transformations=image_transforms['train'])
validation_data = ImageDataset('data/MaskedFace/val', transformations=image_transforms['val'])
train_data_loader = DataLoader(training_data, batch_size=32, shuffle=True)
validation_data_loader = DataLoader(validation_data, batch_size=32)
# Setup device, loss function, optimizer, and learning rate scheduler
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
pretrained_model.to(device)
Out[6]:
In [7]:
import copy
from sklearn.metrics import mean_absolute_error
# Setup device, loss function, optimizer, and learning rate scheduler
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
pretrained_model.to(device)
loss_function = torch.nn.MSELoss()
optimizer = torch.optim.SGD(pretrained_model.parameters(), lr=0.001, momentum=0.9)
learning_rate_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
# Function to evaluate the model's performance on validation data
def evaluate_performance(model, loader):
model.eval()
total_error = 0.0
for imgs, lbls in loader:
imgs, lbls = imgs.to(device), lbls.to(device)
with torch.no_grad():
predictions = model(imgs)
error = mean_absolute_error(lbls.cpu().detach().numpy(), predictions.cpu().detach().numpy(), multioutput='raw_values')
total_error += np.sum(error)
return total_error / len(loader.dataset)
# Early stopping and model saving setup
best_model_wts = copy.deepcopy(pretrained_model.state_dict())
best_loss = float('inf')
early_stopping_patience = 3
patience_counter = 0
# Training loop
epochs = 10
for epoch in range(epochs):
pretrained_model.train()
epoch_loss = 0.0
for imgs, lbls in tqdm(train_data_loader):
imgs, lbls = imgs.to(device), lbls.to(device)
optimizer.zero_grad()
predictions = pretrained_model(imgs)
loss = loss_function(predictions, lbls)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
learning_rate_scheduler.step()
# Validation phase
validation_loss = evaluate_performance(pretrained_model, validation_data_loader)
print(f'Epoch {epoch+1}, Loss: {epoch_loss / len(train_data_loader):.3f}, Validation Loss: {validation_loss:.3f}')
# Check for early stopping
if validation_loss < best_loss:
best_loss = validation_loss
best_model_wts = copy.deepcopy(pretrained_model.state_dict())
torch.save(pretrained_model.state_dict(), 'best_model.pth')
# Load the best model weights
pretrained_model.load_state_dict(torch.load('best_model.pth'))
# Final evaluation on the validation dataset
validation_error = evaluate_performance(pretrained_model, validation_data_loader)
print(f'Validation MAE: {validation_error * 100:.2f}%')
# Print label counts from the training dataset
print(training_data.labels_array)
Evaluate on the test set¶
In [8]:
# Function to evaluate the model's performance on validation data
def evaluate_performance(model, loader):
model.eval()
total_error = 0.0
for imgs, lbls in loader:
imgs, lbls = imgs.to(device), lbls.to(device)
with torch.no_grad():
predictions = model(imgs)
error = mean_absolute_error(lbls.cpu().detach().numpy(), predictions.cpu().detach().numpy(), multioutput='raw_values')
print(error)
total_error += np.sum(error)
return total_error / 3
In [9]:
# Load the best model weights
pretrained_model.load_state_dict(torch.load('best_model.pth'))
# Final evaluation on the validation dataset
validation_error = evaluate_performance(pretrained_model, validation_data_loader)
print(f'Validation MAE: {validation_error * 100:.2f}%')
In [11]:
counts = []
for idx , (img , lbls) in enumerate(tqdm(validation_data)):
img, lbls = img.to(device), lbls.to(device)
with torch.no_grad():
predictions = pretrained_model(torch.unsqueeze(img, 0))[0]
counts.append(predictions.detach().numpy())
In [17]:
counts
Out[17]:
MAPE¶
In [12]:
def compute_mape(prediction, truth):
mape = np.mean( np.abs(truth - prediction) / np.maximum(truth, np.ones_like(truth)) ) * 100
return mape
In [13]:
MAPE = compute_mape(np.array(counts), gt_counts)
In [14]:
print(MAPE)
Final Score¶
In [15]:
if MAPE <= 10:
print("Score: ", 25*1.0)
elif MAPE <= 15:
print("Score: ", 25*0.875)
elif MAPE <= 20:
print("Score: ", 25*0.75)
elif MAPE <= 25:
print("Score: ", 25*0.625)
elif MAPE <= 30:
print("Score: ", 25*0.5)
else:
print("Score: ", 0)
In [ ]: