Behavioural cloning project


In [1]:
# Configure matlab to show graphics in the notebook
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

In [2]:
# Change to 'data' when training on a GPU
PATH_TO_UDACITY_DATA = 'data'
PATH_TO_TRAIN_DATA = 'train'
PATH_TO_VALID_DATA = 'valid'
PATH_TO_TEST_DATA = 'test'

In [3]:
# Shape of the image
input_shape = (66, 200, 3)

In [4]:
def img_folder(data_folder):
    return '{}/IMG'.format(data_folder)

def path_driving_log(data_folder):
    return '{}/driving_log.csv'.format(data_folder)

In [5]:
datasets = ['train', 'test', 'valid']

In [6]:
def get_driving_log_dataframe(data_folder):
    driving_log_df = pd.read_csv(path_driving_log(data_folder))
    return driving_log_df

In [7]:
!pip install imutils


Requirement already satisfied: imutils in /home/carnd/anaconda3/envs/carnd-term1/lib/python3.5/site-packages

In [8]:
import cv2
import imutils

In [9]:
get_driving_log_dataframe(PATH_TO_TRAIN_DATA)['steering'].min()


Out[9]:
-1.0

In [10]:
from scipy.ndimage import imread
from os import listdir
from sklearn.utils import shuffle

from PIL import Image

import math


def get_weight(label):
    return math.exp(abs(label))


def left_center_right_generator(data_folder, batch_size=64):
    driving_log_df = get_driving_log_dataframe(data_folder)
    number_of_examples = len(driving_log_df)
    image_columns = ['center', 'left', 'right']
    
    X_train = []
    y_train = []
    weights = []
    index_in_batch = 0
    batch_number = 0
    
    angle_offset = 0.2
    
    while True:
        for image_column in image_columns:
            image_series = driving_log_df[image_column]
            steering_series = driving_log_df['steering']
            for offset in range(0, number_of_examples, batch_size):
                X_train = []
                y_train = []
                weights = []

                end_of_batch = min(number_of_examples, offset + batch_size)

                for j in range(offset, end_of_batch):
                    image_filename = image_series[j].lstrip().rstrip()
                    
                    image = Image.open('{0}/{1}'.format(data_folder, image_filename))
                    image = image.convert('YCbCr')
                    image_np = np.asarray(image.resize((200, 66)))
                    label = steering_series[j]
                    
                    if image_column == 'left':
                        delta_steering = -angle_offset
                    elif image_column == 'right':
                        delta_steering = angle_offset
                    else:
                        delta_steering = 0
                    
                    label = label + delta_steering
                    
                    X_train.append(image_np)
                    y_train.append(label)
                    weights.append(get_weight(label))
                    
                    flipped_image = cv2.flip(image_np, 1)
                    flipped_label = -label
                    
                    X_train.append(flipped_image)
                    y_train.append(flipped_label)
                    weights.append(get_weight(flipped_label))
                
                    
                X_train, y_train, weights = shuffle(X_train, y_train, weights)
                yield np.array(X_train), np.array(y_train), np.array(weights)

In [11]:
def center_only_generator(data_folder, batch_size=64):
    driving_log_df = get_driving_log_dataframe(data_folder)
    number_of_examples = len(driving_log_df)
    
    X_train = []
    y_train = []
    weights = []
    index_in_batch = 0
    batch_number = 0
    
    angle_offset = 0.2
    
    while True:
        image_series = driving_log_df['center']
        steering_series = driving_log_df['steering']
        for offset in range(0, number_of_examples, batch_size):
            X_train = []
            y_train = []
            weights = []

            end_of_batch = min(number_of_examples, offset + batch_size)

            for j in range(offset, end_of_batch):
                image_filename = image_series[j].lstrip().rstrip()

                image = Image.open('{0}/{1}'.format(data_folder, image_filename))
                image = image.convert('YCbCr')
                image_np = np.asarray(image.resize((200, 66)))
                label = steering_series[j]

                X_train.append(image_np)
                y_train.append(label)
                weights.append(get_weight(label))

                flipped_image = cv2.flip(image_np, 1)
                flipped_label = -label

                X_train.append(flipped_image)
                y_train.append(flipped_label)
                weights.append(get_weight(flipped_label))


            X_train, y_train, weights = shuffle(X_train, y_train, weights)
            yield np.array(X_train), np.array(y_train), np.array(weights)

In [12]:
X, y, w = next(left_center_right_generator(PATH_TO_UDACITY_DATA))

In [13]:
plt.imshow(X[0])


Out[13]:
<matplotlib.image.AxesImage at 0x7fdaf6f16710>

In [14]:
X, y, w = next(center_only_generator(PATH_TO_UDACITY_DATA))

In [15]:
plt.imshow(X[0])
print(w[0])


1.0

