Shital Shah 2018-10-08 22:53:35 -07:00
Родитель 5f7893099b
Коммит a57da5204e
7 изменённых файлов: 876 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,225 @@
import random
import csv
from PIL import Image
import numpy as np
import pandas as pd
import sys
import os
import errno
from collections import OrderedDict
import h5py
from pathlib import Path
import copy
import re
# This constant is used as an upper bound for normalizing the car's speed to be between 0 and 1
MAX_SPEED = 70.0
def checkAndCreateDir(full_path):
"""Checks if a given path exists and if not, creates the needed directories.
Inputs:
full_path: path to be checked
"""
if not os.path.exists(os.path.dirname(full_path)):
try:
os.makedirs(os.path.dirname(full_path))
except OSError as exc: # Guard against race condition
if exc.errno != errno.EEXIST:
raise
def readImagesFromPath(image_names):
""" Takes in a path and a list of image file names to be loaded and returns a list of all loaded images after resize.
Inputs:
image_names: list of image names
Returns:
List of all loaded and resized images
"""
returnValue = []
for image_name in image_names:
im = Image.open(image_name)
imArr = np.asarray(im)
#Remove alpha channel if exists
if len(imArr.shape) == 3 and imArr.shape[2] == 4:
if (np.all(imArr[:, :, 3] == imArr[0, 0, 3])):
imArr = imArr[:,:,0:3]
if len(imArr.shape) != 3 or imArr.shape[2] != 3:
print('Error: Image', image_name, 'is not RGB.')
sys.exit()
returnIm = np.asarray(imArr)
returnValue.append(returnIm)
return returnValue
def splitTrainValidationAndTestData(all_data_mappings, split_ratio=(0.7, 0.2, 0.1)):
"""Simple function to create train, validation and test splits on the data.
Inputs:
all_data_mappings: mappings from the entire dataset
split_ratio: (train, validation, test) split ratio
Returns:
train_data_mappings: mappings for training data
validation_data_mappings: mappings for validation data
test_data_mappings: mappings for test data
"""
if round(sum(split_ratio), 5) != 1.0:
print("Error: Your splitting ratio should add up to 1")
sys.exit()
train_split = int(len(all_data_mappings) * split_ratio[0])
val_split = train_split + int(len(all_data_mappings) * split_ratio[1])
train_data_mappings = all_data_mappings[0:train_split]
validation_data_mappings = all_data_mappings[train_split:val_split]
test_data_mappings = all_data_mappings[val_split:]
return [train_data_mappings, validation_data_mappings, test_data_mappings]
def generateDataMapAirSim(folders):
""" Data map generator for simulator(AirSim) data. Reads the driving_log csv file and returns a list of 'center camera image name - label(s)' tuples
Inputs:
folders: list of folders to collect data from
Returns:
mappings: All data mappings as a dictionary. Key is the image filepath, the values are a 2-tuple:
0 -> label(s) as a list of double
1 -> previous state as a list of double
"""
all_mappings = {}
for folder in folders:
print('Reading data from {0}...'.format(folder))
current_df = pd.read_csv(os.path.join(folder, 'airsim_rec.txt'), sep='\t')
for i in range(1, current_df.shape[0] - 1):
if current_df.iloc[i-1]['Brake'] != 0: # Consider only training examples without breaks
continue
norm_steering = [ (float(current_df.iloc[i-1][['Steering']]) + 1) / 2.0 ] # Normalize steering: between 0 and 1
norm_throttle = [ float(current_df.iloc[i-1][['Throttle']]) ]
norm_speed = [ float(current_df.iloc[i-1][['Speed (kmph)']]) / MAX_SPEED ] # Normalize speed: between 0 and 1
previous_state = norm_steering + norm_throttle + norm_speed # Append lists
#compute average steering over 3 consecutive recorded images, this will serve as the label
norm_steering0 = (float(current_df.iloc[i][['Steering']]) + 1) / 2.0
norm_steering1 = (float(current_df.iloc[i+1][['Steering']]) + 1) / 2.0
temp_sum_steering = norm_steering[0] + norm_steering0 + norm_steering1
average_steering = temp_sum_steering / 3.0
current_label = [average_steering]
image_filepath = os.path.join(os.path.join(folder, 'images'), current_df.iloc[i]['ImageName']).replace('\\', '/')
if (image_filepath in all_mappings):
print('Error: attempting to add image {0} twice.'.format(image_filepath))
all_mappings[image_filepath] = (current_label, previous_state)
mappings = [(key, all_mappings[key]) for key in all_mappings]
random.shuffle(mappings)
return mappings
def generatorForH5py(data_mappings, chunk_size=32):
"""
This function batches the data for saving to the H5 file
"""
for chunk_id in range(0, len(data_mappings), chunk_size):
# Data is expected to be a dict of <image: (label, previousious_state)>
data_chunk = data_mappings[chunk_id:chunk_id + chunk_size]
if (len(data_chunk) == chunk_size):
image_names_chunk = [a for (a, b) in data_chunk]
labels_chunk = np.asarray([b[0] for (a, b) in data_chunk])
previous_state_chunk = np.asarray([b[1] for (a, b) in data_chunk])
#Flatten and yield as tuple
yield (image_names_chunk, labels_chunk.astype(float), previous_state_chunk.astype(float))
if chunk_id + chunk_size > len(data_mappings):
raise StopIteration
raise StopIteration
def saveH5pyData(data_mappings, target_file_path, chunk_size):
"""
Saves H5 data to file
"""
gen = generatorForH5py(data_mappings,chunk_size)
image_names_chunk, labels_chunk, previous_state_chunk = next(gen)
images_chunk = np.asarray(readImagesFromPath(image_names_chunk))
row_count = images_chunk.shape[0]
checkAndCreateDir(target_file_path)
with h5py.File(target_file_path, 'w') as f:
# Initialize a resizable dataset to hold the output
images_chunk_maxshape = (None,) + images_chunk.shape[1:]
labels_chunk_maxshape = (None,) + labels_chunk.shape[1:]
previous_state_maxshape = (None,) + previous_state_chunk.shape[1:]
dset_images = f.create_dataset('image', shape=images_chunk.shape, maxshape=images_chunk_maxshape,
chunks=images_chunk.shape, dtype=images_chunk.dtype)
dset_labels = f.create_dataset('label', shape=labels_chunk.shape, maxshape=labels_chunk_maxshape,
chunks=labels_chunk.shape, dtype=labels_chunk.dtype)
dset_previous_state = f.create_dataset('previous_state', shape=previous_state_chunk.shape, maxshape=previous_state_maxshape,
chunks=previous_state_chunk.shape, dtype=previous_state_chunk.dtype)
dset_images[:] = images_chunk
dset_labels[:] = labels_chunk
dset_previous_state[:] = previous_state_chunk
for image_names_chunk, label_chunk, previous_state_chunk in gen:
image_chunk = np.asarray(readImagesFromPath(image_names_chunk))
# Resize the dataset to accommodate the next chunk of rows
dset_images.resize(row_count + image_chunk.shape[0], axis=0)
dset_labels.resize(row_count + label_chunk.shape[0], axis=0)
dset_previous_state.resize(row_count + previous_state_chunk.shape[0], axis=0)
# Create the next chunk
dset_images[row_count:] = image_chunk
dset_labels[row_count:] = label_chunk
dset_previous_state[row_count:] = previous_state_chunk
# Increment the row count
row_count += image_chunk.shape[0]
def cook(folders, output_directory, train_eval_test_split, chunk_size):
""" Primary function for data pre-processing. Reads and saves all data as h5 files.
Inputs:
folders: a list of all data folders
output_directory: location for saving h5 files
train_eval_test_split: dataset split ratio
"""
output_files = [os.path.join(output_directory, f) for f in ['train.h5', 'eval.h5', 'test.h5']]
if (any([os.path.isfile(f) for f in output_files])):
print("Preprocessed data already exists at: {0}. Skipping preprocessing.".format(output_directory))
else:
all_data_mappings = generateDataMapAirSim(folders)
split_mappings = splitTrainValidationAndTestData(all_data_mappings, split_ratio=train_eval_test_split)
for i in range(0, len(split_mappings)-1, 1):
print('Processing {0}...'.format(output_files[i]))
saveH5pyData(split_mappings[i], output_files[i], chunk_size)
print('Finished saving {0}.'.format(output_files[i]))

