Fast.ai (2019) Lesson 1: I now know my cat’s breed

Source: Deep Learning on Medium


Go to the profile of JJ Deng

Although Fast.ai v1.0 has been released for a few months already, the corresponding video course hasn’t hit the airwaves on Youtube as of this writing. But I decided to get a head start anyways and downloaded the course notebooks which are already available via Github. One of the first lessons Jeremy, the creator of Fast.ai, has us do is train a neural network to recognize different breeds of cats and dogs.


How to install and set up Fast.ai

(Feel free to skip this section if you’ve already set up Fast.ai 1.0 and want to perform the fun stuff.)

To be brutally honest, actually setting up the Fast.ai environment is the hard part: it’s magnitudes more difficult than performing the actual Machine Learning needed to train a neural network to classify different dog and cat breeds. Although the course will get progressively more difficult, if you know some basic Python and are computer-literate enough to install several packages, you’ll most certainly be able to do this exercise AND apply this knowledge to train a comparable classifier of your own.

First, you’ll need the Anaconda distribution of Python. As of this writing, the latest version packages Python version 3.7 as the flagship interpreter. Although the Fast.ai course no longer covers Tensorflow or Keras, there is still plenty of machine learning code currently written using these two libraries, and unfortunately, Tensorflow still requires Python 3.6 as of January 2019. (Keras will require you use Tensorflow as a backend.)

Next, you’ll want to actually install the Fast.ai library, but before you install, you may want to consider creating a new Anaconda environment. (Note you may need to follow these instructions after installation if you choose to install Fast.ai into a separate environment.) If you’re new to Python or if this is a fresh install of Anaconda, you should be alright not creating a new environment. If you already have an existing Anaconda install with lots of packages installed in your base environment, use your best judgement about whether to create a new environment for Fast.ai or not.

Once you’re ready to install Fast.ai, just invoke the following in a command prompt:

conda install -c pytorch -c fastai fastai

Next, you’ll want to clone the Fast.ai v3 notebook repository which contains the exercises:

git clone https://github.com/fastai/course-v3

If you’re new to Jupyter, you might want to do Lesson 0 which introduces them. Otherwise, just go ahead with Lesson 1: Pets.


Training the Model

The steps for training the model are as follows:

  1. Load the necessary libraries and set up the environment
  2. Obtain and load the dataset and labels
  3. Train the model
  4. Make predictions

Note, I may have done some of these steps a little differently than Jeremy’s notebook and/or he may have made some changes in the meantime after this article is written, so don’t be alarmed if the code doesn’t look exactly alike.

Step 1: Load the necessary libraries and set up the environment

%reload_ext autoreload
%autoreload 2
%matplotlib inline
from fastai.vision import *
from path import Path as p
from pathlib import Path
bs = 64
# bs = 16 # uncomment this line if you run out of memory even after clicking Kernel->Restart

Step 2: Obtain and load the dataset and labels

First, obtaining the dataset:

path = untar_data(URLs.PETS); path
p2 = p(path)
p2.move("../../../data/")
path = Path("../../../data/oxford-iiit-pet"); path
path_anno = path/'annotations'
path_img = path/'images'

Note that I’ve moved the dataset to a directory other than the default location since I’m running out of room on my C: drive where it’s downloaded by default. Feel free to skip that but make sure you set up the path_ano and path_img variables!

Next, we’ll examine the filenames:

fnames = get_image_files(path_img)
fnames[:5]

And you might see something like this:

[WindowsPath('../../../data/oxford-iiit-pet/images/Abyssinian_1.jpg'),  WindowsPath('../../../data/oxford-iiit-pet/images/Abyssinian_10.jpg'),  WindowsPath('../../../data/oxford-iiit-pet/images/Abyssinian_100.jpg'),  WindowsPath('../../../data/oxford-iiit-pet/images/Abyssinian_101.jpg'),  WindowsPath('../../../data/oxford-iiit-pet/images/Abyssinian_102.jpg')]

We’ll need to use a regular expression to extract the pet breeds from the filenames (which are basically the labels for our data.) If you’re running Windows (like me), you’ll want the following regular expression:

pat = r'\\([^\\]+)_\d+.jpg$'

But if you’re running Mac or Linux, use the following instead:

pat = r'/([^/]+)_\d+.jpg$'

Then run the following to load the data and its respective labels:

