StateFarm Distracted Driver Detection


In [1]:
%cd /home/ubuntu/kaggle/state-farm-distracted-driver-detection
# Make sure you are in the main directory (state-farm-distracted-driver-detection)
%pwd


/home/ubuntu/kaggle/state-farm-distracted-driver-detection
Out[1]:
u'/home/ubuntu/kaggle/state-farm-distracted-driver-detection'

In [2]:
# Create references to key directories
import os, sys
from glob import glob
from matplotlib import pyplot as plt
import numpy as np
import keras
np.set_printoptions(precision=4, linewidth=100)
current_dir = os.getcwd()
CHALLENGE_HOME_DIR = current_dir
DATA_HOME_DIR = current_dir+'/data'


Using Theano backend.
Using gpu device 0: Tesla K80 (CNMeM is disabled, cuDNN 5103)
/home/ubuntu/anaconda2/lib/python2.7/site-packages/theano/sandbox/cuda/__init__.py:600: UserWarning: Your cuDNN version is more recent than the one Theano officially supports. If you see any problems, try updating Theano or downgrading cuDNN to version 5.
  warnings.warn(warn)

In [3]:
#Allow relative imports to directories
sys.path.insert(1, os.path.join(sys.path[0], '..'))

#import modules
from utils import *
from utils.vgg16 import Vgg16

import utils; reload(utils)
from utils import *
from utils.utils import *

#Instantiate plotting tool
%matplotlib inline

In [4]:
#Need to correctly import utils.py
import bcolz 
from numpy.random import random, permutation

Create Validation and Sample Sets


In [5]:
#Create directories
%cd $DATA_HOME_DIR
%mkdir valid
%mkdir results
%mkdir -p sample/train
%mkdir -p sample/test
%mkdir -p sample/valid
%mkdir -p sample/results
%mkdir -p test/unknown


[Errno 2] No such file or directory: '/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data'
/home/ubuntu/kaggle/state-farm-distracted-driver-detection

In [6]:
# Create folders for valid/
%cd $DATA_HOME_DIR/valid/
!for i in `seq 0 9`; do mkdir "c"$i; done

# Create folders for sample/train/
%cd $DATA_HOME_DIR/sample/train
!for i in `seq 0 9`; do mkdir "c"$i; done

#Create folders for sample/valid
%cd $DATA_HOME_DIR/sample/valid
!for i in `seq 0 9`; do mkdir "c"$i; done


/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data/valid
/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data/sample/train
/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data/sample/valid

In [7]:
# Move images to validation sets
%cd $DATA_HOME_DIR


/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data

In [8]:
for i in range(10):
    directory = DATA_HOME_DIR + '/train/c'+ str(i)
    l = os.listdir(directory)
    shuf = np.random.permutation(l)
    for j in range(1000): os.rename(directory+'/'+shuf[j], DATA_HOME_DIR+'/valid/c'+str(i) + '/' + shuf[j])

In [9]:
from shutil import copyfile

In [10]:
# Copy images from train to sample/train and sample/valid sets
%cd $DATA_HOME_DIR


/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data

In [11]:
for i in range(10):
    directory = DATA_HOME_DIR + '/train/c'+ str(i)
    l = os.listdir(directory)
    shuf = np.random.permutation(l)
    for j in range(100): copyfile(directory+'/'+shuf[j], DATA_HOME_DIR+'/sample/train/c'+str(i) + '/' + shuf[j])

In [12]:
# Copy images from valid to sample/train and sample/valid sets
for i in range(10):
    directory = DATA_HOME_DIR + '/valid/c'+ str(i)
    l = os.listdir(directory)
    shuf = np.random.permutation(l)
    for j in range(100): copyfile(directory+'/'+shuf[j], DATA_HOME_DIR+'/sample/valid/c'+str(i) + '/' + shuf[j])

In [13]:
# Move a sample of test to sample/test

directory = DATA_HOME_DIR + '/test/'
l = os.listdir(directory)
shuf = np.random.permutation(l)
for j in range(1000): copyfile(directory+'/'+shuf[j], DATA_HOME_DIR+'/sample/test/'+ shuf[j])

Create a Unkown class for the test set


In [14]:
%cd $DATA_HOME_DIR/test
%mv *.jpg unknown/


/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data/test

Finetuning and Training


In [17]:
%cd $DATA_HOME_DIR

#Set path to sample/ path if desired
path = DATA_HOME_DIR + '/sample/'
test_path = path + 'test/' #We use all the test data
results_path= path + 'results/'
train_path=path + 'train/'
valid_path=path + 'valid/'


/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data

In [6]:
#import Vgg16 helper class
vgg = Vgg16()

In [6]:
#Set constants. You can experiment with no_of_epochs to improve the model
batch_size=64
no_of_epochs=3

In [8]:
#Finetune the model
batches = vgg.get_batches(train_path, batch_size=batch_size)
val_batches = vgg.get_batches(valid_path, batch_size=batch_size*2)
vgg.finetune(batches)

vgg.model.optimizer.lr = 0.01


Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 10 classes.

In [9]:
#Pass in the validation dataset to the fit() method
#For each epoch we test our model against the validation set
latest_weights_filename = None
for epoch in range(no_of_epochs):
    print "Running epoch: %d" % epoch
    vgg.fit(batches, val_batches, nb_epoch=1)
    latest_weights_filename = 'ft%d.h5' % epoch
    vgg.model.save_weights(results_path+latest_weights_filename)
print "Completed %s fit operations" % no_of_epochs


Running epoch: 0
Epoch 1/1
1000/1000 [==============================] - 51s - loss: 11.7459 - acc: 0.1430 - val_loss: 12.3785 - val_acc: 0.1670
Running epoch: 1
Epoch 1/1
1000/1000 [==============================] - 51s - loss: 11.9024 - acc: 0.2090 - val_loss: 11.6195 - val_acc: 0.2580
Running epoch: 2
Epoch 1/1
1000/1000 [==============================] - 51s - loss: 11.6401 - acc: 0.2440 - val_loss: 11.7093 - val_acc: 0.2370
Completed 3 fit operations

Generate Predictions

Make predictions on the test data!


In [13]:
batches, preds = vgg.test(test_path, batch_size = batch_size*2)


Found 1000 images belonging to 1 classes.

In [74]:
test_path


Out[74]:
'/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data/sample/test/'

In [14]:
#For every image, vgg.test() generates 10 probabilities 
#based on how we've ordered the train directories.
print preds[:5]

filenames = batches.filenames
print filenames[:5]


