fastai: Deep Learning From Model To production
By: Sofyan Hadi Ahmad
Tags: Google Colab, Pytorch, Fastai, Image Classification, Deep Learning

In this article, I will walk you through the process of developing an image classifier deep learning model using Fastai to production. The goal is to learn how easy to get started with deep learning and be able to achieve near-perfect results with a limited amount of data using pre-trained models and re-use the model in an external application.

TL;DR;

Code and Google Colab Project: https://github.com/sofyan-ahmad/fastai-from-model-to-prodcution.git

What You’ll Learn

Collecting Imagery Data from external storage

Converting downloaded data into DataLoader objects

Data Augmentation

Cleaning Data using Model Training

Exporting the Trained Model

Building an External Application

Install the fastbook package to set up the notebook

# setup fastbook !pip install -Uqq fastbook

import fastbook

fastbook.setup_book()

Import all the functions and classes from the fastbook package and fastai vision widgets API:

# import fastbook and fastai from fastbook import *

from fastai.vision.widgets import *

Fetch data from an external source

petImagesUrl = 'https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip' path = untar_data(petImagesUrl)/'PetImages'

Let’s see what we have on the directory path

!ls {path}

output error:

ls: cannot access ‘/root/.fastai/data/kagglecatsanddogs_3367a/PetImages’: Not a directory

We have a problem. Somehow untar_data didn’t extract the zip file properly. let’s check what inside /root/.fastai/data

!ls /root/.fastai/data/

result: PetImages

!ls /root/.fastai/data/PetImages/

result: Cat Dog

Turns out the file was extracted to /root/.fastai/data/PetImages/, let’s us this path as our working directory

petPath = '/root/.fastai/data/PetImages/'

Load files from the petPath into a new variable

files = get_image_files(petPath)

let’s see what inside files

files

Output: (#25000) [Path(‘/root/.fastai/data/PetImages/Cat/7846.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/3500.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/8283.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/4615.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/12170.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/8523.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/4925.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/10943.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/2621.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/5959.jpg’)…]

Looks good! All files are loaded perfectly!

Remove corrupt data/files

Fortunately, fastai has a built-in method to check corrupt images.

To check for corrupt files we can simply call the verify_images method.

corrupt = verify_images(files)]

corrupt