pat = re.compile(pat)
np.random.seed(2)
data = ImageDataBunch.from_name_re(path_img, fnames, pat, ds_tfms=get_transforms(), size=224, bs=bs
).normalize(imagenet_stats)

Whew, that’s mouthful! In the last line, we’ve loaded our images and their respective labels into the data variable. The ImageDataBunch.from_name_re() function use a regular expression (stored in the pat variable) to deduce labels for each of the images.

Step 3: Train the Model

Unfortunately, this dataset doesn’t have a sufficient number of images to train a decent image classifying neural network from scratch, but we can definitely use Transfer Learning on a pretrained network to do the job. Think of training an neural network from scratch like teaching a baby the concepts of different dog and cat breeds, while transfer learning is more like teaching a young child who is able to label objects in the world including dogs and cats what breeds different cats and dogs belong to. For this, we’ll rely on ImageNet’s pretrained models, more specifically, Resnet34. So now, we’ll load our pretrained network in the learn variable and train it for 4 epochs:

learn = create_cnn(data, models.resnet34, metrics=error_rate)
learn.fit_one_cycle(4)
learn.save('stage-1')

You’ll see something like the following output:

As you can see here, the training and validation losses as well as the error rates are on a downtrend. The notebook also further covers some additional plots to see that we’re on the right track. If we’re on the right track, we’ll be able to confidently proceed to the next step. Previously, we were only able to train the last layer of the neural network, but now once we unfreeze it, we’re able to train all of its layers:

learn.unfreeze()
learn.fit_one_cycle(2, max_lr=slice(1e-6,1e-4))

And you’ll get the following output:

The validation loss improved slightly but it’s not uncommon for losses to stop improving at this point especially if the initial training was highly successful. In that case, feel free to leave out these. Otherwise, we’ll go ahead and save these results:

learn.save("stage-2")

Step 4: Make predictions

Note, you can download the pictures below or get them from my Github repository. I put them in a folder named “lesson1-pics”.

Note I used a separate notebook for making predictions which wasn’t included in the course repository.

You’ll need to import the libraries and load everything first. Skip this step if you decide to continue in the first notebook:

%reload_ext autoreload
%autoreload 2
%matplotlib inline
from fastai.vision import *
import torch
defaults.device = torch.device('cpu')
bs = 64
path = Path("../../../data/oxford-iiit-pet")
path_anno = path/'annotations'
path_img = path/'images'
pat = r'\\([^\\]+)_\d+.jpg$'
fnames = get_image_files(path_img)
data = ImageDataBunch.from_name_re(path_img, fnames, pat, ds_tfms=get_transforms(), size=224, bs=bs
).normalize(imagenet_stats)
learn = create_cnn(data, models.resnet34, metrics=error_rate)
learn.load("stage-2")

Note these optional lines force Fast.ai to use the CPU for predictions. This is useful if you have your GPU tied up training another model since the CPU can often make predictions in a negligible amount of time anyways:

import torch
defaults.device = torch.device('cpu')

Now let’s make predictions on my 6 year old female tabby cat, Mia:

mia1 = open_image("lesson1-pics/mia1.jpg")
mia1
learn.predict(mia1)
mia2 = open_image("lesson1-pics/mia2.jpg")
mia2
learn.predict(mia2)
mia3 = open_image("lesson1-pics/mia3.jpg")
mia3
learn.predict(mia3)

So we got 2 predictions for Bengal and 1 prediction for Egyptian Mau.

To be honest, I don’t know her precise breed since I obtained her from a friend whose female cat had gotten pregnant. But both breeds seem like a reasonable guess, if you look at the pictures in the respective Wikipedia articles.

Now let’s predict Bobby’s breed:

bobby1 = open_image("lesson1-pics/bobby1.jpg")
bobby1
learn.predict(bobby1)
bobby2 = open_image("lesson1-pics/bobby2.jpg")
bobby2
bobby3 = open_image("lesson1-pics/bobby3.jpg")
bobby3
learn.predict(bobby3)

All three pictures carry the prediction of Bombay which also looks reasonable.


Concluding Remarks

So far, the Fast.ai library seems quite robust for this simple task of image classification. If you’ve ever used an earlier version of the library, you’ll soon find the the syntax has been simplified a bit and made more intuitive (esp the function “create_cnn()” for creating a convolutional neural network. Stay tuned for further articles as I explore the next few lessons of the Fast.ai course!