[[  0.0000e+00   0.0000e+00   9.6380e-37   1.3375e-10   1.0031e-04   0.0000e+00   9.9874e-01
    0.0000e+00   1.1566e-03   4.4982e-43]
 [  0.0000e+00   0.0000e+00   3.0388e-36   1.7914e-11   2.4741e-14   0.0000e+00   1.0000e+00
    0.0000e+00   2.1220e-08   0.0000e+00]
 [  0.0000e+00   0.0000e+00   8.5398e-30   8.7354e-13   1.6559e-01   0.0000e+00   2.8549e-03
    0.0000e+00   8.3155e-01   6.5758e-39]
 [  0.0000e+00   0.0000e+00   1.3778e-40   1.0000e+00   3.1087e-09   0.0000e+00   9.8496e-12
    0.0000e+00   1.0578e-20   1.4541e-41]
 [  0.0000e+00   0.0000e+00   0.0000e+00   6.0994e-16   7.5110e-15   0.0000e+00   5.7553e-13
    0.0000e+00   1.0000e+00   9.8091e-45]]
['unknown/img_50115.jpg', 'unknown/img_28418.jpg', 'unknown/img_1970.jpg', 'unknown/img_61626.jpg', 'unknown/img_73912.jpg']

In [28]:
#Verify the column ordering by viewing some images
from PIL import Image
Image.open(test_path + filenames[2])


Out[28]:

In [ ]:
#Save our test results arrays so we can use them again later
save_array(results_path + 'test_preds.dat', preds)
save_array(results_path + 'filenames.dat', filenames)

Inspect Predictions on Validation Set


In [12]:
vgg.model.load_weights(results_path+'ft19.h5')

In [34]:
# Temporary
from matplotlib import pyplot as plt

#Need to correctly import utils.py
def save_array(fname, arr):
    c = bcolz.carray(arr, rootdir=fname, mode='w')
    c.flush()
    
def load_array(fname):
    return bcolz.open(fname)[:]
    
def plots(ims, figsize=(12,6), rows=1, interp=False, titles=None):
    if type(ims[0]) is np.ndarray:
        ims = np.array(ims).astype(np.uint8)
        if (ims.shape[-1] != 3):
            ims = ims.transpose((0,2,3,1))
    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], interpolation=None if interp else 'none')

In [16]:
valid_path


Out[16]:
'/home/ubuntu/kaggle/state-farm-distracted-driver-detection/data/sample/valid/'

In [17]:
val_batches, probs = vgg.test(valid_path, batch_size = batch_size)


Found 1000 images belonging to 10 classes.

In [48]:
estimated_c0 = probs[:,0]

In [35]:
from keras.preprocessing import image

#Helper function to plot images by index in the validation set 
#Plots is a helper function in utils.py
def plots_idx(idx, titles=None):
    plots([image.load_img(valid_path + filenames[i]) for i in idx], titles=titles)
    
#Number of images to view for each visualization task
n_view = 4

In [38]:
expected_labels = val_batches.classes
filenames = val_batches.filenames

In [37]:
expected_labels


Out[37]:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
       4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
       5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
       7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
       8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
       9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
       9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
       9, 9, 9, 9, 9, 9, 9, 9], dtype=int32)

In [47]:
# C0: Safe Driving
# c1: texting - right
# c2: talking on the phone - right
# c3: texting - left
# c4: talking on the phone - left
# c5: operating the radio
# c6: drinking
# c7: reaching behind
# c8: hair and makeup
# c9: talking to passenger
for i in range(10):
    category = np.where(expected_labels == i)[0]
    print "Found {} c{} labels".format(len(category_c0), i)
    estimated = probs[:,i]
    idx = permutation(category)[:n_view]
    plots_idx(idx, estimated[idx])


Found 100 c0 labels
Found 100 c1 labels
Found 100 c2 labels
Found 100 c3 labels
Found 100 c4 labels
Found 100 c5 labels
Found 100 c6 labels
Found 100 c7 labels
Found 100 c8 labels
Found 100 c9 labels

In [43]:
#1. C1: Texting -right
category_c1 = np.where(expected_labels == 1)[0]
print "Found %d c1 labels" % len(category_c1)
idx = permutation(category_c1)[:n_view]
plots_idx(idx, estimated_c1[idx])


Found 100 c1 labels

In [44]:
#1. C2: Talking on the phone -right
category_c2 = np.where(expected_labels == 2)[0]
print "Found %d c2 labels" % len(category_c2)
idx = permutation(category_c2)[:n_view]
plots_idx(idx, estimated_c2[idx])


Found 100 c2 labels

Modifying the model

Retraining the last few dense layers


In [10]:
vgg.model.summary()


