Jovian
⭐️
Sign In
In [2]:
!pip install jovian --upgrade --quiet
In [2]:
 

TODO -

  1. Pick the dataset.
  2. Loading the dataset using PyTorch.
  3. Explore the dataset.
  4. Prepare the dataset for training.
In [3]:
#download the dataset - https://www.kaggle.com/alxmamaev/flowers-recognition
# using opendatasets
!pip install opendatasets --upgrade --quiet
In [4]:
import opendatasets as od
In [7]:
#downloading the dataset directly from kaggle
dataset_url = 'https://www.kaggle.com/alxmamaev/flowers-recognition'
od.download(dataset_url)
Please provide your Kaggle credentials to download this dataset. Learn more: http://bit.ly/kaggle-creds Your Kaggle username: alishagera Your Kaggle Key: ··········
2%|▏ | 9.00M/450M [00:00<00:06, 71.3MB/s]
Downloading flowers-recognition.zip to ./flowers-recognition
100%|██████████| 450M/450M [00:02<00:00, 173MB/s]
In [8]:
import shutil
shutil.rmtree('./flowers-recognition/flowers/flowers')
In [9]:
data_dir = './flowers-recognition/flowers'
import os
os.listdir(data_dir)


Out[9]:
['dandelion', 'rose', 'daisy', 'sunflower', 'tulip']
In [10]:
#use ImageFolder class from torchvision
from torchvision.datasets import ImageFolder
#loading a dataset
dataset = ImageFolder(data_dir)
In [11]:
len(dataset)
Out[11]:
4323
In [12]:
dataset[0]
Out[12]:
(<PIL.Image.Image image mode=RGB size=320x263 at 0x7F6ACF25F9B0>, 0)
In [13]:
dataset.classes
Out[13]:
['daisy', 'dandelion', 'rose', 'sunflower', 'tulip']
In [14]:
for cls in os.listdir(data_dir):
  print(cls, ':', len(os.listdir(data_dir + '/' + cls)))
dandelion : 1055 rose : 784 daisy : 769 sunflower : 734 tulip : 984
In [15]:
import matplotlib.pyplot as plt
%matplotlib inline


In [16]:
img = dataset[120][0]
plt.imshow(img)

Out[16]:
<matplotlib.image.AxesImage at 0x7f6a75582cc0>
Notebook Image
In [17]:
import torchvision.transforms as tt

dataset = ImageFolder(data_dir, tt.Compose([tt.Resize(224),
                                           tt.RandomCrop(224),
                                           tt.ToTensor()]))
In [18]:
img = dataset[120][0]
plt.imshow(img.permute(1,2,0))
Out[18]:
<matplotlib.image.AxesImage at 0x7f6a762d0f60>
Notebook Image
In [19]:
# dividing the dataset
val_pct = 0.1
val_size = int(val_pct*len(dataset))
train_size = int(len(dataset)-val_size)

val_size, train_size


Out[19]:
(432, 3891)
In [20]:
from torch.utils.data import random_split
train_ds,val_ds = random_split(dataset,[train_size,val_size])
len(train_ds),len(val_ds)

Out[20]:
(3891, 432)
In [44]:
from torch.utils.data.dataloader import DataLoader
batch_size=128

train_dl = DataLoader(train_ds,
                      batch_size,
                      shuffle=True,
                      num_workers=4,
                      pin_memory=True)
val_dl = DataLoader(val_ds, 
                    batch_size*2,
                    num_workers=4, 
                    pin_memory=True)
In [22]:
from torchvision.utils import make_grid

def show_batch(dl):
    for images, labels in dl:
        fig, ax = plt.subplots(figsize=(12, 6))
        ax.set_xticks([]); ax.set_yticks([])
        ax.imshow(make_grid(images, nrow=16).permute(1, 2, 0))
        break
In [25]:
show_batch(train_dl)
Notebook Image
In [31]:
import torch
def get_default_device():
    """Pick GPU if available, else CPU"""
    if torch.cuda.is_available():
        return torch.device('cuda')
    else:
        return torch.device('cpu')
    
def to_device(data, device):
    """Move tensor(s) to chosen device"""
    if isinstance(data, (list,tuple)):
        return [to_device(x, device) for x in data]
    return data.to(device, non_blocking=True)

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 [ ]:
 
In [28]:
import torch
torch.cuda.is_available()
Out[28]:
True
In [33]:
device = get_default_device()
device
Out[33]:
device(type='cuda')
In [34]:
img = dataset[0][0]
img
Out[34]:
tensor([[[0.6118, 0.6078, 0.5961,  ..., 0.6549, 0.6510, 0.6549],
         [0.6667, 0.6588, 0.6549,  ..., 0.6549, 0.6510, 0.6549],
         [0.6471, 0.6392, 0.6314,  ..., 0.6549, 0.6549, 0.6549],
         ...,
         [0.4941, 0.4941, 0.4980,  ..., 0.5020, 0.5059, 0.5098],
         [0.4941, 0.4941, 0.4980,  ..., 0.4980, 0.5020, 0.5059],
         [0.4941, 0.4902, 0.4941,  ..., 0.5020, 0.5020, 0.5020]],

        [[0.6157, 0.6118, 0.5961,  ..., 0.6588, 0.6549, 0.6588],
         [0.6706, 0.6627, 0.6588,  ..., 0.6588, 0.6549, 0.6588],
         [0.6510, 0.6431, 0.6353,  ..., 0.6588, 0.6588, 0.6588],
         ...,
         [0.4863, 0.4863, 0.4902,  ..., 0.4980, 0.5020, 0.5059],
         [0.4863, 0.4863, 0.4902,  ..., 0.4980, 0.4980, 0.5020],
         [0.4863, 0.4824, 0.4863,  ..., 0.5020, 0.4980, 0.4980]],

        [[0.6314, 0.6275, 0.6235,  ..., 0.6667, 0.6627, 0.6667],
         [0.6863, 0.6784, 0.6784,  ..., 0.6667, 0.6627, 0.6667],
         [0.6706, 0.6588, 0.6471,  ..., 0.6667, 0.6667, 0.6667],
         ...,
         [0.4980, 0.4980, 0.5020,  ..., 0.4902, 0.4941, 0.4980],
         [0.4980, 0.4980, 0.5020,  ..., 0.4902, 0.4902, 0.4941],
         [0.4980, 0.4941, 0.4980,  ..., 0.4941, 0.4902, 0.4902]]])
