This tutorial explains the qickstart examples and some core abstractions FastAI.jl is built on.
{cell=main style="display:none;" result=false}
using FastAI
On the quickstart page, we showed how to train models on common tasks in a few lines of code like these:
using FastAI
data, blocks = loaddataset("imagenette2-160", (Image, Label))
method = ImageClassificationSingle(blocks)
learner = methodlearner(method, data, callbacks=[ToGPU()])
fitonecycle!(learner, 10)
showoutputs(method, learner)
Each of the five lines encapsulates one part of the deep learning pipeline to give a high-level API while still allowing customization. Let's have a closer look.
{cell=main}
data, blocks = loaddataset("imagenette2-160", (Image, Label))
This line downloads and loads the ImageNette image classification dataset, a small subset of ImageNet with 10 different classes. data
is a data container that can be used to load individual observations, here of images and the corresponding labels. We can use getobs(data, i)
to load the i
-th observation and nobs
to find out how many observations there are.
{cell=main }
image, class = sample = getobs(data, 1000)
@show class
image
blocks
describe the format of the data that you want to use for learning. For supervised training tasks, they are a tuple of (inputblock, targetblock)
. Since we want to do image classification, the input block is Image{2}()
, representing a 2-dimensional image and the target block is Label(classes)
, representing the class the image belongs to.
{cell=main}
blocks
{cell=main}
method = ImageClassificationSingle(blocks)
The next line defines a learning method which encapsulates the data preprocessing pipeline and other logic related to the task. ImageClassificationSingle
is a simple wrapper around BlockMethod
which takes in blocks and data processing steps, so-called encodings. Using it, we can replace the above line with
method = BlockMethod(
(Image{2}(), Label(classes)),
(
ProjectiveTransforms((128, 128)),
ImagePreprocessing(),
OneHot()
)
)
Based on the blocks and encodings, the learning method can derive lots of functionality:
- data processing
- visualization
- constructing task-specific models from a backbone
- creating a loss function
{cell=main}
learner = methodlearner(method, data, callbacks=[ToGPU(), Metrics(accuracy)])
Next we create a Learner
that encapsulates everything needed for training, including:
- parallelized training and validation data loaders using
methoddataloaders
- a loss function using
methodlossfn
- a task-specific model using
methodmodel
The customizable, expanded version of the code looks like this:
dls = methoddataloaders(data, method)
model = methodmodel(method, Models.xresnet18())
lossfn = methodlossfn(method)
learner = Learner(model, dls, ADAM(), lossfn, ToGPU(), Metrics(accuracy))
At this step, we can also pass in any number of callbacks to customize the training. Here ToGPU
ensures an available GPU is used, and Metrics
adds additional metrics to track during training.
fitonecycle!(learner, 10)
Training now is quite simple. You have several options for high-level training schedules:
lrfind
to run a learning rate finderfinetune!
for when you're using a pretrained backbonefitonecycle!
for when you're training a model from scratch
showoutputs(method, learner)
Finally, the last line visualizes the predictions of the trained model. It takes some samples from the training data loader, runs them through the model and decodes the outputs. How each piece of data is visualized is also inferred through the blocks in the learning method.