____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
lambda_1 (Lambda)                (None, 3, 224, 224)   0           lambda_input_1[0][0]             
____________________________________________________________________________________________________
zeropadding2d_1 (ZeroPadding2D)  (None, 3, 226, 226)   0           lambda_1[0][0]                   
____________________________________________________________________________________________________
convolution2d_1 (Convolution2D)  (None, 64, 224, 224)  0           zeropadding2d_1[0][0]            
____________________________________________________________________________________________________
zeropadding2d_2 (ZeroPadding2D)  (None, 64, 226, 226)  0           convolution2d_1[0][0]            
____________________________________________________________________________________________________
convolution2d_2 (Convolution2D)  (None, 64, 224, 224)  0           zeropadding2d_2[0][0]            
____________________________________________________________________________________________________
maxpooling2d_1 (MaxPooling2D)    (None, 64, 112, 112)  0           convolution2d_2[0][0]            
____________________________________________________________________________________________________
zeropadding2d_3 (ZeroPadding2D)  (None, 64, 114, 114)  0           maxpooling2d_1[0][0]             
____________________________________________________________________________________________________
convolution2d_3 (Convolution2D)  (None, 128, 112, 112) 0           zeropadding2d_3[0][0]            
____________________________________________________________________________________________________
zeropadding2d_4 (ZeroPadding2D)  (None, 128, 114, 114) 0           convolution2d_3[0][0]            
____________________________________________________________________________________________________
convolution2d_4 (Convolution2D)  (None, 128, 112, 112) 0           zeropadding2d_4[0][0]            
____________________________________________________________________________________________________
maxpooling2d_2 (MaxPooling2D)    (None, 128, 56, 56)   0           convolution2d_4[0][0]            
____________________________________________________________________________________________________
zeropadding2d_5 (ZeroPadding2D)  (None, 128, 58, 58)   0           maxpooling2d_2[0][0]             
____________________________________________________________________________________________________
convolution2d_5 (Convolution2D)  (None, 256, 56, 56)   0           zeropadding2d_5[0][0]            
____________________________________________________________________________________________________
zeropadding2d_6 (ZeroPadding2D)  (None, 256, 58, 58)   0           convolution2d_5[0][0]            
____________________________________________________________________________________________________
convolution2d_6 (Convolution2D)  (None, 256, 56, 56)   0           zeropadding2d_6[0][0]            
____________________________________________________________________________________________________
zeropadding2d_7 (ZeroPadding2D)  (None, 256, 58, 58)   0           convolution2d_6[0][0]            
____________________________________________________________________________________________________
convolution2d_7 (Convolution2D)  (None, 256, 56, 56)   0           zeropadding2d_7[0][0]            
____________________________________________________________________________________________________
maxpooling2d_3 (MaxPooling2D)    (None, 256, 28, 28)   0           convolution2d_7[0][0]            
____________________________________________________________________________________________________
zeropadding2d_8 (ZeroPadding2D)  (None, 256, 30, 30)   0           maxpooling2d_3[0][0]             
____________________________________________________________________________________________________
convolution2d_8 (Convolution2D)  (None, 512, 28, 28)   0           zeropadding2d_8[0][0]            
____________________________________________________________________________________________________
zeropadding2d_9 (ZeroPadding2D)  (None, 512, 30, 30)   0           convolution2d_8[0][0]            
____________________________________________________________________________________________________
convolution2d_9 (Convolution2D)  (None, 512, 28, 28)   0           zeropadding2d_9[0][0]            
____________________________________________________________________________________________________
zeropadding2d_10 (ZeroPadding2D) (None, 512, 30, 30)   0           convolution2d_9[0][0]            
____________________________________________________________________________________________________
convolution2d_10 (Convolution2D) (None, 512, 28, 28)   0           zeropadding2d_10[0][0]           
____________________________________________________________________________________________________
maxpooling2d_4 (MaxPooling2D)    (None, 512, 14, 14)   0           convolution2d_10[0][0]           
____________________________________________________________________________________________________
zeropadding2d_11 (ZeroPadding2D) (None, 512, 16, 16)   0           maxpooling2d_4[0][0]             
____________________________________________________________________________________________________
convolution2d_11 (Convolution2D) (None, 512, 14, 14)   0           zeropadding2d_11[0][0]           
____________________________________________________________________________________________________
zeropadding2d_12 (ZeroPadding2D) (None, 512, 16, 16)   0           convolution2d_11[0][0]           
____________________________________________________________________________________________________
convolution2d_12 (Convolution2D) (None, 512, 14, 14)   0           zeropadding2d_12[0][0]           
____________________________________________________________________________________________________
zeropadding2d_13 (ZeroPadding2D) (None, 512, 16, 16)   0           convolution2d_12[0][0]           
____________________________________________________________________________________________________
convolution2d_13 (Convolution2D) (None, 512, 14, 14)   0           zeropadding2d_13[0][0]           
____________________________________________________________________________________________________
maxpooling2d_5 (MaxPooling2D)    (None, 512, 7, 7)     0           convolution2d_13[0][0]           
____________________________________________________________________________________________________
flatten_1 (Flatten)              (None, 25088)         0           maxpooling2d_5[0][0]             
____________________________________________________________________________________________________
dense_1 (Dense)                  (None, 4096)          0           flatten_1[0][0]                  
____________________________________________________________________________________________________
dropout_1 (Dropout)              (None, 4096)          0           dense_1[0][0]                    
____________________________________________________________________________________________________
dense_2 (Dense)                  (None, 4096)          0           dropout_1[0][0]                  
____________________________________________________________________________________________________
dropout_2 (Dropout)              (None, 4096)          0           dense_2[0][0]                    
____________________________________________________________________________________________________
dense_4 (Dense)                  (None, 10)            40970       dropout_2[0][0]                  
====================================================================================================
Total params: 40970
____________________________________________________________________________________________________

In [11]:
dense_idx = [idx for idx, layer in enumerate(vgg.model.layers) if type(layer) is keras.layers.core.Dense][0]

In [12]:
print 'First dense layer index: ', dense_idx
print 'Number of Layers: ', len(vgg.model.layers)
print vgg.model.layers[dense_idx:]


First dense layer index:  33
Number of Layers:  38
[<keras.layers.core.Dense object at 0x7f5e3443d410>, <keras.layers.core.Dropout object at 0x7f5e3443dc90>, <keras.layers.core.Dense object at 0x7f5e3439bb10>, <keras.layers.core.Dropout object at 0x7f5e343a3d90>, <keras.layers.core.Dense object at 0x7f5e34dad090>]

In [13]:
for layer in vgg.model.layers[dense_idx:]: layer.trainable = True

In [14]:
# Retrain all the dense layers
latest_weights_filename = None
for epoch in range(no_of_epochs):
    print "Running epoch: %d" % epoch
    vgg.fit(batches, val_batches, nb_epoch=1)
    latest_weights_filename = 'ft_trainable_dense_%d.h5' % epoch
    vgg.model.save_weights(results_path+latest_weights_filename)
print "Completed %s fit operations" % no_of_epochs


Running epoch: 0
Epoch 1/1
1000/1000 [==============================] - 51s - loss: 11.7044 - acc: 0.2370 - val_loss: 11.7977 - val_acc: 0.2320
Running epoch: 1
Epoch 1/1
1000/1000 [==============================] - 51s - loss: 11.5474 - acc: 0.2520 - val_loss: 11.3543 - val_acc: 0.2740
Running epoch: 2
Epoch 1/1
1000/1000 [==============================] - 51s - loss: 11.6460 - acc: 0.2480 - val_loss: 11.5530 - val_acc: 0.2550
Completed 3 fit operations

Removing Dropout


In [11]:
%cd /home/ubuntu/kaggle/state-farm-distracted-driver-detection


/home/ubuntu/kaggle/state-farm-distracted-driver-detection

In [22]:
# Create a new instance of VGG16 and finetune the model by popping the last layer 
# and adding a dense layer with 10 outputs
model = vgg_ft(10)

In [23]:
# Load Weights
model.load_weights(results_path+ 'ft_trainable_dense_2.h5')

In [24]:
# Split model to Convolution and Dense
conv_idx = [idx for idx, layer in enumerate(model.layers) if type(layer) is keras.layers.convolutional.Convolution2D][-1]

In [25]:
conv_idx


Out[25]:
30

In [26]:
model.layers[conv_idx]


Out[26]:
<keras.layers.convolutional.Convolution2D at 0x7f5e177d2890>

In [27]:
conv_layers = model.layers[:conv_idx+1]
conv_model = Sequential(conv_layers)

fc_layers = model.layers[conv_idx+1:]

In [46]:
batches = get_batches(train_path, shuffle=False, batch_size=batch_size)
val_batches = get_batches(valid_path, shuffle=False, batch_size=batch_size)

val_classes = val_batches.classes
trn_classes = batches.classes
val_labels = onehot(val_classes)
trn_labels = onehot(trn_classes)


Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 10 classes.

In [47]:
# Precalculate the output of the conv layers, so that we dont need to 
# redudently re-calculate them on every epoch

val_features = conv_model.predict_generator(val_batches, val_batches.nb_sample)
trn_features = conv_model.predict_generator(batches, batches.nb_sample)

In [117]:
save_array(results_path+'valid_convlayer_features.bc', val_features)
save_array(results_path+'train_convlayer_features.bc', trn_features)

In [118]:
val_features.shape


Out[118]:
(1000, 512, 14, 14)

In [74]:
a = [1, 2, 3, 4, 5, 6, 7, 8]
b = [9, 10, 11]
for i,j in zip(a,b): print i, j


1 9
2 10
3 11

In [73]:
print len(model.layers), len(fc_layers)


38 7

In [119]:
# Since we are removing dropouts, we need to half the weights
def half_wgts(layer): return [w/2 for w in layer.get_weights()]