Просмотреть файл

@ -0,0 +1,342 @@
from keras.preprocessing import image
import numpy as np
import keras.backend as K
import os
import cv2
import PIL
from PIL import Image
from PIL import ImageChops
import cv2
class DriveDataGenerator(image.ImageDataGenerator):
def __init__(self,
featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
zca_epsilon=1e-6,
rotation_range=0.,
width_shift_range=0.,
height_shift_range=0.,
shear_range=0.,
zoom_range=0.,
channel_shift_range=0.,
fill_mode='nearest',
cval=0.,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format=None,
brighten_range=0):
super(DriveDataGenerator, self).__init__(featurewise_center,
samplewise_center,
featurewise_std_normalization,
samplewise_std_normalization,
zca_whitening,
zca_epsilon,
rotation_range,
width_shift_range,
height_shift_range,
shear_range,
zoom_range,
channel_shift_range,
fill_mode,
cval,
horizontal_flip,
vertical_flip,
rescale,
preprocessing_function,
data_format)
self.brighten_range = brighten_range
def flow(self, x_images, x_prev_states = None, y=None, batch_size=32, shuffle=True, seed=None,
save_to_dir=None, save_prefix='', save_format='png', zero_drop_percentage=0.5, roi=None):
return DriveIterator(
x_images, x_prev_states, y, self,
batch_size=batch_size,
shuffle=shuffle,
seed=seed,
data_format=self.data_format,
save_to_dir=save_to_dir,
save_prefix=save_prefix,
save_format=save_format,
zero_drop_percentage=zero_drop_percentage,
roi=roi)
def random_transform_with_states(self, x, seed=None):
"""Randomly augment a single image tensor.
# Arguments
x: 3D tensor, single image.
seed: random seed.
# Returns
A tuple. 0 -> randomly transformed version of the input (same shape). 1 -> true if image was horizontally flipped, false otherwise
"""
img_row_axis = self.row_axis
img_col_axis = self.col_axis
img_channel_axis = self.channel_axis
is_image_horizontally_flipped = False
# use composition of homographies
# to generate final transform that needs to be applied
if self.rotation_range:
theta = np.pi / 180 * np.random.uniform(-self.rotation_range, self.rotation_range)
else:
theta = 0
if self.height_shift_range:
tx = np.random.uniform(-self.height_shift_range, self.height_shift_range) * x.shape[img_row_axis]
else:
tx = 0
if self.width_shift_range:
ty = np.random.uniform(-self.width_shift_range, self.width_shift_range) * x.shape[img_col_axis]
else:
ty = 0
if self.shear_range:
shear = np.random.uniform(-self.shear_range, self.shear_range)
else:
shear = 0
if self.zoom_range[0] == 1 and self.zoom_range[1] == 1:
zx, zy = 1, 1
else:
zx, zy = np.random.uniform(self.zoom_range[0], self.zoom_range[1], 2)
transform_matrix = None
if theta != 0:
rotation_matrix = np.array([[np.cos(theta), -np.sin(theta), 0],
[np.sin(theta), np.cos(theta), 0],
[0, 0, 1]])
transform_matrix = rotation_matrix
if tx != 0 or ty != 0:
shift_matrix = np.array([[1, 0, tx],
[0, 1, ty],
[0, 0, 1]])
transform_matrix = shift_matrix if transform_matrix is None else np.dot(transform_matrix, shift_matrix)
if shear != 0:
shear_matrix = np.array([[1, -np.sin(shear), 0],
[0, np.cos(shear), 0],
[0, 0, 1]])
transform_matrix = shear_matrix if transform_matrix is None else np.dot(transform_matrix, shear_matrix)
if zx != 1 or zy != 1:
zoom_matrix = np.array([[zx, 0, 0],
[0, zy, 0],
[0, 0, 1]])
transform_matrix = zoom_matrix if transform_matrix is None else np.dot(transform_matrix, zoom_matrix)
if transform_matrix is not None:
h, w = x.shape[img_row_axis], x.shape[img_col_axis]
transform_matrix = image.transform_matrix_offset_center(transform_matrix, h, w)
x = image.apply_transform(x, transform_matrix, img_channel_axis,
fill_mode=self.fill_mode, cval=self.cval)
if self.channel_shift_range != 0:
x = image.random_channel_shift(x,
self.channel_shift_range,
img_channel_axis)
if self.horizontal_flip:
if np.random.random() < 0.5:
x = image.flip_axis(x, img_col_axis)
is_image_horizontally_flipped = True
if self.vertical_flip:
if np.random.random() < 0.5:
x = image.flip_axis(x, img_row_axis)
if self.brighten_range != 0:
random_bright = np.random.uniform(low = 1.0-self.brighten_range, high=1.0+self.brighten_range)
img = cv2.cvtColor(x, cv2.COLOR_RGB2HSV)
img[:, :, 2] = np.clip(img[:, :, 2] * random_bright, 0, 255)
x = cv2.cvtColor(img, cv2.COLOR_HSV2RGB)
return (x, is_image_horizontally_flipped)
class DriveIterator(image.Iterator):
"""Iterator yielding data from a Numpy array.
# Arguments
x: Numpy array of input data.
y: Numpy array of targets data.
image_data_generator: Instance of `ImageDataGenerator`
to use for random transformations and normalization.
batch_size: Integer, size of a batch.
shuffle: Boolean, whether to shuffle the data between epochs.
seed: Random seed for data shuffling.
data_format: String, one of `channels_first`, `channels_last`.
save_to_dir: Optional directory where to save the pictures
being yielded, in a viewable format. This is useful
for visualizing the random transformations being
applied, for debugging purposes.
save_prefix: String prefix to use for saving sample
images (if `save_to_dir` is set).
save_format: Format to use for saving sample images
(if `save_to_dir` is set).
"""
def __init__(self, x_images, x_prev_states, y, image_data_generator,
batch_size=32, shuffle=False, seed=None,
data_format=None,
save_to_dir=None, save_prefix='', save_format='png', zero_drop_percentage = 0.5, roi = None):
if y is not None and len(x_images) != len(y):
raise ValueError('X (images tensor) and y (labels) '
'should have the same length. '
'Found: X.shape = %s, y.shape = %s' %
(np.asarray(x_images).shape, np.asarray(y).shape))
if data_format is None:
data_format = K.image_data_format()
self.x_images = x_images
self.zero_drop_percentage = zero_drop_percentage
self.roi = roi
if self.x_images.ndim != 4:
raise ValueError('Input data in `NumpyArrayIterator` '
'should ave rank 4. You passed an array '
'with shape', self.x_images.shape)
channels_axis = 3 if data_format == 'channels_last' else 1
if self.x_images.shape[channels_axis] not in {1, 3, 4}:
raise ValueError('NumpyArrayIterator is set to use the '
'data format convention "' + data_format + '" '
'(channels on axis ' + str(channels_axis) + '), i.e. expected '
'either 1, 3 or 4 channels on axis ' + str(channels_axis) + '. '
'However, it was passed an array with shape ' + str(self.x_images.shape) +
' (' + str(self.x_images.shape[channels_axis]) + ' channels).')
if x_prev_states is not None:
self.x_prev_states = x_prev_states
else:
self.x_prev_states = None
if y is not None:
self.y = y
else:
self.y = None
self.image_data_generator = image_data_generator
self.data_format = data_format
self.save_to_dir = save_to_dir
self.save_prefix = save_prefix
self.save_format = save_format
self.batch_size = batch_size
super(DriveIterator, self).__init__(x_images.shape[0], batch_size, shuffle, seed)
def next(self):
"""For python 2.x.
# Returns
The next batch.
"""
# Keeps under lock only the mechanism which advances
# the indexing of each batch.
with self.lock:
index_array = next(self.index_generator)
# The transformation of images is not under thread lock
# so it can be done in parallel
return self.__get_indexes(index_array)
def __get_indexes(self, index_array):
index_array = sorted(index_array)
if self.x_prev_states is not None:
batch_x_images = np.zeros(tuple([self.batch_size]+ list(self.x_images.shape)[1:]),
dtype=K.floatx())
batch_x_prev_states = np.zeros(tuple([self.batch_size]+list(self.x_prev_states.shape)[1:]), dtype=K.floatx())
else:
batch_x_images = np.zeros(tuple([self.batch_size] + list(self.x_images.shape)[1:]), dtype=K.floatx())
if self.roi is not None:
batch_x_images = batch_x_images[:, self.roi[0]:self.roi[1], self.roi[2]:self.roi[3], :]
used_indexes = []
is_horiz_flipped = []
for i, j in enumerate(index_array):
x_images = self.x_images[j]
if self.roi is not None:
x_images = x_images[self.roi[0]:self.roi[1], self.roi[2]:self.roi[3], :]
transformed = self.image_data_generator.random_transform_with_states(x_images.astype(K.floatx()))
x_images = transformed[0]
is_horiz_flipped.append(transformed[1])
x_images = self.image_data_generator.standardize(x_images)
batch_x_images[i] = x_images
if self.x_prev_states is not None:
x_prev_states = self.x_prev_states[j]
if (transformed[1]):
x_prev_states[0] *= -1.0
batch_x_prev_states[i] = x_prev_states
used_indexes.append(j)
if self.x_prev_states is not None:
batch_x = [np.asarray(batch_x_images)]
else:
batch_x = np.asarray(batch_x_images)
if self.save_to_dir:
for i in range(0, self.batch_size, 1):
hash = np.random.randint(1e4)
img = image.array_to_img(batch_x_images[i], self.data_format, scale=True)
fname = '{prefix}_{index}_{hash}.{format}'.format(prefix=self.save_prefix,
index=1,
hash=hash,
format=self.save_format)
img.save(os.path.join(self.save_to_dir, fname))
batch_y = self.y[list(sorted(used_indexes))]
idx = []
num_of_close_samples = 0
num_of_non_close_samples = 0
for i in range(0, len(is_horiz_flipped), 1):
if batch_y.shape[1] == 1:
if (is_horiz_flipped[i]):
batch_y[i] *= -1
if (np.isclose(batch_y[i], 0.5, rtol=0.005, atol=0.005)):
num_of_close_samples += 1
if (np.random.uniform(low=0, high=1) > self.zero_drop_percentage):
idx.append(True)
else:
idx.append(False)
else:
num_of_non_close_samples += 1
idx.append(True)
else:
if (batch_y[i][int(len(batch_y[i])/2)] == 1):
if (np.random.uniform(low=0, high=1) > self.zero_drop_percentage):
idx.append(True)
else:
idx.append(False)
else:
idx.append(True)
if (is_horiz_flipped[i]):
batch_y[i] = batch_y[i][::-1]
batch_y = batch_y[idx]
batch_x[0] = batch_x[0][idx]
return batch_x, batch_y
def _get_batches_of_transformed_samples(self, index_array):
return self.__get_indexes(index_array)

