Quickstart

This page follows fastai's quickstart page by quickly showing a few learning tasks. More will be added here as they are added to the library.

FastAI.jl's learning tasks all use the same basic steps and code:

  • create a data container

  • create a learning task

  • create learner

  • fit the model

  • make predictions or view results

In this quick start, we'll show these steps for a wide range of difference applications and datasets. As you'll see, the code in each case is extremely similar, despite the very different models and data being used.


			
			
			
			using
			
			 

	
			FastAI
			,
			
			 
			FastVision
			,
			
			 
			FastTabular
			,
			
			 
			FastMakie
			,
			
			 
			Metalhead
			
			

			
			import
			
			 
			CairoMakie
			;
			 
			
			
			CairoMakie
			.
			
			activate!
			(
			
			type
			=
			
			"
			png
			"
			)

Computer vision

Classification

Single-label

Let's train a model to classify images in the ImageNette dataset, a subset of ImageNet with 10 classes. The following lines download the dataset, prepare a data preprocessing pipeline and create a model suitable for classification that is ready to train:


			
			
			
			
			data
			,
			 
			blocks
			 
			=
			 
			
			load
			(
			
			

	
			datarecipes
			(
			)
			[
			
			"
			imagenette2-320
			"
			]
			)
			

			
			task
			 
			=
			 
			

	
			ImageClassificationSingle
			(
			blocks
			,
			 
			
			size
			=
			
			(
			256
			,
			 
			256
			)
			)
			

			
			learner
			 
			=
			 
			

	
			tasklearner
			(
			task
			,
			 
			data
			,
			 
			
			callbacks
			=
			
			[
			

	
			ToGPU
			(
			)
			,
			 
			

	
			Metrics
			(

	
			accuracy
			)
			]
			,
			 
			
			backbone
			=
			
			
			
			ResNet
			(
			34
			)
			.
			
			layers
			[
			
			1
			:
			
			end
			-
			1
			]
			)

			Learner()

With this, we can start training! Let's train for 10 epochs (i.e. iterations through the whole dataset) and a maximum learning rate of 0.004:


			
			
			

	
			fitonecycle!
			(
			learner
			,
			 
			10
			,
			 
			0.004
			)

			Epoch 1 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:20m
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   1.0 │ 2.00719 │  0.32779 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 1 ValidationPhase(): 100%|████████████████████████| Time: 0:00:15
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   1.0 │ 2.10231 │  0.37191 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 2 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:09
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   2.0 │ 1.81599 │  0.42517 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 2 ValidationPhase(): 100%|████████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   2.0 │ 2.50382 │  0.47347 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 3 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:11
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   3.0 │ 1.50171 │  0.52674 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 3 ValidationPhase(): 100%|████████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   3.0 │ 1.31716 │  0.57614 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 4 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:09
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   4.0 │ 1.22013 │  0.61345 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 4 ValidationPhase(): 100%|████████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   4.0 │ 1.02075 │  0.65426 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 5 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:10
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   5.0 │ 1.01926 │   0.6719 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 5 ValidationPhase(): 100%|████████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   5.0 │ 1.03147 │  0.66908 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 6 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:13
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   6.0 │ 0.90075 │    0.705 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 6 ValidationPhase(): 100%|████████████████████████| Time: -1:-59:-53
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   6.0 │ 0.84108 │  0.73292 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 7 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:20
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   7.0 │ 0.79412 │  0.74507 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 7 ValidationPhase(): 100%|████████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   7.0 │ 0.84146 │   0.7309 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 8 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:14
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   8.0 │ 0.69101 │  0.77818 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 8 ValidationPhase(): 100%|████████████████████████| Time: 0:00:07
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   8.0 │ 0.92366 │  0.69921 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 9 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:13
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   9.0 │ 0.55766 │  0.81549 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 9 ValidationPhase(): 100%|████████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   9.0 │ 0.69418 │   0.7792 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 10 TrainingPhase(): 100%|█████████████████████████| Time: 0:01:09
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │  10.0 │ 0.43443 │  0.85947 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 10 ValidationPhase(): 100%|███████████████████████| Time: 0:00:06
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │  10.0 │ 0.58856 │  0.81417 │
└─────────────────┴───────┴─────────┴──────────┘

Let's save the model for later use or deployment:


			
			
			

	
			savetaskmodel
			(
			
			"
			./quickstart_clf.jld2
			"
			,
			 
			task
			,
			 
			
			learner
			.
			
			model
			)

And now, look at some example model predictions on the validation dataset:


			
			
			

	
			showoutputs
			(
			task
			,
			 
			learner
			)

Segmentation