In [120]:
conv_layers[-1].output_shape[1:]


Out[120]:
(512, 14, 14)

In [121]:
def get_fc_model():
    model = Sequential([
            MaxPooling2D(input_shape=conv_layers[-1].output_shape[1:]),
            Flatten(),
            Dense(4096, activation='relu'),
            Dropout(0.),
            Dense(4096, activation='relu'),
            Dropout(0.),
            Dense(10, activation='softmax')
        ])
    
    for l1, l2 in zip(model.layers, fc_layers): l1.set_weights(half_wgts(l2))
    
    model.compile(optimizer=Adam(lr=lr), loss='categorical_crossentropy', metrics=['accuracy'])
    return model

In [122]:
lr = 0.01
fc_model = get_fc_model()

In [93]:
# Fit the fully connected layers (Dense) with the pre-computed outputs of the convolutions layers
fc_model.fit(trn_features, trn_labels, nb_epoch=5, batch_size=batch_size, validation_data=(val_features, val_labels))


Train on 1000 samples, validate on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 20s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 2/5
1000/1000 [==============================] - 20s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 3/5
1000/1000 [==============================] - 20s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 4/5
1000/1000 [==============================] - 20s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 5/5
1000/1000 [==============================] - 20s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Out[93]:
<keras.callbacks.History at 0x7fafe2169b90>

In [94]:
fc_model.save_weights(results_path+'no_dropout.h5')

In [123]:
# Attach the fully connected model to the convolutional model by ensuring 
# that the Convol layers are not trainable
fc_model = get_fc_model()

for layer in conv_model.layers: layer.trainable = False
conv_model.add(fc_model)

In [124]:
# Compile, train and save
conv_model.compile(optimizer=Adam(lr=lr), loss='categorical_crossentropy', metrics=['accuracy'])

In [101]:
fc_model.summary()


____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
maxpooling2d_13 (MaxPooling2D)   (None, 512, 7, 7)     0           maxpooling2d_input_3[0][0]       
____________________________________________________________________________________________________
flatten_5 (Flatten)              (None, 25088)         0           maxpooling2d_13[0][0]            
____________________________________________________________________________________________________
dense_15 (Dense)                 (None, 4096)          102764544   flatten_5[0][0]                  
____________________________________________________________________________________________________
dropout_9 (Dropout)              (None, 4096)          0           dense_15[0][0]                   
____________________________________________________________________________________________________
dense_16 (Dense)                 (None, 4096)          16781312    dropout_9[0][0]                  
____________________________________________________________________________________________________
dropout_10 (Dropout)             (None, 4096)          0           dense_16[0][0]                   
____________________________________________________________________________________________________
dense_17 (Dense)                 (None, 10)            40970       dropout_10[0][0]                 
====================================================================================================
Total params: 119586826
____________________________________________________________________________________________________

In [98]:
#Train
# Use fit_generator() since we want to pull random images from the directories on every batch
conv_model.fit_generator(batches, samples_per_epoch=batches.nb_sample, nb_epoch=3, 
                         validation_data=val_batches, nb_val_samples=val_batches.nb_sample)


Epoch 1/3
1000/1000 [==============================] - 546s - loss: 15.1416 - acc: 0.0480 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 2/3
1000/1000 [==============================] - 545s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 3/3
1000/1000 [==============================] - 545s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Out[98]:
<keras.callbacks.History at 0x7fafddd70610>

In [102]:
conv_model.save_weights(results_path+ 'full_model_dropout.h5')

Data Augmentation


In [104]:
# dim_ordering='tf' uses tensorflow dimension ordering,
#   which is the same order as matplotlib uses for display.
# Therefore when just using for display purposes, this is more convenient
gen = image.ImageDataGenerator(rotation_range=5, width_shift_range=0.1, 
       height_shift_range=0.1, shear_range=0.15, zoom_range=0.1, 
       channel_shift_range=10., horizontal_flip=False, batch_size=batch_size)

In [105]:
# Create a batch from a single image
img = np.expand_dims(ndimage.imread(train_path+'c0/img_100050.jpg'),0)
# Request generator to create augmented images from this image
aug_itr = gen.flow(img)

# Get 8 augmented images
aug_imgs = [next(aug_itr)[0].astype(np.uint8) for i in range(8)]

In [106]:
# Original
plt.imshow(img[0])


Out[106]:
<matplotlib.image.AxesImage at 0x7fafe28e1d50>

In [107]:
# Augemnted data
plots(aug_imgs, (20,7), 2)



In [125]:
# Create training set with augmented images
batches = get_batches(train_path, gen, batch_size=batch_size)
val_batches = get_batches(valid_path, shuffle=False, batch_size=batch_size)


Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 10 classes.

In [128]:
batches.image_shape


Out[128]:
(224, 224, 3)

In [129]:
batches = get_batches(train_path, shuffle=False, batch_size=batch_size)
val_batches = get_batches(valid_path, shuffle=False, batch_size=batch_size)

#Train
# Use fit_generator() since we want to pull random images from the directories on every batch
conv_model.fit_generator(batches, samples_per_epoch=batches.nb_sample, nb_epoch=3, 
                         validation_data=val_batches, nb_val_samples=val_batches.nb_sample)


Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 10 classes.
Epoch 1/3
1000/1000 [==============================] - 548s - loss: 14.5156 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 2/3
1000/1000 [==============================] - 545s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Epoch 3/3
1000/1000 [==============================] - 544s - loss: 14.5063 - acc: 0.1000 - val_loss: 14.5063 - val_acc: 0.1000
Out[129]:
<keras.callbacks.History at 0x7fafdc8bbe90>

In [127]:
conv_model.summary()


