Learn data science and machine learning by building real-world projects on Jovian

pytorchimg.png

PyTorch Course Project - ZERO TO GANs With CNN, ResNet & Pre-Trained Models

Sushil Rajeeva Bhandary

sushilrajeeva@gmail.com

Welcome to my PyTorch project for the course Zero to GANs from Jovian.ai. I'd like to take a moment to thank the instructor Aakash N S and the whole team for this awesome course, I got to learn a whole lot of maths, coding, intuitives behind the Deep Learning and PyTorch and I appreciate them greatly for that.

In this course project, I will be doing Image Classification by using the Fruit and Vegetable DataSet from Kaggle (https://www.kaggle.com/kritikseth/fruit-and-vegetable-image-recognition) that is with about 3600 images for training, 1000 images for validation, another 1000 images for testing and 36 classes to be classified defined as follows:


Context

This dataset contains images of the following food items:

  1. fruits - banana, apple, pear, grapes, orange, kiwi, watermelon, pomegranate, pineapple, mango.

  2. vegetables - cucumber, carrot, capsicum, onion, potato, lemon, tomato, raddish, beetroot, cabbage, lettuce, spinach, soy bean, cauliflower, bell pepper, chilli pepper, turnip, corn, sweetcorn, sweet potato, paprika, jalepeño, ginger, garlic, peas, eggplant.

This dataset contains three folders:

train (100 images each)
test (10 images each)
validation (10 images each)

each of the above folders contains subfolders for different fruits and vegetables wherein the images for respective food items are present.

Data Collection

The images in this dataset were scraped by someone from Bing Image Search for a project.

Inspiration

The idea was to build an application which recognizes the food item(s) from the captured photo and gives its user different recipes that can be made using the food item(s).

I will be using Convulational Neural Network (CNN), Resnet and Pre-Trained Model (Resent50) to train on this dataset and compare the results in attempt to go through the lessons of the whole course all over again. So let's get started.

In [2]:
project_name = 'Final-Course-Project-PyTorch-FruitVegitableDataSet'
In [ ]:
!pip install opendatasets --upgrade --quiet

import opendatasets as od

od.download('https://www.kaggle.com/kritikseth/fruit-and-vegetable-image-recognition')
Please provide your Kaggle credentials to download this dataset. Learn more: http://bit.ly/kaggle-creds Your Kaggle username: sushilrajeeva Your Kaggle Key: ··········
0%| | 5.00M/2.19G [00:00<01:24, 27.8MB/s]
Downloading fruit-and-vegetable-image-recognition.zip to ./fruit-and-vegetable-image-recognition
100%|██████████| 2.19G/2.19G [00:18<00:00, 127MB/s]
In [ ]:
import os
import torch
import torchvision
import torch.nn.functional as F
from torch.utils.data import random_split, DataLoader, Dataset
from torchvision.utils import make_grid
import torchvision.transforms as T
from torchvision.datasets import ImageFolder

import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

matplotlib.rcParams['figure.facecolor'] = '#ffffff'
In [ ]:
# to ignore the filterwarnings
import warnings
warnings.filterwarnings("ignore")
In [ ]:
# Look into the data directory
data_dir = '/content/fruit-and-vegetable-image-recognition'
print(os.listdir(data_dir))
classes = sorted(os.listdir(data_dir + "/train")) #sorting the list alphabetically as they are supposed to be and so that the images and the lables are matching
print(classes)
['test', 'validation', 'train'] ['apple', 'banana', 'beetroot', 'bell pepper', 'cabbage', 'capsicum', 'carrot', 'cauliflower', 'chilli pepper', 'corn', 'cucumber', 'eggplant', 'garlic', 'ginger', 'grapes', 'jalepeno', 'kiwi', 'lemon', 'lettuce', 'mango', 'onion', 'orange', 'paprika', 'pear', 'peas', 'pineapple', 'pomegranate', 'potato', 'raddish', 'soy beans', 'spinach', 'sweetcorn', 'sweetpotato', 'tomato', 'turnip', 'watermelon']
Total number of classes to be classified
In [ ]:
# total number of classes to be classified
len(classes)
Out[]:
36
In [3]:
#Saving the work done so far
jovian.commit(project=project_name, environment=None)
[jovian] Detected Colab notebook... [jovian] Please enter your API key ( from https://jovian.ai/ ): API KEY: ·········· [jovian] Uploading colab notebook to Jovian... [jovian] Committed successfully! https://jovian.ai/sushilrajeev1997/final-course-project-pytorch-fruitvegitabledataset
Walking through the directories and their folders to check the number of images for each classes.
In [ ]:
# checking the number of images for each class.
train_directory = '/content/fruit-and-vegetable-image-recognition/train'
num_images = []
for root, dirs, files in os.walk(train_directory):
  if files == []:
    continue
  else:
    num_images.append(len(files))
print(num_images)
[100, 100, 95, 100, 99, 99, 99, 100, 100, 100, 100, 99, 100, 100, 100, 97, 100, 100, 98, 99, 98, 100, 100, 100, 100, 100, 100, 99, 99, 100, 100, 100, 100, 100, 100, 100]
In [ ]:
val_directory = '/content/fruit-and-vegetable-image-recognition/validation'

num_images = []
for root, dirs, files in os.walk(val_directory):
  if files == []:
    continue
  else:
    num_images.append(len(files))
print(num_images)
[10, 10, 9, 10, 10, 10, 10, 9, 10, 10, 10, 10, 9, 9, 9, 10, 9, 10, 10, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9]
In [ ]:
test_directory = '/content/fruit-and-vegetable-image-recognition/test'

num_images = []
for root, dirs, files in os.walk(test_directory):
  if files == []:
    continue
  else:
    num_images.append(len(files))
print(num_images)
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]

There's supposed to have 100 images for each of 36 classes in training, 10 images for each classes in testing and 10 for each in validation. However, os seemed to miss reading some images and thus returned less number of images than it's supposed to have returned.

The following codes are to check the size of the images to make sure everything is in order before we feed the image tensors to the model to train on.

In [ ]:
# to test the size of the images
image_size_test = ImageFolder(train_directory, T.ToTensor())
len(image_size_test)
Out[]:
3581
In [ ]:
#checking the size of the first 10 images in the dataset

im_count = 0
for image,_ in image_size_test:
  im_count += 1
  print(image.shape)
  if im_count == 10:
    break    
torch.Size([3, 2216, 2218]) torch.Size([3, 1200, 1200]) torch.Size([3, 1809, 1200]) torch.Size([3, 720, 1280]) torch.Size([3, 630, 1200]) torch.Size([3, 720, 960]) torch.Size([3, 720, 1280]) torch.Size([3, 1600, 1067]) torch.Size([3, 249, 250]) torch.Size([3, 800, 1200])
In [ ]:
image, label = image_size_test[120]
image, label
Out[]:
(tensor([[[0.2235, 0.2000, 0.1843,  ..., 0.5569, 0.5529, 0.5451],
          [0.2039, 0.1961, 0.2078,  ..., 0.5529, 0.5529, 0.5490],
          [0.2078, 0.2235, 0.2627,  ..., 0.5490, 0.5529, 0.5490],
          ...,
          [0.3255, 0.3294, 0.3333,  ..., 0.4392, 0.4353, 0.4353],
          [0.3373, 0.3373, 0.3412,  ..., 0.4353, 0.4353, 0.4353],
          [0.3529, 0.3451, 0.3451,  ..., 0.4353, 0.4353, 0.4353]],
 
         [[0.2314, 0.2157, 0.2000,  ..., 0.4667, 0.4627, 0.4549],
          [0.2118, 0.2078, 0.2235,  ..., 0.4627, 0.4627, 0.4588],
          [0.2078, 0.2314, 0.2706,  ..., 0.4588, 0.4627, 0.4588],
          ...,
          [0.3137, 0.3176, 0.3216,  ..., 0.4118, 0.4039, 0.3961],
          [0.3059, 0.3137, 0.3176,  ..., 0.4078, 0.4078, 0.3961],
          [0.3098, 0.3137, 0.3137,  ..., 0.4078, 0.4078, 0.3961]],
 
         [[0.2784, 0.2588, 0.2431,  ..., 0.3961, 0.3922, 0.3843],
          [0.2588, 0.2431, 0.2353,  ..., 0.3922, 0.3922, 0.3882],
          [0.2392, 0.2275, 0.2275,  ..., 0.3882, 0.3922, 0.3882],
          ...,
          [0.3569, 0.3608, 0.3569,  ..., 0.3882, 0.3922, 0.3882],
          [0.3569, 0.3608, 0.3569,  ..., 0.3843, 0.3843, 0.3882],
          [0.3647, 0.3647, 0.3569,  ..., 0.3765, 0.3843, 0.3882]]]), 1)
In [ ]:
image.shape
Out[]:
torch.Size([3, 1000, 2000])
In [ ]:
#checking the maximum and minimum size of the images of the whole dataset

height = []
width = []
for image,_ in image_size_test:
  height.append(image.shape[1])
  width.append(image.shape[2])
print("minimum height: {}, maximum height: {}, minimum width: {}, maximum width: {}".format(min(height), max(height), min(width), max(width)))
minimum height: 146, maximum height: 6351, minimum width: 133, maximum width: 7360
In [ ]:
def show_example(img,label):
  print('Label: ', classes[label], '('+str(label)+')')
  plt.imshow(img.permute(1, 2, 0))
In [ ]:
image, label = image_size_test[0]
image.shape
Out[]:
torch.Size([3, 2216, 2218])
In [ ]:
show_example(*image_size_test[0])
Label: apple (0)
Notebook Image
In [ ]:
# calculate the means of each color channel to use them for normalization
mean = 0.0
for img, _ in image_size_test:
    mean += img.mean([1,2])
mean = mean/len(image_size_test)
print(mean)
tensor([0.6265, 0.5739, 0.4267])
In [ ]:
# calculate the stds of each color channel
%%time
std = 0.0
for img, _ in image_size_test:
    std += img.std([1,2])
std = std/len(image_size_test)
print(std)
tensor([0.2300, 0.2376, 0.2650]) CPU times: user 6min 26s, sys: 5.76 s, total: 6min 32s Wall time: 6min 32s

This following whole step is just to check if everything is in order by using make_grid method before we start feeding the image tensors to model for training.

In [ ]:
import torchvision.transforms as T
img_size = 224
stats = ([0.6265, 0.5739, 0.4267], [0.2300, 0.2376, 0.2650])

normalnets_train_tfms = T.Compose([T.Resize(img_size),
                                   T.RandomRotation(degrees=15),
                                   T.RandomHorizontalFlip(),
                                   T.Pad(8, padding_mode='reflect'),
                                   T.RandomCrop(160),
                                   T.ToTensor(),
                                   T.Normalize(*stats)])
normalnets_val_tfms = T.Compose([T.Resize((160,160)),
                                 T.ToTensor(),
                                 T.Normalize(*stats)])
In [ ]:
normalnet_train_ds = ImageFolder(train_directory, normalnets_train_tfms)
normalnet_val_ds = ImageFolder(val_directory, normalnets_val_tfms)
In [ ]:
#checking the size of the first 10 images in the train dataset after transformation

im_count = 0
for image,_ in normalnet_train_ds:
  im_count += 1
  print(image.shape)
  if im_count == 10:
    break    
torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160])
In [ ]:
#checking the size of the first 10 images in the val dataset after transformation

