Paper · 2020-02-11 · cs.LG, cs.CV, cs.NE, stat.ML
fastai: A Layered API for Deep Learning
fastai is a PyTorch-based deep learning library with a layered API: one-call training for vision, text, tabular and collaborative filtering, plus data blocks, two-way callbacks, a generic optimizer, metrics and low-level transforms. Headline results are author-reported: a course rewritten on it and ResNet-50 on ImageNet in 18 minutes (DawnBench); no benchmark tables.
Learning-rate schedulesTransfer learningData augmentationDeep learninglibrarytraining-apitransfer-learninglearning-rate-schedule
arXivPDFCodeMarkdown12 min read
AI-compiled by claude-haiku-5-5 on 2026-10-24, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
fastai is a Python library for training deep learning models, built on top of PyTorch. It is meant for two kinds of people at once. Practitioners get a few high-level functions that pick sensible defaults, so a working image classifier takes only a handful of lines. Researchers get smaller building blocks that they can mix, replace and extend without rewriting the whole pipeline.
The trick is layering. Each high-level function is assembled from middle-level pieces, and those are built on lower-level ones. You can drop down a layer when you need control, and you never have to learn the bottom layer just to get started. Training itself is driven by callbacks: small hooks that can look at and change almost anything during training, such as the data, the predictions, the gradients or the learning rate.
The library also takes a few specific choices that the authors found work well in practice, including a one-cycle learning-rate schedule and a learning-rate finder. Its aim is to be easy for beginners, hackable for experts, and to avoid forcing a choice between the two.
TL;DR
- fastai is a layered PyTorch library: high-level one-call training for vision, text, tabular and collaborative filtering, built on mid-level data, training and optimizer APIs and low-level transform and tensor abstractions §1 §2 §4 §5.
- Its central mechanisms are the data block API, a
Learnerthat bundles model, optimizer, loss and data, two-way callbacks that can change training at any point, and a generic optimizer built from stats and steppers §3.1 §4.1 §4.2 §4.3. - Results are author-reported and qualitative: a course rewritten on the library, ResNet-50 on ImageNet in 18 minutes in a DawnBench entry, and short code examples. The paper has no benchmark tables §1 §8.
- The paper targets fastai v2.0.0 (pre-release); the code at the pinned commit is fastai 2.8.13, so names and defaults have drifted §1 [code: fastai/init.py#L1].
Problem
- Deep learning libraries of the time often offered either a high-level API or a flexible low-level one, and it was hard to combine both §1. Many libraries had a high-level API and a low-level API with nothing in between, so customizing the high level meant rewriting parts of it §4 intro.
- Transfer learning is sensitive to details. In the authors' tests, batch-normalization handling changed the error rate by as much as 300% depending on configuration, and only one approach worked across all their datasets §4.1.
- Adding a technique to an existing research codebase often needed a rewrite. The paper's example is mixup, whose released code worked on one dataset, metric and optimizer §3.3.
- Data preparation is a common source of mistakes, such as forgetting a validation set or shuffling it §2 intro §1.
- The paper assumes PyTorch as the base and does not aim to hide it: PyTorch modules can be used inside fastai, and fastai components can be used inside PyTorch code §1 §3.2.
Method
The library is organised in three layers, plus a development environment §1 §5:
- High level:
*_learnerfunctions and*DataLoadersclasses for vision, text, tabular and collaborative filtering. They choose defaults from the data and the model §1 §2. - Middle level: the data block API,
DataLoaders,Learner, two-way callbacks, the generic optimizer, the metric API,DataLoadercustomisation, and architecture building blocks such asSequentialEx,MergeLayerandXResNet§4. - Low level: transform pipelines with encode and decode, type dispatch, semantic tensor types, GPU batch augmentation and convenience utilities §5.
- nbdev: a notebook-based tool used to write the library and generate its documentation §6.
Steps as the paper describes them:
- Describe the data as blocks. A data block set defines get-items, split, label, processing and batching in that order. Blocks are mixed and matched, for example image input with a category label §3.1. The paper gives this as a functional
DataBlockwith keyword arguments, not a chained API §3.1. - Transform items and batches. Item transforms run per item, usually on the CPU. Batch transforms run on the mini-batch, on the GPU when available. Images in a batch must share a size first §2.1. A transform can have an inverse (
decode), which is used to display batches and predictions §5.2 §4.6. - Bundle the training state in a
Learner. It holds the model, optimizer, loss, data and metrics. A splitter divides the model into parameter groups, which can be frozen or given different learning rates §4.1. The loss and optimizer are optional when they can be inferred §4.1. - Run training through callbacks. Each step of the loop fires an event, and callbacks can read or edit the state, skip a batch, an epoch or a phase, or cancel training §4.2. The paper's per-batch loop is summarised below.
- Schedule the learning rate and momentum together. The one-cycle policy warms up the learning rate while momentum goes down, then anneals the learning rate while momentum goes back up §3.1 fig. 8.
- Pick a starting learning rate. The learning-rate finder runs a short mock training with a learning rate that grows exponentially over 100 iterations, then reads a suggestion off the loss curve §3.1 fig. 9 schedule.py schedule.py.
- Build optimizers from stats and steppers. Statistics such as moving averages are tracked separately from the update rules. Decoupled weight decay (AdamW-style) is a single switch §4.3.
- Compute metrics incrementally. A metric has
reset,accumulateandvalue, so it does not need to keep every prediction in memory §4.4. - Compose architectures from blocks.
SequentialExgives each layer access to the block input, so skip connections need no customforward.MergeLayertakes add or concatenate.XResNetcovers many ResNet variants through parameters §4.8.
The per-batch training loop from the paper (§4.2), with a callback at every step. Event names follow the paper.
# Train on one batch; every step fires a callback event [§4.2]
# Input: batch (inputs, targets); callbacks cb(event) that may edit state
# Output: none (weights and callback state are updated in place)
def train_one_batch(batch, model, loss_func, optimizer, cb):
# Split the batch into model inputs and targets [§4.2]
# [code: fastai/learner.py#L203-L205]
inputs, targets = split(batch)
cb('begin_batch')
try:
# Forward pass; callbacks can change the predictions [§4.2]
predictions = model(inputs)
cb('after_pred')
# No targets means inference only, so stop before the loss [§4.2]
if len(targets) == 0: return
loss = loss_func(predictions, targets)
cb('after_loss')
# Backward and step only while training [§4.2]
if not training: return
loss.backward()
cb('after_back')
optimizer.step()
cb('after_step')
optimizer.zero_grad()
except CancelBatchException:
# A callback may skip the rest of this batch [§4.2]
cb('after_cancel')
finally:
# Runs on normal exit, early return and cancel [§4.2]
cb('after_batch')The one-cycle schedule as the paper describes it and as the default code computes it. The paper states no numeric defaults; the values below are the code's defaults, not the paper's.
# One-cycle schedule for learning rate and momentum [§3.1] [fig. 8]
# Input: progress in [0, 1]; lr_max; moms = (high, low, high); pct_start
# Output: (lr, mom) for this point of training
# cosine(a, b, t): cosine interpolation from a (t=0) to b (t=1)
def one_cycle(progress, lr_max, moms, pct_start):
# Warm-up: lr rises from lr_max / div to lr_max [§3.1]
# [code: fastai/callback/schedule.py#L114-L121]
if progress < pct_start:
fraction = progress / pct_start
lr = cosine(lr_max / 25, lr_max, fraction) # div=25 in code
mom = cosine(moms[0], moms[1], fraction)
# Annealing: lr falls to lr_max / div_final; momentum rises back [§3.1]
else:
fraction = (progress - pct_start) / (1 - pct_start)
lr = cosine(lr_max, lr_max / 1e5, fraction) # div_final=1e5 in code
mom = cosine(moms[1], moms[2], fraction)
return lr, momThe Dice metric from §4.4, reshaped in the metric's own terms. The paper gives it as Python code, and the current code matches its logic metrics.py.
# Dice coefficient for a binary target [§4.4]
# Input per batch: scores (B, C, H, W), targets (B, H, W) in {0, 1}
# Output: one scalar, or None if nothing was accumulated
def reset():
# Start a new epoch with empty sums [§4.4]
intersection = 0
union = 0
def accumulate(scores, targets):
# Hard predictions from the class axis [§4.4]
predictions = argmax(scores, axis=1)
intersection += sum(predictions * targets)
union += sum(predictions + targets)
def value():
# Dice = 2 * intersection / union, computed once at the end [§4.4]
if union > 0: return 2 * intersection / union
return NoneHyperparameters
The paper gives most of its values in code listings, not in tables. Values are copied from those listings unless marked as code defaults. Listing numbers are by section, since the paper does not number listings.
| Name | Value | Where | Notes |
|---|---|---|---|
| Pets batch size | 64 | §2.1 | listing in §2.1 |
| Pets item transform | RandomResizedCrop(450, min_scale=0.75) | §2.1 | item-level, CPU |
| Pets batch transforms | aug_transforms(size=224, max_warp=0.); ImageNet normalisation stats | §2.1 | GPU; max_warp set to 0 |
| Pets model | resnet34, ImageNet-pretrained | §2.1 | head replaced for the dataset |
| Pets label pattern | /([^/]+)_\d+.jpg$ | §2.1 | regex on file names |
| Pets schedule | fit_one_cycle(4); learning rate not stated | §2.1 | falls back to the Learner default |
| CamVid batch size | 8 | §2.1 | segmentation |
| CamVid input size | (360, 480) | §2.1 | aug_transforms(size=(360,480)) |
| CamVid model and schedule | unet_learner, resnet34; fit_one_cycle(8, pct_start=0.9) | §2.1 | |
| IMDb language model | AWD_LSTM; fit_one_cycle(1, 2e-2, moms=(0.8, 0.7, 0.8)) | §2.2 | metric Perplexity |
| IMDb classifier | AWD_LSTM; fit_one_cycle(1, 2e-2, moms=(0.8, 0.7, 0.8)) | §2.2 | metric accuracy |
| Language model via data blocks | batch size 64; sequence length 72 | §3.1 | databunch call in the paper |
| Tabular | batch size 64; layers [200, 100]; fit_one_cycle(3) | §2.3 | validation rows range(1024, 1260) |
| Tabular processing | Categorify, FillMissing, Normalize | §2.3 | |
| Collaborative filtering | 50 factors; y_range [0, 5.5]; batch size 64; seed 42; fit_one_cycle(3) | §2.4 | |
| MNIST data block | GrandparentSplitter; batch size not stated | §3.1 | |
| COCO data block | Resize(128) item transform | §3.1 | bounding boxes and labels |
| CamVid data block | RandomSplitter; aug_transforms() with default arguments | §3.1 | |
| Incremental PyTorch example | Adam, F.nll_loss, accuracy; fit_one_cycle(epochs, lr) | §3.2 | epochs and lr not stated |
| LR finder | start 1e-7, end 10, 100 iterations | fig. 9 schedule.py | exponential growth per the code docstring |
| LR suggestion | "minimum divided by 10" | fig. 9 | code default is valley, see Paper vs code |
| One-cycle warm-up and annealing | not stated in paper | schedule.py | code defaults: div=25, div_final=1e5, pct_start=0.25 |
| Momentum range | not stated in paper | learner.py | code default (0.95, 0.85, 0.95) |
| Batch-norm handling | do not freeze batch-norm layers; keep updating their statistics | §4.1 | code: train_bn=True, wd_bn_bias=False |
| Adaptive GAN switch thresholds | not stated | §4.2.1 | code default is None for both (see Pitfalls) |
aug_transforms defaults | not in paper | augment.py | max_rotate=10, max_zoom=1.1, max_lighting=0.2 |
ResNet tweaks in XResNet | not listed in paper | §4.8 | cites the "bag of tricks" paper for them |
| DawnBench ResNet-50 | 18 minutes | §1 | target accuracy not stated |
Results
Author-reported. The paper has no benchmark table, no baseline and no variance. Setup: examples on Oxford-IIIT Pets and CamVid §2.1, IMDb §2.2, an Adult sample §2.3, a MovieLens sample §2.4, and MNIST and COCO samples §3.1. Hardware is stated only as "a single GPU" for Pets §2.1 and for the DawnBench entry, where the paper gives no further detail §1.
| Setting | Metric | Baseline | This paper | Source |
|---|---|---|---|---|
| Pets fine-tuning, four lines of user code | Accuracy | "close to state of the art" (no number) | not stated numerically | §2.1 |
| ResNet-50 on ImageNet (DawnBench entry) | Wall-clock time to target accuracy | not stated | 18 minutes | §1 |
| Batch-norm handling in transfer learning | Error rate across configurations | other configurations (not tabulated) | up to 300% change between configurations, author observation | §4.1 |
Language-model DataLoader | Lines of code | 90 lines (earlier fastai) | 30 lines | §4.6 |
| Decoupled weight decay (AdamW) | Lines of code to add | a new class of over 50 lines in PyTorch | one line | §4.3 |
| Adoption | Share of data scientists | none | 10% of data scientists in the Kaggle community use fastai, per the 2019 Kaggle survey as cited | §8 |
| Course rewrite | Scope | previous course version | 14 hours, seven modules; results "replicate or improve" (no numbers) | §8 |
The authors also say that they reimplemented recent research papers in a short amount of work and got results close to the originals §1. No per-paper numbers are given.
Paper vs code
Code: github.com/fastai/fastai at commit 46231bdc3e88a457c940dd7bde8a03c92fb42901 (default branch, shallow clone, fastai/__init__.py reports version 2.8.13), Apache-2.0. The paper's listings and text describe fastai v2.0.0 §1; this code is much newer, so the comparisons below are with the current code and are not a check of the v2.0.0 release.
- Learning-rate suggestion. Paper: a suggestion of the minimum loss value divided by 10 fig. 9. Code: the same rule exists as
minimum(lr_min / 10) schedule.py, butlr_finddefaults tovalley, which picks a point near the middle of the longest run of decreasing losses schedule.py schedule.py. The first 10% and last 5 points are dropped before suggesting schedule.py. - Learner arguments. Paper:
Learner.__init__has 11 keyword arguments §5.6. Code: 13 keyword arguments besidesdlsandmodel(includingwd_bn_bias,train_bn,moms,default_cbs) learner.py. - Data block method. Paper:
databunch§3.1. Code:dataloadersis the method name block.py;databunchis not found in the file. - Mish. Paper: the Mish activation is optimised with the JIT compiler §4.8. Code: the function
mishcalls PyTorch's built-inF.mish; the JIT version is the separateMishJitmodule layers.py. - Batch norm. Paper:
Learnerbypasses batch-norm layers when freezing §4.1. Code: frozen batch-norm and bias parameters are flagged to keep training (force_train) by default, and get no weight decay by default learner.py. This matches the paper's intent; the mechanism differs. - Weight decay. Paper: the decoupled switch is a one-line change §4.3. Code: each optimizer has
decouple_wd=Trueby default, so decoupled weight decay is the default for the Adam family optimizer.py. The paper names the liststeppers; the code calls itcbs. - Dice metric. Paper and code agree on the logic §4.4 metrics.py. The code adds an explicit
dim=keyword. - Adaptive GAN switcher. Paper: switches when losses reach thresholds §4.2.1. Code: if a threshold is
None, the switch happens after every batch gan.py. - Code examples. Several listings use
fastai2.*import paths §2.3 §2.4; the current package isfastai. The paper's own text notes the listings were run against v2.0.0 §1. - Listing errors. The language-model listing ends with
metrics=Perplexity()]), which has an extra bracket. The classifierTextDataLoaders.from_df(...)call is missing a comma afterpath=path§2.2. - LAMB. The paper says the authors did not define the φ function, and that its code is based on the official TensorFlow implementation §4.3. This card does not check the LAMB code.
No other discrepancies were checked. Specifically not compared: the two-way callback event names and their order, the exact XResNet tweaks, and the 15 DataLoader extension points §4.6.
Pitfalls
- Version drift. The paper describes fastai v2.0.0 §1. Names and defaults differ at the pinned commit (see Paper vs code). Anyone implementing from the paper should pin a version, or use the paper's names only as a guide.
- Unstated defaults. The paper never gives the one-cycle
div,div_final,pct_start, or the momentum range. Code defaults arediv=25,div_final=1e5,pct_start=0.25, and momentum(0.95, 0.85, 0.95)schedule.py learner.py. The Pets example passes no learning rate, so it uses theLearnerdefault §2.1. - LR suggestion. The figure caption says "minimum divided by 10" fig. 9. The default suggestion is a different rule (
valley), so a plot-based guess and the returned value can disagree schedule.py. Choose the suggestion function explicitly. - Batch-norm policy is a default, not a rule.
train_bn=Trueis the default. Setting it toFalseswitches to the usual freezing behaviour, which the paper argues against §4.1 learner.py. - Augmentation defaults are hidden. The Pets listing sets
max_warp=0and leaves rotation, zoom and lighting at code defaults (max_rotate=10,max_zoom=1.1,max_lighting=0.2). The paper does not list these augment.py. - GAN switcher. With no thresholds,
AdaptiveGANSwitcherchanges the training target after every batch. The paper's threshold values are not stated §4.2.1 gan.py. - Two-way callbacks can change anything. The paper's design lets any callback change data, losses, gradients and control flow §4.2. This is powerful, but the paper reports no combined cases of conflicting callbacks beyond "not yet come across any cases where mixing ... has caused any problems" §4.2. Test combinations yourself.
- Mish speed. The paper's JIT claim applies to the
MishJitmodule, not to themishfunction layers.py. - Compute is not reported. The paper gives wall-clock examples for two tasks only ("a couple of minutes" on a single GPU for Pets §2.1, and 18 minutes for ImageNet §1). No memory use, throughput or cost comparisons are given.
- Callback and metric ordering. The paper says
resetruns "at the start of training" §4.4. The implementation was not checked for whenresetis called, so verify this before relying on it. - Section numbering. The paper's own numbering comes from the MDPI class, which numbers sections automatically. Section numbers here follow the order of
\sectionand\subsectionin the source (for example §4.2.1 is the GAN case study). Check against the rendered PDF when precision matters. - Figures. Figure numbers are by order of appearance in the source: schedule is fig. 8, the LR finder is fig. 9, LAMB is fig. 10, interpolation is fig. 11 and nbdev is fig. 12.
Lineage
- Builds on: the one-cycle policy §3.1, the learning-rate finder §3.1, ULMFiT-style NLP transfer learning and discriminative learning rates §2.2, and the batch-norm literature §4.1. These are named in the paper; this wiki has no cards for them yet.
- Builds on the frameworks PyTorch §5.1, NumPy §1, pandas, PIL and scikit-learn §5.1 §5.6.
- Compared against: Keras and TensorFlow-style high-level APIs, scikit-learn pipelines, tf.data, and Torchvision and Detectron-style task libraries §3.3 §7.
- Superseded by: not known from wiki content.
Source and license
Adapted from “fastai: A Layered API for Deep Learning” by Jeremy Howard, Sylvain Gugger (arXiv:2002.04688), used under CC BY-SA 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. This page is licensed under CC BY-SA 4.0. Code citations refer to https://github.com/fastai/fastai at commit 46231bdc3e88 (Apache-2.0). Any quoted code is shown under that license.