Transfer Learning For Image Classification in Pytorch
Transfer Learning For Image Classification in Pytorch
dinushasan / transfer-learning-pytorch 0 0
2 months ago
In [2]:
In [3]:
In [4]:
download_url('https://s3.amazonaws.com/fast-ai-imageclas/oxford-iiit-pet.tgz', '.')
In [5]:
import tarfile
In [6]:
In [7]:
import os
DATA_DIR = './data/oxford-iiit-pet/images'
files = os.listdir(DATA_DIR)
files[:5]
Out[7]:
['shiba_inu_181.jpg',
'scottish_terrier_180.jpg',
'american_pit_bull_terrier_161.jpg',
'wheaten_terrier_116.jpg',
'american_pit_bull_terrier_55.jpg']
In [8]:
def parse_breed(fname):
parts = fname.split('_')
return ' '.join(parts[:-1])
In [9]:
parse_breed(files[4])
Out[9]:
'american pit bull terrier'
In [10]:
def open_image(path):
with open(path, 'rb') as f:
img = Image.open(f)
return img.convert('RGB')
import os
class PetsDataset(Dataset):
def __init__(self, root, transform):
super().__init__()
self.root = root
self.files = [fname for fname in os.listdir(root) if fname.endswith('.jpg')]
self.classes = list(set(parse_breed(fname) for fname in files))
self.transform = transform
def __len__(self):
return len(self.files)
In [12]:
import torchvision.transforms as T
img_size = 224
imagenet_stats = ([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
dataset = PetsDataset(DATA DIR T Compose([T Resize(img size)
dataset = PetsDataset(DATA_DIR, T.Compose([T.Resize(img_size),
T.Pad(8, padding_mode='reflect'),
T.RandomCrop(img_size),
T.ToTensor(),
T.Normalize(*imagenet_stats)]))
In [13]:
len(dataset)
Out[13]:
7390
In [14]:
import torch
import matplotlib.pyplot as plt
%matplotlib inline
In [15]:
show_image(*dataset[2])
CreatingTrainingandValidation Sets
Creating Training and Validation Sets
In [16]:
In [17]:
val_pct = 0.1
val_size = int(val_pct * len(dataset))
In [18]:
In [19]:
def show_batch(dl):
for images, labels in dl:
fig, ax = plt.subplots(figsize=(16, 16))
ax.set_xticks([]); ax.set_yticks([])
images = denormalize(images[:64], *imagenet_stats)
ax.imshow(make_grid(images, nrow=8).permute(1, 2, 0))
break
In [20]:
show_batch(train_dl)
Modifying a Pretrained Model (ResNet34)
Transfer learning (source):
In [21]:
import torch.nn as nn
import torch.nn.functional as F
In [22]:
class PetsModel(ImageClassificationBase):
def __init__(self, num_classes, pretrained=True):
super().__init__()
# Use a pretrained model
self.network = models.resnet34(pretrained=pretrained)
# Replace last layer
self.network.fc = nn.Linear(self.network.fc.in_features, num_classes)
In [24]:
models.resnet34(pretrained=True)
Downloading: "https://download.pytorch.org/models/resnet34-333f7ec4.pth" to
/root/.cache/torch/hub/checkpoints/resnet34-333f7ec4.pth
HBox(children=(FloatProgress(value=0 0 max=87306240 0) HTML(value='')))
HBox(children=(FloatProgress(value=0.0, max=87306240.0), HTML(value= )))
Out[24]:
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)
)
(2): 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)
def get_default_device():
"""Pick GPU if available, else CPU"""
if torch.cuda.is_available():
return torch.device('cuda')
else:
return torch.device('cpu')
class DeviceDataLoader():
"""Wrap a dataloader to move data to a device"""
( )
def __init__(self, dl, device):
self.dl = dl
self.device = device
def __iter__(self):
"""Yield a batch of data after moving it to device"""
for b in self.dl:
yield to_device(b, self.device)
def __len__(self):
"""Number of batches"""
return len(self.dl)
In [26]:
import torch
from tqdm.notebook import tqdm
@torch.no_grad()
def evaluate(model, val_loader):
model.eval()
outputs = [model.validation_step(batch) for batch in val_loader]
return model.validation_epoch_end(outputs)
def get_lr(optimizer):
for param_group in optimizer.param_groups:
return param_group['lr']
# Gradient clipping
if grad_clip:
nn.utils.clip_grad_value_(model.parameters(), grad_clip)
optimizer.step()
optimizer.zero_grad()
# Validation phase
result = evaluate(model, val_loader)
result['train_loss'] = torch.stack(train_losses).mean().item()
result['lrs'] = lrs
model.epoch_end(epoch, result)
history.append(result)
return history
In [27]:
device = get_default_device()
device
Out[27]:
device(type='cuda')
In [28]:
model = PetsModel(len(dataset.classes))
to_device(model, device);
In [30]:
Out[30]:
[{'val_acc': 0.02273196540772915, 'val_loss': 3.9689669609069824}]
In [31]:
epochs = 6
max_lr = 0.01
grad_clip = 0.1
weight_decay = 1e-4
opt_func = torch.optim.Adam
In [32]:
%%time
history += fit_one_cycle(epochs, max_lr, model, train_dl, valid_dl,
grad_clip=grad_clip,
weight_decay=weight_decay,
opt_func=opt_func)
HBox(children=(FloatProgress(value=0.0, max=26.0), HTML(value='')))
Epoch [4],last lr: 0.00133, train loss: 0.4891, val loss: 0.8032, val acc: 0.7526
Epoch [4],last_lr: 0.00133, train_loss: 0.4891, val_loss: 0.8032, val_acc: 0.7526
HBox(children=(FloatProgress(value=0.0, max=26.0), HTML(value='')))
In [33]:
In [34]:
Out[34]:
[{'val_acc': 0.02125636674463749, 'val_loss': 89.57513427734375}]
In [35]:
%%time
history2 += fit_one_cycle(epochs, max_lr, model2, train_dl, valid_dl,
grad_clip=grad_clip,
weight_decay=weight_decay,
opt_func=opt_func)
HBox(children=(FloatProgress(value=0.0, max=26.0), HTML(value='')))
While the pretrained model reached an accuracy of 80% in less than 3 minutes, the model without pretrained weights
could only reach an accuracy of 24%.
In [36]:
In [37]:
import jovian
In [38]:
jovian.commit(project='transfer-learning-pytorch')