In [35]:
img.device
Out[35]:
device(type='cpu')
In [36]:
img_gpu = to_device(img,device)
img_gpu.device
Out[36]:
device(type='cuda', index=0)
In [37]:
train_dl = DeviceDataLoader(train_dl,device)
val_dl = DeviceDataLoader(val_dl,device)

Model and Training Utilities

In [38]:
import torch.nn as nn
class ImageClassificationBase(nn.Module):
    def training_step(self, batch):
      # calculate loss for a training data
        images, labels = batch 
        out = self(images)                  # Generate predictions
        loss = F.cross_entropy(out, labels) # Calculate loss
        return loss
    
    def validation_step(self, batch):
      #calculate loss and accuracy for a batch of validation data
        images, labels = batch 
        out = self(images)                    # Generate predictions
        loss = F.cross_entropy(out, labels)   # Calculate loss
        acc = accuracy(out, labels)           # Calculate accuracy
        return {'val_loss': loss.detach(), 'val_acc': acc}
        
    def validation_epoch_end(self, outputs):
      #combine the results
        batch_losses = [x['val_loss'] for x in outputs]
        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses
        batch_accs = [x['val_acc'] for x in outputs]
        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuracies
        return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
    
    def epoch_end(self, epoch, result):
      #printing the info at each end
        print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
            epoch, result['train_loss'], result['val_loss'], result['val_acc']))
        
def accuracy(outputs, labels):
    _, preds = torch.max(outputs, dim=1)
    return torch.tensor(torch.sum(preds == labels).item() / len(preds))
In [39]:
@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 fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):
    history = []
    optimizer = opt_func(model.parameters(), lr)
    for epoch in range(epochs):
        # Training Phase 
        model.train()
        train_losses = []
        for batch in train_loader:
            loss = model.training_step(batch)
            train_losses.append(loss)
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
        # Validation phase
        result = evaluate(model, val_loader)
        result['train_loss'] = torch.stack(train_losses).mean().item()
        model.epoch_end(epoch, result)
        history.append(result)
    return history

Defining a Model

In [40]:
def conv_block(in_channels, out_channels, pool=False):
    layers = [nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), 
              nn.BatchNorm2d(out_channels), 
              nn.ReLU(inplace=True)]
    if pool: layers.append(nn.MaxPool2d(2))
    return nn.Sequential(*layers)

class ResNet9(ImageClassificationBase):
    def __init__(self, in_channels, num_classes):
        super().__init__()
        
        self.conv1 = conv_block(in_channels, 64)
        self.conv2 = conv_block(64, 128, pool=True)
        self.res1 = nn.Sequential(conv_block(128, 128), conv_block(128, 128))
        
        self.conv3 = conv_block(128, 256, pool=True)
        self.conv4 = conv_block(256, 512, pool=True)
        self.res2 = nn.Sequential(conv_block(512, 512), conv_block(512, 512))
        
        self.classifier = nn.Sequential(nn.MaxPool2d(4), 
                                        nn.Flatten(), 
                                        nn.Dropout(0.2),
                                        nn.Linear(512, num_classes))
        
    def forward(self, xb):
        out = self.conv1(xb)
        out = self.conv2(out)
        out = self.res1(out) + out
        out = self.conv3(out)
        out = self.conv4(out)
        out = self.res2(out) + out
        out = self.classifier(out)
        return out
In [41]:
model = to_device(ResNet9(3, len(dataset.classes)), device)
model
Out[41]:
ResNet9(
  (conv1): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace=True)
  )
  (conv2): Sequential(
    (0): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace=True)
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (res1): Sequential(
    (0): Sequential(
      (0): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): ReLU(inplace=True)
    )
    (1): Sequential(
      (0): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): ReLU(inplace=True)
    )
  )
  (conv3): Sequential(
    (0): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace=True)
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (conv4): Sequential(
    (0): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace=True)
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (res2): Sequential(
    (0): Sequential(
      (0): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): ReLU(inplace=True)
    )
    (1): Sequential(
      (0): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
      (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (2): ReLU(inplace=True)
    )
  )
  (classifier): Sequential(
    (0): MaxPool2d(kernel_size=4, stride=4, padding=0, dilation=1, ceil_mode=False)
    (1): Flatten(start_dim=1, end_dim=-1)
    (2): Dropout(p=0.2, inplace=False)
    (3): Linear(in_features=512, out_features=5, bias=True)
  )
)
In [43]:
model.conv1[0].weight.device

Out[43]:
device(type='cuda', index=0)

Pass one batch of input tensor through the model.

In [ ]:
for batch in train_dl:
  images,labels = batch
  print('images.shape',images.shape)
  print('images.device',images.device)
  preds = model(images)
  print()
  break
In [ ]:
jovian.commit('FlowersImageProcessingProject')