____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
lambda_2 (Lambda)                (None, 3, 224, 224)   0           lambda_input_2[0][0]             
____________________________________________________________________________________________________
zeropadding2d_14 (ZeroPadding2D) (None, 3, 226, 226)   0           lambda_2[0][0]                   
____________________________________________________________________________________________________
convolution2d_14 (Convolution2D) (None, 64, 224, 224)  0           zeropadding2d_14[2][0]           
____________________________________________________________________________________________________
zeropadding2d_15 (ZeroPadding2D) (None, 64, 226, 226)  0           convolution2d_14[2][0]           
____________________________________________________________________________________________________
convolution2d_15 (Convolution2D) (None, 64, 224, 224)  0           zeropadding2d_15[2][0]           
____________________________________________________________________________________________________
maxpooling2d_6 (MaxPooling2D)    (None, 64, 112, 112)  0           convolution2d_15[2][0]           
____________________________________________________________________________________________________
zeropadding2d_16 (ZeroPadding2D) (None, 64, 114, 114)  0           maxpooling2d_6[2][0]             
____________________________________________________________________________________________________
convolution2d_16 (Convolution2D) (None, 128, 112, 112) 0           zeropadding2d_16[2][0]           
____________________________________________________________________________________________________
zeropadding2d_17 (ZeroPadding2D) (None, 128, 114, 114) 0           convolution2d_16[2][0]           
____________________________________________________________________________________________________
convolution2d_17 (Convolution2D) (None, 128, 112, 112) 0           zeropadding2d_17[2][0]           
____________________________________________________________________________________________________
maxpooling2d_7 (MaxPooling2D)    (None, 128, 56, 56)   0           convolution2d_17[2][0]           
____________________________________________________________________________________________________
zeropadding2d_18 (ZeroPadding2D) (None, 128, 58, 58)   0           maxpooling2d_7[2][0]             
____________________________________________________________________________________________________
convolution2d_18 (Convolution2D) (None, 256, 56, 56)   0           zeropadding2d_18[2][0]           
____________________________________________________________________________________________________
zeropadding2d_19 (ZeroPadding2D) (None, 256, 58, 58)   0           convolution2d_18[2][0]           
____________________________________________________________________________________________________
convolution2d_19 (Convolution2D) (None, 256, 56, 56)   0           zeropadding2d_19[2][0]           
____________________________________________________________________________________________________
zeropadding2d_20 (ZeroPadding2D) (None, 256, 58, 58)   0           convolution2d_19[2][0]           
____________________________________________________________________________________________________
convolution2d_20 (Convolution2D) (None, 256, 56, 56)   0           zeropadding2d_20[2][0]           
____________________________________________________________________________________________________
maxpooling2d_8 (MaxPooling2D)    (None, 256, 28, 28)   0           convolution2d_20[2][0]           
____________________________________________________________________________________________________
zeropadding2d_21 (ZeroPadding2D) (None, 256, 30, 30)   0           maxpooling2d_8[2][0]             
____________________________________________________________________________________________________
convolution2d_21 (Convolution2D) (None, 512, 28, 28)   0           zeropadding2d_21[2][0]           
____________________________________________________________________________________________________
zeropadding2d_22 (ZeroPadding2D) (None, 512, 30, 30)   0           convolution2d_21[2][0]           
____________________________________________________________________________________________________
convolution2d_22 (Convolution2D) (None, 512, 28, 28)   0           zeropadding2d_22[2][0]           
____________________________________________________________________________________________________
zeropadding2d_23 (ZeroPadding2D) (None, 512, 30, 30)   0           convolution2d_22[2][0]           
____________________________________________________________________________________________________
convolution2d_23 (Convolution2D) (None, 512, 28, 28)   0           zeropadding2d_23[2][0]           
____________________________________________________________________________________________________
maxpooling2d_9 (MaxPooling2D)    (None, 512, 14, 14)   0           convolution2d_23[2][0]           
____________________________________________________________________________________________________
zeropadding2d_24 (ZeroPadding2D) (None, 512, 16, 16)   0           maxpooling2d_9[2][0]             
____________________________________________________________________________________________________
convolution2d_24 (Convolution2D) (None, 512, 14, 14)   0           zeropadding2d_24[2][0]           
____________________________________________________________________________________________________
zeropadding2d_25 (ZeroPadding2D) (None, 512, 16, 16)   0           convolution2d_24[2][0]           
____________________________________________________________________________________________________
convolution2d_25 (Convolution2D) (None, 512, 14, 14)   0           zeropadding2d_25[2][0]           
____________________________________________________________________________________________________
zeropadding2d_26 (ZeroPadding2D) (None, 512, 16, 16)   0           convolution2d_25[2][0]           
____________________________________________________________________________________________________
convolution2d_26 (Convolution2D) (None, 512, 14, 14)   0           zeropadding2d_26[2][0]           
____________________________________________________________________________________________________
sequential_10 (Sequential)       (None, 10)            119586826   convolution2d_26[2][0]           
====================================================================================================
Total params: 119586826
____________________________________________________________________________________________________

In [ ]:
conv_model.save_weights(results_path+'_augmented_images.h5')

Including Batch Normalization

conv_layers[-1].output_shape[1:]


In [33]:
%cd /home/ubuntu/kaggle/state-farm-distracted-driver-detection


/home/ubuntu/kaggle/state-farm-distracted-driver-detection

In [37]:
from utils.vgg16bn import Vgg16BN

In [28]:
def get_bn_layers(p):
    return [
        MaxPooling2D(input_shape=conv_layers[-1].output_shape[1:]),
        Flatten(),
        Dense(4096, activation='relu'),
        BatchNormalization(),
        Dropout(p),
        Dense(4096, activation='relu'),
        BatchNormalization(),
        Dropout(p),
        Dense(1000, activation='softmax')
        ]

In [39]:
def load_fc_weights_from_vgg16bn(model):
    "Load weights for model from the dense layers of the Vgg16BN model."
    # See imagenet_batchnorm.ipynb for info on how the weights for
    # Vgg16BN can be generated from the standard Vgg16 weights.
    vgg16_bn = Vgg16BN()
    _, fc_layers = split_at(vgg16_bn.model, Convolution2D)
    copy_weights(fc_layers, model.layers)

In [30]:
p=0.6

In [31]:
bn_model = Sequential(get_bn_layers(0.6))

In [40]:
load_fc_weights_from_vgg16bn(bn_model)


Downloading data from http://files.fast.ai/models/vgg16_bn.h5
553590784/553620808 [============================>.] - ETA: 0s

In [41]:
def proc_wgts(layer, prev_p, new_p):
    scal = (1-prev_p)/(1-new_p)
    return [o*scal for o in layer.get_weights()]

In [42]:
for l in bn_model.layers: 
    if type(l)==Dense: l.set_weights(proc_wgts(l, 0.5, 0.6))

In [43]:
bn_model.pop()
for layer in bn_model.layers: layer.trainable=False

In [44]:
bn_model.add(Dense(10,activation='softmax'))

In [45]:
bn_model.compile(Adam(), 'categorical_crossentropy', metrics=['accuracy'])

In [53]:
bn_model.fit(trn_features, trn_labels, nb_epoch=8, validation_data=(val_features, val_labels))


Train on 1000 samples, validate on 1000 samples
Epoch 1/8
1000/1000 [==============================] - 1s - loss: 1.0491 - acc: 0.7930 - val_loss: 1.3991 - val_acc: 0.8210
Epoch 2/8
1000/1000 [==============================] - 1s - loss: 1.1693 - acc: 0.7820 - val_loss: 1.4169 - val_acc: 0.8140
Epoch 3/8
1000/1000 [==============================] - 1s - loss: 1.0278 - acc: 0.7870 - val_loss: 1.5300 - val_acc: 0.8060
Epoch 4/8
1000/1000 [==============================] - 1s - loss: 1.1509 - acc: 0.7810 - val_loss: 1.5090 - val_acc: 0.8180
Epoch 5/8
1000/1000 [==============================] - 1s - loss: 1.0864 - acc: 0.8000 - val_loss: 1.5552 - val_acc: 0.8110
Epoch 6/8
1000/1000 [==============================] - 1s - loss: 1.1439 - acc: 0.7920 - val_loss: 1.5657 - val_acc: 0.8050
Epoch 7/8
1000/1000 [==============================] - 1s - loss: 1.0491 - acc: 0.8090 - val_loss: 1.5895 - val_acc: 0.8150
Epoch 8/8
1000/1000 [==============================] - 1s - loss: 0.9283 - acc: 0.8210 - val_loss: 1.6585 - val_acc: 0.7990
Out[53]:
<keras.callbacks.History at 0x7f5e15932b90>

