Image classification using Fast.ai
By: Manohar Krishna
Tags: Classification, Python, Cnn, Fastai, Data Science

Let’s have a look at the size of the images

from PIL import Image def get_num_pixels(filepath):

width, height = Image.open(filepath).size

return width,height print(get_num_pixels("images/1.jpg")) Output: (224,224)

Now that we’ve separated train and test images, let’s jump into building the classification model. End-to end process for the solution is divided into 6 steps that are as followed:

Reading the data defining the model Defining Learning Rate Fitting the model Interpreting the results Inferencing

Let’s start with reading the data from folder

ImageDataBunch helps us to create training, validation and test data sets

xtra = [(contrast(scale=(0.1,2.0), p = 0.5)),

(brightness(change = 0.6, p = 0.25)),

(perspective_warp(magnitude = 0.25, p = 0.25)),

(squish(scale = 1.2, p = 0.25))] augmentations = get_transforms(do_flip=True,flip_vert=False, max_rotate=None, max_zoom=1.5,max_warp=None, p_affine = 0.5, max_lighting=0.2,p_lighting=0.5, xtra_tfms=xtra)

data=ImageDataBunch.from_folder('/content/gdrive/My Drive/', train = 'train_images', valid= 'train_images' ,size=224, bs=16, ds_tfms = get_transforms(do_flip=True,

max_rotate=None, ## amount of rotaion

max_zoom=1.2, ## amount of zoom

max_warp=0.2, ## amount of wraping

p_affine = 0.5, ## probability by which above 3 tfms will take place

max_lighting=0.2, ## amount of lightin

p_lighting=0.5 ## probability for lighting)) data.normalize(imagenet_stats) #for extra augmentations -

#ds_tfms = augmentations.normalize(imagenet_stats)

Let’s check the classes present in the data

data.classes Output: ['emergency', 'non_emergency']

Let’s now define the model

cnn_learner method is used to get a model suitable for transfer learning quickly. In short, it is a concept used to train our model using a predefined/trained model. For the moment, we are building a model which will take images as input and will output the predicted probability for each of the categories.

## Defining callbacks Save_Model_Callback=partial(SaveModelCallback, every='improvement', monitor='accuracy', name='av_cv')

EarlyStoppingCallback = partial(EarlyStoppingCallback, monitor='valid_loss', min_delta=0.01, patience=5) #defining model and optimizer learn = cnn_learner(data, models.resnet152 , pretrained= True ,opt_func=optim.Adam, metrics=[accuracy,error_rate,FBeta(beta=1),Precision(),Recall()], callback_fns=[Save_Model_Callback,EarlyStoppingCallback])

The reason to use a pretrained model is to avoid training the model from scratch. Pretrained models are well trained with millions of images and thousands of categories using ImageNet dataset. Last layer of ImageNet consists of 1000 columns which correspond to 1000 different categories. There is no guarantee that the predefined categories contain the category of our requirement, but one thing we are confident is that the model knows something and can differentiate between a dog and a truck(for say).

There are a lot of pretrained architectures available that are trained on different image datasets. Here, I have taken resnet152 trained on ImageNet dataset.

As far as optimizer is concerned, I’ve tried both Adam and SGD while Adam gave better results.

There are a bunch of metrics one could use to print at the time of training and I’ve called for accuracy, error_rate, FBeta(beta=1), Precision(), Recall().

The “Save_Model_Callback” is used to automatically save a new ‘best loss’ model during training. At the end of the training, it loads the top model. One can specify the criteria that defines the best model. I’ve specified the criteria to be ‘accuracy’.

The “EarlyStoppingCallback” is used to automatically stop training when it encounters three continuous increase in loss or three consecutive changes in the loss that is less than 0.01

Defining Learning Rate

# This helps to find best learning rate for model. learn.lr_find() #Plot of LR learn.recorder.plot()

We might want to give the learning rate while fitting the model(next step) for better results. It defines how quickly the parameters get updated in the model. The plot looks something like this.

The range where the curve shows a steep decrease is taken into consideration. We can observe that the learning rate showed a steep decrease in the range [1e-04,1e-02].

Fitting the model

num_of_epochs = 20 learn.fit_one_cycle(num_of_epochs,max_lr=slice(1e-04,1e-02),moms=(0.95,0.85)) learn.recorder.plot_losses() #Iterations Vs LR and Iterations Vs Moms learn.recorder.plot_lr(show_moms=True) #Plot Metrics across Iterations learn.recorder.plot_metrics()

Here, number of epochs is how many times the entire data should be passed through the model to learn.

Interpreting the results

learn.load('av_cv') interpret=ClassificationInterpretation.from_learner(learn) interpret.plot_confusion_matrix(figsize=(5,5),dpi=100)

Plotting the confusion matrix helps us in understanding the performance of the model.

interpret.plot_top_losses(6, heatmap=True)

plot_top_losses displays the images that were wrongly predicted and have maximum contribution in the loss.

Inferencing

Export the model into a .pkl fomat so that it could be used while making predictions on unseen data.

learn.export('av_cv.pkl')

Next step is to load the test data to the learner and make predictions

test = ImageList.from_folder(base_path + 'test_images') learn = load_learner(base_path, file = 'av_cv.pkl', test = test)

thresh = 0.5 preds, _ = learn.get_preds(ds_type=DatasetType.Test) labelled_preds = [' '.join([learn.data.classes[i] for i,p in enumerate(pred) if p > thresh]) for pred in preds] fnames = [f.name[:-4] for f in learn.data.test_ds.items] df = pd.DataFrame({'image_name':fnames, 'tags':labelled_preds}, columns=['image_name', 'tags']) df.to_csv('fastai_adam.csv', index=False)

The predictions gets saved in the form of a csv file.