Fundamentals
DataLoaders
DataLoaders
is a thin class around DataLoader, and makes them available as train
and valid
.
Same thing applies to Datasets
and Dataset
.
In pytorch, Dataset
is fed into a DataLoader
.
DataBlocks
Use this to create
DataLoaders
= DataBlock(
bears =(ImageBlock, CategoryBlock),
blocks=get_image_files,
get_items=RandomSplitter(valid_pct=0.2, seed=42),
splitter=parent_label,
get_y=Resize(128)) item_tfms
DataBlocks
are a template for creating DataLoaders
, and need to be instantiated somehow - for example given a path where to find the data:
= bears.dataloaders(path) dls
You can modify the settings of a DataBlock
with new
:
= bears.new(item_tfms=RandomResizedCrop(128, min_scale=0.3)) #book has more examples
bears = bears.dataloaders(path) dls
You can sanity check / see transformed data with show_batch
:
>>> dls.train.show_batch(max_n=8, nrows=2, unique=True)
... images
You also use DataBlocks
for data augmentation, with batch_tfms
:
= bears.new(
bears =Resize(128),
item_tfms=aug_transforms(mult=2)
batch_tfms
)= bears.dataloaders(path)
dls =8, nrows=2, unique=True) dls.train.show_batch(max_n
Training
Most things use learn.fine_tune()
, when you cannot fine-tune like tabular data, you often use learn.fit_one_cycle
You can also do learn.show_results(...)
from fastai.vision.all import *
= untar_data(URLs.PETS)/'images'
path
def is_cat(x):
return x[0].isupper()
= ImageDataLoaders.from_name_func(
dls =str(path),
path=get_image_files(path),
fnames=0.2,
valid_pct=42,
seed=is_cat,
label_func=Resize(224))
item_tfms
= cnn_learner(dls, resnet34, metrics=error_rate)
learn 1) learn.fine_tune(
More info on what this is in later sections.
Interpetability
= ClassificationInterpretation.from_learner(learn)
interp interp.plot_confusion_matrix()
Also see top losses:
5, nrows=1) interp.plot_top_losses(
Cleaning
You can get a ImageClassifierCleaner
which allows you to choose (1) a category and (2) data partition (train/val) and shows you the highest loss items so you can decide whether to Keep, Delete, Change etc.
= ImageClassifierCleaner(learn)
cleaner cleaner
The thing doesn’t actually delete/change anything but gives you the idxs that allow you to do things with them
for idx in cleaner.delete(): cleaner.fns[idx].unlink()
for idx,cat in cleaner.change(): shutil.move(str(cleaner.fns[idx]), path/cat)
Loading / Saving
Saving a model can be done with learn.export
, when you do this, fastai will save a file called “export.pkl”
learn.export()
load_learner
can be used to load a model
= load_learner(path/'export.pkl') learn_inf
Predicting
When you call predict, you will get three things: (1) class, (2) the index of the predicted category (3) Probabilities of each category
>>> learn_inf.predict('images/grizzly.jpg')
'grizzly', tensor(1), tensor([9.0767e-06, 9.9999e-01, 1.5748e-07])) (
You can see all the classes with dls.vocab
:
>>> learn_inf.dls.vocab
#3) ['black','grizzly','teddy'] (
Zach: learn.dls.vocab
or learn.dls.categorize.vocab
is another way to get the class names.
Computer Vision
You can open an image with Pilow (PIL)
= Image.open(im3_path)
im3
im3
#convert to numpy
array(im3)# convert to pytorch tensor
tensor(im3)
Pixel Similarity Baseline
- Compute avg pixel value for 3’s and 7’s
- At inference time, see which one its similar too, using
RMSE (L2 Norm)
andMAE (L1 Norm)
Kind of like KNN
Taking an inference tensor, a_3
and calculate distance to mean 3 and 7:
# MAE & RMSE for 3 vs avg3
= (a_3 - mean3).abs().mean()
dist_3_abs = ((a_3 - mean3)**2).mean().sqrt()
dist_3_sqr
# MAE & RMSE for 3 vs avg7
= (a_3 - mean7).abs().mean()
dist_7_abs = ((a_3 - mean7)**2).mean().sqrt()
dist_7_sqr
# Use Pytorch Losses to do the same thing for 3 vs avg 7
float(),mean7), F.mse_loss(a_3,mean7).sqrt() F.l1_loss(a_3.
numpy
Take the mean over an axis:
def mnist_distance(a,b):
#(-2,1) means take the average of the last 2 axis
return (a-b).abs().mean((-2,-1))
SGD from scratch
Minimal Example
# the loss function
def mse(y, yhat):
return (y - yhat).square().mean().sqrt()
# the function that produces the data
def quadratic(x, params=[.75, -25.5, 15]):
= params
a,b,c = (torch.randn(len(x)) * 3)
noise return a*(x**2) + b*x +c + noise
# generate training data
= torch.arange(1, 40, 1)
x = quadratic(x)
y
# define the training loop
def apply_step(params, pr=True):
= 1.05e-4
lr = quadratic(x, params)
preds = mse(preds, y)
loss
loss.backward()-= params.grad.data * lr
params.data if pr: print(f'loss: {loss}')
= None
params.grad
# initialize random params
= torch.rand(3)
params
params.requires_grad_()assert params.requires_grad
# train the model
for _ in range(1000):
apply_step(params)
MNIST
A Dataset
in pytorch is required to return a tuple of (x,y) when indexed. You can do this in python as follows:
# Turn mnist data into vectors 3dim -> 2dim
= torch.cat([stacked_threes, stacked_sevens]).view(-1, 28*28)
train_x # Generate label tensor
= tensor([1]*len(threes) + [0]*len(sevens)).unsqueeze(1)
train_y # Create dataset
= list(zip(train_x,train_y))
dset
# See shapes from first datum in the dataset
>>> x,y = dset[0]
>>> x.shape, y.shape
784]), torch.Size([1]))
(torch.Size([
# Do the same thing for the validation set
....
Mini Batch SGD
# `@` and dot product is the same:
= torch.rand(10), torch.rand(10)
a, b assert a.dot(b) == a@b
# define model
def init_params(size, std=1.0):
return (torch.randn(size)*std).requires_grad_()
= init_params((28*28,1))
weights = init_params(1)
bias
def linear1(xb): return xb@weights + bias
#naive loss (for illustration)
= (preds>0.0).float() == train_y
corrects float().mean().item()
corrects.
# define loss
def mnist_loss(preds, targets):
= preds.sigmoid() #squash b/w 0 and 1
preds return torch.where(targets==1, 1-preds, preds).mean() # average distance loss
Create a dataloader
You want to load your data in batches, so you will want to create a dataloader. Recall that in pytorch, a Dataset
is required to return a tuple of (x,y) when indexed, which is quite easy to do:
# define a data loader using `dset`
= list(zip(train_x,train_y)) dset
Pytorch offers a utility to then create a Dataloader
from a dataset, but Jeremy basically rolled his own (w/same api):
= DataLoader(dset, batch_size=256)
dl = DataLoader(valid_dset, batch_size=256) valid_dl
The Training Loop
def calc_grad(xb, yb, model):
= model(xb)
preds = mnist_loss(preds, yb)
loss
loss.backward()
def train_epoch(model, lr, params):
for xb,yb in dl:
calc_grad(xb, yb, model)for p in params:
-= p.grad*lr
p.data #updates in place
p.grad.zero_()
### Calculate metrics
def batch_accuracy(xb, yb):
= xb.sigmoid()
preds = (preds>0.5) == yb
correct return correct.float().mean()
def validate_epoch(model):
= [batch_accuracy(model(xb), yb) for xb,yb in valid_dl]
accs return round(torch.stack(accs).mean().item(), 4)
# Train model
= 1.
lr = weights,bias
params
train_epoch(linear1, lr, params)
validate_epoch(linear1)
# Train model w/epochs
for i in range(20):
train_epoch(linear1, lr, params)print(validate_epoch(linear1), end=' ')
Using Pytorch
Blueprint: 1. Define a dataset and then a dataloader 2. Create a model, which will have parameters 3. Create an optimizer, that: - Updates the params: params.data -= parmas.grad.data * lr - Zeros out the gradients: setting params.grad = None
or zeroing out the gradients with params.grad.zero_()
4. Generate the predictions 5. Calculate the loss 6. Calculate the gradients loss.backward()
7. Using the optimizer, update the weights step
and zero out the gradients zero_grad
8. Put 4-7 in a loop.
Create an optimizer and use nn.Linear
= nn.Linear(28*28,1)
linear_model = linear_model.parameters()
w,b
# Define an optimizer
class BasicOptim:
def __init__(self,params,lr): self.params,self.lr = list(params),lr
def step(self, *args, **kwargs):
for p in self.params: p.data -= p.grad.data * self.lr
def zero_grad(self, *args, **kwargs):
for p in self.params: p.grad = None
= BasicOptim(linear_model.parameters(), lr)
opt # alternative, fastai provides SGD
= SGD(linear_model.parameters(), lr)
opt
# Define Metrics
def batch_accuracy(xb, yb):
= xb.sigmoid()
preds = (preds>0.5) == yb
correct return correct.float().mean()
# Helper to calculate metrics on validation set
def validate_epoch(model):
= [batch_accuracy(model(xb), yb) for xb,yb in valid_dl]
accs return round(torch.stack(accs).mean().item(), 4)
def train_epoch(model):
for xb,yb in dl:
calc_grad(xb, yb, model)
opt.step()
opt.zero_grad()
def train_model(model, epochs):
for i in range(epochs):
train_epoch(model)print(validate_epoch(model), end=' ')
20) train_model(linear_model,
Using fastai
We can substitute the above with learner.fit
from fastai We just have to supply the following:
- Dataloaders
- Model
- Optimization function
- Loss function
- Metrics
= DataLoaders(dl, valid_dl)
dls = Learner(dls, nn.Linear(28*28,1), opt_func=SGD,
learn =mnist_loss,
loss_func=batch_accuracy)
metrics
10, lr=lr) learn.fit(
What if you used the full power of fastai? It would look like this:
= ImageDataLoaders.from_folder(path)
dls # Lots of things have defaults like optimization func
= cnn_learner(dls, resnet18, pretrained=False,
learn =F.cross_entropy,
loss_func=accuracy)
metrics1, 0.1) learn.fit_one_cycle(
Simple Neural Nets
The next step is to introduce a non-linearity
= nn.Sequential(
simple_net 28*28, 30),
nn.Linear(
nn.ReLU(),30, 1)
nn.Linear(
)
# Construct the learner as before
= learner(dls, simple_net, opt_func=SGD,
learn =mnist_loss, metrics=batch_accuracy)
loss_func
40, 0.1) learner.fit(
Inspecting Training History
The training history is saved in learn.recorder
. You can plot your training progress with:
2) plt.plot(learn.recorder.values).itemgot(