im_count = 0
for image,_ in normalnet_val_ds:
  im_count += 1
  print(image.shape)
  if im_count == 10:
    break   
torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160]) torch.Size([3, 160, 160])
In [ ]:
len(normalnet_train_ds), len(normalnet_val_ds)
Out[]:
(3581, 351)
In [ ]:
from torch.utils.data import DataLoader
batch_size = 64

normalnet_train_dl = DataLoader(normalnet_train_ds, batch_size, shuffle=True, num_workers=8, pin_memory=True)
normalnet_val_dl = DataLoader(normalnet_val_ds, batch_size*2, num_workers=8, pin_memory=True)
In [ ]:
def denorm(img_tensors):
    return img_tensors * stats[1][0] + stats[0][0]

def show_images(images, nmax=64):
    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_xticks([]); ax.set_yticks([])
    ax.imshow(make_grid(denorm(images.detach()[:nmax]), nrow=8).permute(1, 2, 0))

def show_batch(dl, nmax=64):
    for images, _ in dl:
        show_images(images, nmax)
        break
In [ ]:
show_batch(normalnet_train_dl)
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Notebook Image
In [5]:
jovian.commit(project=project_name)
[jovian] Detected Colab notebook... [jovian] Uploading colab notebook to Jovian... [jovian] Capturing environment.. [jovian] Committed successfully! https://jovian.ai/sushilrajeev1997/final-course-project-pytorch-fruitvegitabledataset