In [16]:
from keras.models import Sequential
from keras.layers import BatchNormalization
from keras.layers import Dense
from keras.layers import Convolution2D
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers import Lambda


Using TensorFlow backend.

In [25]:
from keras.regularizers import l2, activity_l2

model = Sequential()

regularization_coef = 10e-7

model.add(Lambda(lambda x: x/255.0,
            input_shape=input_shape))
model.add(Convolution2D(24, 
                        5, 5,
                       subsample=(2, 2),
                       init='he_normal',
                       W_regularizer=l2(regularization_coef), activity_regularizer=activity_l2(regularization_coef)))
model.add(BatchNormalization())
model.add(Convolution2D(36, 
                        5, 5,
                       subsample=(2, 2),
                       init='he_normal',
                       W_regularizer=l2(regularization_coef), activity_regularizer=activity_l2(regularization_coef)))
model.add(BatchNormalization())
model.add(Convolution2D(48, 
                        5, 5,
                       subsample=(2, 2),
                       init='he_normal',
                       W_regularizer=l2(regularization_coef), activity_regularizer=activity_l2(regularization_coef)))
model.add(BatchNormalization())
model.add(Dropout(0.2))
model.add(Convolution2D(64,
                       3, 3,
                       init='he_normal',
                       W_regularizer=l2(regularization_coef), activity_regularizer=activity_l2(regularization_coef)))
model.add(BatchNormalization())
model.add(Dropout(0.3))
model.add(Convolution2D(64,
                       3, 3,
                       init='he_normal',
                       W_regularizer=l2(regularization_coef), activity_regularizer=activity_l2(regularization_coef)))
model.add(BatchNormalization())
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(100, activation='relu', init='he_normal'))
model.add(Dense(50, activation='relu', init='he_normal'))
model.add(Dense(10, activation='relu', init='he_normal'))
model.add(Dense(1, init='he_normal'))

In [26]:
model.summary()


