import torch
from torchvision import datasets, models, transforms
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
device
device(type='cuda', index=0)
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
train_transform = transforms.Compose([
transforms.Resize(256),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean, std)])
test_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean, std)])
#we perform these on the training data, test data is not randomly flipped or cropped
pwd
'C:\\Users\\cam12\\datasets'
data_dir = 'datasetsToUse/HPV/'
#holds training and test data
image_datasets ={}
#image folder allows us to load images and apply a series of transformations on the, read everything from train and apply
#transforms etc
image_datasets['train']= datasets.ImageFolder(data_dir + '/HPVTraining', train_transform)
image_datasets['test']= datasets.ImageFolder(data_dir + '/HPVVal', test_transform)
#both stored as dictionaries
print("Training data size - %d" % len(image_datasets['train']))
print("Test data size - %d" % len(image_datasets['test']))
Training data size - 24520
Test data size - 5996
class_names = image_datasets['train'].classes
class_names
['HPV+', 'HPV-']
image_datasets['train'].class_to_idx
#This shows that HPV+ is a 0 and HPV positive is a 1
{'HPV+': 0, 'HPV-': 1}
dataloaders ={}
dataloaders['train'] = torch.utils.data.DataLoader(image_datasets['train'],
batch_size=8,
shuffle=True,
num_workers=4)
dataloaders['test'] = torch.utils.data.DataLoader(image_datasets['test'],
batch_size=8,
shuffle=True,
num_workers=4)
dataloaders
{'train': <torch.utils.data.dataloader.DataLoader at 0x27fe824e3a0>,
'test': <torch.utils.data.dataloader.DataLoader at 0x27fe824ee50>}
inputs, labels = next(iter(dataloaders['train']))
inputs.shape
torch.Size([8, 3, 224, 224])
labels
#we have 2 folders in our training folder, 0 and 1
tensor([0, 0, 0, 0, 0, 0, 0, 0])
import torchvision
#make grid to view using matplotlib, stack side by side
inp = torchvision.utils.make_grid(inputs)
inp.shape
#include padding
torch.Size([3, 228, 1810])
inp.max()
#maxiumum value for RGB is 2.64
tensor(2.5354)
import numpy as np
np.clip(inp.cpu(), 0, 1).max()
tensor(1.)
inp.numpy().transpose((1, 2, 0)).shape
(228, 1810, 3)
import matplotlib.pyplot as plt
plt.ion()
model = models.resnet18(pretrained=True)
model.to(device)
ResNet(
(conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
(layer1): Sequential(
(0): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(1): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer2): Sequential(
(0): BasicBlock(
(conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer3): Sequential(
(0): BasicBlock(
(conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer4): Sequential(
(0): BasicBlock(
(conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
(fc): Linear(in_features=512, out_features=1000, bias=True)
)
#add last linear layer that works with our data, need number of input features
num_ftrs = model.fc.in_features
num_ftrs
512
import torch.nn as nn
#takes in 512 features as extracted above, and it ouputs to 2, + or -
#This layer replaces the layer in the pretrained model
model.fc = nn.Linear(num_ftrs, 2)
criterion = nn.CrossEntropyLoss()
criterion.to(device)
CrossEntropyLoss()
import torch.optim as optim
optimizer = optim.SGD(model.parameters(),
lr=0.001,
momentum=0.9)
from torch.optim import lr_scheduler
exp_lr_scheduler = lr_scheduler.StepLR(optimizer,
step_size=7,
gamma=0.1)
def calculate_accuracy(phase, running_loss, running_corrects):
epoch_loss = running_loss / len(image_datasets[phase])
epoch_acc = running_corrects.double() / len(image_datasets[phase])
print('{} Loss: {:.4f} Acc: {:.4f}'.format( phase, epoch_loss, epoch_acc))
return (epoch_loss, epoch_acc)
def phase_train(model, criterion, optimizer, scheduler):
model.train()
running_loss = 0.0
running_corrects = 0
#setup for training, loop through inputs, zero gradients, enable for training
#run predictions and extract predicted values, calculate loss
#run backward and update optimiser
for inputs, labels in dataloaders['train']:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(True):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
scheduler.step()
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
calculate_accuracy('train', running_loss, running_corrects)
#We are going to train the model and evaluate the accuracy,
#we will only save params that give best accuracy
import copy
best_acc = 0.0
def phase_test(model, criterion, optimizer):
model.eval()
running_loss = 0.0
running_corrects = 0
global best_acc
#keeps track of model weights that predicts best accuracy
for inputs, labels in dataloaders['test']:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
#run with no grads and zero grads before
with torch.no_grad():
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
#get predicted output and caclulate loss
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss, epoch_acc = calculate_accuracy('test', running_loss, running_corrects)
#If we get a more accurate result - store the model params in best model wts
if epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
return best_model_wts
def build_model(model, criterion, optimizer, scheduler, num_epochs=10):
#will be updated if we getrr a better accuracy
best_model_wts = copy.deepcopy(model.state_dict())
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
phase_train(model, criterion, optimizer, scheduler)
best_model_wts = phase_test(model, criterion, optimizer)
#once we train on an epoch - we update our model wts
print()
print('Best test Acc: {:4f}'.format(best_acc))
model.load_state_dict(best_model_wts)
return model
#runs for 1 epoch just for demonstration purposes
print ("Got here")
model = build_model(model,
criterion,
optimizer,
exp_lr_scheduler,
num_epochs=1)
Got here
Epoch 0/0
----------
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-143-05c4ef04f42d> in <module>
1 #runs for 1 epoch just for demonstration purposes
2 print ("Got here")
----> 3 model = build_model(model,
4 criterion,
5 optimizer,
<ipython-input-142-fa210670951b> in build_model(model, criterion, optimizer, scheduler, num_epochs)
7 print('-' * 10)
8
----> 9 phase_train(model, criterion, optimizer, scheduler)
10 best_model_wts = phase_test(model, criterion, optimizer)
11 #once we train on an epoch - we update our model wts
<ipython-input-138-9d206a49c1bd> in phase_train(model, criterion, optimizer, scheduler)
16 outputs = model(inputs)
17 _, preds = torch.max(outputs, 1)
---> 18 loss = criterion(outputs, labels)
19
20 loss.backward()
~\anaconda3\lib\site-packages\torch\nn\modules\module.py in _call_impl(self, *input, **kwargs)
720 result = self._slow_forward(*input, **kwargs)
721 else:
--> 722 result = self.forward(*input, **kwargs)
723 for hook in itertools.chain(
724 _global_forward_hooks.values(),
~\anaconda3\lib\site-packages\torch\nn\modules\loss.py in forward(self, input, target)
945
946 def forward(self, input: Tensor, target: Tensor) -> Tensor:
--> 947 return F.cross_entropy(input, target, weight=self.weight,
948 ignore_index=self.ignore_index, reduction=self.reduction)
949
~\anaconda3\lib\site-packages\torch\nn\functional.py in cross_entropy(input, target, weight, size_average, ignore_index, reduce, reduction)
2420 if size_average is not None or reduce is not None:
2421 reduction = _Reduction.legacy_get_string(size_average, reduce)
-> 2422 return nll_loss(log_softmax(input, 1), target, weight, None, ignore_index, None, reduction)
2423
2424
~\anaconda3\lib\site-packages\torch\nn\functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction)
2216 .format(input.size(0), target.size(0)))
2217 if dim == 2:
-> 2218 ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
2219 elif dim == 4:
2220 ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
RuntimeError: Expected object of device type cuda but got device type cpu for argument #1 'self' in call to _thnn_nll_loss_forward
with torch.no_grad():
inputs, labels = iter(dataloaders['test']).next()
inp = torchvision.utils.make_grid(inputs)
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
for j in range(len(inputs)):
inp = inputs.data[j]
img_show(inp, 'predicted:' + class_names[preds[j]])
Freezing a model’s layers
Here, we freeze all the layers in the network except the final layer.
We need to set requires_grad == False to freeze the parameters so that the gradients are not computed in backward().
frozen_model = models.resnet18(pretrained=True)
frozen_model.to(device)
for param in frozen_model.parameters():
param.requires_grad = False
We add the final layer which is not frozen
frozen_model.fc = nn.Linear(num_ftrs, 5)
Add an optimizer - for only the final layer
There is no gradient calculation for the other layers, so we cannot use an optimizer for those
optimizer = optim.SGD(frozen_model.fc.parameters(),
lr=0.001,
momentum=0.9)
Create a scheduler for this model
Just like with the earlier model, we attach a scheduler to this optimizer in order to slow down the learning rate
exp_lr_scheduler = lr_scheduler.StepLR(optimizer,
step_size=7,
gamma=0.1)
The loss calculation is the same CrossEntropyLoss as earlier
This is just included here as a reminder as we are not changing the definition
criterion = nn.CrossEntropyLoss()
Reset the best_acc score
This is so that the new frozen model can start from scratch
best_acc = 0.0
Build the new model with the frozen layers
This is similar to what we did previously. Except this time, the gradient calculation is turned off for the parameters when a call is made to backward()
frozen_model = build_model(frozen_model,
criterion,
optimizer,
exp_lr_scheduler,
num_epochs=8)
We test a batch of flowers on this new model
with torch.no_grad():
inputs, labels = iter(dataloaders['test']).next()
inp = torchvision.utils.make_grid(inputs)
outputs = frozen_model(inputs)
_, preds = torch.max(outputs, 1)
for j in range(len(inputs)):
inp = inputs.data[j]
img_show(inp, 'predicted:' + class_names[preds[j]])