Everything looks fine. So I will now be going for the next step.

Defining all the neccessary functions and classes to start training

In [ ]:
import torch.nn as nn
import torch.nn.functional as F

def accuracy(outputs, labels):
    _, preds = torch.max(outputs, dim=1)
    return torch.tensor((torch.sum(preds == labels).item() / len(preds)) * 100)


class FVClassificationBase(nn.Module):
    def training_step(self, batch):
        images, labels = batch
        out = self(images)                  # Generate predictions
        loss = F.cross_entropy(out, labels)  # Calculate loss
        return loss

    def validation_step(self, batch):
        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):
        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):
        print("Epoch [{}],{} train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
            epoch+1, "last_lr: {:.8f},".format(result['lrs'][-1]) if 'lrs' in result else '', 
            result['train_loss'], result['val_loss'], result['val_acc']))
In [ ]:
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 [ ]:
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 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 tqdm(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

def get_lr(optimizer):
    for param_group in optimizer.param_groups:
        return param_group['lr']

def fit_one_cycle(epochs, max_lr, model, train_loader, val_loader,
                  weight_decay=0, grad_clip=None, opt_func=torch.optim.SGD):
    torch.cuda.empty_cache()
    history = []

    # Set up custom optimizer with weight decay
    optimizer = opt_func(model.parameters(), max_lr, weight_decay=weight_decay)
    # Set up one-cycle learning rate scheduler
    sched = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr, epochs=epochs,
                                                steps_per_epoch=len(train_loader))

    for epoch in range(epochs):
        # Training Phase
        model.train()
        train_losses = []
        lrs = []
        for batch in tqdm(train_loader):
            loss = model.training_step(batch)
            train_losses.append(loss)
            loss.backward()

            # Gradient clipping
            if grad_clip:
                nn.utils.clip_grad_value_(model.parameters(), grad_clip)

            optimizer.step()
            optimizer.zero_grad()

            # Record & update learning rate
            lrs.append(get_lr(optimizer))
            sched.step()

        # 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 [ ]:
device = get_default_device()
device
Out[]:
device(type='cuda')
In [ ]:

def plot_accuracies(history):
  accuracies = [x['val_acc'] for x in history]
  plt.plot(accuracies, '-x')
  plt.xlabel('epoch')
  plt.ylabel(['accuracy'])
  plt.title('Accuracy vs No. of epoch')


def plot_losses(history):
  train_losses = [x.get('train_loss') for x in history]
  val_losses = [x['val_loss'] for x in history]
  plt.plot(train_losses, '-bx')
  plt.plot(val_losses, '-rx')
  plt.xlabel('epoch')
  plt.ylabel('loss')
  plt.legend(['Training','Validation'])
  plt.title('Loss vs No. of epoch')

def predict_image(img, model):
  # Convert to a batch of 1
  xb = to_device(img.unsqueeze(0),device)
  yb = model(xb)
  # Pick index with highest probability
  _, preds = torch.max(yb, dim = 1)
  # Retrieve the class label
  return classes[preds[0].item()]

CNN MODEL

In [ ]:
import torchvision.transforms as T
img_size = 224
stats = ([0.6265, 0.5739, 0.4267], [0.2300, 0.2376, 0.2650]) #for CNN and Resnet, I will be using the means and stds of the color channels of this dataset instead of the standard means and stds of Imagenet for pretrained models.

CNN_train_tfms = T.Compose([T.Resize(img_size),
                                   T.RandomRotation(degrees=15),
                                   T.RandomHorizontalFlip(),
                                   T.Pad(3, padding_mode='reflect'),
                                   T.RandomCrop(160),
                                   T.ToTensor(),
                                   T.Normalize(*stats)])
CNN_val_tfms = T.Compose([T.Resize((160,160)),
                                 T.ToTensor(),
                                 T.Normalize(*stats)])
