Python
Ants and Bees using CNN

Ants and Bees using CNN

We are trying to build a model using CNN with the help of PyTorch library that can differentiate between images of Ants and Bees.

Data used in this task is available at : https://www.kaggle.com/watanabe2362/hymenoptera

This process is divided into two parts:

  • Part I : Building the model and saving it.
  • Part II: Loading the model and using it.

Part I:

Importing necessary libraries

import torchvision
import torch.nn as nn
import torch
import torch.nn.functional as F
from torchvision import transforms,models,datasets
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
from torch import optim

#connecting python script to gpu
device = 'cuda' if torch.cuda.is_available() else 'cpu'
import cv2, glob, numpy as np, pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from glob import glob
!pip install torchsummary

Load Data

train_data_dir = '.../Data/hymenoptera_data/train'
val_data_dir = '.../Data/hymenoptera_data/val'

Making a class which will help to load and label data

from torch.utils.data import DataLoader, Dataset
class ants_bees(Dataset):
  def __init__(self, folder):
    ants = glob(folder+'/ants/*.jpg')
    bees = glob(folder+'/bees/*.jpg')
    self.fpaths = ants + bees
    from random import shuffle, seed; seed(10); shuffle(self.fpaths)
    self.targets = [fpath.split('/')[-2].startswith('bees') for fpath in self.fpaths] # ants=0 & bees=1
  def __len__(self): return len(self.fpaths)
  def __getitem__(self, ix):
    f = self.fpaths[ix]
    target = self.targets[ix]
    im = (cv2.imread(f)[:,:,::-1])
    im = cv2.resize(im, (224,224))
    return torch.tensor(im/255).permute(2,0,1).to(device).float(), torch.tensor([target]).float().to(device)

Checking and Visualizing Data

data = ants_bees(train_data_dir)
im, label = data[220]
plt.imshow(im.permute(1,2,0).cpu())
print(label)
len(data)

Defining Neural Network Layers

#Defining convolution layer
def conv_layer(ni,no,kernel_size,stride=1):
    return nn.Sequential(
    nn.Conv2d(ni, no, kernel_size, stride),
    nn.ReLU(),
    nn.BatchNorm2d(no),
    nn.MaxPool2d(2)
    )

#Defining deep CNN model
def get_model():
    model = nn.Sequential(
    conv_layer(3, 64, 3),
    conv_layer(64, 512, 3),
    conv_layer(512, 512, 3),
    conv_layer(512, 512, 3),
    conv_layer(512, 512, 3),
    conv_layer(512, 512, 3),
    nn.Flatten(),
    nn.Linear(512, 1),
    nn.Sigmoid(),
    ).to(device)

    #Binary cross entropy is used to calculate loss
    loss_fn = nn.BCELoss()
    #Adam us used as an optimizer
    optimizer = torch.optim.Adam(model.parameters(), lr= 1e-3)
    return model, loss_fn, optimizer

Summary of the Model

#Printing summary of model
from torchsummary import summary
model, loss_fn, optimizer = get_model()
summary(model, input_size=(3, 224, 224))

Defining functions for loading training and accuracy

def train_batch(x, y, model, opt, loss_fn):
    prediction = model(x)
    batch_loss = loss_fn(prediction, y)
    batch_loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    return batch_loss.item()

@torch.no_grad()
def accuracy(x, y, model):
    prediction = model(x)
    is_correct = (prediction > 0.5) == y
    return is_correct.cpu().numpy().tolist()

Functions to load data and to calculate validation loss

def get_data():     
    train = ants_bees(train_data_dir)
    trn_dl = DataLoader(train, batch_size=32, shuffle=True, drop_last = True)
    val = ants_bees(val_data_dir)
    val_dl = DataLoader(val, batch_size=32, shuffle=True, drop_last = True)
    return trn_dl, val_dl

@torch.no_grad()
def val_loss(x, y, model):
    prediction = model(x)
    val_loss = loss_fn(prediction, y)
    return val_loss.item()

Loading Data and model along with its parameters

trn_dl, val_dl = get_data()
model, loss_fn, optimizer = get_model()

Training for 10 epochs; Calculation train accuracy, validation accuracy, train loss and validation loss