Output: (#2)[Path(‘/root/.fastai/data/PetImages/Cat/666.jpg’),Path(‘/root/.fastai/data/PetImages/Dog/11702.jpg’)]

As you can see we have 2 corrupt files. To remove the files you can easily run the following command:

corrupt.map(Path.unlink);

Let’s check the files variable

files

Output: (#25000) [Path(‘/root/.fastai/data/PetImages/Cat/7846.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/3500.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/8283.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/4615.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/12170.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/8523.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/4925.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/10943.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/2621.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/5959.jpg’)…]

Apparently files is not affected. Let's reload the files again

files = get_image_files(petPath) files

Output: (#24998) [Path(‘/root/.fastai/data/PetImages/Cat/7846.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/3500.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/8283.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/4615.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/12170.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/8523.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/4925.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/10943.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/2621.jpg’),Path(‘/root/.fastai/data/PetImages/Cat/5959.jpg’)…]

Voila! all files are good now

Creating The Model

Fastai extends the concept of DataLoader from PyTorch, It represents a Python iterable over a dataset, with support for:

map-style and iterable-style datasets,

customizing data loading order,

automatic batching,

single- and multi-process data loading,

automatic memory pinning.

Fastai DataLoaders can stores multiple DataLoader objects passed into it and makes them available as a training and validation set.

To convert the data of the file into a DataLoader object, we have to provide four things:

What kinds of data we are working with (ImageBlock, CategoryBlock)

How to get the list of items (get_image_files)

How to label these items (parent_label)

How to create the validation set (RandomSplitter(valid_pct=0.2, seed=41))

To simplify the creation of DataLoaders, fastai offers a flexible method called DataBlock. This mechanism will help us to provide/feed data into our model training.

pets = DataBlock(

blocks=(ImageBlock, CategoryBlock),

get_items=get_image_files,

splitter=RandomSplitter(valid_pct=0.2, seed=88),

get_y=parent_label,

item_tfms=Resize(128))

blocks — specifies the feature variables (images) and the target variable(a category for each image)

— specifies the feature variables (images) and the target variable(a category for each image) get_items — retrieves the underlying items which are images in our case and we have a get_image_files function that returns a list of all of the images in that path.

— retrieves the underlying items which are images in our case and we have a get_image_files function that returns a list of all of the images in that path. splitter — splits the data as per the provided method; we are using random split with 20% of the data reserved for the validation set and specified the seed to get the same split on every run. Random seed serves just to initialize the (pseudo) random number generator, mainly in order to make the random data reproducible.

— splits the data as per the provided method; we are using random split with 20% of the data reserved for the validation set and specified the seed to get the same split on every run. Random seed serves just to initialize the (pseudo) random number generator, mainly in order to make the random data reproducible. get_y — the target variable is referred to as y; to create the labels, we are using the parent_label function which gets the name of the folder where the file resides as its label.

— the target variable is referred to as y; to create the labels, we are using the parent_label function which gets the name of the folder where the file resides as its label. item_tfms — we have images of different sizes and this causes a problem because we always send a batch of files to the model instead of a single file; therefore we need to preprocess these images by resizing them to a standard and then group them in a tensor to pass through the model. We are using the Resize transformation here.

Now, we have DataBlock object, we just need to load the files into dataLoaders

dls = pets.dataloaders(petPath)

Let’s check for images in the dataloader object using the show_batch method

dls.valid.show_batch()

Cat Dog

Data Transformation

There is no reason to do data transformation. But it will be beneficial in real life to transform the data to introduce some variations into the data set. The data will appear different but must represent the same facts.

For our particular case, let’s rotate, warp, flip, or change the brightness/contrast of the images to introduce variations. Fastai has a standard set of augmentations encapsulated in aug_transforms a method that works pretty well for a majority of computer vision datasets.

Let’s apply the transformations to an entire batch of images as all the images are of the same size

Note: 224 pixels is the standard size for image classification problems

pets = pets.new(item_tfms=RandomResizedCrop(224, min_scale=0.5),

batch_tfms=aug_transforms(mult=2)) dls = pets.dataloaders(petPath) dls.train.show_batch(max_n=10, nrows=2, unique=True)

The Model

It’s time to train our model.

Fastai offers tons of architecture to use for transfer learning. In this article, we’re going to create a Convolutional Neural Network (CNN) model using the pre-trained models that work for most of the applications/datasets, with ResNet architecture. Both are pretty solid for our problem.

We’re going to use 18 layers of neural networks, so we need to pass resnet18 in the arguments.

For metric, to help us measure the quality of the model, we are using error_rate which tells how frequently the model is making incorrect predictions

model = cnn_learner(dls, resnet18, metrics=error_rate)

We need to tune (fit) our models. To train and tune the model, we need to specify the number of times (epochs) we want each image to be used as training data. Please note the higher number means more tuning/fitting, which can lead to overfitting

model.fine_tune(1)

Let’s visualize the predictions and compare them with the actual labels using the confusion matrix.

interp = ClassificationInterpretation.from_learner(model) interp.plot_confusion_matrix()

As you can see, we have a total of 15+54 =69 incorrect predictions from 2521+2409=4930 ~ (0.01399), which pretty acceptable.

Let’s check for the top losses i.e. the images with the highest loss in the dataset:

interp.plot_top_losses(10, nrows=2)

Nothing is perfect, no model can predict 100% correctly.

If you want, you can still do a couple of rounds of hyperparameter tuning, and once you’re happy with your model, you need to save it so that we can deploy it on a server to be used in production.

Export The Model

Your model is useless unless you can save it and use it inside your application

Fastai has an export() method to save the model in a pickle file with the extension *.pkl, which latter you can call from your application code

model.export() path = Path()

path.ls(file_exts='.pkl')

Let’s test the exported model, by the load it into a new learner object using the load_learner method

model_export = load_learner(path/'export.pkl')

We need a way to provide test data, let’s create an upload button

uploader = widgets.FileUpload()

uploader

Let’s run image recognition using the exported model

img = PILImage.create(uploader.data[0]) what,_,probs = model_export.predict(img) print(f"It's a {what}!")

print(f"Probability it's a {what}: {probs[1].item():.6f}")

It’s a Dog!

Probability it’s a Dog: 0.999985

Voila! we can reuse the model using exported Pickle files

Let’s recheck the exported model to make sure it contains the labels from the models dataloader vocabulary

model_export.dls.vocab

[‘Cat’, ‘Dog’]

But how to download from Google Colab?

from google.colab import files files.download(path/'export.pkl')

That’s it!

If you want the full source code and Google Colab project please visit the repository: https://github.com/sofyan-ahmad/fastai-from-model-to-prodcution.git

👋 Join FAUN today and receive similar stories each week in your inbox! ️ Get your weekly dose of the must-read tech stories, news, and tutorials.

Follow us on Twitter 🐦 and Facebook 👥 and Instagram 📷 and join our Facebook and Linkedin Groups 💬