Introduction

This tutorial explains the quickstart examples and some core abstractions FastAI.jl is built on.

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
			 
			=
			 
			
			load
			(
			
			

	
			datarecipes
			(
			)
			[
			
			"
			imagenette2-160
			"
			]
			)
			

			
			task
			 
			=
			 
			

	
			ImageClassificationSingle
			(
			blocks
			)
			

			
			learner
			 
			=
			 
			

	
			tasklearner
			(
			task
			,
			 
			data
			,
			 
			
			callbacks
			=
			
			[
			

	
			ToGPU
			(
			)
			]
			)
			

			

	
			fitonecycle!
			(
			learner
			,
			 
			10
			)
			

			

	
			showoutputs
			(
			task
			,
			 
			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.

Dataset


			
			
			
			
			data
			,
			 
			blocks
			 
			=
			 
			
			load
			(
			
			

	
			datarecipes
			(
			)
			[
			
			"
			imagenette2-160
			"
			]
			)

			((mapobs(loadfile, ObsView{FileDataset{typeof(identity), String}} with 13394 observations; batched=:auto), mapobs(parentname, ObsView{FileDataset{typeof(identity), String}} with 13394 observations; batched=:auto)), (Image{2}(), Label{String}(["n01440764", "n02102040", "n02979186", "n03000684", "n03028079", "n03394916", "n03417042", "n03425413", "n03445777", "n03888257"])))

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 numobs to find out how many observations there are.


			
			
			
			
			image
			,
			 
			class
			 
			=
			
			 
			sample
			 
			=
			  
			

	
			getobs
			(
			data
			,
			 
			1000
			)
			

			
			@
			show
			 
			class
			

			image

			class = "n02102040"

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.


			
			
			blocks

			(Image{2}(), Label{String}(["n01440764", "n02102040", "n02979186", "n03000684", "n03028079", "n03394916", "n03417042", "n03425413", "n03445777", "n03888257"]))

Learning task


			
			
			
			task
			 
			=
			 
			

	
			ImageClassificationSingle
			(
			blocks
			)

			SupervisedTask(Image{2} -> Label{String})

The next line defines a learning task which encapsulates the data preprocessing pipeline and other logic related to the task. ImageClassificationSingle is a simple wrapper around BlockTask which takes in blocks and data processing steps, so-called encodings. Using it, we can replace the above line with


			
			
			
			task
			 
			=
			 
			

	
			BlockTask
			(
			
    
			
			(
			
			

	
			Image
			{
			2
			}
			(
			)
			,
			 
			

	
			Label
			(
			classes
			)
			)
			,
			
    
			
			(
			
        
			

	
			ProjectiveTransforms
			(
			
			(
			128
			,
			 
			128
			)
			)
			,
			
        
			

	
			ImagePreprocessing
			(
			)
			,
			
        
			

	
			OneHot
			(
			)
			
    
			)
			

			)

Based on the blocks and encodings, the learning task can derive lots of functionality:

  • data processing

  • visualization

  • constructing task-specific models from a backbone

  • creating a loss function

Learner


			
			
			
			learner
			 
			=
			 
			

	
			tasklearner
			(
			task
			,
			 
			data
			,
			 
			
			callbacks
			=
			
			[
			

	
			ToGPU
			(
			)
			,
			 
			

	
			Metrics
			(

	
			accuracy
			)
			]
			)

			Learner()

Next we create a Learner that encapsulates everything needed for training, including:

The customizable, expanded version of the code looks like this:


			
			
			
			dls
			 
			=
			 
			

	
			taskdataloaders
			(
			data
			,
			 
			task
			)
			

			
			model
			 
			=
			 
			

	
			taskmodel
			(
			task
			,
			 
			
			
			Models
			.
			
			xresnet18
			(
			)
			)
			

			
			lossfn
			 
			=
			 
			

	
			tasklossfn
			(
			task
			)
			

			
			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.

Training


			
			
			

	
			fitonecycle!
			(
			learner
			,
			 
			10
			)

Training now is quite simple. You have several options for high-level training schedules:

  • lrfind to run a learning rate finder

  • finetune! for when you're using a pretrained backbone

  • fitonecycle! for when you're training a model from scratch

Visualization


			
			
			

	
			showoutputs
			(
			task
			,
			 
			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 task.