Achieving world-class results using the new fastai library | by Aayush Agrawal | Towards Data Science

I am currently doing a fast.ai Live MOOC called “Practical Deep learning for Coders” which will be publically available in January 2019 on fast.ai website. The following code is based on lesson 1 from that course. I will be using fastai V1 library which sits on top of Pytorch 1.0. The fastai library provides many useful functions that enable us to quickly and easily build neural networks and train our models. I am writing this blog as a part of experimenting the course example on a dataset which is different in structure and complexity and to show how easy it is to use fastai library.
In the below example you will see how ridiculously easy is to do transfer learning and achieve world-class results on PlantVintage Dataset. PlantVintage data have images of plant leaves which consist of 38 disease classes which are commonly found on crops and one background class from Stanford’s open dataset of background images — DAGS. I downloaded data from links given on this Github Repo. I am particularly interested in this example because at the time I am writing this blog I work for an organization which helps farmers grow their business by providing products and tech solutions for better farm management. Let’s get started!
PS: This blog is also released as a jupyter notebook on my GitHub profile.
It’s a standard practice to start the notebook with the following three lines; they ensure that any edits to libraries you make are reloaded here automatically, and also that any charts or images displayed are shown in this notebook.
%reload_ext autoreload%autoreload 2%matplotlib inline
Let’s import fastai library and define our batch_size parameter to 64. Usually, image databases are huge, so we need to feed these images into a GPU using batches, batch size 64 means that we will feed 64 images at once to update parameters of our deep learning model. If you are running out of memory because of smaller GPU RAM, you can reduce batch size to 32 or 16.
from fastai import *from fastai.vision import *bs =64
The first thing we do when we approach a problem is to take a look at the data. We always need to understand very well what the problem is and what the data looks like before we can figure out how to solve it. Taking a look at the data means understanding how the data directories are structured, what the labels are and what some sample images look like. Our data is already split in train and validation folder, and inside each subdirectory, our folder name represents the class name of all the images present within that subfolder. Fortunately, the fastai library has a handy function made exactly for this, ImageDataBunch.from_folder gets the label names from the folder name automatically. fastai library has awesome documentation to navigate through their library functions with live examples on how to use them. Once the data is loaded, we can also normalize the data by using .normalize to ImageNet parameters.
## Declaring path of datasetpath_img = Path('/home/jupyter/fastai_v3_experimentation/data/PlantVillage/')## Loading data data = ImageDataBunch.from_folder(path=path_img, train='train', valid='valid', ds_tfms=get_transforms(),size=224, bs=bs, check_ext=False)## Normalizing data based on Image net parametersdata.normalize(imagenet_stats)
To look at a random sample of images, we can use .show_batch() function ImageDataBunch class. As we can see below, we have some cases of diseases leaf on different crops plus some background noise images from DAGS dataset which will act as noise.
data.show_batch(rows=3, figsize=(10,8))
Let’s print all the data classes present in the database. In total, we have images in 39 classes as mentioned above in the motivation section.
print(data.classes)len(data.classes),data.c
Now we will start training our model. We will use a convolutional neural network backbone ResNet 50 and a fully connected head with a single hidden layer as a classifier. You can also read the ResNet paper if you want to understand all the architectural detail. To create the transfer learning model we will need to use function create_cnn from Learner class and feed a pre-trained model from models class.
## To create a ResNET 50 with pretrained weightslearn = create_cnn(data, models.resnet50, metrics=error_rate)
The ResNet50 model created by create_cnn function have initial layers frozen, and we are just going to learn weights of the last fully connected layers.
learn.fit_one_cycle(5)
As we can see above by just running five epochs with the default setting our accuracy for this fine-grained classification task is around ~99.64% on the validation dataset. Let’s save the model as we are going to fine tune it later. If you want to know how good this result is, it already beats the shallow learning(only training last layer) benchmark of 96.53% from this Github Page.
learn.save('plant_vintage_stage1')
FastAI library also provides functions to explore results faster and find if our model is learning what it is supposed to learn. We will first see which were the categories that the model most confused with one another. We will try to see if what the model predicted was reasonable or not using ClassificationInterpretation class.
interp = ClassificationInterpretation.from_learner(learn)interp.plot_top_losses(4, figsize=(20,25))
In this case, the model is getting confused in detecting Northern Leaf blight from Gray leaf spots on Corn plant and early/late blight in tomato leaves which visually looks pretty similar. This is an indicator that our classifier is working correctly. Furthermore, when we plot the confusion matrix, we can see that most of the things are classified correctly, and it’s almost a near perfect model.
interp.plot_confusion_matrix(figsize=(20,20), dpi=60)
So until now, we have only been training the last classification layers, but what if we want to optimize earlier layers too. In transfer learning, tweaking initial layers should be done with caution, and the learning rate should be kept pretty low. FastAI library provides a function to see what will be the ideal learning rate to train upon, so let’s plot it. The lr_find function runs the model for a subset of data at multiple learning rate to determine which learning rate would be best.
learn.lr_find()learn.recorder.plot()
It looks like we should keep our learning rate lower than 10e-4. We can use slice function to logarithmically distribute learning rate between 10e-6 to 10e-4 for different layers in the network. Keeping the lowest learning rate for the initial layers and increasing it for later layers. Let’s unfreeze all the layers so that we can train the entire model using unfreeze() function.
learn.unfreeze()learn.fit_one_cycle(2, max_lr=slice(1e-7,1e-5))
As we can see by training all the layers, we improved our accuracy to 99.7% which is comparable to 99.76% on the Github benchmark using Inception-v3 model.
Fast.ai is an excellent initiative by Jeremy Howard and his team, and I believe fastai library can genuinely achieve the motive of democratizing deep learning to everyone by making building deep learning models super simple.
I hope you enjoyed reading, and feel free to use my code to try it out for your purposes. Also, if there is any feedback on code or just the blog post, feel free to reach out on LinkedIn or email me at aayushmnit@gmail.com.