3 minute read

Balanceo de Dataset

El objetivo para esta semana era el de balancear nuestro dataset (creado la semana anterior) y entrenar un modelo centrándonos en la predicción del volante.

Esta semana hemos creado un nuevo dataset, esta vez centrándonos en crear muestras en las que haya más acción del volante (steer), además de la del acelerador, para así probar la predicción del volante de dos tipos de arquitecturas: PilotNet (NVIDIA) y MobileNet (Google).

Una vez creado el dataset, de 120¡250 frames, se ha realizado una representación de su histograma tridimensional, pera ver el número de muestras que tenemos para cada caso: giro a la izquierda con acelerador al máximo (steer= -0.5, throttle = 1.0), por ejemplo.

Dataset

Como se puede ver, el dataset está desbalanceado. El número de muestras en que [steer = 0 , throttle = 1 ó 0] superan de una manera muy importante a los demás casos en los que se realiza un giro, por ejemplo. Para ello hemos realizado una disminución de los dos tipo de caso más frecuentes. El dataset balanceado resultante es de 7055 muestras. A continuación, se muestra el histograma en 3D del dataset balanceado:

Dataset balanceado

Entrenamiento con PilotNet

Una vez ya tenemos balanceado el dataset, hemos creado un modelo PilotNet: con 5 capas convolucionales con función de activación ReLU y un cabezal denso también con función ReLU, y con función de pérdida MSE (usamos el optimizador Adam(1e-4) para minimizarla).

def pilotnet(input_shape=(120, 160, 3), n_outputs=3, l2=1e-4, dropout=0.2):
    reg = tf.keras.regularizers.l2(l2) if l2 and l2 > 0 else None
    kw  = dict(kernel_initializer="he_normal", kernel_regularizer=reg)

    inputs = layers.Input(shape=input_shape)

    # Convolucional (estilo NVIDIA PilotNet)
    x = layers.Conv2D(24, 5, strides=2, activation='relu', **kw)(inputs)
    x = layers.Conv2D(36, 5, strides=2, activation='relu', **kw)(x)
    x = layers.Conv2D(48, 5, strides=2, activation='relu', **kw)(x)
    x = layers.Conv2D(64, 3, strides=1, activation='relu', **kw)(x)
    x = layers.Conv2D(64, 3, strides=1, activation='relu', **kw)(x)

    x = layers.Flatten()(x)

    # Cabezal denso
    x = layers.Dense(100, activation='relu', **kw)(x)
    if dropout and dropout > 0:
        x = layers.Dropout(dropout)(x)
    x = layers.Dense(50, activation='relu', **kw)(x)
    x = layers.Dense(10, activation='relu', **kw)(x)

    outputs = layers.Dense(n_outputs, activation='linear', name='controls')(x)

    model = models.Model(inputs, outputs, name="PilotNet_120x160x3")
    model.compile(
        optimizer=tf.keras.optimizers.Adam(1e-4),
        loss='mse',
        metrics=[tf.keras.metrics.MeanAbsoluteError(name='mae')]
    )
    return model

model = pilotnet(input_shape=(120, 160, 3), n_outputs=3, l2=1e-4, dropout=0.2)
model.summary()

Para probar el modelo y cómo predice el valor de steer, hemos realizado una simulación en la que el vehículo tiene el acelerador constante, throttle = 0.45, y freno sin pulsar (brake = 0). Tras varias pruebas realizadas, detectamos que en los primeros minutos de conducción tiene un buen control del volante. No obstante, a los 2 minutos aproximadamente, con este modelo, se desvía de la carretera y choca con obstáculos de la calle. A continuación, un video de una de las simulaciones realizadas: https://youtu.be/LEmVjOHfuyY

Entrenamiento con MobileNet

Hemos entrenado también el modelo MobileNet, al congelado las capas base y hemos añadido un cabezal de regresión. Se ha usado la función de pérdida MSE y el optimizador Adam.

base_model = MobileNetV2(input_shape=(IMG_HEIGHT, IMG_WIDTH, 3),
                         include_top=False,
                         weights='imagenet')

# Congelar las capas base
base_model.trainable = False

# Añadir cabezal de regresión
inputs = tf.keras.Input(shape=(IMG_HEIGHT, IMG_WIDTH, 3))
x = base_model(inputs, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(3, activation='tanh')(x)
model = models.Model(inputs, outputs)

model.compile(optimizer=tf.keras.optimizers.Adam(1e-4), loss='mse')
model.summary()

history = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs= 50, batch_size=32)

Tras entrenar el modelo con el dataset balanceado, se ha realizado el mismo test que con el anterior modelo, una simulación a velocidad constante a la que le hemos prestado especial atención al valor de steer. Tras varios intentos, se ha podido observar que los primeros minutos sí que el control del volante es estable y acorde con el escenario o la situación de la conducción. Sin embargo, se autocontrola correctamente durante los primeros minutos de simulación (hasta 4 minutos seguidos). A continuación, un video de una de las simulaciones realizadas: https://youtu.be/b-eWsj4DkCg

Resumen

Esta semana hemos balanceado nuestro dataset (reduciendo el número de los casos más frecuentes), hemos entrenado los modelos PilotNet y MobileNet, y posteriormente realizado una simulación a modo de test con el valor de throttle constante y con la predicción, de nuestros modelos entrenados, del valor de steer.

Observando la conducción con ambos modelos, se puede concluir de forma muy orientativa que la conducción “más exitosa” ha sido con MobileNet, pues además de corregir las desviaciones del volante al seguir una trayectoria recto o al desviarse de la carretera, ha conseguido estar activo durante más tiempo que en la conducción con PilotNet.

Las próximas semanas queda seguir mejorando el código de los modelos y optimizarlos para mejorar el entrenamiento, o bien mejorar la entrada de datos.