train_losses, train_accuracies = [], []
val_losses, val_accuracies = [], []
for epoch in range(10):
    
    print(epoch)
    train_epoch_losses, train_epoch_accuracies = [], []
    val_epoch_accuracies = []
    for ix, batch in enumerate(iter(trn_dl)):
        #print(ix)
        x, y = batch
        batch_loss = train_batch(x, y, model, optimizer, loss_fn)
        #print(y)
        train_epoch_losses.append(batch_loss)        
    train_epoch_loss = np.array(train_epoch_losses).mean()

    for ix, batch in enumerate(iter(trn_dl)):
        x, y = batch
        is_correct = accuracy(x, y, model)
        train_epoch_accuracies.extend(is_correct)
    train_epoch_accuracy = np.mean(train_epoch_accuracies)

    for ix, batch in enumerate(iter(val_dl)):
        x, y = batch
        val_is_correct = accuracy(x, y, model)
        val_epoch_accuracies.extend(val_is_correct)
        validation_loss = val_loss(x, y, model)
    val_epoch_accuracy = np.mean(val_epoch_accuracies)

    train_losses.append(train_epoch_loss)
    train_accuracies.append(train_epoch_accuracy)
    val_losses.append(validation_loss)
    val_accuracies.append(val_epoch_accuracy)

Loss and Accuracy after training for 10 epochs

print(f'Training loss after 10 epochs: {train_epoch_loss}\n')
print(f'Validation loss after 10 epochs: {validation_loss}\n')
print(f'Training accuracy after 10 epochs: {train_epoch_accuracy*100}\n')
print(f'Validation accuracy after 10 epochs: {val_epoch_accuracy*100}\n')

Plotting graph of accuracy and losses

epochs = np.arange(10)+1
import matplotlib.ticker as mtick
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
%matplotlib inline
plt.rcParams["figure.figsize"] = (10,5)
plt.subplot(211)
plt.plot(epochs, train_losses, 'bo', label='Training loss')
plt.plot(epochs, val_losses, 'r', label='Validation loss')
plt.gca().xaxis.set_major_locator(mticker.MultipleLocator(1))
plt.title('Training and validation loss with CNN')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid('off')
plt.show()
plt.subplot(212)
plt.plot(epochs, train_accuracies, 'bo', label='Training accuracy')
plt.plot(epochs, val_accuracies, 'r', label='Validation accuracy')
plt.gca().xaxis.set_major_locator(mticker.MultipleLocator(1))
plt.title('Training and validation accuracy with CNN')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.gca().set_yticklabels(['{:.0f}%'.format(x*100) for x in plt.gca().get_yticks()]) 
plt.legend()
plt.grid('off')
plt.show()

Save Model

#save model
model_scripted = torch.jit.script(model) # Export to TorchScript
model_scripted.save('.../ants_bees_model_epoch_10_scripted.pt') 

Part II: Loading model and using it

Importing Files

import torch
device = 'cuda' if torch.cuda.is_available() else 'cpu'
import cv2, glob, numpy as np, pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from glob import glob
from google.colab import files
import warnings
warnings.filterwarnings('ignore')

Loading model

model = torch.jit.load('.../ants_bees_model_epoch_10_scripted.pt')
model.eval()

Define Function which takes image as an input and predicts whether its an ant or a bee

def predict(img_file):

  im = (cv2.imread(file_name)[:,:,::-1])

  im = cv2.resize(im, (224,224))
  im = torch.tensor(im/255).permute(2,0,1).to(device).float()
  img = im
  img = img.view(3,224, 224)
  img2  = torch.tensor(img).to(device).float()
  img3 = torch.tensor(img2).view(-1,3,224,224)
  np_output = model(img3).cpu().detach().numpy()
  print(np_output)
  if((np.round(np_output[0][0],decimals=0))==1):
    prediction = 'This is a bee'
  else:
    prediction = 'This is an ant'
  plt.imshow(im.permute(1,2,0).cpu())

  return prediction

Loading The image

uploaded = files.upload()
file_name = uploaded.keys()
file_name = list(file_name)[0]
predict(file_name)

Final Output

When model got it wrong