Segmentation is similar to classfication, but instead of assigning one class to an image, we try to classify every single pixel in an image. The CamVid dataset consists of images taken in traffic with pixel labels for various relevant image parts like the street, sky, sidewalk etc.


			
			
			
			
			data
			,
			 
			blocks
			 
			=
			 
			
			load
			(
			
			

	
			datarecipes
			(
			)
			[
			
			"
			camvid
			"
			]
			)
			

			
			task
			 
			=
			 
			

	
			ImageSegmentation
			(
			blocks
			)
			

			
			learner
			 
			=
			 
			

	
			tasklearner
			(
			task
			,
			 
			data
			,
			 
			
			callbacks
			=
			
			[
			

	
			ToGPU
			(
			)
			]
			,
			 
			
			backbone
			=
			
			
			
			ResNet
			(
			34
			)
			.
			
			layers
			[
			
			1
			:
			
			end
			-
			1
			]
			)

			Learner()

			
			
			

	
			fitonecycle!
			(
			learner
			,
			 
			10
			,
			 
			0.1
			)

			Epoch 1 TrainingPhase(): 100%|██████████████████████████| Time: 0:04:49
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   1.0 │ 2.40698 │
└───────────────┴───────┴─────────┘
Epoch 1 ValidationPhase(): 100%|████████████████████████| Time: 0:00:10
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   1.0 │ 3.26103 │
└─────────────────┴───────┴─────────┘
Epoch 2 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   2.0 │ 1.27837 │
└───────────────┴───────┴─────────┘
Epoch 2 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   2.0 │ 2.25442 │
└─────────────────┴───────┴─────────┘
Epoch 3 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   3.0 │ 1.20744 │
└───────────────┴───────┴─────────┘
Epoch 3 ValidationPhase(): 100%|████████████████████████| Time: 0:00:01
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   3.0 │ 1.50661 │
└─────────────────┴───────┴─────────┘
Epoch 4 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   4.0 │ 1.17844 │
└───────────────┴───────┴─────────┘
Epoch 4 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   4.0 │ 1.34925 │
└─────────────────┴───────┴─────────┘
Epoch 5 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   5.0 │ 1.10546 │
└───────────────┴───────┴─────────┘
Epoch 5 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   5.0 │ 1.15682 │
└─────────────────┴───────┴─────────┘
Epoch 6 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   6.0 │ 1.07065 │
└───────────────┴───────┴─────────┘
Epoch 6 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   6.0 │ 1.59546 │
└─────────────────┴───────┴─────────┘
Epoch 7 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬────────┐
│         Phase  Epoch    Loss │
├───────────────┼───────┼────────┤
│ TrainingPhase │   7.0 │ 1.0559 │
└───────────────┴───────┴────────┘
Epoch 7 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   7.0 │ 1.08011 │
└─────────────────┴───────┴─────────┘
Epoch 8 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   8.0 │ 0.99461 │
└───────────────┴───────┴─────────┘
Epoch 8 ValidationPhase(): 100%|████████████████████████| Time: 0:00:01
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   8.0 │ 1.13413 │
└─────────────────┴───────┴─────────┘
Epoch 9 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:05
┌───────────────┬───────┬─────────┐
│         Phase  Epoch     Loss │
├───────────────┼───────┼─────────┤
│ TrainingPhase │   9.0 │ 0.95487 │
└───────────────┴───────┴─────────┘
Epoch 9 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │   9.0 │ 1.19755 │
└─────────────────┴───────┴─────────┘
Epoch 10 TrainingPhase(): 100%|█████████████████████████| Time: 0:00:05
┌───────────────┬───────┬────────┐
│         Phase  Epoch    Loss │
├───────────────┼───────┼────────┤
│ TrainingPhase │  10.0 │ 0.9283 │
└───────────────┴───────┴────────┘
Epoch 10 ValidationPhase(): 100%|███████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┐
│           Phase  Epoch     Loss │
├─────────────────┼───────┼─────────┤
│ ValidationPhase │  10.0 │ 0.95868 │
└─────────────────┴───────┴─────────┘

			
			
			

	
			savetaskmodel
			(
			
			"
			./quickstart_segmentation.jld2
			"
			,
			 
			task
			,
			 
			
			learner
			.
			
			model
			)

As with all other tasks, let's have a look at some model outputs:


			
			
			

	
			showoutputs
			(
			task
			,
			 
			learner
			)

Tabular data

FastAI.jl also supports training models to understand tabular data.

Classification

Let's train a model on the Adult dataset that classifies, based on the other attributes, whether a person earns a salary below or above 50k. Since the dimensionality of the data and the model are quite small, this will run fast enough on a CPU, so we don't need to use the ToGPU callback.


			
			
			
			
			data
			,
			 
			blocks
			 
			=
			 
			
			load
			(
			
			

	
			datarecipes
			(
			)
			[
			
			"
			adult_sample/clf_salary
			"
			]
			)
			

			
			task
			 
			=
			 
			

	
			TabularClassificationSingle
			(
			blocks
			,
			 
			data
			)
			

			
			learner
			 
			=
			 
			

	
			tasklearner
			(
			task
			,
			 
			data
			
			;
			 
			
			callbacks
			=
			
			[
			

	
			Metrics
			(

	
			accuracy
			)
			]
			,
			 
			

	
			batchsize
			=
			128
			)

			┌ Warning: There is a missing value present for category 'occupation' which will be removed from Categorify dict
