Lesson 1 Dogbreeds CodeAlong



In [1]:
%reload_ext autoreload
%autoreload 2
%matplotlib inline

In [2]:
from fastai.imports import *
from fastai.torch_imports import *
from fastai.transforms import *
from fastai.model import *
from fastai.dataset import *
from fastai.sgdr import *
from fastai.plots import *
from fastai.conv_learner import *

In [3]:
PATH = "data/dogbreeds/"
sz = 224
arch = resnext101_64
bs = 64

In [4]:
label_csv = f'{PATH}labels.csv'
n = len(list(open(label_csv)))-1
val_idxs = get_cv_idxs(n)

In [17]:
val_idxs, n, len(val_idxs)


Out[17]:
(array([3694, 1573, 6281, ..., 5734, 5191, 5390]), 10222, 2044)

2. Initial Exploration


In [5]:
!ls {PATH}


labels.csv	models	 sample_submission.csv.zip  test      tmp    train.zip
labels.csv.zip	results  subm			    test.zip  train  valid

In [7]:
label_df = pd.read_csv(label_csv)
label_df.head()


Out[7]:
id breed
0 000bec180eb18c7604dcecc8fe0dba07 boston_bull
1 001513dfcb2ffafc82cccf4d8bbaba97 dingo
2 001cdf01b096e06d78e9e5112d419397 pekinese
3 00214f311d5d2247d5dfe4fe24b2303d bluetick
4 0021f9ceb3235effd7fcde7f7538ed62 golden_retriever

In [10]:
# use Pandas to create pivot table which shows how many of each label:
label_df.pivot_table(index='breed', aggfunc=len).sort_values('id', ascending=False)


Out[10]:
id
breed
scottish_deerhound 126
maltese_dog 117
afghan_hound 116
entlebucher 115
bernese_mountain_dog 114
shih-tzu 112
great_pyrenees 111
pomeranian 111
basenji 110
samoyed 109
airedale 107
tibetan_terrier 107
leonberg 106
cairn 106
beagle 105
japanese_spaniel 105
australian_terrier 102
blenheim_spaniel 102
miniature_pinscher 102
irish_wolfhound 101
lakeland_terrier 99
saluki 99
papillon 96
whippet 95
siberian_husky 95
norwegian_elkhound 95
pug 94
chow 93
italian_greyhound 92
pembroke 92
... ...
german_short-haired_pointer 75
boxer 75
bull_mastiff 75
borzoi 75
pekinese 75
cocker_spaniel 74
american_staffordshire_terrier 74
doberman 74
brittany_spaniel 73
malinois 73
standard_schnauzer 72
flat-coated_retriever 72
redbone 72
border_collie 72
curly-coated_retriever 72
kuvasz 71
chihuahua 71
soft-coated_wheaten_terrier 71
french_bulldog 70
vizsla 70
tibetan_mastiff 69
german_shepherd 69
giant_schnauzer 69
walker_hound 69
otterhound 69
golden_retriever 67
brabancon_griffon 67
komondor 67
briard 66
eskimo_dog 66

120 rows × 1 columns


In [5]:
tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1)
data = ImageClassifierData.from_csv(PATH, folder='train', csv_fname=f'{PATH}labels.csv', 
                                    test_name='test', val_idxs=val_idxs, suffix='.jpg',
                                    tfms=tfms, bs=bs)

In [26]:
fn = PATH + data.trn_ds.fnames[0]; fn


Out[26]:
'data/dogbreeds/train/000bec180eb18c7604dcecc8fe0dba07.jpg'

In [27]:
img = PIL.Image.open(fn); img


Out[27]:

In [28]:
img.size


Out[28]:
(500, 375)

In [30]:
size_d = {k: PIL.Image.open(PATH + k).size for k in data.trn_ds.fnames}

In [31]:
row_sz, col_sz = list(zip(*size_d.values()))

In [36]:
row_sz = np.array(row_sz); col_sz = np.array(col_sz)

In [37]:
row_sz[:5]


Out[37]:
array([500, 500, 400, 500, 231])

In [41]:
plt.hist(row_sz);



In [42]:
plt.hist(row_sz[row_sz < 1000])


Out[42]:
(array([  148.,   600.,  1307.,  1205.,  4581.,   122.,    78.,    62.,    15.,     7.]),
 array([  97. ,  186.3,  275.6,  364.9,  454.2,  543.5,  632.8,  722.1,  811.4,  900.7,  990. ]),
 <a list of 10 Patch objects>)

In [45]:
plt.hist(col_sz);



In [46]:
plt.hist(col_sz[col_sz < 1000])


Out[46]:
(array([  243.,   721.,  2218.,  2940.,  1837.,    95.,    29.,    29.,     8.,     8.]),
 array([ 102. ,  190.2,  278.4,  366.6,  454.8,  543. ,  631.2,  719.4,  807.6,  895.8,  984. ]),
 <a list of 10 Patch objects>)

In [50]:
len(data.trn_ds), len(data.test_ds)


Out[50]:
(8178, 10357)

In [51]:
len(data.classes), data.classes[:5]


Out[51]:
(120,
 ['affenpinscher',
  'afghan_hound',
  'african_hunting_dog',
  'airedale',
  'american_staffordshire_terrier'])

3. Initial Model

starting w/ small images, large batch sizes to train model v.fast in beginning; increase image size and decrease batch-size as go along.