Просмотреть файл

@ -0,0 +1,49 @@
# Imitation Learning
This section is about training a model to steer our Formula car using imitation learning.
The code in this section is based on the [Autonomous Driving Cookbook](https://github.com/Microsoft/AutonomousDrivingCookbook/tree/master/AirSimE2EDeepLearning) from Airsim and it's highly recommended to read the tutorial first.
## Prerequisites
* Operating system: Windows 10
* GPU: Nvidia GTX 1080 or higher (recommended)
* Software: Unreal Engine 4.18 and Visual Studio 2017 (see [upgrade instructions](../../docs/unreal_upgrade.md))
* Development: CUDA 9.0 and python 3.5.
* Python libraries: Keras 2.1.2, TensorFlow 1.6.0.
* Note: Newer versions of keras or tensorflow are recommended but can cause syntax errors.
## What's inside
![imitation learning](../../docs/images/imitation_learning_example.gif)
*Driving in simulation using trained imitation learning model, based on recorded data*
Imitation learning includes the usage of labeled data as input to a training algorithm with the purpose of having the algorithm imitate the actions of people who recorded the data.
![diagram](../../docs/images/imitation_diagram.PNG)
This diagram is represented by these files:
**cook_data.py**
This file is responsible for preparing .h5 dataset files for the training procedure.
The code rely on having two adjacent folders:
'raw_data' - contains folders of recorded data by airsim's recording method.
'cooked_data' - empty folder to store the .h5 files.
The flag "COOK_ALL_DATA" gives the option to choose all subfolders, or exclude some of them.
**train_model.py**
This file is responsible to train a model using the .h5 dataset files.
The code rely on having two adjacent folders:
'cooked_data' - contains the .h5 dataset files.
'models' - empty folder to store the generated models.
The file will preprocess the data, add augmentations and create a neural network model that predicts the next steering angle.
**drive_model.py**
This file connects to the simulation in order to upload a trained model and drive using it.
By using the predicted steering value, the code calculates related control parameters and maintain driving with steady velocities.
## Training Tips
We recommend on using augmentation and recording techniques.
We give here an example for two methods:
- [CycleLight](../../docs/graphic_features.md) - Animation of a day light cycle in a changeable, potentially very short period of time.
- Shifted images - Altering the cameras position to the right or the left of the car, so that it can record images in extreme conditions. To simulate driving back to the center from those extreme situations, post-process the recorded angle of the steering accordingly (manually).

Просмотреть файл

@ -0,0 +1,38 @@
#%matplotlib inline
import numpy as np
import pandas as pd
import h5py
from matplotlib import use
use("TkAgg")
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
import os
import Cooking
# chunk size for training batches
chunk_size = 32
# No test set needed, since testing in our case is running the model on an unseen map in AirSim
train_eval_test_split = [0.8, 0.2, 0.0]
# Point this to the directory containing the raw data
RAW_DATA_DIR = './raw_data/'
# Point this to the desired output directory for the cooked (.h5) data
COOKED_DATA_DIR = './cooked_data/'
# Choose The folders to search for data under RAW_DATA_DIR
COOK_ALL_DATA = True
data_folders = []
#if COOK_ALL_DATA is set to False, append your desired data folders here
# data_folder.append('folder_name1')
# data_folder.append('folder_name2')
# ...
if COOK_ALL_DATA:
data_folders = [name for name in os.listdir(RAW_DATA_DIR)]
full_path_raw_folders = [os.path.join(RAW_DATA_DIR, f) for f in data_folders]
Cooking.cook(full_path_raw_folders, COOKED_DATA_DIR, train_eval_test_split, chunk_size)

Просмотреть файл

@ -0,0 +1,76 @@
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
from keras.models import load_model
import sys
import time
import numpy as np
import airsim
import keras.backend as K
from keras.preprocessing import image
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
# Trained model path
MODEL_PATH = './models/example_model.h5'
model = load_model(MODEL_PATH)
# Connect to AirSim
client = airsim.CarClient()
client.confirmConnection()
client.enableApiControl(True)
car_controls = airsim.CarControls()
# Start driving
car_controls.steering = 0
car_controls.throttle = 0
car_controls.brake = 0
client.setCarControls(car_controls)
# Initialize image buffer
image_buf = np.zeros((1, 66, 200, 3))
def get_image():
"""
Get image from AirSim client
"""
image_response = client.simGetImages([airsim.ImageRequest("0", airsim.ImageType.Scene, False, False)])[0]
image1d = np.fromstring(image_response.image_data_uint8, dtype=np.uint8)
image_rgba = image1d.reshape(image_response.height, image_response.width, 4)
return image_rgba[78:144,27:227,0:3].astype(float)
while True:
# Update throttle value according to steering angle
if abs(car_controls.steering) <= 1.0:
car_controls.throttle = 0.8-(0.4*abs(car_controls.steering))
else:
car_controls.throttle = 0.4
image_buf[0] = get_image()
image_buf[0] /= 255 # Normalization
start_time = time.time()
# Prediction
model_output = model.predict([image_buf])
end_time = time.time()
received_output = model_output[0][0]
# Rescale prediction to [-1,1] and factor by 0.82 for drive smoothness
car_controls.steering = round((0.82*(float((model_output[0][0]*2.0)-1))), 2)
# Print progress
print('Sending steering = {0}, throttle = {1}, prediction time = {2}'.format(received_output, car_controls.throttle,str(end_time-start_time)))
# Update next car state
client.setCarControls(car_controls)
# Wait a bit between iterations
time.sleep(0.05)
client.enableApiControl(False)

Просмотреть файл

@ -0,0 +1,52 @@
# Import this module to automatically setup path to local airsim module
# This module first tries to see if airsim module is installed via pip
# If it does then we don't do anything else
# Else we look up grand-parent folder to see if it has airsim folder
# and if it does then we add that in sys.path
import os,sys,inspect,logging
#this class simply tries to see if airsim
class SetupPath:
@staticmethod
def getDirLevels(path):
path_norm = os.path.normpath(path)
return len(path_norm.split(os.sep))
@staticmethod
def getCurrentPath():
cur_filepath = os.path.abspath(inspect.getfile(inspect.currentframe()))
return os.path.dirname(cur_filepath)
@staticmethod
def getGrandParentDir():
cur_path = SetupPath.getCurrentPath()
if SetupPath.getDirLevels(cur_path) >= 2:
return os.path.dirname(os.path.dirname(cur_path))
return ''
@staticmethod
def getParentDir():
cur_path = SetupPath.getCurrentPath()
if SetupPath.getDirLevels(cur_path) >= 1:
return os.path.dirname(cur_path)
return ''
@staticmethod
def addAirSimModulePath():
# if airsim module is installed then don't do anything else
#import pkgutil
#airsim_loader = pkgutil.find_loader('airsim')
#if airsim_loader is not None:
# return
parent = SetupPath.getParentDir()
if parent != '':
airsim_path = os.path.join(parent, 'airsim')
client_path = os.path.join(airsim_path, 'client.py')
if os.path.exists(client_path):
sys.path.insert(0, parent)
else:
logging.warning("airsim module not found in parent folder. Using installed package (pip install airsim).")
SetupPath.addAirSimModulePath()

Просмотреть файл

@ -0,0 +1,94 @@
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential, Model
from keras.layers.convolutional import Convolution2D
from keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, Lambda, Input, concatenate
from keras.layers.core import Activation
from keras.layers.normalization import BatchNormalization
from keras.layers.advanced_activations import ELU, LeakyReLU
from keras.optimizers import Adam, SGD, Adamax, Nadam
from keras.callbacks import ReduceLROnPlateau, ModelCheckpoint, CSVLogger, EarlyStopping
import keras.backend as K
from keras.preprocessing import image
from keras_tqdm import TQDMNotebookCallback
import json
import os
import numpy as np
import pandas as pd
from Generator import DriveDataGenerator
import h5py
import math
# Hyper-parameters
batch_size = 32
learning_rate = 0.0001
number_of_epochs = 500
# Activation functions
activation = 'relu'
out_activation = 'sigmoid'
#Stop training if in the last 20 epochs, there was no change of the best recorded validation loss
training_patience = 20
# << The directory containing the cooked data from the previous step >>
COOKED_DATA_DIR = './cooked_data/'
# << The directory in which the model output will be placed >>
MODEL_OUTPUT_DIR = './models/'
train_dataset = h5py.File(os.path.join(COOKED_DATA_DIR, 'train.h5'), 'r')
eval_dataset = h5py.File(os.path.join(COOKED_DATA_DIR, 'eval.h5'), 'r')
num_train_examples = train_dataset['image'].shape[0]
num_eval_examples = eval_dataset['image'].shape[0]
# Use ROI of [78,144,27,227] for FOV 60 with Formula car
data_generator = DriveDataGenerator(rescale=1./255., horizontal_flip=False, brighten_range=0.4)
train_generator = data_generator.flow\
(train_dataset['image'], train_dataset['previous_state'], train_dataset['label'], batch_size=batch_size, zero_drop_percentage=0.95, roi=[78,144,27,227])
eval_generator = data_generator.flow\
(eval_dataset['image'], eval_dataset['previous_state'], eval_dataset['label'], batch_size=batch_size, zero_drop_percentage=0.95, roi=[78,144,27,227])
[sample_batch_train_data, sample_batch_test_data] = next(train_generator)
image_input_shape = sample_batch_train_data[0].shape[1:]
pic_input = Input(shape=image_input_shape)
#Network definition
img_stack = Conv2D(24, (5, 5), name="conv1", strides=(2, 2), padding="valid", activation=activation, kernel_initializer="he_normal")(pic_input)
img_stack = Conv2D(36, (5, 5), name="conv2", strides=(2, 2), padding="valid", activation=activation, kernel_initializer="he_normal")(img_stack)
img_stack = Conv2D(48, (5, 5), name="conv3", strides=(2, 2), padding="valid", activation=activation, kernel_initializer="he_normal")(img_stack)
img_stack = Dropout(0.5)(img_stack)
img_stack = Conv2D(64, (3, 3), name="conv4", strides=(1, 1), padding="valid", activation=activation, kernel_initializer="he_normal")(img_stack)
img_stack = Conv2D(64, (3, 3), name="conv5", strides=(1, 1), padding="valid", activation=activation, kernel_initializer="he_normal")(img_stack)
img_stack = Flatten(name = 'flatten')(img_stack)
img_stack = Dense(100, name="fc2", activation=activation,kernel_initializer="he_normal")(img_stack)
img_stack = Dense(50, name="fc3", activation=activation, kernel_initializer="he_normal")(img_stack)
img_stack = Dense(10, name="fc4", activation=activation, kernel_initializer="he_normal")(img_stack)
img_stack = Dense(1, name="output", activation = out_activation, kernel_initializer="he_normal")(img_stack)
adam = Adam(lr=learning_rate, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
model = Model(inputs=[pic_input], outputs=img_stack)
model.compile(optimizer=adam, loss='mse')
model.summary()
plateau_callback = ReduceLROnPlateau(monitor="val_loss", factor=0.5, patience=3, min_lr=learning_rate, verbose=1)
csv_callback = CSVLogger(os.path.join(MODEL_OUTPUT_DIR, 'training_log.csv'))
checkpoint_filepath = os.path.join(MODEL_OUTPUT_DIR, 'fresh_models', '{0}_model.{1}-{2}.h5'.format('model', '{epoch:02d}', '{val_loss:.7f}'))
checkpoint_callback = ModelCheckpoint(checkpoint_filepath, save_best_only=True, verbose=1)
early_stopping_callback = EarlyStopping(monitor="val_loss", patience=training_patience, verbose=1)
callbacks=[plateau_callback, csv_callback, checkpoint_callback, early_stopping_callback, TQDMNotebookCallback()]
history = model.fit_generator(train_generator, steps_per_epoch=num_train_examples//batch_size, epochs=number_of_epochs, callbacks=callbacks,\
validation_data=eval_generator, validation_steps=num_eval_examples//batch_size, verbose=2)