which-flower / utilities.py
rdkulkarni's picture
Upload utilities.py
4b317fb
raw
history blame
8.42 kB
import torch
from torchvision import datasets, transforms
from PIL import Image
import numpy as np
import math
import argparse
import time
import copy
def get_input_args_train():
parser = argparse.ArgumentParser()
parser.add_argument('dir', metavar = 'Data Directory (path/to/dir/)', type = str, default = 'flowers/', help = 'Specify path to folder of images to be used for training & validation')
parser.add_argument('--save_dir', type = str, default = 'checkpoints/', help = 'Specify path to folder to save Model Checkpoints')
parser.add_argument('--arch', type = str, default = 'vgg13', nargs = '?',
choices = ["alexnet", "vgg11_bn", "vgg13", "vgg16", "densenet121","resnet18"],
# choices = [ "alexnet", "squeezenet1_0", "vgg13", "vgg16", "densenet121", "googlenet",
# "convnext_tiny", "inception_v3", "shufflenet_v2_x1_0", "mobilenet_v2","resnext50_32x4d", "wide_resnet50_2","mnasnet1_0"
# ],
help = 'Provide model architecture to be be used')
parser.add_argument('--arch_type', type = str, default = 'existing', nargs = '?', choices = ["existing","new","custom"],
help = 'Provide type of model architecture to be be used')
parser.add_argument('--learning_rate', type = float, default = 0.003, help = 'Provide learning rate to be be used, default is 0.003')
parser.add_argument('--hidden_units', type = list, default = [512, 256], help = 'Provide number of hidden units to use, default is 128')
parser.add_argument('--epochs', type = int, default = 3, help = 'Provide number of epochs to use for training')
parser.add_argument('--gpu', type = str, nargs='?', default = 'cpu', const = 'gpu', help = 'GPU will be used for training if you specific --gpu')
parser.add_argument('--feature_extract', action='store_false', help = 'Indicate whether to extract features or fine tune the whole existing model')
return parser.parse_args()
def get_input_args_predict():
parser = argparse.ArgumentParser()
parser.add_argument('path', metavar = 'Path to image (path/to/image)', default = 'flowers/valid/10/image_07094.jpg', type = str, help = 'Specify path to image for which model needs to predict')
parser.add_argument('--save_dir', type = str, default = 'checkpoints/checkpoint-custom-resnet18.pth', help = 'Specify path to folder to retrieve Checkpoints')
parser.add_argument('----category_names', type = str, default = 'cat_to_name.json', help = 'Provide mapping of class indexes to category names')
parser.add_argument('--top_k', type = int, default = 3, help = 'Provide a number of most likely classes to be returned')
parser.add_argument('--gpu', type = str, nargs='?', default = 'cpu', const = 'gpu', help = 'GPU will be used for training if you specific --gpu')
return parser.parse_args()
#Load training & validation data and transform plus load label mapping
def data_transforms():
return {
'train' : transforms.Compose([transforms.RandomRotation(30),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])]),
'valid' : transforms.Compose([transforms.Resize(255),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])])
}
def image_datasets(directory, data_transforms):
return {
'train' : datasets.ImageFolder(directory + 'train/', transform = data_transforms['train']),
'valid' : datasets.ImageFolder(directory + 'valid/', transform = data_transforms['valid'])
}
def data_loaders(image_datasets):
data = {
'train' : torch.utils.data.DataLoader(image_datasets['train'], batch_size=64, shuffle = True),
'valid' : torch.utils.data.DataLoader(image_datasets['valid'], batch_size=64, shuffle=True)
}
#FOR TEST
# from torch.utils.data.sampler import SubsetRandomSampler
# sampler = torch.utils.data.sampler.SubsetRandomSampler(list(range(10)))
# data = {
# 'train' : torch.utils.data.DataLoader(image_datasets['train'], sampler = sampler, batch_size=64),
# 'valid' : torch.utils.data.DataLoader(image_datasets['valid'], sampler = sampler, batch_size=64)
# }
return data
def process_image(image_path):
im = Image.open(image_path)
# Resize
if im.size[1] < im.size[0]:
im.thumbnail((255, math.pow(255, 2)))
else:
im.thumbnail((math.pow(255, 2), 255))
#Crop
width, height = im.size
left = (width - 224)/2
top = (height - 224)/2
right = (width + 224)/2
bottom = (height + 224)/2
im = im.crop((left, top, right, bottom))
#Convert to np.array
np_image = np.array(im)/255
#Undo Mean, Standard Deviation and Transpose
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
np_image = (np_image - mean)/ std
np_image = np.transpose(np_image, (2, 0, 1))
return np_image
def train_model(model, dataloaders, criterion, optimizer, num_epochs, device, is_inception=False):
since = time.time()
val_acc_history = []
best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
# Each epoch has a training and validation phase
for phase in ['train', 'valid']:
if phase == 'train':
model.train() # Set model to training mode
else:
model.eval() # Set model to evaluate mode
running_loss = 0.0
running_corrects = 0
for inputs, labels in dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device)
optimizer.zero_grad() # zero the parameter gradients
with torch.set_grad_enabled(phase == 'train'): # forward track history if only in train
# Get model outputs and calculate loss, In train mode we calculate the loss by summing the final output and the auxiliary output
# but in testing we only consider the final output.
if is_inception and phase == 'train': # From https://discuss.pytorch.org/t/how-to-optimize-inception-model-with-auxiliary-classifiers/7958
outputs, aux_outputs = model(inputs)
loss1 = criterion(outputs, labels)
loss2 = criterion(aux_outputs, labels)
loss = loss1 + 0.4*loss2
else:
outputs = model(inputs)
loss = criterion(outputs, labels)
_, preds = torch.max(outputs, 1)
if phase == 'train': # backward + optimize only if in training phase
loss.backward()
optimizer.step()
# statistics
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
# deep copy the model
if phase == 'valid' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
if phase == 'valid':
val_acc_history.append(epoch_acc)
print()
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))
# load best model weights
model.load_state_dict(best_model_wts)
return model, val_acc_history