WARNING : Put this file in the /courses/dl1 folder of your Fastai folder.
# Put these at the top of every notebook, to get automatic reloading and inline plotting
%reload_ext autoreload
%autoreload 2
%matplotlib inline
# This file contains all the main external libs we'll use
from fastai.imports import *
from fastai.transforms import *
from fastai.conv_learner import *
from fastai.model import *
from fastai.dataset import *
from fastai.sgdr import *
from fastai.plots import *
from fastai.conv_learner import *
The dataset is available at http://files.fast.ai/data/dogscats.zip. You can download it directly on your server by running the following line in your terminal. wget http://files.fast.ai/data/dogscats.zip. You should put the data in a subdirectory of this notebook's directory, called data/. Note that this data is already available in Crestle and the Paperspace fast.ai template.
# Put the path to your dogscats folder that corresponds to your installation
PATH = "data/dogscats/"
# Size of images when they are used by GPU
sz=224
(from Welton Rodrigo Torres Nascimento) https://github.com/weltonrodrigo/mag-fofa/blob/master/scripts/create_sets.sh
torch.cuda.is_available(), torch.backends.cudnn.enabled
os.listdir(PATH)
os.listdir(f'{PATH}valid')
files = os.listdir(f'{PATH}valid/cats')[:5]
files
img = plt.imread(f'{PATH}valid/cats/{files[0]}')
plt.imshow(img);
Fastai notes : Enable data augmentation, and precompute=True
# Uncomment the below if you need to reset your precomputed activations
shutil.rmtree(f'{PATH}tmp', ignore_errors=True)
In the lesson1.ipynb, Jeremy Howard uses resnet34.
After completing this exercice with resnet34, test resnet50, resnet101 and resnet152. Check if the validation accuracy changes.
# our model
# If you want to know the list of the pretrained models under pytorch : http://pytorch.org/docs/master/torchvision/models.html
arch=resnet34
# arch=resnet50
# arch=resnet101
# arch=resnet152
# our data transformation
tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1)
# CODE HERE of the data object using PATH, the test set, tfms and bs=64 (even if it is the batch default value) (1 line)
# data = ...
# CODE HERE of the learn object using the pretrained model, the data object and precompute=True (1 line)
# learn = ...
# get all details about layers of your model
learn.summary()
# get the layers structure of your model
learn.get_layer_groups()
Fastai notes : Use lr_find() to find highest learning rate where loss is still clearly improving
# CODE HERE of the function to search for the best learning rate (1 line)
# CODE HERE of the function to plot the loss versus the learning rate (1 line)
Fastai notes : Train last layer from precomputed activations for 1-2 epochs
# CODE HERE for training the learn model with learning rate 1e-2 and 1 epoch (1 line)
# learn.fit(...)
Fastai notes : Train last layer with data augmentation (i.e. precompute=False) for 2-3 epochs with cycle_len=1
# CODE HERE for setting the learn model to precompute=False (1 line)
# CODE HERE for training the learn model with learning rate 1e-2, 3 cycles and one learning rate restart by epoch (1 line)
# learn.fit(...)
# CODE HERE for saving your model (the values of the parameters/weights) to 224_lastlayer (1 line)
Fastai notes : Unfreeze all layers
# CODE HERE for loading your model (the values of the parameters/weights) from 224_lastlayer (1 line)
# CODE HERE for unfreezing the first layers of the model (1 line)
Fastai notes : Set earlier layers to 3x-10x lower learning rate than next higher layer
# CODE HERE of the function to search for the best learning rate (1 line)
# CODE HERE of the function to plot the loss versus the learning rate (1 line)
# CODE HERE for setting the lr variable to an array of 3 learning rates (1 line)
# lr = ...
# CODE HERE for training the learn model with the diferential learning rate, 3 cycles, one learning rate restart by epoch
# and double the length of a cycle after each cycle (1 line)
# learn.fit(...)
# CODE HERE for saving your model (the values of the parameters/weights) to 224_all (1 line)
# get the log of the prediction for the validation set using TTA()
# log_preds,y = ...
# For each image in the validation set, the TTA() provides with 5 predictions for each class : we need to get the mean of them
# To get the probabilities, do not forget to take the exp of the logs
# probs = ...
# Get the accuracy of the validation set
accuracy_np(probs, y)
# It is possible to use TTA on the test set because the test set has been passed to learn
log_preds,y = learn.TTA(is_test=True)
probs = np.mean(np.exp(log_preds), axis=0)
preds = np.argmax(probs, axis=1)
# Get the path to a specific image of the test set (for example : the number 16)
files = os.listdir(f'{PATH}/test/')
img = open_image(f'{PATH}/test/{files[15]}')
plt.imshow(img)
# Get the transformation to be applied on the image and then get the prediction
trn_tfms, val_tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1)
im = val_tfms(img)
pred = learn.predict_array(im[None])
result = np.argmax(pred, axis=1)
print(f'The class is : {data.classes[result[0]]}')
# Visualize the image after transformation by tfms_from_model()
print(im.shape)
im_show = np.swapaxes(im,0,2)
im_show = np.swapaxes(im_show,0,1)
print(im_show.shape)
plt.imshow(im_show)
# Display the scatter diagram of the predictions (probabilities) for the image
# nb_classes : the number of classes
matplotlib.pyplot.scatter(list(range(nb_classes), np.exp(pred))
preds = np.argmax(probs, axis=1)
probs = probs[:,1]
A common way to analyze the result of a classification model is to use a confusion matrix. Scikit-learn has a convenient function we can use for this purpose:
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y, preds)
We can just print out the confusion matrix, or we can show a graphical view (which is mainly useful for dependents with a larger number of categories).
# Increase the display size of the confusion matrix
matplotlib.rcParams['figure.figsize'] = [30,20]
plot_confusion_matrix(cm, data.classes)
fn = PATH+data.trn_ds.fnames[0]; fn
img = PIL.Image.open(fn); img
img.size
size_d = {k: PIL.Image.open(PATH+k).size for k in data.trn_ds.fnames}
row_sz, col_sz = zip(*size_d.values())
row_sz = np.array(row_sz)
col_sz = np.array(col_sz)
row_sz[:5]
plt.hist(row_sz);
plt.hist(row_sz[row_sz<1000])
len(col_sz)
plt.hist(col_sz);
plt.hist(col_sz[col_sz<1000])
len(data.trn_ds),len(data.test_ds)
len(data.classes),data.classes[:5]
As well as looking at the overall metrics, it's also a good idea to look at examples of each of:
# This is the label for a val data
data.val_y
# from here we know that 'cats' is label 0 and 'dogs' is label 1.
data.classes
# this gives prediction for validation set. Predictions are in log scale
log_preds = learn.predict()
log_preds.shape
log_preds[:10]
preds = np.argmax(log_preds, axis=1) # from log probabilities to 0 or 1
probs = np.exp(log_preds[:,1]) # pr(dog)
def rand_by_mask(mask): return np.random.choice(np.where(mask)[0], 4, replace=False)
def rand_by_correct(is_correct): return rand_by_mask((preds == data.val_y)==is_correct)
def plot_val_with_title(idxs, title):
imgs = np.stack([data.val_ds[x][0] for x in idxs])
title_probs = [probs[x] for x in idxs]
print(title)
return plots(data.val_ds.denorm(imgs), rows=1, titles=title_probs)
def plots(ims, figsize=(12,6), rows=1, titles=None):
f = plt.figure(figsize=figsize)
for i in range(len(ims)):
sp = f.add_subplot(rows, len(ims)//rows, i+1)
sp.axis('Off')
if titles is not None: sp.set_title(titles[i], fontsize=16)
plt.imshow(ims[i])
def load_img_id(ds, idx): return np.array(PIL.Image.open(PATH+ds.fnames[idx]))
def plot_val_with_title(idxs, title):
imgs = [load_img_id(data.val_ds,x) for x in idxs]
title_probs = [probs[x] for x in idxs]
print(title)
return plots(imgs, rows=1, titles=title_probs, figsize=(16,8))
# 1. A few correct labels at random
plot_val_with_title(rand_by_correct(True), "Correctly classified")
# 2. A few incorrect labels at random
plot_val_with_title(rand_by_correct(False), "Incorrectly classified")
def most_by_mask(mask, mult):
idxs = np.where(mask)[0]
return idxs[np.argsort(mult * probs[idxs])[:4]]
def most_by_correct(y, is_correct):
mult = -1 if (y==1)==is_correct else 1
return most_by_mask(((preds == data.val_y)==is_correct) & (data.val_y == y), mult)
plot_val_with_title(most_by_correct(0, True), "Most correct cats")
plot_val_with_title(most_by_correct(1, True), "Most correct dogs")
plot_val_with_title(most_by_correct(0, False), "Most incorrect cats")
plot_val_with_title(most_by_correct(1, False), "Most incorrect dogs")
most_uncertain = np.argsort(np.abs(probs -0.5))[:4]
plot_val_with_title(most_uncertain, "Most uncertain predictions")
def plot_loss_change(sched, sma=1, n_skip=20, y_lim=(-0.01,0.01)):
"""
Plots rate of change of the loss function.
Parameters:
sched - learning rate scheduler, an instance of LR_Finder class.
sma - number of batches for simple moving average to smooth out the curve.
n_skip - number of batches to skip on the left.
y_lim - limits for the y axis.
"""
derivatives = [0] * (sma + 1)
for i in range(1 + sma, len(learn.sched.lrs)):
derivative = (learn.sched.losses[i] - learn.sched.losses[i - sma]) / sma
derivatives.append(derivative)
plt.ylabel("d/loss")
plt.xlabel("learning rate (log scale)")
plt.plot(learn.sched.lrs[n_skip:], derivatives[n_skip:])
plt.xscale('log')
plt.ylim(y_lim)
# search for best learning rate
lrf=learn.lr_find()
#learn.sched.plot_lr()
learn.sched.plot()
plot_loss_change(learn.sched, sma=20)