FastAi is a research lab with the mission of making AI accessible by providing an easy to use library build on top of PyTorch, as well as exceptionally good tutorials/courses like the Practical Deep Learning for Coders course which I am currently enrolled in.
In their courses, they use a “top-down” teaching approach, which directly throws you into coding and lets you solve problems (real problems not just practice once), rather than teaching the material “bottom up” by explaining the theory first. This allows for a rapid learning process with lots of success moments.
The FastAi library is a high-level library build on PyTorch, which allows us to build models using only a few lines of code. Furthermore it implements some of the newest state-of-the-art technics taken from research papers that allow you to get state-of-the-art results on almost any type of problem.
An example of this is the differential learning rates feature, which allows us to perform transfer learning with less code and time by giving us the ability to set different learning rates for different parts in the network. This allows us to train the earlier layers less than the latter layers.
In this article, we will learn how to use FastAI to work through a computer vision example. After this article you will know how to perform the following steps:
- Download image dataset
- Load and view your data
- Create and train a model
- Clear your dataset
- Interpret the results
If you prefer a visual tutorial you can check out my video on the topic.
The FastAI library can be installed by either using conda or pip.
conda install -c pytorch pytorch-cpu torchvision conda install -c fastai fastai or pip install http://download.pytorch.org/whl/cpu/torch-1.0.0-cp36-cp36m-linux_x86_64.whl pip install fastai
For more information about the installation visit the official guide.
If you successfully installed the library you can now import the vision module by typing:
from fastai.vision import *
Downloading image data
The FastAI library provides a lot of different datasets which can be loaded in directly, but it also provides functionality for downloading images given a file containing the URLs of these images.
urls = Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el=>JSON.parse(el.textContent).ou); window.open('data:text/csv;charset=utf-8,' + escape(urls.join('\n')));
For this article, I will create an animal classifier which can distinguish between cats, cows, dogs, and horses. To do that I searched for all four of them and use the above command to save a csv file containing the links.
After we downloaded the csv files we can download the data using the download_images method.
path = Path('<path>') for file, folder in [('Cats.csv', 'Cats'), ('Cows.csv', 'Cows'), ('Dogs.csv', 'Dogs'), ('Horses.csv', 'Horses')]: dest = path/folder # path + '/' + folder dest.mkdir(parents=True, exist_ok=True) download_images(path/file, dest)
We can verify that we don’t have any corrupt images using the verify_images method.
for folder in ('Cats', 'Cows', 'Dogs', 'Horses'): print(folder) verify_images(path/folder, delete=True, max_size=500)
This will try if any image in the given folder can be opened and has n_channels (defaults to 3). If one of these conditions isn’t given the image will be deleted.
Loading and viewing data
FastAI has specific data objects called databunches which are needed to train a model. These databunches can be created in two main ways.
The first way is to use problem-specific methods like the ImageDataBunch.from_folder which can be used to load data that has the following structure.
data-dir -Train -Class1 -Class2 -... -Valid -Class1 -Class2 -...
These methods are easy for specific folder structures but can’t be used for others so FastAI has another way of loading in data called the data block api which gives you more options by isolating the underlying parts of that process in separate blocks. More information on both methods can be found in the FastAi docs.
Loading in our data:
np.random.seed(42) data = ImageDataBunch.from_folder(path, train='.', valid_pct=0.2, ds_tfms=get_transforms(), size=224, num_workers=4).normalize(imagenet_stats)
Now we can use the data object to get more information about or data.
data.classes Out: ['Cats', 'Cows', 'Dogs', 'Horses']
We can show a random batch of images using the show_batch method.
data.show_batch(rows=3, figsize=(7, 8))
Creating a model and initial training
The FastAI library is designed to let you create models (FastAi calls them learners) with only a few lines of code. They provide a method called create_cnn, which can be used to create a convolutional neural network.
The method needs two arguments, the data, and the architecture, but also supports many other parameters that can be used to customize the model for a given problem.
from fastai.metrics import error_rate # 1 - accuracy learn = create_cnn(data, models.resnet34, metrics=error_rate)
By default, only the fully connected layers at the top are unfrozen (can be trained), which if you are familiar with transfer learning makes perfect sense.
To train the layers we can use the fit or fit_one_cycle method. The fit method is the “normal” way of training a neural net with a constant learning rate, whilst the fit_one_cycle method uses something called the 1 cycle policy, which basically changes the learning rate over time to achieve better results.
defaults.device = torch.device('cuda') # makes sure the gpu is used learn.fit_one_cycle(4)
Now that the fully-connected layers are well trained we can unfreeze the other layers and train the whole network.
As mentioned at the start of the article, FastAI provides another technic to enhance transfer learning called differential learning rates, which allows us to set different learning rates for different parts in the network.
To find the perfect learning rates we can use the lr_find and recorder.plot methods which create a plot that relates the learning rate with the loss.
learn.unfreeze() # must be done before calling lr_find learn.lr_find() learn.recorder.plot()
Here we are searching for the point with the steepest downward slope that still has a high value, which in this case is around 3e-5. Interpreting this plot takes a lot of intuition and Jeremy Howard talks a lot about it in the first few lessons of the course.
To now train the model using differential learning rates we need to pass the max_lr argument to the fit_one_cycle method.
learn.fit_one_cycle(4, max_lr=slice(3e-5, 3e-4))
The model can now be saved using the save method.
FastAI also provides functionality for cleaning your data using Jupyter widgets. The ImageCleaner class displays images for relabeling or deletion and saves changes in path as 'cleaned.csv'.
To use ImageCleaner we must first use DatasetFormatter().from_toplosses to get the suggested indices for misclassified images.
from fastai.widgets import * ds, idxs = DatasetFormatter().from_toplosses(learn) ImageCleaner(ds, idxs, path)
The results of the cleaning are saved as cleaned.csv which can now be used to load in the data.
df = pd.read_csv(path/'cleaned.csv', header='infer') print(df.head()) db = (ImageItemList.from_df(df, path) .random_split_by_pct(0.2) .label_from_df() .transform(get_transforms(), size=224) .databunch(bs=8)).normalize(imagenet_stats)
We can now print out the lengths of both the new and old dataset to see how many images we deleted.
print(data.classes, data.c, len(data.train_ds), len(data.valid_ds)) print(db.classes, db.c, len(db.train_ds), len(db.valid_ds)) Old:['Cats', 'Cows', 'Dogs', 'Horses'] 4 1181 295 New:['Cats', 'Cows', 'Dogs', 'Horses'] 4 923 230
We can also show a random batch again.
Now we can apply the same training steps as above but using the new data. We are also going to use the saved weights so we don’t need to start from scratch.
learn.load('animal-detection-stage-1') # loading the weights learn.data = db # replacing the data learn.freeze() learn.fit_one_cycle(4) learn.unfreeze() learn.lr_find() learn.recorder.plot() learn.fit_one_cycle(4, max_lr=slice(3e-5, 3e-4)) learn.save('animal-detection-stage-2')
Lastly, we can use FastAIs ClassificationInterpretation class to interpret our results. To create an interpretation object we need to call the from_learner method and pass it our learner/model. Then we can use methods like plot_confusion_matrix, plot_top_losses or most_confused.
interp = ClassificationInterpretation.from_learner(learn)
Plot confusion matrix:
Plot top losses:
The FastAi library is a high-level library build on PyTorch which allows for easy prototyping and gives you access to a lot of state-of-the-art methods/techniques.
If you liked this article consider subscribing to my Youtube Channel and following me on social media.
The code covered in this article is available as a Github Repository.
If you have any questions, recommendations or critiques, I can be reached via Twitter or the comment section.