forked from subodh-malgonde/behavioral-cloning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
model.py
186 lines (132 loc) · 5.36 KB
/
model.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Convolution2D, ELU, Flatten, Dropout, Dense, Lambda, MaxPooling2D
from keras.preprocessing.image import img_to_array, load_img
import cv2
rows, cols, ch = 64, 64, 3
TARGET_SIZE = (64, 64)
def augment_brightness_camera_images(image):
'''
:param image: Input image
:return: output image with reduced brightness
'''
# convert to HSV so that its easy to adjust brightness
image1 = cv2.cvtColor(image,cv2.COLOR_RGB2HSV)
# randomly generate the brightness reduction factor
# Add a constant so that it prevents the image from being completely dark
random_bright = .25+np.random.uniform()
# Apply the brightness reduction to the V channel
image1[:,:,2] = image1[:,:,2]*random_bright
# convert to RBG again
image1 = cv2.cvtColor(image1,cv2.COLOR_HSV2RGB)
return image1
def resize_to_target_size(image):
return cv2.resize(image, TARGET_SIZE)
def crop_and_resize(image):
'''
:param image: The input image of dimensions 160x320x3
:return: Output image of size 64x64x3
'''
cropped_image = image[55:135, :, :]
processed_image = resize_to_target_size(cropped_image)
return processed_image
def preprocess_image(image):
image = crop_and_resize(image)
image = image.astype(np.float32)
#Normalize image
image = image/255.0 - 0.5
return image
def get_augmented_row(row):
steering = row['steering']
# randomly choose the camera to take the image from
camera = np.random.choice(['center', 'left', 'right'])
# adjust the steering angle for left anf right cameras
if camera == 'left':
steering += 0.25
elif camera == 'right':
steering -= 0.25
image = load_img("udacity_data/" + row[camera].strip())
image = img_to_array(image)
# decide whether to horizontally flip the image:
# This is done to reduce the bias for turning left that is present in the training data
flip_prob = np.random.random()
if flip_prob > 0.5:
# flip the image and reverse the steering angle
steering = -1*steering
image = cv2.flip(image, 1)
# Apply brightness augmentation
image = augment_brightness_camera_images(image)
# Crop, resize and normalize the image
image = preprocess_image(image)
return image, steering
def get_data_generator(data_frame, batch_size=32):
N = data_frame.shape[0]
batches_per_epoch = N // batch_size
i = 0
while(True):
start = i*batch_size
end = start+batch_size - 1
X_batch = np.zeros((batch_size, 64, 64, 3), dtype=np.float32)
y_batch = np.zeros((batch_size,), dtype=np.float32)
j = 0
# slice a `batch_size` sized chunk from the dataframe
# and generate augmented data for each row in the chunk on the fly
for index, row in data_frame.loc[start:end].iterrows():
X_batch[j], y_batch[j] = get_augmented_row(row)
j += 1
i += 1
if i == batches_per_epoch - 1:
# reset the index so that we can cycle over the data_frame again
i = 0
yield X_batch, y_batch
def get_model():
model = Sequential()
# model.add(Lambda(preprocess_batch, input_shape=(160, 320, 3), output_shape=(64, 64, 3)))
# layer 1 output shape is 32x32x32
model.add(Convolution2D(32, 5, 5, input_shape=(64, 64, 3), subsample=(2, 2), border_mode="same"))
model.add(ELU())
# layer 2 output shape is 15x15x16
model.add(Convolution2D(16, 3, 3, subsample=(1, 1), border_mode="valid"))
model.add(ELU())
model.add(Dropout(.4))
model.add(MaxPooling2D((2, 2), border_mode='valid'))
# layer 3 output shape is 12x12x16
model.add(Convolution2D(16, 3, 3, subsample=(1, 1), border_mode="valid"))
model.add(ELU())
model.add(Dropout(.4))
# Flatten the output
model.add(Flatten())
# layer 4
model.add(Dense(1024))
model.add(Dropout(.3))
model.add(ELU())
# layer 5
model.add(Dense(512))
model.add(ELU())
# Finally a single output, since this is a regression problem
model.add(Dense(1))
model.compile(optimizer="adam", loss="mse")
return model
if __name__ == "__main__":
BATCH_SIZE = 32
data_frame = pd.read_csv('udacity_data/driving_log.csv', usecols=[0, 1, 2, 3])
# shuffle the data
data_frame = data_frame.sample(frac=1).reset_index(drop=True)
# 80-20 training validation split
training_split = 0.8
num_rows_training = int(data_frame.shape[0]*training_split)
training_data = data_frame.loc[0:num_rows_training-1]
validation_data = data_frame.loc[num_rows_training:]
# release the main data_frame from memory
data_frame = None
training_generator = get_data_generator(training_data, batch_size=BATCH_SIZE)
validation_data_generator = get_data_generator(validation_data, batch_size=BATCH_SIZE)
model = get_model()
samples_per_epoch = (20000//BATCH_SIZE)*BATCH_SIZE
model.fit_generator(training_generator, validation_data=validation_data_generator,
samples_per_epoch=samples_per_epoch, nb_epoch=3, nb_val_samples=3000)
print("Saving model weights and configuration file.")
model.save_weights('model.h5') # always save your weights after training or during training
with open('model.json', 'w') as outfile:
outfile.write(model.to_json())