In [54]:
bn_model.save_weights(results_path+'bn.h5')

In [57]:
bn_layers = get_bn_layers(0.6)
bn_layers.pop()
bn_layers.append(Dense(10,activation='softmax'))

In [55]:
bn_model.layers


Out[55]:
[<keras.layers.pooling.MaxPooling2D at 0x7f5e17745510>,
 <keras.layers.core.Flatten at 0x7f5e17745810>,
 <keras.layers.core.Dense at 0x7f5e177457d0>,
 <keras.layers.normalization.BatchNormalization at 0x7f5e17601a50>,
 <keras.layers.core.Dropout at 0x7f5e176289d0>,
 <keras.layers.core.Dense at 0x7f5e17628890>,
 <keras.layers.normalization.BatchNormalization at 0x7f5e17637650>,
 <keras.layers.core.Dropout at 0x7f5e176376d0>,
 <keras.layers.core.Dense at 0x7f5e1752bfd0>]

In [58]:
final_model = Sequential(conv_layers)
for layer in final_model.layers: layer.trainable = False
for layer in bn_layers: final_model.add(layer)

In [59]:
for l1,l2 in zip(bn_model.layers, bn_layers):
    l2.set_weights(l1.get_weights())

In [60]:
final_model.compile(optimizer=Adam(), 
                    loss='categorical_crossentropy', metrics=['accuracy'])

In [64]:
final_model.fit_generator(batches, samples_per_epoch=batches.nb_sample, nb_epoch=10, 
                        validation_data=val_batches, nb_val_samples=val_batches.nb_sample)


Epoch 1/10
1000/1000 [==============================] - 52s - loss: 10.3571 - acc: 0.3180 - val_loss: 10.8025 - val_acc: 0.2770
Epoch 2/10
1000/1000 [==============================] - 51s - loss: 9.9895 - acc: 0.3350 - val_loss: 10.9235 - val_acc: 0.2440
Epoch 3/10
1000/1000 [==============================] - 51s - loss: 9.8730 - acc: 0.3300 - val_loss: 10.7910 - val_acc: 0.2690
Epoch 4/10
1000/1000 [==============================] - 51s - loss: 9.3169 - acc: 0.3780 - val_loss: 10.0569 - val_acc: 0.3010
Epoch 5/10
1000/1000 [==============================] - 51s - loss: 9.1652 - acc: 0.3850 - val_loss: 9.6168 - val_acc: 0.3430
Epoch 6/10
1000/1000 [==============================] - 51s - loss: 8.9542 - acc: 0.4040 - val_loss: 10.0907 - val_acc: 0.3090
Epoch 7/10
1000/1000 [==============================] - 51s - loss: 8.6311 - acc: 0.4210 - val_loss: 9.6839 - val_acc: 0.3500
Epoch 8/10
1000/1000 [==============================] - 51s - loss: 8.4654 - acc: 0.4560 - val_loss: 9.8355 - val_acc: 0.3100
Epoch 9/10
1000/1000 [==============================] - 51s - loss: 8.3690 - acc: 0.4600 - val_loss: 9.1577 - val_acc: 0.3750
Epoch 10/10
1000/1000 [==============================] - 51s - loss: 8.3037 - acc: 0.4670 - val_loss: 9.7740 - val_acc: 0.3120
Out[64]:
<keras.callbacks.History at 0x7f5e07bacc50>

In [65]:
final_model.save_weights(results_path+'final.h5')

Basic Single Conv Layer

Create batches


In [15]:
batches = get_batches(train_path, batch_size=batch_size)
val_batches = get_batches(valid_path, batch_size=batch_size*2, shuffle=False)


Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 10 classes.

In [18]:
(val_classes, trn_classes, val_labels, trn_labels, val_filenames, filenames,
    test_filename) = get_classes(path)


Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 10 classes.
Found 1000 images belonging to 1 classes.

Conv Model

2 Conv layer with max pool followed by a dense network for a CNN


In [7]:
def simple_conv(batches):
    model = Sequential([
            BatchNormalization(axis=1, input_shape=(3,224,224)),
            Convolution2D(32,3,3, activation='relu'),
            BatchNormalization(axis=1),
            MaxPooling2D((3,3)),
            Convolution2D(64,3,3, activation='relu'),
            BatchNormalization(axis=1),
            MaxPooling2D((3,3)),
            Flatten(),
            Dense(200, activation='relu'),
            BatchNormalization(),
            Dense(10, activation='softmax')
        ])
    
    model.compile(Adam(lr=1e-4), loss='categorical_crossentropy', metrics=['accuracy'])
    model.fit_generator(batches, batches.nb_sample, nb_epoch=2, validation_data=val_batches, 
                     nb_val_samples=val_batches.nb_sample)
    model.optimizer.lr = 0.001
    model.fit_generator(batches, batches.nb_sample, nb_epoch=4, validation_data=val_batches, 
                     nb_val_samples=val_batches.nb_sample)
    return model

In [19]:
simple_conv(batches)


Epoch 1/2
1000/1000 [==============================] - 31s - loss: 1.9192 - acc: 0.4130 - val_loss: 2.7576 - val_acc: 0.2290
Epoch 2/2
1000/1000 [==============================] - 27s - loss: 0.4896 - acc: 0.9050 - val_loss: 1.7872 - val_acc: 0.3310
Epoch 1/4
1000/1000 [==============================] - 30s - loss: 0.1538 - acc: 0.9910 - val_loss: 1.7337 - val_acc: 0.3300
Epoch 2/4
1000/1000 [==============================] - 24s - loss: 0.0759 - acc: 0.9940 - val_loss: 1.7826 - val_acc: 0.2930
Epoch 3/4
1000/1000 [==============================] - 23s - loss: 0.0404 - acc: 0.9970 - val_loss: 1.8411 - val_acc: 0.2860
Epoch 4/4
1000/1000 [==============================] - 25s - loss: 0.0241 - acc: 1.0000 - val_loss: 1.9002 - val_acc: 0.2790
Out[19]:
<keras.models.Sequential at 0x7f9f417fdfd0>

Since the training accuracy (1.0) is very high but the validation accuracy is still low (0.28). We need to regularize this. (Options: Data Augmentation, Dropouts, Batch Normalization, ..)

Data Augementation

Experiment with different types and levels of data augmentation


In [22]:
# Width shift: Move image left and right
gen_t = image.ImageDataGenerator(width_shift_range=0.1)
w_batches = get_batches(train_path, gen_t, batch_size=batch_size)


Found 1000 images belonging to 10 classes.

In [23]:
simple_conv(w_batches)


