Flower recognition

 Classfication of flowers using pytorch

Here, we will be learning about classification of flowers using pytorch using the kaggle dataset.

In this tutorial we will be using conv2d neural network using pytorch to classify the flowers.

We follow the following tutorial and change our code according to our requirements.


Importing necessary packages

import torch
import torchvision
import torchvision.transforms as transforms
import os
import random
import pandas as pd
import numpy as np
import shutil

pytorch package will not be available, so we have install it explicitly, the following command can be used to install pytorch.

For windows:

            conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch

For Mac:

              conda install pytorch torchvision torchaudio -c pytorch


Splitting the Data into Train and Test

We now, create necessary folders to train and split, in the tutorial we are following, they have not shown how they split their data. So, we are using our own login to split the data into train and test folders. Our data folder have 5 subfolders of each flower in which we have images of the respective flowers.

'daisy', 'dandelion', 'rose', 'sunflower', 'tulip'
 
following is the logic I used to split the data.

Img_dir_Test = 'C:/Users/ajays/OneDrive/Desktop/Data mining/Assignment1 New/test'
_, dirs, _ = next(os.walk(Img_dir_train))
train_ratio = 0.77
images_per_class = np.zeros(5) # 5 is number of classes
for i in range(len(images_per_class)):
    path = os.path.join(Img_dir_train,dirs[i])
    files = np.asarray(os.listdir(path))
    images_per_class[i] = len(files)
    
test_counter = np.round(images_per_class * (1-train_ratio))

# transfer files
for i in range(len(images_per_class)):
    source = os.path.join(Img_dir_train, dirs[i])
    destination = os.path.join(Img_dir_Test, dirs[i])
    if not os.path.exists(destination):
        os.makedirs(destination)
    files = np.asarray(os.listdir(source))
    for j in range(int(test_counter[i])):
        dst = os.path.join(destination, files[j])
        src = os.path.join(source, files[j])
        shutil.move(src,dst)

Normalize and load the data


As we have created the train and test datasets, next thing is to normalize the data. Normalizing the data is a key step in the process. We might have images in different aspect ratio, sizes etc., So, we normalize them by making their properties similar.

Following is the logic used to normalize

transform = transforms.Compose(
    [transforms.Resize(256),
     transforms.CenterCrop(256),
        transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

And the next step is to load data that is transformed. The transformed data will be loaded into train and test loaders respectively.

batch_size = 4

trainset = torchvision.datasets.ImageFolder(root=Img_dir_train,transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,shuffle=True, num_workers=2)

testset = torchvision.datasets.ImageFolder(root=Img_dir_Test,transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,shuffle=True, num_workers=2)
  
let's have a look at some of the images

import matplotlib.pyplot as plt
import numpy as np

# functions to show an image


def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join(f'{classes[labels[j]]:5s}' for j in range(batch_size)))
accur=[]

Output:



sunflower rose  tulip daisy

Define a convolutional neural network


In this step, we will be defining our model. Here we are using the same model that is 
used in the tutorial we are following, following are the parameters we have used in our
model.

import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 18, 5)
        self.fc1 = nn.Linear(18 * 61 * 61, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 5)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = torch.flatten(x, 1) # flatten all dimensions except batch
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()


Define optimizer and loss function 



import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

Train the network

This is the main part in the process, here we will be iterating the model over the dataset multiple times, so 
that the model optimizes itself by calculating loss and then reduces the loss.

for epoch in range(25):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data[0],data[1]

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 200 == 199:    # print every 2000 mini-batches
            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 200:.3f}')
            running_loss = 0.0

print('Finished Training')

Output :




Test the model using the test data

we have trained the model multiple times, at first I have trained the model using 2 epoch's and
then gradually increased it to 10 and 20, finally at last I got a gradual increase from 52 to 55.


correct = 0
total = 0
# since we're not training, we don't need to calculate the gradients for our outputs
with torch.no_grad():
    for data in testloader:
        inputs, labels = data[0],data[1]
        # calculate outputs by running images through the network
        outputs = net(inputs)
        # the class with the highest energy is what we choose as prediction
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f'Accuracy of the network on the test images: {100 * correct // total} %')
accur.append(100* correct // total)

Output:

Accuracy of the network on the test images: 55 %

checking accuracy for each class,

# prepare to count predictions for each class
correct_pred = {classname: 0 for classname in classes}
total_pred = {classname: 0 for classname in classes}

# again no gradients needed
with torch.no_grad():
    for data in testloader:
        images, labels = data[0],data[1]
        outputs = net(images)
        _, predictions = torch.max(outputs, 1)
        # collect the correct predictions for each class
        for label, prediction in zip(labels, predictions):
            if label == prediction:
                correct_pred[classes[label]] += 1
            total_pred[classes[label]] += 1


# print accuracy for each class
for classname, correct_count in correct_pred.items():
    accuracy = 100 * float(correct_count) / total_pred[classname]
    print(f'Accuracy for class: {classname:5s} is {accuracy:.1f} %')

Output:

Accuracy for class: daisy is 46.6 %
Accuracy for class: dandelion is 62.4 %
Accuracy for class: rose  is 46.1 %
Accuracy for class: sunflower is 63.3 %
Accuracy for class: tulip is 57.1 %

Experimenting


Plotting accuracy over different epoch values.


Plotting accuracy's for different classes.




References:


My contribution :

In the tutorial we have followed, they didn't show how they split their data, but
we have built our own logic to split data into train and test folders.

Trained multiple times, with multiple epoch values to improve accuracy over a
period of time.

Challenge :


Initially, I had very less accuracy and model was not training well and as expected, I
tried everything but finally found out that I forgot to shuffle the training data, then
I shuffled data which made training well and got a good accuracy and also choosing the
hyperparameters and number of layers was challenge which I got them after multiple attempts.















Comments