Lesson 6 - FastAI
path = untar_data(URLs.PASCAL_2007)
Path.BASE_PATH = path
path.ls()
df = pd.read_csv(path/'train.csv')
df.head()
df.iloc[:,0] #column 0
df.iloc[0,:] #row 0
# Trailing :s are always optional (in numpy, pytorch, pandas, etc.),
# so this is equivalent:
df.iloc[0]
df['fname']
tmp_df = pd.DataFrame({'a':[1,2], 'b':[3,4]})
tmp_df
tmp_df['c'] = tmp_df['a']+tmp_df['b']
tmp_df
a = list(enumerate(string.ascii_lowercase))
a[0], len(a)
dl_a = DataLoader(a, batch_size=8, shuffle=True)
first(dl_a)
a = list((string.ascii_lowercase))
dss = Datasets(a)
dss[0]
dblock = DataBlock()
dsets = dblock.datasets(df) #This creates a train and valid set
len(dsets.train),len(dsets.valid)
x,y = dsets.train[0]
x,y
Notice that the x and y are identical! This means we need to create our own independent and dependent var
x['fname'] #This is our independent var
y['labels'] #This is our dependent var
dblock = DataBlock(get_x = lambda r: r['fname'],
get_y = lambda r: r['labels'])
dsets = dblock.datasets(df)
dsets.train[0]
#Awesome it works, just one more thing, we need the path
def get_x(r): return r['fname']
def get_y(r): return r['labels']
dblock = DataBlock(
get_x = get_x,
get_y = get_y)
dsets = dblock.datasets(df)
dsets.train[0]
This is good but we need the path and need to index the labels
def get_x(r): return path/'train'/r['fname'] #Need path
def get_y(r): return r['labels'].split(' ') #Split into each index
dblock = DataBlock(
get_x = get_x,
get_y = get_y)
dsets = dblock.datasets(df)
dsets.train[0]
#looks good
Looks good
def get_x(r): return path/'train'/r['fname'] #Need path
def get_y(r): return r['labels'].split(' ') #Split into each index
dblock = DataBlock(blocks=(ImageBlock, MultiCategoryBlock), #Adding blocks
get_x = get_x,
get_y = get_y)
dsets = dblock.datasets(df)
x,y = dsets.train[0]
x
y
dsets.vocab
dsets.train[0][1] # Also known as y (Stored above)
idxs = torch.where(dsets.train[0][1]==1)[0]
dsets.vocab[idxs]
def splitter(df):
train = df.index[~df['is_valid']].tolist() # ~ means NOT
valid = df.index[df['is_valid']].tolist()
return train,valid
dblock = DataBlock(blocks=(ImageBlock, MultiCategoryBlock),
splitter=splitter,
get_x=get_x,
get_y=get_y)
dsets = dblock.datasets(df)
dsets.train[0]
dblock = DataBlock(blocks=(ImageBlock, MultiCategoryBlock),
splitter=splitter,
get_x=get_x,
get_y=get_y,
item_tfms = RandomResizedCrop(128, min_scale=0.35)) #Must make all images same size
dls = dblock.dataloaders(df) #Switching to dataloaders
dls.show_batch(nrows=1, ncols=3)
learn = cnn_learner(dls, resnet18)
x,y = to_cpu(dls.train.one_batch())
activs = learn.model(x) #Can view activations on a single batch
activs.shape #64 imgs with 20 actv each
activs[0]
def binary_cross_entropy(inputs, targets):
inputs = inputs.sigmoid()
return -torch.where(targets==1, inputs, 1-inputs).log().mean()
loss_func = nn.BCEWithLogitsLoss() #Same thing as the func above
loss = loss_func(activs, y)
loss
One change compared to the last chapter is the metric we use: because this is a multilabel problem, we can't use the accuracy function. Why is that? Well, accuracy was comparing our outputs to our targets like so:
def accuracy(inp, targ, axis=-1):
"Compute accuracy with `targ` when `pred` is bs * n_classes"
pred = inp.argmax(dim=axis)
return (pred == targ).float().mean()
The class predicted was the one with the highest activation (this is what argmax
does). Here it doesn't work because we could have more than one prediction on a single image. After applying the sigmoid to our activations (to make them between 0 and 1), we need to decide which ones are 0s and which ones are 1s by picking a threshold. Each value above the threshold will be considered as a 1, and each value lower than the threshold will be considered a 0:
def accuracy_multi(inp, targ, thresh=0.5, sigmoid=True):
"Compute accuracy when `inp` and `targ` are the same size."
if sigmoid: inp = inp.sigmoid()
return ((inp>thresh)==targ.bool()).float().mean()
def say_hello(name, say_what="Hello"): return f"{say_what} {name}."
say_hello('Jeremy'),say_hello('Jeremy', 'Ahoy!')
f = partial(say_hello, say_what="Bonjour")
f("Jeremy"),f("Sylvain")
learn = cnn_learner(dls, resnet50, metrics=partial(accuracy_multi, thresh=0.2))
learn.fine_tune(3, base_lr=3e-3, freeze_epochs=4)
95% acc with a treshold of .2, lets try some more
learn.metrics = partial(accuracy_multi, thresh=0.1) #Try different thresh
learn.validate()
learn.metrics = partial(accuracy_multi, thresh=0.99)
learn.validate()
preds,targs = learn.get_preds()
accuracy_multi(preds, targs, thresh=0.9, sigmoid=False)
xs = torch.linspace(0.05,0.95,29)
accs = [accuracy_multi(preds, targs, thresh=i, sigmoid=False) for i in xs]
plt.plot(xs,accs);
path = untar_data(URLs.BIWI_HEAD_POSE)
path.ls().sorted()
(path/'01').ls().sorted()
img_files = get_image_files(path) #jpg's
im = PILImage.create(img_files[0])
im.shape
im.to_thumb(160)
def img2pose(x):
return Path(f'{str(x)[:-7]}pose.txt')
img2pose(img_files[0])
cal = np.genfromtxt(path/'01'/'rgb.cal', skip_footer=6)
#Given func to get center of face
def get_ctr(f):
ctr = np.genfromtxt(img2pose(f), skip_header=3)
c1 = ctr[0] * cal[0][0]/ctr[2] + cal[0][2]
c2 = ctr[1] * cal[1][1]/ctr[2] + cal[1][2]
return tensor([c1,c2])
get_ctr(img_files[0])
biwi = DataBlock(
blocks=(ImageBlock, PointBlock),
get_items=get_image_files,
get_y=get_ctr,
splitter=FuncSplitter(lambda o: o.parent.name=='13'), #Just grabbing person number 13 as our valid set
batch_tfms=[*aug_transforms(size=(240,320)),
Normalize.from_stats(*imagenet_stats)]
)
dls = biwi.dataloaders(path)
dls.show_batch(max_n=9, figsize=(8,6))
xb,yb = dls.one_batch()
xb.shape,yb.shape
yb[0]
learn = cnn_learner(dls, resnet18, y_range=(-1,1)) #range states what values are within the acceptable range
# -1 is far-left and bottom, 1 is far-right and top
def sigmoid_range(x, lo, hi): return torch.sigmoid(x) * (hi-lo) + lo
plot_function(partial(sigmoid_range,lo=-1,hi=1), min=-4, max=4)
dls.loss_func
learn.lr_find()
lr = 1e-2
learn.fine_tune(3, lr)
math.sqrt(0.0001) #error
learn.show_results(ds_idx=1, nrows=3, figsize=(6,8))
- How could multi-label classification improve the usability of the bear classifier?
It would be able to classify more bears in the image. Also, it would allow for the classification of no bears present. - How do we encode the dependent variable in a multi-label classification problem?
It is one-hot encoded: Here the vector is the same length as the vocab. 0 and 1 are used to repersent if a class if present. - How do you access the rows and columns of a DataFrame as if it was a matrix?
df.iloc[0][1] - row 0, col 1 - How do you get a column by name from a DataFrame?
df['colName'] - What is the difference between a
Dataset
andDataLoader
?
Dataset returns tuples of x, y
DataLoader is an extention of Dataset, here it return minibatches of x,y - What does a
Datasets
object normally contain?
Training and validation set - What does a
DataLoaders
object normally contain?
Training dataloader and validation dataloader - What does
lambda
do in Python?
Lambda is an anonymous function that can be created on the spot. Do note that they are not serializable, however. - What are the methods to customize how the independent and dependent variables are created with the data block API?
get_x
get_y - Why is softmax not an appropriate output activation function when using a one hot encoded target?
Softmax forces model to pick only one class - Why is
nll_loss
not an appropriate loss function when using a one-hot-encoded target?
Similer to Softmax, this works better when you want only one class - What is the difference between
nn.BCELoss
andnn.BCEWithLogitsLoss
?
BCELoss assumes you did sigmoid prior
BCEWithLogitsLoss does sigmoid - Why can't we use regular accuracy in a multi-label problem?
Regular accuracy assumes that only 1 class is correct. However, in multi-label problems there can be multiple labels, so a threshold is assigned. - When is it okay to tune a hyperparameter on the validation set?
When the hyper-parameter and the metric being observed is smooth - How is
y_range
implemented in fastai? (See if you can implement it yourself and test it without peeking!)def sigmoid_range(x,lo, hi): return x.sigmoid() * (hi-lo) + lo
- What is a regression problem? What loss function should you use for such a problem?
Dependent values are continuous. The loss functions used often is mean squared error loss. - What do you need to do to make sure the fastai library applies the same data augmentation to your inputs images and your target point coordinates?
You must used the correct DataBlock, PointBlock.
- Read a tutorial about Pandas DataFrames and experiment with a few methods that look interesting to you. See the book's website for recommended tutorials.
- Retrain the bear classifier using multi-label classification. See if you can make it work effectively with images that don't contain any bears, including showing that information in the web application. Try an image with two different kinds of bears. Check whether the accuracy on the single-label dataset is impacted using multi-label classification.
Completed, see here [https://usama280.github.io/PasteBlogs/]