Epoch 1/2
1000/1000 [==============================] - 32s - loss: 2.5035 - acc: 0.2440 - val_loss: 2.8477 - val_acc: 0.1930
Epoch 2/2
1000/1000 [==============================] - 22s - loss: 1.5027 - acc: 0.5170 - val_loss: 2.0383 - val_acc: 0.2680
Epoch 1/4
1000/1000 [==============================] - 31s - loss: 1.1178 - acc: 0.6780 - val_loss: 1.9999 - val_acc: 0.2360
Epoch 2/4
1000/1000 [==============================] - 27s - loss: 0.8352 - acc: 0.7640 - val_loss: 2.1056 - val_acc: 0.2120
Epoch 3/4
1000/1000 [==============================] - 25s - loss: 0.6073 - acc: 0.8490 - val_loss: 2.1344 - val_acc: 0.2720
Epoch 4/4
1000/1000 [==============================] - 23s - loss: 0.4681 - acc: 0.8770 - val_loss: 2.3552 - val_acc: 0.2160
Out[23]:
<keras.models.Sequential at 0x7f9f1beb6e10>

In [24]:
# Hieght shift: Move image up and down
gen_t = image.ImageDataGenerator(height_shift_range=0.1)
h_batches = get_batches(train_path, gen_t, batch_size=batch_size)


Found 1000 images belonging to 10 classes.

In [25]:
simple_conv(h_batches)


Epoch 1/2
1000/1000 [==============================] - 26s - loss: 2.3525 - acc: 0.2750 - val_loss: 3.0578 - val_acc: 0.1800
Epoch 2/2
1000/1000 [==============================] - 24s - loss: 1.3482 - acc: 0.5620 - val_loss: 1.8882 - val_acc: 0.3330
Epoch 1/4
1000/1000 [==============================] - 32s - loss: 0.9370 - acc: 0.7270 - val_loss: 1.8995 - val_acc: 0.3110
Epoch 2/4
1000/1000 [==============================] - 25s - loss: 0.7098 - acc: 0.7960 - val_loss: 2.0917 - val_acc: 0.2940
Epoch 3/4
1000/1000 [==============================] - 23s - loss: 0.5751 - acc: 0.8440 - val_loss: 2.1556 - val_acc: 0.2670
Epoch 4/4
1000/1000 [==============================] - 26s - loss: 0.3935 - acc: 0.9120 - val_loss: 2.2635 - val_acc: 0.2170
Out[25]:
<keras.models.Sequential at 0x7f9f164fd950>

In [26]:
# Random shear rotation (in degrees)
gen_t = image.ImageDataGenerator(shear_range=0.1)
s_batches = get_batches(train_path, gen_t, batch_size=batch_size)


Found 1000 images belonging to 10 classes.

In [27]:
simple_conv(s_batches)


Epoch 1/2
1000/1000 [==============================] - 31s - loss: 2.1504 - acc: 0.3320 - val_loss: 2.8713 - val_acc: 0.1620
Epoch 2/2
1000/1000 [==============================] - 23s - loss: 0.7624 - acc: 0.7890 - val_loss: 2.0041 - val_acc: 0.3010
Epoch 1/4
1000/1000 [==============================] - 30s - loss: 0.3379 - acc: 0.9390 - val_loss: 1.8754 - val_acc: 0.2700
Epoch 2/4
1000/1000 [==============================] - 25s - loss: 0.1916 - acc: 0.9750 - val_loss: 2.0403 - val_acc: 0.2320
Epoch 3/4
1000/1000 [==============================] - 25s - loss: 0.1090 - acc: 0.9920 - val_loss: 2.1652 - val_acc: 0.2530
Epoch 4/4
1000/1000 [==============================] - 26s - loss: 0.0635 - acc: 0.9980 - val_loss: 2.2216 - val_acc: 0.2680
Out[27]:
<keras.models.Sequential at 0x7f9f14590dd0>

In [28]:
# Random rotation rotation (in degrees)
gen_t = image.ImageDataGenerator(rotation_range=10)
h_batches = get_batches(train_path, gen_t, batch_size=batch_size)


Found 1000 images belonging to 10 classes.

In [29]:
simple_conv(h_batches)


Epoch 1/2
1000/1000 [==============================] - 29s - loss: 2.2639 - acc: 0.3080 - val_loss: 2.2608 - val_acc: 0.2010
Epoch 2/2
1000/1000 [==============================] - 24s - loss: 1.0280 - acc: 0.6780 - val_loss: 1.8321 - val_acc: 0.2970
Epoch 1/4
1000/1000 [==============================] - 28s - loss: 0.6513 - acc: 0.8280 - val_loss: 1.9598 - val_acc: 0.2310
Epoch 2/4
1000/1000 [==============================] - 25s - loss: 0.4191 - acc: 0.9040 - val_loss: 2.1563 - val_acc: 0.2240
Epoch 3/4
1000/1000 [==============================] - 22s - loss: 0.3004 - acc: 0.9360 - val_loss: 2.3488 - val_acc: 0.1560
Epoch 4/4
1000/1000 [==============================] - 25s - loss: 0.2074 - acc: 0.9590 - val_loss: 2.5511 - val_acc: 0.1370
Out[29]:
<keras.models.Sequential at 0x7f9f0e8b1d50>

In [31]:
# Color shift: Randomly change R,G,B color
gen_t = image.ImageDataGenerator(channel_shift_range=25)
c_batches = get_batches(train_path, gen_t, batch_size=batch_size)


Found 1000 images belonging to 10 classes.

In [32]:
simple_conv(c_batches)


Epoch 1/2
1000/1000 [==============================] - 30s - loss: 2.1002 - acc: 0.3560 - val_loss: 2.3494 - val_acc: 0.2750
Epoch 2/2
1000/1000 [==============================] - 22s - loss: 0.6631 - acc: 0.8340 - val_loss: 1.7191 - val_acc: 0.3670
Epoch 1/4
1000/1000 [==============================] - 28s - loss: 0.2504 - acc: 0.9640 - val_loss: 1.7177 - val_acc: 0.3780
Epoch 2/4
1000/1000 [==============================] - 24s - loss: 0.1454 - acc: 0.9840 - val_loss: 1.7542 - val_acc: 0.2640
Epoch 3/4
1000/1000 [==============================] - 24s - loss: 0.0596 - acc: 0.9980 - val_loss: 1.9309 - val_acc: 0.2150
Epoch 4/4
1000/1000 [==============================] - 24s - loss: 0.0365 - acc: 0.9990 - val_loss: 1.9406 - val_acc: 0.2260
Out[32]:
<keras.models.Sequential at 0x7f9f0723e7d0>

In [33]:
# Put it all together!!

gen_t = image.ImageDataGenerator(rotation_range=15, height_shift_range=0.1, 
                shear_range=0.1, channel_shift_range=25, width_shift_range=0.1)
da_batches = get_batches(train_path, gen_t, batch_size=batch_size)


Found 1000 images belonging to 10 classes.

In [36]:
model = simple_conv(da_batches)