└ @ DataAugmentation /home/lorenz/.julia/dev/DataAugmentation/src/rowtransforms.jl:108

			Learner()

			
			
			

	
			fitonecycle!
			(
			learner
			,
			 
			5
			,
			 
			0.1
			)

			Epoch 1 TrainingPhase(): 100%|██████████████████████████| Time: 0:01:00
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   1.0 │ 0.37418 │  0.82691 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 1 ValidationPhase(): 100%|████████████████████████| Time: 0:00:04
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   1.0 │ 0.31771 │  0.85111 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 2 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:01
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   2.0 │ 0.33603 │  0.84142 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 2 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   2.0 │ 0.31734 │  0.85845 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 3 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:01
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   3.0 │ 0.33076 │   0.8466 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 3 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   3.0 │ 0.30928 │  0.85607 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 4 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:01
┌───────────────┬───────┬───────┬──────────┐
│         Phase  Epoch   Loss  Accuracy │
├───────────────┼───────┼───────┼──────────┤
│ TrainingPhase │   4.0 │ 0.322 │  0.84691 │
└───────────────┴───────┴───────┴──────────┘
Epoch 4 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬────────┬──────────┐
│           Phase  Epoch    Loss  Accuracy │
├─────────────────┼───────┼────────┼──────────┤
│ ValidationPhase │   4.0 │ 0.3056 │  0.86124 │
└─────────────────┴───────┴────────┴──────────┘
Epoch 5 TrainingPhase(): 100%|██████████████████████████| Time: 0:00:01
┌───────────────┬───────┬─────────┬──────────┐
│         Phase  Epoch     Loss  Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │   5.0 │ 0.31348 │  0.85295 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 5 ValidationPhase(): 100%|████████████████████████| Time: 0:00:00
┌─────────────────┬───────┬─────────┬──────────┐
│           Phase  Epoch     Loss  Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │   5.0 │ 0.30139 │  0.86165 │
└─────────────────┴───────┴─────────┴──────────┘

As with every task, we can visualize some model predictions. Since it fits the data better, we use the ShowText visualization backend here instead of the default ShowMakie used above.


			
			
			

	
			showoutputs
			(
			task
			,
			 
			learner
			,
			 
			
			backend
			=
			

	
			ShowText
			(
			)
			)

			┌─────────────────────────────────────────────────────────────┬──────────────────────────────────┐
│ Encoded sample                                               Output                           │
├─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
│ ┌──────────────────────┬──────────────────────────────────┐ │          ┌                    ┐  │
│ │ EncodedTableRow(...) │          ┌                    ┐  │ │    >=50k ■■■■■■■■■■■■■ 0.871    │
│ │                      │    >=50k ■■■■■■■■■■■■■■■ 1.0    │ │     <50k ■■ 0.129               │
│ │                      │     <50k  0.0                   │ │          └                    ┘  │
│ │                      │          └                    ┘  │ │                                  │
│ └──────────────────────┴──────────────────────────────────┘ │                                  │
│                                                             │                                  │
├─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
│ ┌──────────────────────┬──────────────────────────────────┐ │          ┌                    ┐  │
│ │ EncodedTableRow(...) │          ┌                    ┐  │ │    >=50k ■■■■■■■ 0.357          │
│ │                      │    >=50k  0.0                   │ │     <50k ■■■■■■■■■■■■■ 0.643    │
│ │                      │     <50k ■■■■■■■■■■■■■■■ 1.0    │ │          └                    ┘  │
│ │                      │          └                    ┘  │ │                                  │
│ └──────────────────────┴──────────────────────────────────┘ │                                  │
│                                                             │                                  │
├─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
│ ┌──────────────────────┬──────────────────────────────────┐ │          ┌                    ┐  │
│ │ EncodedTableRow(...) │          ┌                    ┐  │ │    >=50k  0.0303                │
│ │                      │    >=50k  0.0                   │ │     <50k ■■■■■■■■■■■■■■ 0.97    │
│ │                      │     <50k ■■■■■■■■■■■■■■■ 1.0    │ │          └                    ┘  │
│ │                      │          └                    ┘  │ │                                  │
│ └──────────────────────┴──────────────────────────────────┘ │                                  │
│                                                             │                                  │
├─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
│ ┌──────────────────────┬──────────────────────────────────┐ │          ┌                    ┐  │
│ │ EncodedTableRow(...) │          ┌                    ┐  │ │    >=50k ■■ 0.107               │
│ │                      │    >=50k  0.0                   │ │     <50k ■■■■■■■■■■■■■ 0.893    │
│ │                      │     <50k ■■■■■■■■■■■■■■■ 1.0    │ │          └                    ┘  │
│ │                      │          └                    ┘  │ │                                  │
│ └──────────────────────┴──────────────────────────────────┘ │                                  │
│                                                             │                                  │
└─────────────────────────────────────────────────────────────┴──────────────────────────────────┘