Emergency vs Non-Emergency Vehicle Classification | by Venus Rohilla | Towards Data Science

Fatalities due to traffic delays of emergency vehicles such as ambulance & fire brigade is a huge problem. In daily life, we often see that emergency vehicles face difficulty in passing through traffic. So differentiating a vehicle into an emergency and non emergency category can be an important component in traffic monitoring as well as self drive car systems as reaching on time to their destination is critical for these services.
In order to find a solution to this problem we would try building a Image classification model that would classify vehicle images as either belonging to the emergency vehicle or non-emergency vehicle category.
The first thing we need to do is to get train and test data set of vehicle images. Here the data set used to build the classification model is downloaded from Analytics vidhya , it consists of emergency and non emergency vehicle images where emergency vehicles usually includes police cars, ambulance and fire brigades .
After this is done we can read in the provided training data set using pandas.
We would be using the fastai vision library for building our image classification model.
from fastai.vision import *df = pd.read_csv('/content/train.csv')df.head()
We have two columns. The image_names column which contains the names of the images and the emergency_or_not column which contains the labels for each image.Here 1 represents label for emergency vehicles and 0 for non emergency vehicles.
This quick look at the data frame provides us with enough information to load in the data and start building our image classification model but before we will do that we will apply transformations to the images that would help us in generalizing our model , that is to gain a high accuracy.
The transformations we would be applying are : do_flip — enabled by default — and flip_vert,max_rotate=50.We will also play around with the lighting by tuning the max_lighting=0.1 and max_warp=0.
tfms= get_transforms(do_flip=True,flip_vert=True,max_rotate=50,max_lighting=0.1,max_warp=0 )
Now that we have our transforms ready to be applied we can load in the data using FastAIs data block api.
data = ImageDataBunch.from_df('/content/cars', df,ds_tfms=tfms,label_delim= None,valid_pct=0.2,fn_col=0, label_col=1 , size=299,bs=64).normalize(imagenet_stats)
The .normalize(imagenet_stats) method above is used to normalize the data set based on the stats of the RGB channels from the ImageNet data set.
What normalization does is that it brings down the intensities of images (original intensity of images is between 0 and 255) between 0 and 1 .This helps increase the computation power of our model .
Now that we’ve loaded our data set, let us have a peek into what it looks like:
data.show_batch(rows=3, figsize=(7,7)
We can also check this by calling a function that tells us about the number of image classes.
print(data.classes)[0, 1]
Since we have our data ready so its time to feed it into a model .We can do this by building a convolution neural network from scratch but doing this would be practically inefficient . So, we take the weights of a pre trained CNN model that has learnt to recognize features(certain kind of things e.g gradient, edges circles e.t.c)
Here we would be using a pre-trained ResNet50 Convolution Neural Net model, and use transfer learning to learn weights of only the last layer of the network.
Transfer learning is when you begin with an existing (trained) neural network used for image recognition — and then tweak it a bit (or more) here and there to train a model for your particular use case, we do this because training a neural network form scratch would mean needing approximately 300,000 image samples, and to achieve really good performance, we’re going to need at least a million images. But with transfer learning we can achieve high performance with limited data set and in much less time .
We would be fine tuning it by keeping some layers frozen and unfreezing some of them and training over it. This is because the top layers learn simple basic features and we need not to train those layers , while the later layers learn more sophisticated features .
In the above picture each different layer learns different features of the images .The latter layers of this convolution neural networks learns features like eyes of birds e.t.c .
We use the cnn_learner function for loading a pre-trained ResNet50 network, that was trained on around a million images from the ImageNet database.
learn =cnn_learner(data,resnet50,pretrained=True,metrics=[accuracy])
Let us now fit one cycle of 4 epochs to see how our model performs on this data set:
learn.fit_one_cycle(4)
The reason for using just 4 epochs is to get an idea of how our model performs and later fine tuning it for better results. As we can see our validation loss is greater then our training loss this clearly indicates that our model is suffering from the problem of under fitting.
Let’s have a look at our model’s predictions :
interp = ClassificationInterpretation.from_learner(learn)
Interpreting top losses:
interp.plot_top_losses(9, figsize=(7,7))
Looking at the top losses we can tell our model makes the same mistake as a human can make when looking at some cars for the first time that seemed to be emergency vehicles but interpreted as non emergency vehicles.
Another useful tool, kind of, is to use something called a confusion matrix which basically shows you for every actual type of emergency and non emergency vehicle prediction , how many times was it predicted to be that .
`interp.plot_confusion_matrix(figsize=(8,8), dpi=60)
17 times our model predicted emergency vehicles as non emergency vehicles , while 4 times it predicted non emergency vehicles as emergency vehicles. Confused uh? after all we are dealing with a confusion matrix!
A frozen layer is a layer that is not trained i.e it is not updated.
So far anything we have done has not changed the pre-trained model weights .All we have done is we added some new layers on top and learned how to mix and match pre-trained features.
But we want the model to learn features specific to our images .In order to do so we would unfreeze some of the layer to update the weights of the entire network according to our images .
Layer one which detects edge and gradient and layer two which detects curves and corners don’t need much learning, they don’t need to change much. while the much later layers need to change. This is universally true when training for other image recognition.
The earlier layers have more general-purpose features. Therefore we would expect them to need less fine-tuning for new datasets. For this reason we will use different learning rates for different layers: the first few layers will be at 1e-4 for basic geometric features and layers closest to the pixels,and 1e-2 as before for the layers we add on top (fully connected layers). We refer to this as differential learning rates.
learn.unfreeze()
We would again run a fit-one-cycle on our unfreeze network and see how it goes now.
learn.fit_one_cycle(3)
The most important hyper-parameter for tuning our model is learning rate.It helps us in finding the optimal solution by adjusting the weights of our network with respect the loss gradient.The lower the value, the slower we travel along the downward slope. While this might be a good idea (using a low learning rate) in terms of making sure that we do not miss any local minima, it could also mean that we’ll be taking a long time to converge — especially if we get stuck on a plateau region.
Here we can see the various effects of learning rate on convergence.
Hence Picking the right learning rate is important, rather than giving it a random guess or multiple trial-and-errors.
To do this we would be using a function called lr_find().This function is an implementation of Leslie Smith’s paper( Cyclical Learning Rates for Training Neural Networks.) on tuning neural network hyper-parameters.
learn.lr_find()
To understand it better we would plot it with help of another function.
learn.recorder.plot()
The learning rate is plotted against the losses.With the help of this graph we can pick our learning rate very easily.
As we can see the loss start increasing after 1e-03 so we would pick a number before that also for updating weights of initial layers we would be choosing a lower learning rate .1e-04 seems good.
With the chosen learning rate we will again train our unfreeze model but this time a bit longer with 10 epochs.
We would use accuracy as another parameter often called metric to evaluate how our model is doing .
or
Before going further let us also get an intuition about fit_one_cycle method.
In this method we do a cycle with two steps of equal lengths, one going from a lower learning rate to a higher one than go back to the minimum.The maximum should be the value picked with the Learning Rate Finder, and the lower one can be ten times lower. -to read a bit more about fit_one_cycle ,do check out this blog .
Now Lets see how our model is doing?
learn.fit_one_cycle(10,max_lr=slice(1e-4,1e-3))
Our model is doing good enough ,there is no under-fitting as our model had this problem earlier and no over-fitting also (Over fitting would mean that the training loss is much lower than the validation loss), as our validation loss is just approximately 0.02 times greater then training loss.
We have reached an accuracy of 96.96%~ 97%.
Since we have updated our model’s weights according to our vehicle classifying problem we will freeze the layers of our model and save it with a name car_classification.
learn.freeze()learn.save('car_classification')
We were able to reach an accuracy of approximately 97% , while we can experiment more with our model in terms of increasing number of epochs , finding more optimal learning rates using plot function, more data augmentation techniques, etc.
[1]:https://arxiv.org/pdf/1803.09820.pdf
[2]:https://sgugger.github.io/the-1cycle-policy.html
[3]:https://arxiv.org/pdf/1506.01186.pdf
[4]:Lesson 1 Notebook, fast.ai Part 1 V2
That’s all for today .💃🏻
Thank you for reading!