Epoch 1/2
1000/1000 [==============================] - 30s - loss: 2.7559 - acc: 0.1690 - val_loss: 2.4659 - val_acc: 0.1550
Epoch 2/2
1000/1000 [==============================] - 24s - loss: 2.2109 - acc: 0.2870 - val_loss: 2.1768 - val_acc: 0.1860
Epoch 1/4
1000/1000 [==============================] - 28s - loss: 1.9349 - acc: 0.3570 - val_loss: 2.0778 - val_acc: 0.2290
Epoch 2/4
1000/1000 [==============================] - 24s - loss: 1.7604 - acc: 0.3940 - val_loss: 2.0889 - val_acc: 0.2760
Epoch 3/4
1000/1000 [==============================] - 22s - loss: 1.6867 - acc: 0.4310 - val_loss: 2.1656 - val_acc: 0.2480
Epoch 4/4
1000/1000 [==============================] - 22s - loss: 1.5958 - acc: 0.4790 - val_loss: 2.1676 - val_acc: 0.2500

Validation accuracy does not look encouraging, training set is better. However, we should try to reduce the learning rate and run a few more epochs before making further decisions.


In [37]:
model.optimizer.lr = 0.0001
model.fit_generator(da_batches, da_batches.nb_sample, nb_epoch=5, validation_data=val_batches,
                nb_val_samples=val_batches.nb_sample)


Epoch 1/5
1000/1000 [==============================] - 30s - loss: 1.5100 - acc: 0.4980 - val_loss: 2.2094 - val_acc: 0.2800
Epoch 2/5
1000/1000 [==============================] - 26s - loss: 1.4291 - acc: 0.5050 - val_loss: 2.2438 - val_acc: 0.2720
Epoch 3/5
1000/1000 [==============================] - 25s - loss: 1.4324 - acc: 0.5160 - val_loss: 2.2507 - val_acc: 0.2690
Epoch 4/5
1000/1000 [==============================] - 24s - loss: 1.3758 - acc: 0.5530 - val_loss: 2.1700 - val_acc: 0.2810
Epoch 5/5
1000/1000 [==============================] - 27s - loss: 1.3191 - acc: 0.5760 - val_loss: 2.1511 - val_acc: 0.3210
Out[37]:
<keras.callbacks.History at 0x7f9eff36c290>

Starting to make progress! Lets keep going!


In [38]:
model.fit_generator(da_batches, da_batches.nb_sample, nb_epoch=25, validation_data=val_batches,
                nb_val_samples=val_batches.nb_sample)


Epoch 1/25
1000/1000 [==============================] - 32s - loss: 1.2401 - acc: 0.5880 - val_loss: 2.1167 - val_acc: 0.3210
Epoch 2/25
1000/1000 [==============================] - 23s - loss: 1.2337 - acc: 0.5830 - val_loss: 2.1371 - val_acc: 0.2910
Epoch 3/25
1000/1000 [==============================] - 24s - loss: 1.2297 - acc: 0.5900 - val_loss: 2.0194 - val_acc: 0.3160
Epoch 4/25
1000/1000 [==============================] - 23s - loss: 1.1824 - acc: 0.6100 - val_loss: 1.9935 - val_acc: 0.3010
Epoch 5/25
1000/1000 [==============================] - 25s - loss: 1.1374 - acc: 0.6240 - val_loss: 1.8619 - val_acc: 0.3460
Epoch 6/25
1000/1000 [==============================] - 23s - loss: 1.1299 - acc: 0.6230 - val_loss: 1.8228 - val_acc: 0.3460
Epoch 7/25
1000/1000 [==============================] - 27s - loss: 1.0681 - acc: 0.6560 - val_loss: 1.8485 - val_acc: 0.3380
Epoch 8/25
1000/1000 [==============================] - 26s - loss: 1.0394 - acc: 0.6810 - val_loss: 1.6928 - val_acc: 0.3850
Epoch 9/25
1000/1000 [==============================] - 23s - loss: 1.0088 - acc: 0.6830 - val_loss: 1.6019 - val_acc: 0.4020
Epoch 10/25
1000/1000 [==============================] - 25s - loss: 1.0077 - acc: 0.6720 - val_loss: 1.6158 - val_acc: 0.3870
Epoch 11/25
1000/1000 [==============================] - 24s - loss: 0.9480 - acc: 0.6890 - val_loss: 1.5270 - val_acc: 0.4020
Epoch 12/25
1000/1000 [==============================] - 25s - loss: 0.9551 - acc: 0.6800 - val_loss: 1.3259 - val_acc: 0.5150
Epoch 13/25
1000/1000 [==============================] - 23s - loss: 1.0040 - acc: 0.6690 - val_loss: 1.3467 - val_acc: 0.5070
Epoch 14/25
1000/1000 [==============================] - 25s - loss: 0.9256 - acc: 0.7140 - val_loss: 1.2329 - val_acc: 0.5680
Epoch 15/25
1000/1000 [==============================] - 24s - loss: 0.8991 - acc: 0.6900 - val_loss: 1.2541 - val_acc: 0.5480
Epoch 16/25
1000/1000 [==============================] - 24s - loss: 0.8983 - acc: 0.7110 - val_loss: 1.1335 - val_acc: 0.5750
Epoch 17/25
1000/1000 [==============================] - 24s - loss: 0.8693 - acc: 0.7170 - val_loss: 1.1758 - val_acc: 0.5470
Epoch 18/25
1000/1000 [==============================] - 22s - loss: 0.8640 - acc: 0.7190 - val_loss: 1.0443 - val_acc: 0.6270
Epoch 19/25
1000/1000 [==============================] - 24s - loss: 0.8369 - acc: 0.7390 - val_loss: 0.8892 - val_acc: 0.7170
Epoch 20/25
1000/1000 [==============================] - 23s - loss: 0.8276 - acc: 0.7520 - val_loss: 0.8726 - val_acc: 0.7110
Epoch 21/25
1000/1000 [==============================] - 24s - loss: 0.7616 - acc: 0.7610 - val_loss: 0.8548 - val_acc: 0.7100
Epoch 22/25
1000/1000 [==============================] - 25s - loss: 0.8262 - acc: 0.7360 - val_loss: 0.7273 - val_acc: 0.7640
Epoch 23/25
1000/1000 [==============================] - 23s - loss: 0.7749 - acc: 0.7530 - val_loss: 0.7266 - val_acc: 0.8020
Epoch 24/25
1000/1000 [==============================] - 22s - loss: 0.6838 - acc: 0.7980 - val_loss: 0.6897 - val_acc: 0.7960
Epoch 25/25
1000/1000 [==============================] - 22s - loss: 0.7615 - acc: 0.7470 - val_loss: 0.6405 - val_acc: 0.8250
Out[38]:
<keras.callbacks.History at 0x7f9eff3eae10>

Seems like good results with Data Augmentation. Lets next use the full dataset and use dropout to prevent overfitting


In [39]:
model.save_weights(results_path+'simple_conv_sample.h5')