In [5]:
def get_data(sz, bs):
    tfms = tfms_from_model(arch, sz, aug_tfms=transforms_side_on, max_zoom=1.1)
    data = ImageClassifierData.from_csv(PATH, 'train', f'{PATH}labels.csv', test_name='test', 
                                        num_workers=4, val_idxs=val_idxs, suffix='.jpg', 
                                        tfms=tfms, bs=bs)
    return data if sz > 300 else data.resize(340, 'tmp')

3.1 Precompute


In [7]:
data = get_data(sz, bs)




In [57]:
learn = ConvLearner.pretrained(arch, data, precompute=True) # GTX870M;bs=64;sz=224;MEM:2431/3017


100%|██████████| 128/128 [08:52<00:00,  4.16s/it]
100%|██████████| 32/32 [02:13<00:00,  4.17s/it]
100%|██████████| 162/162 [11:14<00:00,  4.16s/it]

In [58]:
learn.fit(1e-2, 5)


[ 0.       0.99659  0.38915  0.91051]                        
[ 1.       0.45763  0.30366  0.92331]                         
[ 2.       0.30911  0.27408  0.92523]                         
[ 3.       0.2478   0.25869  0.92617]                         
[ 4.       0.19318  0.24091  0.92812]                         

3.2 Augment


In [6]:
from sklearn import metrics

In [ ]:
# data = get_data(sz, bs)

In [8]:
learn = ConvLearner.pretrained(arch, data, precompute=True, ps=0.5)

In [62]:
learn.fit(1e-2, 2)


[ 0.       1.32176  0.48284  0.90566]                        
[ 1.       0.60258  0.33452  0.91497]                         


In [ ]:
lrf = learn.find_lr()
learn.sched.plot()

In [9]:
# turn precompute off then use dataug
learn.precompute = False

In [64]:
learn.fit(1e-2, 5, cycle_len=1)


[ 0.       0.49921  0.30245  0.92279]                        
[ 1.       0.43628  0.27797  0.92425]                        
[ 2.       0.42434  0.26453  0.9262 ]                        
[ 3.       0.38247  0.25876  0.92718]                        
[ 4.       0.34982  0.25522  0.92816]                        


In [65]:
learn.save('224_pre')

In [10]:
learn.load('224_pre')

3.3 Increase Size

If you train smth on a smaller size, you can call learn.set_data() and pass in a larger sized dataset. That'll take your model, however it's trained so far, and continue to train on larger images.

This is another way to get SotA results. Starting training on small images for a few epochs, then switching to larger images and continuing training is an amazing effective way to avoid overfitting.

J.Howard (paraphrased)

NOTE: Fully-Convolutional Architectures only.


In [14]:
learn.set_data(get_data(299, bs=32))
learn.freeze() # just making all but last layer already frozen




In [15]:
learn.fit(1e-2, 3, cycle_len=1) # precompute is off so DataAugmentation is back on


[ 0.       0.3695   0.24151  0.92571]                        
[ 1.       0.34674  0.22724  0.93157]                        
[ 2.       0.30918  0.22338  0.92857]                        


In [16]:
learn.fit(1e-2, 3, cycle_len=1, cycle_mult=2)


[ 0.       0.2992   0.22882  0.92662]                        
[ 1.       0.2987   0.22414  0.92906]                        
[ 2.       0.25606  0.22171  0.93101]                        
[ 3.       0.25937  0.22618  0.92759]                        
[ 4.       0.25302  0.22781  0.92997]                        
[ 5.       0.20119  0.22306  0.93004]                        
[ 6.       0.19636  0.2187   0.93192]                        


In [17]:
log_preds, y = learn.TTA()
probs = np.exp(log_preds)
accuracy(log_preds, y), metrics.log_loss(y, probs)


                                              
Out[17]:
(0.9368884540117417, 0.21353581871378871)

In [18]:
learn.save('299_pre')

In [ ]:
# learn.load('299_pre')

In [19]:
learn.fit(1e-2, 1, cycle_len=2)


[ 0.       0.2224   0.22244  0.93199]                        
[ 1.       0.18582  0.21979  0.93011]                        


In [20]:
learn.save('299_pre')

In [21]:
log_preds, y = learn.TTA()
probs = np.exp(log_preds)
accuracy(log_preds, y), metrics.log_loss(y, probs)


                                              
Out[21]:
(0.93297455968688847, 0.21648379113713476)

In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:
SUBM = f'{PATH}subm/'
os.makedirs(SUBM, exist_ok=True)
df.to_csv(f'{SUBM}subm.gz', compression='gzip', index=False)

In [ ]:
FileLink(f'{SUBM}subm.gz')

6. Individual Prediction


In [6]:
fn = data.val_ds.fnames[0]

In [7]:
fn


Out[7]:
'train/001513dfcb2ffafc82cccf4d8bbaba97.jpg'

In [8]:
Image.open(PATH+fn).resize((150,150))


Out[8]:

In [9]:
trn_tfms, val_tfms = tfms_from_model(arch, sz)

In [12]:
learn = ConvLearner.pretrained(arch, data)
learn.load('299_pre')

In [ ]:
# ds = FilesIndexArrayDataset([fn], np.array([0]), val_tfms, PATH)
# dl = DataLoader(ds)
# preds = learn.predict_dl(dl)
# np.argmax(preds)

In [ ]:
im = trn_tfms(Image.open(PATH+fn))
preds = to_np(learn.model(V(T(im[None]).cuda())))
np.argmax(preds)

In [ ]:
trn_tfms, val_tfms = tfms_from_model(arch, sz)

In [ ]:
im = val_tfms(Image.open(PATH+fn)) # or could apply trn_tfms(.)
preds = learn.predict_array(im[None]) # index into image as[None] to create minibatch of 1 img
np.argmax(preds)