____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
lambda_3 (Lambda)                (None, 66, 200, 3)    0           lambda_input_3[0][0]             
____________________________________________________________________________________________________
convolution2d_11 (Convolution2D) (None, 31, 98, 24)    1824        lambda_3[0][0]                   
____________________________________________________________________________________________________
batchnormalization_9 (BatchNorma (None, 31, 98, 24)    96          convolution2d_11[0][0]           
____________________________________________________________________________________________________
convolution2d_12 (Convolution2D) (None, 14, 47, 36)    21636       batchnormalization_9[0][0]       
____________________________________________________________________________________________________
batchnormalization_10 (BatchNorm (None, 14, 47, 36)    144         convolution2d_12[0][0]           
____________________________________________________________________________________________________
convolution2d_13 (Convolution2D) (None, 5, 22, 48)     43248       batchnormalization_10[0][0]      
____________________________________________________________________________________________________
batchnormalization_11 (BatchNorm (None, 5, 22, 48)     192         convolution2d_13[0][0]           
____________________________________________________________________________________________________
dropout_7 (Dropout)              (None, 5, 22, 48)     0           batchnormalization_11[0][0]      
____________________________________________________________________________________________________
convolution2d_14 (Convolution2D) (None, 3, 20, 64)     27712       dropout_7[0][0]                  
____________________________________________________________________________________________________
batchnormalization_12 (BatchNorm (None, 3, 20, 64)     256         convolution2d_14[0][0]           
____________________________________________________________________________________________________
dropout_8 (Dropout)              (None, 3, 20, 64)     0           batchnormalization_12[0][0]      
____________________________________________________________________________________________________
convolution2d_15 (Convolution2D) (None, 1, 18, 64)     36928       dropout_8[0][0]                  
____________________________________________________________________________________________________
batchnormalization_13 (BatchNorm (None, 1, 18, 64)     256         convolution2d_15[0][0]           
____________________________________________________________________________________________________
dropout_9 (Dropout)              (None, 1, 18, 64)     0           batchnormalization_13[0][0]      
____________________________________________________________________________________________________
flatten_3 (Flatten)              (None, 1152)          0           dropout_9[0][0]                  
____________________________________________________________________________________________________
dense_9 (Dense)                  (None, 100)           115300      flatten_3[0][0]                  
____________________________________________________________________________________________________
dense_10 (Dense)                 (None, 50)            5050        dense_9[0][0]                    
____________________________________________________________________________________________________
dense_11 (Dense)                 (None, 10)            510         dense_10[0][0]                   
____________________________________________________________________________________________________
dense_12 (Dense)                 (None, 1)             11          dense_11[0][0]                   
====================================================================================================
Total params: 253,163
Trainable params: 252,691
Non-trainable params: 472
____________________________________________________________________________________________________

In [27]:
from keras.callbacks import ModelCheckpoint
filepath="weights-improvement-{epoch:02d}.hdf5"
checkpoint = ModelCheckpoint(filepath)
callbacks_list = [checkpoint]

In [28]:
from keras.optimizers import Adam
adam = Adam(lr=10e-4)

In [29]:
model.compile(optimizer=adam, 
              loss='mse',
             metrics=['mean_squared_error'])

In [24]:
image_generator = left_center_right_generator(PATH_TO_UDACITY_DATA)
validation_generator = center_only_generator(PATH_TO_VALID_DATA)
samples = len(get_driving_log_dataframe(PATH_TO_UDACITY_DATA)) * 3 * 2
nb_val_samples = len(get_driving_log_dataframe(PATH_TO_VALID_DATA))
model.fit_generator(image_generator, 
                    samples_per_epoch=samples, 
                    nb_epoch=20,
                   callbacks=callbacks_list,
                   validation_data=validation_generator,
                   nb_val_samples=nb_val_samples)


Epoch 1/5
48216/48216 [==============================] - 69s - loss: 2.9051 - mean_squared_error: 0.1531 - val_loss: 0.4765 - val_mean_squared_error: 0.0115
Epoch 2/5
48216/48216 [==============================] - 63s - loss: 0.7477 - mean_squared_error: 0.0435 - val_loss: 0.3422 - val_mean_squared_error: 0.0124
Epoch 3/5
48216/48216 [==============================] - 64s - loss: 0.3773 - mean_squared_error: 0.0410 - val_loss: 0.2762 - val_mean_squared_error: 0.0166
Epoch 4/5
48216/48216 [==============================] - 64s - loss: 0.2144 - mean_squared_error: 0.0235 - val_loss: 0.1714 - val_mean_squared_error: 0.0357
Epoch 5/5
48216/48216 [==============================] - 64s - loss: 0.1416 - mean_squared_error: 0.0193 - val_loss: 0.1414 - val_mean_squared_error: 0.0355
Out[24]:
<keras.callbacks.History at 0x7fdacc172f60>

In [37]:
test_generator = center_only_generator(PATH_TO_TEST_DATA)
samples = len(get_driving_log_dataframe(PATH_TO_TEST_DATA))
model.evaluate_generator(test_generator, samples)


Out[37]:
[0.2434078310342396, 0.16039755677475648]

In [30]:
image_generator = center_only_generator(PATH_TO_TRAIN_DATA)
validation_generator = center_only_generator(PATH_TO_VALID_DATA)
samples = len(get_driving_log_dataframe(PATH_TO_TRAIN_DATA)) * 2
nb_val_samples = len(get_driving_log_dataframe(PATH_TO_VALID_DATA))
model.fit_generator(image_generator, 
                    samples_per_epoch=samples, 
                    nb_epoch=20,
                   callbacks=callbacks_list,
                   validation_data=validation_generator,
                   nb_val_samples=nb_val_samples)


Epoch 1/20
69156/69156 [==============================] - 92s - loss: 1.6947 - mean_squared_error: 0.0424 - val_loss: 0.2466 - val_mean_squared_error: 0.0114
Epoch 2/20
69156/69156 [==============================] - 89s - loss: 0.2363 - mean_squared_error: 0.0117 - val_loss: 0.1159 - val_mean_squared_error: 0.0124
Epoch 3/20
69156/69156 [==============================] - 89s - loss: 0.1053 - mean_squared_error: 0.0098 - val_loss: 0.0799 - val_mean_squared_error: 0.0090
Epoch 4/20
69156/69156 [==============================] - 89s - loss: 0.0754 - mean_squared_error: 0.0082 - val_loss: 0.0556 - val_mean_squared_error: 0.0108
Epoch 5/20
69156/69156 [==============================] - 89s - loss: 0.0466 - mean_squared_error: 0.0077 - val_loss: 0.1065 - val_mean_squared_error: 0.0209
Epoch 6/20
69156/69156 [==============================] - 89s - loss: 0.0359 - mean_squared_error: 0.0072 - val_loss: 0.0652 - val_mean_squared_error: 0.0153
Epoch 7/20
69156/69156 [==============================] - 89s - loss: 0.0291 - mean_squared_error: 0.0069 - val_loss: 0.0311 - val_mean_squared_error: 0.0076
Epoch 8/20
69156/69156 [==============================] - 89s - loss: 0.0253 - mean_squared_error: 0.0067 - val_loss: 0.0247 - val_mean_squared_error: 0.0073
Epoch 9/20
69156/69156 [==============================] - 89s - loss: 0.0179 - mean_squared_error: 0.0061 - val_loss: 0.0172 - val_mean_squared_error: 0.0058
Epoch 10/20
69156/69156 [==============================] - 89s - loss: 0.0143 - mean_squared_error: 0.0058 - val_loss: 0.0200 - val_mean_squared_error: 0.0089
Epoch 11/20
69156/69156 [==============================] - 89s - loss: 0.0116 - mean_squared_error: 0.0055 - val_loss: 0.0140 - val_mean_squared_error: 0.0080
Epoch 12/20
69156/69156 [==============================] - 89s - loss: 0.0092 - mean_squared_error: 0.0052 - val_loss: 0.0083 - val_mean_squared_error: 0.0049
Epoch 13/20
69156/69156 [==============================] - 89s - loss: 0.0086 - mean_squared_error: 0.0050 - val_loss: 0.0086 - val_mean_squared_error: 0.0051
Epoch 14/20
69156/69156 [==============================] - 89s - loss: 0.0081 - mean_squared_error: 0.0049 - val_loss: 0.0112 - val_mean_squared_error: 0.0067
Epoch 15/20
69156/69156 [==============================] - 89s - loss: 0.0084 - mean_squared_error: 0.0051 - val_loss: 0.0089 - val_mean_squared_error: 0.0054
Epoch 16/20
69156/69156 [==============================] - 89s - loss: 0.0075 - mean_squared_error: 0.0048 - val_loss: 0.0071 - val_mean_squared_error: 0.0049
Epoch 17/20
69156/69156 [==============================] - 89s - loss: 0.0072 - mean_squared_error: 0.0047 - val_loss: 0.0189 - val_mean_squared_error: 0.0140
Epoch 18/20
69156/69156 [==============================] - 89s - loss: 0.0072 - mean_squared_error: 0.0048 - val_loss: 0.0187 - val_mean_squared_error: 0.0140
Epoch 19/20
69156/69156 [==============================] - 89s - loss: 0.0069 - mean_squared_error: 0.0046 - val_loss: 0.0300 - val_mean_squared_error: 0.0237
Epoch 20/20
69156/69156 [==============================] - 89s - loss: 0.0068 - mean_squared_error: 0.0046 - val_loss: 0.0135 - val_mean_squared_error: 0.0100
Out[30]:
<keras.callbacks.History at 0x7fda7edaae10>

In [31]:
model_json = model.to_json()
with open("model.json", "w") as json_file:
    json_file.write(model_json)
# serialize weights to HDF5
model.save_weights("model.h5")
model.save("trained_model.h5")

In [45]:
y_val


Out[45]:
array([-0.1670138 ,  0.07132844,  0.1670138 ,  0.1670138 , -0.1670138 ,
       -0.07132844, -0.0617599 ,  0.05975719, -0.04076847,  0.0617599 ,
        0.01391724, -0.05975719, -0.        ,  0.1670138 , -0.        ,
       -0.07132844, -0.0617599 , -0.1167233 , -0.        , -0.0617599 ,
        0.09773462,  0.05975719,  0.        , -0.05975719, -0.07132844,
        0.07132844,  0.        ,  0.        , -0.01391724, -0.09773462,
        0.        ,  0.07132844,  0.0617599 , -0.1670138 ,  0.0617599 ,
        0.04076847, -0.        ,  0.1167233 ])

In [1]:
from keras.models import load_model

loaded_model = load_model("trained_model.h5")


Using TensorFlow backend.

In [26]:
image_generator = left_ce(PATH_TO_DATA)
samples = len(get_driving_log_dataframe(PATH_TO_DATA))

loaded_model.fit_generator(image_generator, 
                    samples_per_epoch=samples, 
                    nb_epoch=10,
                   callbacks=callbacks_list)


Epoch 1/10
14816/14816 [==============================] - 50s - loss: 0.3507 - mean_squared_error: 0.0380    
Epoch 2/10
14816/14816 [==============================] - 48s - loss: 0.2957 - mean_squared_error: 0.0293    
Epoch 3/10
14816/14816 [==============================] - 48s - loss: 0.2632 - mean_squared_error: 0.0268    
Epoch 4/10
14816/14816 [==============================] - 48s - loss: 0.2357 - mean_squared_error: 0.0250    
Epoch 5/10
14816/14816 [==============================] - 48s - loss: 0.2257 - mean_squared_error: 0.0248    
Epoch 6/10
14816/14816 [==============================] - 48s - loss: 0.2068 - mean_squared_error: 0.0233    
Epoch 7/10
14816/14816 [==============================] - 48s - loss: 0.2041 - mean_squared_error: 0.0234    
Epoch 8/10
14816/14816 [==============================] - 48s - loss: 0.2040 - mean_squared_error: 0.0233    
Epoch 9/10
14816/14816 [==============================] - 48s - loss: 0.1937 - mean_squared_error: 0.0221    
Epoch 10/10
14816/14816 [==============================] - 48s - loss: 0.1858 - mean_squared_error: 0.0214    
Out[26]:
<keras.callbacks.History at 0x7f898c29a710>

In [ ]: