EC332 Machine Learning
Department of Computer Science
University of the Punjab
Author: Nazar Khan
Your task is to build and train a deep learning model or models using PyTorch to classify handwritten numbers from an imbalanced dataset available at this link. Due to the imbalanced nature of the dataset, you are encouraged to use appropriate data augmentation techniques and other strategies to improve model performance. You may also use additional training data from some other dataset of handwritten digits, if that helps.
train
and test
0
, 0.5
, 1
, 1.5
, 2
, 2.5
, 3
, 3.5
, 4
, 4.5
, 5
, 5.5
, 6
, 6.5
, 7
, 7.5
, with each sub-folder containing images representing handwritten instances of the corresponding number.Data Loading and Preprocessing (15 marks):
Data Handling (10 marks):
Model Development (25 marks):
Training the Model (25 marks):
Evaluation (15 marks):
Analysis and Reflection (10 marks):
Assignment.ipynb
) containing:
requirements.txt
file with all dependencies used.model.pth
).from torchvision import transforms
from torch.utils.data import Dataset
from PIL import Image
import os
class HandwrittenDataset(Dataset):
def __init__(self, root_dir, transform=None):
self.root_dir = root_dir
self.transform = transform
self.image_paths = []
self.labels = []
for label in os.listdir(root_dir):
label_path = os.path.join(root_dir, label)
if os.path.isdir(label_path):
for img in os.listdir(label_path):
self.image_paths.append(os.path.join(label_path, img))
self.labels.append(float(label)) # Convert labels to float
def __len__(self):
return len(self.image_paths)
def __getitem__(self, idx):
image_path = self.image_paths[idx]
label = self.labels[idx]
image = Image.open(image_path).convert("RGB")
if self.transform:
image = self.transform(image)
return image, label
# Define the RandomAffine transformation
transform = transforms.Compose([
transforms.RandomAffine(
degrees=15, # Random rotation between -15 to +15 degrees
translate=(0.1, 0.1), # Randomly translate up to 10% of width and height
scale=(0.9, 1.1), # Randomly scale between 90% to 110%
shear=10 # Randomly shear up to 10 degrees
),
transforms.ToTensor(), # Convert image to PyTorch tensor
transforms.Normalize(mean=[0.5], std=[0.5]) # Normalize to [-1, 1] range
])
import torch
from torch import nn, optim
def train_model(model, dataloaders, criterion, optimizer, num_epochs=10):
for epoch in range(num_epochs):
print(f"Epoch {epoch+1}/{num_epochs}")
print("-" * 10)
for phase in ['train', 'val']:
if phase == 'train':
model.train()
else:
model.eval()
running_loss = 0.0
corrects = 0
for inputs, labels in dataloaders[phase]:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
loss = criterion(outputs, labels)
_, preds = torch.max(outputs, 1)
if phase == 'train':
loss.backward()
optimizer.step()
running_loss += loss.item() * inputs.size(0)
corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = corrects.double() / len(dataloaders[phase].dataset)
print(f"{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}")
This assignment combines foundational concepts in PyTorch with practical problem-solving, emphasizing the handling of class imbalance and the application of data augmentation techniques.