Fastai V2 Upgrade Review Guide. Whats new in Fastai Version 2?
Fastai2 was released on August 21st 2020 (Fastai2 and new course now released). As Jeremy writes fastai v2 is not API-compatible with fastai v1 (it’s a from-scratch rewrite).
. Fortunately, though, the overall architecture of fastai remained the same, so upgrading to fastai2 is less of a hassle as it sounds like in the announcement.
As I would have wished for an upgrade guide which is missing so far, I am writing this post to guide you through upgrading your fastai1 code to the latest fastai2. My focus is on the core and visual packages, as those are the ones I have been working with. Please add everything that’s missing in this guide in the comments as resources for others.
Overall, most of the changes I noticed are moving the same functionality to different places and renaming stuff.
This guide will hopefully also show you some useful functionality of fastai that you haven’t discovered yet, there are many hidden gems in the library, which makes it highly effective for production usage. You get many great defaults and helpers that give you a bunch of additional performance (both accuracy and speed) for free.
All of this was done as part of my work at https://www.intuitionmachines.com
Setup
Fastai2 requires you to import from fastai.vision.all import *
. The reason is they do a lot of monkey patching, and things fall apart, if you don’t do the all import. This goes against good coding standards, and one can only hope that in future they refactor this.
Data loading
The low-level API for handling data structurally changed between fastai version 1 and fastai version 2.
In fastai version 1 we created a DataBunch by chaining for steps: Provide inputs, split the data, label inputs and finally convert to a DataBunch which is then passed to the learner (https://fastai1.fast.ai/data_block.html).
In fastai version 2 the steps are to create a DataBlock object, which then is converted to a Datasets or DataLoaders object (https://docs.fast.ai/tutorial.datablock). The DataLoaders object can be passed to a learner object, the use case for the Datasets object remains unclear to me (it is a member of the DataLoaders object and seems to take the role of the datasource for the DataLoader).
In both versions there are helper functions to create datasets that are suitable for most use-cases. In fastai version 1 these were the DataBunch factory methods (https://fastai1.fast.ai/vision.data.html#Factory-methods), which became the ImageDataLoaders (https://docs.fast.ai/vision.data#ImageDataLoaders). The arguments of both are mostly the same, size=224, ds_tfms=vision.get_transforms()
became: batch_tfms=[*aug_transforms(size=224)], item_tfms=Resize(256)
. It’s unfortunate that one needs to supply the resize and cropping size independently, now. If you haven’t tried the predefined augmentations so far, you should, it’s likely giving you some additional performance improvements. (from fastai.vision.augment import aug_transforms, Resize
)
Within the learner, data has been renamed to dls: learn.data
→ learn.dls
.
DataLoaders
still have the train_ds
, valid_ds
and test_ds
attributes. But Datasets
does not have x
and y
attributes anymore, it only contains the underlying dataframe in .items
, so in order to obtain all labels one needs to do this: labels = learn.dls.train_ds.items[learn.dls.train_ds.cols[1].items]
.
The number of classes and the class names dictionary has been moved from the Dataset
to the DataLoaders
object, and renamed: DataLoaders.c
and DataLoaders.vocab
(was dataset.classes
).
As a side note, because it’s not documented: The vocab
attribute is a CategoryMap (when does fastai finally introduce type annotations everywhere?), but the vocab
function argument (eg. in CategoryBlock
) accepts a dictionary in the following form: {class_name_str: class_idx_int}
.
Callbacks
Callbacks are moved from fastai.callbacks
to fastai.callback
, and the LearnerCallback
functionality is moved into the general Callback
.
Fastai version 1 required the learner as an argument of all callbacks, this argument is now removed.
Callback functions have been renamed in the following way: on_*_begin
→ before_*
and on_*_end
→ after_*
, and *_train
→ *_fit
.
And the tracker operator is now comp: t.operator
→ t.comp
Finally, the dict return status codes were replaced by excpetions, eg.: return {"stop_training": True}
→ raise CancelFitException()
Metrics
from fastai.metrics import auc_roc_score
and callback → from fastai.metrics import RocAuc
Distributed
Fastai version 2 does a lot of monkey patching as mentioned before, so instead of inheriting a distributed learner, the learner is now patched: from fastai.distributed import Learner
→ from fastai.distributed import to_parallel
(and call the to_parallel()
function on the learner).
wandb
Weights & Biases (www.wandb.com) is a great library to track your machine learning experiments, results, models and data. In fastai version 1 the wandb library shipped a fastai callback, for fastai version 2 the appropriate callback is in the fastai library itself.
from wandb.fastai import WandbCallback
→ from fastai.callback.wandb import WandbCallback
Prediction
Became a lot easier! Yet, I never understood why it was so complicated before to make predictions for unlabeled/new data.
In fastai1 one needed to add_test
to the label_list
of learn.data
, with an empty label, create a DataLoader
for that label_list
and wrap the DataLoader
in a DeviceDataLoader
before one was able to call learn.predict()
and get_preds()
to obtain the results. Now all this mess became two lines of code:
dl = learn.dls.test_dl(filenames)
logits, _ = learn.get_preds(dl=dl, drop_last=False)
(fastai version 2 currently has the bug that if you don’t explicitly supply , drop_last=False, it drops the last batch during predict)
Why I still need to create a test_dl
as a user of this core functionality remains unclear to me; imho this should be as simple as learn.predict(filenames)
.
Renaming / Moving
Many packages, classes and functions have been renamed and moved around, here’s a non-exhaustive list of everything I discovered during my migration:
fastai version 1 → fastai version 2
fastai.datasets.URLs
→ fastai.data.external.URLs
fastai.vision.untar_data
→ fastai.data.external.untar_data
fastai.callbacks.csv_logger.CSVLogger
→ fastai.callback.progress.CSVLogger
fastai.core.camel2snake
→ fastcore.utils.camel2snake
fastai.core.defaults
→ fastcore.foundation.defaults
fastai.basic_train.load_learner
→ fastai.learner.load_learner
fastai.vision.cnn_learner
→ fastai.vision.learner.cnn_learner
fastai.basic_train.get_preds
→ fastai.learner.Learner.get_preds
(now a function of the learner object)
Removed / not yet implemented
self.learner.destroy()
, with fastai version 1 and pytorch 1.4 there existed a memory leak that required to destroy the learner object to release all memory. This was necessary in situations where one wanted to run several experiments in a row (e.g. for hyperparameter search). The destroy()
is not implemented yet, and I need to verify if the memory leak still exists.
@jeremy I hope this is still relevent for anybody, despite being late. If you see use for it, I am happy to create a PR to add it to the documentation, eg. in “migrating from other libraries”?