In [ ]:
CNN_train_ds = ImageFolder(train_directory, CNN_train_tfms)
CNN_val_ds = ImageFolder(val_directory, CNN_val_tfms)
In [ ]:
batch_size = 64

CNN_train_dl = DataLoader(CNN_train_ds, batch_size, shuffle=True, num_workers=8, pin_memory=True)
CNN_val_dl = DataLoader(CNN_val_ds, batch_size*2, num_workers=8, pin_memory=True)
In [ ]:
train_dl = DeviceDataLoader(CNN_train_dl, device)
val_dl = DeviceDataLoader(CNN_val_dl, device)
In [ ]:
class CnnModel(FVClassificationBase):
    def __init__(self):
        super().__init__()
        self.network = nn.Sequential(
            #input 3 x 160 x 160
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(4, 4), # output: 64 x 40 x 40

            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(128, 512, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2), # output: 512 x 20 x 20

            nn.Conv2d(512, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(4, 4), # output: 256 x 5 x 5

            nn.Flatten(), 
            nn.Linear(256*5*5, 64),
            nn.ReLU(),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, len(classes)))
        
    def forward(self, xb):
        return self.network(xb)
In [ ]:
CNNmodel = to_device(CnnModel(), device)
CNNmodel
Out[]:
CnnModel(
  (network): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU()
    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU()
    (4): MaxPool2d(kernel_size=4, stride=4, padding=0, dilation=1, ceil_mode=False)
    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (6): ReLU()
    (7): Conv2d(128, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): ReLU()
    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (10): Conv2d(512, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU()
    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU()
    (14): MaxPool2d(kernel_size=4, stride=4, padding=0, dilation=1, ceil_mode=False)
    (15): Flatten(start_dim=1, end_dim=-1)
    (16): Linear(in_features=6400, out_features=64, bias=True)
    (17): ReLU()
    (18): Linear(in_features=64, out_features=32, bias=True)
    (19): ReLU()
    (20): Linear(in_features=32, out_features=36, bias=True)
  )
)
In [ ]:
CNNhistory = [evaluate(CNNmodel, val_dl)]
CNNhistory
Out[]:
[{'val_acc': 2.6041667461395264, 'val_loss': 3.5862956047058105}]
In [ ]:
epochs = 10
max_lr = 0.0003
grad_clip = 0.1
weight_decay = 1e-4
opt_func = torch.optim.Adam
In [ ]:
%%time
CNNhistory += fit_one_cycle(epochs, max_lr, CNNmodel, train_dl, val_dl, 
                         grad_clip=grad_clip, 
                         weight_decay=weight_decay, 
                         opt_func=opt_func)
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [1],last_lr: 0.00008244, train_loss: 3.5881, val_loss: 3.5802, val_acc: 2.3438
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [2],last_lr: 0.00022722, train_loss: 3.5272, val_loss: 3.3596, val_acc: 9.2379
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [3],last_lr: 0.00030000, train_loss: 3.3128, val_loss: 3.0724, val_acc: 16.2911
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [4],last_lr: 0.00028515, train_loss: 3.1054, val_loss: 2.6654, val_acc: 21.3624
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [5],last_lr: 0.00024352, train_loss: 2.9339, val_loss: 2.4701, val_acc: 25.7895
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [6],last_lr: 0.00018338, train_loss: 2.7533, val_loss: 2.2848, val_acc: 27.4424
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [7],last_lr: 0.00011662, train_loss: 2.6565, val_loss: 2.1728, val_acc: 31.6996
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [8],last_lr: 0.00005648, train_loss: 2.5932, val_loss: 2.1294, val_acc: 34.1228
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [9],last_lr: 0.00001486, train_loss: 2.5360, val_loss: 2.0867, val_acc: 35.4468
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [10],last_lr: 0.00000000, train_loss: 2.5086, val_loss: 2.0845, val_acc: 36.8394 CPU times: user 21.9 s, sys: 18 s, total: 39.9 s Wall time: 25min 56s
In [ ]:
%%time
CNNhistory += fit_one_cycle(epochs, 0.0001, CNNmodel, train_dl, val_dl, 
                         grad_clip=grad_clip, 
                         weight_decay=weight_decay, 
                         opt_func=opt_func)
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [1],last_lr: 0.00008244, train_loss: 2.5021, val_loss: 2.0682, val_acc: 36.4995
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [2],last_lr: 0.00022722, train_loss: 2.5383, val_loss: 2.1795, val_acc: 31.6996
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [3],last_lr: 0.00030000, train_loss: 2.5619, val_loss: 2.1313, val_acc: 32.1848
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [4],last_lr: 0.00028515, train_loss: 2.5452, val_loss: 2.0312, val_acc: 39.9178
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [5],last_lr: 0.00024352, train_loss: 2.4518, val_loss: 2.0188, val_acc: 37.6096
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [6],last_lr: 0.00018338, train_loss: 2.4379, val_loss: 1.9516, val_acc: 41.5378
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [7],last_lr: 0.00011662, train_loss: 2.3317, val_loss: 1.8087, val_acc: 42.5905
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [8],last_lr: 0.00005648, train_loss: 2.2744, val_loss: 1.7615, val_acc: 45.9868
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [9],last_lr: 0.00001486, train_loss: 2.2161, val_loss: 1.7446, val_acc: 45.1946
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [10],last_lr: 0.00000000, train_loss: 2.1962, val_loss: 1.7367, val_acc: 46.5077 CPU times: user 24.2 s, sys: 20.5 s, total: 44.7 s Wall time: 25min 53s
In [ ]:
%%time
CNNhistory += fit_one_cycle(epochs, 0.0001, CNNmodel, train_dl, val_dl, 
                         grad_clip=grad_clip, 
                         weight_decay=weight_decay, 
                         opt_func=opt_func)
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [1],last_lr: 0.00002748, train_loss: 2.1667, val_loss: 1.7012, val_acc: 45.8059
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [2],last_lr: 0.00007574, train_loss: 2.1816, val_loss: 1.7138, val_acc: 48.3306
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [3],last_lr: 0.00010000, train_loss: 2.1833, val_loss: 1.7215, val_acc: 43.3498
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [4],last_lr: 0.00009505, train_loss: 2.1673, val_loss: 1.7256, val_acc: 43.7116
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [5],last_lr: 0.00008117, train_loss: 2.1652, val_loss: 1.6896, val_acc: 45.5455
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [6],last_lr: 0.00006113, train_loss: 2.1194, val_loss: 1.6180, val_acc: 50.2440
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [7],last_lr: 0.00003887, train_loss: 2.0710, val_loss: 1.6035, val_acc: 48.7610
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [8],last_lr: 0.00001883, train_loss: 2.0747, val_loss: 1.5920, val_acc: 50.3344
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [9],last_lr: 0.00000495, train_loss: 2.0479, val_loss: 1.5716, val_acc: 51.6365
HBox(children=(FloatProgress(value=0.0, max=56.0), HTML(value='')))
Epoch [10],last_lr: 0.00000000, train_loss: 2.0630, val_loss: 1.5722, val_acc: 51.2856 CPU times: user 22.7 s, sys: 19 s, total: 41.7 s Wall time: 26min 11s
In [ ]:
time = '01:22:46'
jovian.log_metrics(train_loss = CNNhistory[-1]['train_loss'],
                   val_loss = CNNhistory[-1]['val_loss'],
                   val_acc = CNNhistory[-1]['val_acc'],
                   time = time)
jovian.reset()

jovian.log_hyperparams({
    'num_epochs': 30,
    'opt_func': opt_func.__name__,
    'batch_size': batch_size,
    'lr': 0.0003,
})
[jovian] Metrics logged. [jovian] Hyperparams logged.
In [ ]:
plot_accuracies(CNNhistory)
Notebook Image
In [ ]:
num_epochs = 
plot_losses(CNNhistory)
Notebook Image
In [ ]:
test_tfms = T.Compose([T.Resize((160,160)),
                       T.ToTensor(),
                       T.Normalize(*stats)])

test_ds = ImageFolder(test_directory, test_tfms)
In [ ]:
img, label = test_ds[0]
plt.imshow(denorm(img).permute(1,2,0))
print("Label:", classes[label], ', Predicted:', predict_image(img,CNNmodel))
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Label: apple , Predicted: beetroot