<jupyter_start><jupyter_text>Behavioral Cloning Project using Nvidia network<jupyter_code># Import modules
import csv
import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.image as mpimg
import random
import sklearn
from sklearn.utils import shuffle
# In this model only the lateral images are used; the steering angle is corected with 'corr'
corr = 0.25
samples = []
# to ensure generalization the data was collected driving in a clockwise direction;
# in this way, when the car is actually tested on the track it will see completely new images
with open('.\\data\\driving_log.csv') as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        samples.append(line)

# the data is split in train and validation before any image is seen; this will speed up the algorithm
from sklearn.model_selection import train_test_split
train_samples, validation_samples = train_test_split(samples, test_size=0.2)<jupyter_output><empty_output><jupyter_text>**I recommend plotting a histogram of the angles in the training dataset to check the balance of the output. Remember that an unbalanced dataset can result in a biased model.**<jupyter_code># the cases when the steering angle is null in fact means no action for the car
# to avoid this cases, when the car has no reaction, we only use only the lateral images
def generator(samples, batch_size=128):
    num_samples = len(samples)
    while 1:
        shuffle(samples, random_state=17)
        for offset in range(0, num_samples, batch_size):   
            batch_samples = samples[offset:offset+batch_size]
            images = []
            steering = []
            for batch_sample in batch_samples:
                angle = float(batch_sample[3])
                
                # Left images
                filename = '.\\data\\IMG\\'+batch_sample[1].split('\\')[-1]
                image = mpimg.imread(filename)
                images.append(image)
                steering.append(angle+corr)
                # augementing the data with the reflected image
                images.append(cv2.flip(image,1))
                steering.append((angle+corr)*(-1.0))
                
                # Right images
                filename = '.\\data\\IMG\\'+batch_sample[2].split('\\')[-1]
                image = mpimg.imread(filename)
                images.append(image)
                steering.append(angle-corr)
                # augementing the data with the reflected image
                images.append(cv2.flip(image,1))
                steering.append((angle+corr)*(-1.0))
            
            X_train = np.array(images)
            y_train = np.array(steering)
            yield shuffle(X_train, y_train, random_state=19)<jupyter_output><empty_output><jupyter_text>**I recommend using a different generator without augmentation for validation to simulate real-world usage.**<jupyter_code># applying the generator function to create the training and validation data sets
train_generator = generator(train_samples, batch_size=128)
validation_generator = generator(validation_samples, batch_size=128)<jupyter_output><empty_output><jupyter_text>Creating the network<jupyter_code># importing network modules
import keras as ks
from keras.models import Sequential
from keras.layers import Lambda, Cropping2D, Conv2D, Flatten, Dense
# this function will resize the images to the same size as in the Nvidia paper
# after this, the images are normalized for faster convergence
def resize_normalize_function(input):
    from keras.backend import tf
    resized = tf.image.resize_images(input, (66,200))
    normalized = resized / 255.0 - 0.5
    return normalized

# initialize network
model = Sequential()
# crop the top and bottom
model.add(Cropping2D(cropping=((75, 25), (0, 0)), input_shape=(160,320,3)))
# resize and normalize images; the wonders of lambda functions
model.add(Lambda(resize_normalize_function))

# 3 convolutions with 2x2 strides and 2 convolutions with default stride
model.add(Conv2D(24, (5,5), strides=(2, 2), activation='relu'))
model.add(Conv2D(36, (5, 5), strides=(2, 2), activation='relu'))
model.add(Conv2D(48, (5, 5), strides=(2, 2), activation='relu'))
model.add(Conv2D(64, (3,3), strides=(1, 1), activation='relu'))
model.add(Conv2D(64, (3,3), strides=(1, 1), activation='relu'))

# flatten and start the fully conected layers
model.add(Flatten())
model.add(Dense(100))
model.add(Dense(50))
model.add(Dense(10))

# the output layer
model.add(Dense(1))
adam = ks.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)
model.compile(loss='mse', optimizer=adam)
# three epochs is just enough
model.fit_generator(train_generator, steps_per_epoch=len(train_samples), validation_data=validation_generator, validation_steps=len(validation_samples), epochs=3)
# saving the model
model.save('./model.h5')<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup