DEV Community

Guillermo Ruiz for AWS Español

Posted on

Descubriendo AWS DeepRacer: Diseñando tu función de Recompensa

¿Cómo Crear una Función de Recompensa Efectiva?

Esta es una de las preguntas que más nos hacen. En este blog, compartiremos algunos consejos sobre cómo diseñar (o mejorar). tu función de recompensa.

1. Lo primero de todo es entender el circuito. ¿Tiene muchas curvas cerradas? ¿Hay zonas estrechas? La función de recompensa debería reflejar las características específicas de la pista.

Image description

2. Recompensas Incrementales: En lugar de dar recompensas solo cuando el coche alcanza ciertos hitos (como completar una vuelta), es mejor dar recompensas incrementales por comportamientos deseables, como mantenerse en el centro del carril o acercarse a un punto de control.

Para crear una función de recompensa que otorgue recompensas incrementales basándonos, por ejemplo, en qué tan cerca está el agente de la línea central de la pista, podemos emplear una escala lineal o exponencial para aumentar la recompensa. Aquí tenéis un ejemplo de cómo podríais hacerlo:

def reward_function(params):
    '''
    Reward function that provides incremental rewards for staying closer to the center line of the track.
    '''

    # Read input parameters
    all_wheels_on_track = params['all_wheels_on_track']
    distance_from_center = params['distance_from_center']
    track_width = params['track_width']

    # Initialize the reward with a tiny number to avoid zero
    reward = 1e-3

    # Calculate the distance from the center as a percentage of the track width
    distance_percentage = distance_from_center / (track_width / 2)

    # Check if all four wheels are on the track
    if all_wheels_on_track:
        # Provide a reward based on the inverse of distance percentage
        reward = max(1e-3, (1 - distance_percentage)**2)

    # Penalize if the car goes off track
    else:
        reward = 1e-3

    # Always return a float value
    return float(reward)
Enter fullscreen mode Exit fullscreen mode

En esta función de recompensa, distance_percentage calcula qué tan lejos está el agente del centro de la pista como un porcentaje del ancho total. Utilizamos el cuadrado de la inversa de este porcentaje para calcular la recompensa, de modo que cuanto más cerca esté el agente del centro, mayor será la recompensa. Si el agente está en el centro, la recompensa es máxima (1.0), y decrece a medida que se aleja del centro.

3. Penalizaciones Claras: Si el coche se sale de la pista o realiza acciones no deseadas, asegúrate de penalizarlo claramente. Esto ayudará a que el modelo aprenda rápidamente qué acciones evitar.

Por ejemplo, si queremos diseñar una función de recompensa que únicamente penalice al vehículo cuando se sale de la pista, podríamos indicarle que:

def reward_function(params):
    '''
    Reward function that penalizes the agent only if it goes off track.
    '''

    # Read input parameters
    all_wheels_on_track = params['all_wheels_on_track']

    # Initially give a default reward
    reward = 1.0

    # Penalize the agent heavily if any wheel is off the track
    if not all_wheels_on_track:
        reward = 1e-3  # Small reward, acting as a penalty

    # Always return a float value
    return float(reward)
Enter fullscreen mode Exit fullscreen mode

Esta función de recompensa es muy sencilla: comienza asignando una recompensa predeterminada (en este caso 1.0), que es lo que el agente recibe si todas las ruedas se mantienen en la pista. Si el vehículo se sale de la pista (es decir, all_wheels_on_track es False), entonces recibe una penalización significativa reduciendo la recompensa a un valor muy pequeño (1e-3). Esto crea un fuerte incentivo para que el agente evite salirse de la pista, ya que hacerlo resulta en una pérdida casi total de la recompensa.

4. Empezad con funciones simples. Muchas veces los participantes quieren crear una función que de primeras tenga todas las opciones posibles…ERROR! Es recomendable buscar la simplicidad primero. Comenzar con una función de recompensa simple y luego ir añadiendo complejidad gradualmente. Es más fácil diagnosticar problemas y hacer ajustes a medida que progresas.

Por ejemplo, tengo una función de recompensa por mantenernos dentro de los límites de la pista:

def reward_function(params):
    '''
    Example of rewarding the agent to stay inside the two borders of the track
    '''

    # Read input parameters
    all_wheels_on_track = params['all_wheels_on_track']
    distance_from_center = params['distance_from_center']
    track_width = params['track_width']

    # Give a very low reward by default
    reward = 1e-3

    # Give a high reward if no wheels go off the track and
    # the agent is somewhere in between the track borders
    if all_wheels_on_track and (0.5*track_width - distance_from_center) >= 0.05:
        reward = 1.0

    # Always return a float value
    return float(reward)
Enter fullscreen mode Exit fullscreen mode

La recompensa funciona correctamente, pero queremos añadirle que no sólo no se salga del circuito sino fomentar que el agente siga la línea central de la pista. Podemos modificar la función de recompensa anterior para que ofrezca mayores recompensas cuanto más cerca esté el agente del centro de la pista.

def reward_function(params):
    '''
    Reward function that rewards the agent for staying close to the center of the track.
    '''

    # Read input parameters
    all_wheels_on_track = params['all_wheels_on_track']
    distance_from_center = params['distance_from_center']
    track_width = params['track_width']

    # Calculate the distance from the track center
    marker_1 = 0.1 * track_width
    marker_2 = 0.25 * track_width
    marker_3 = 0.5 * track_width

    # Start with a small reward
    reward = 1e-3

    # Give a higher reward if the car is closer to the centerline
    if all_wheels_on_track:
        if distance_from_center <= marker_1:
            reward = 1.0  # Closest to the center
        elif distance_from_center <= marker_2:
            reward = 0.5  # Moderately close
        elif distance_from_center <= marker_3:
            reward = 0.1  # Farther from the center but still on the track

    # Always return a float value
    return float(reward)
Enter fullscreen mode Exit fullscreen mode

En esta versión, la recompensa se gradúa según la proximidad del agente al centro de la pista. Se establecen tres "marcadores" como puntos de referencia para determinar qué tan cerca está el agente del centro y se otorgan diferentes niveles de recompensa en consecuencia. El marcador más cercano al centro proporciona la mayor recompensa, incentivando así al agente a mantenerse lo más cercano posible al centro de la pista.

5. Otro punto en el que cae mucha gente. La función de recompensa perfecta en la teoría puede no serlo en la práctica. Hay que entrenar el modelo, observar su comportamiento y ajustar la función según sea necesario. Es un proceso iterativo.

6. Utiliza Todos los Parámetros Disponibles: DeepRacer proporciona varios parámetros en el diccionario de entrada de la función de recompense.

Image description

7. Evita Recompensas Conflictivas: si recompensas al coche por ir rápido y también por tomar curvas con cuidado, estas recompensas pueden entrar en conflicto en una curva cerrada.

def reward_function(params):
    '''
    Conflicting reward function that rewards for both high speed and careful cornering.
    '''

    # Read input parameters
    all_wheels_on_track = params['all_wheels_on_track']
    speed = params['speed']
    waypoints = params['waypoints']
    closest_waypoints = params['closest_waypoints']
    heading = params['heading']
    is_offtrack = params['is_offtrack']

    # Define the speed threshold
    SPEED_THRESHOLD = 3.0

    # High reward for high speed
    reward = speed/SPEED_THRESHOLD

    # Determine if the agent is in a straight way or a corner
    next_point = waypoints[closest_waypoints[1]]
    prev_point = waypoints[closest_waypoints[0]]

    # Calculate the direction of the center line based on the next waypoints
    track_direction = math.atan2(next_point[1] - prev_point[1], next_point[0] - prev_point[0])
    track_direction = math.degrees(track_direction)

    # Calculate the difference between the track direction and the heading direction of the car
    direction_diff = abs(track_direction - heading)
    if direction_diff > 180:
        direction_diff = 360 - direction_diff

    # Reward careful cornering
    # Penalize if the direction difference is large (car is not heading in the intended track direction)
    if direction_diff > 10:
        reward *= 0.5  # Penalty for not aligning with the track direction

    # Check if car is off the track
    if is_offtrack:
        reward = 1e-3  # Minimum reward when off track

    # Always return a float value
    return float(reward)
Enter fullscreen mode Exit fullscreen mode

En esta función:

  1. Recompensamos al coche por mantener una alta velocidad relativa al umbral definido.

  2. Se calcula la dirección deseada de la pista utilizando los puntos de referencia (waypoints) más cercanos.

  3. Recompensamos al coche por alinear su dirección de movimiento (heading) con la dirección de la pista (track_direction). Cuanto mayor sea la desalineación, mayor será la penalización, lo que podría entrar en conflicto con la recompensa por velocidad si el coche intenta tomar las curvas a alta velocidad.

  4. Si el coche se sale de la pista, se le asigna una recompensa mínima.

Como veis, podríamos hacer que el agente tenga un comportamiento conflictivo, especialmente en las curvas, donde debe elegir entre 1/mantener la velocidad y 2/alinear correctamente su dirección con la pista para evitar penalizaciones.

8. Entrena, entrena y entrena. Alcanzar una función de recompensa óptima es el objetivo final. Sin embargo, no hay atajos para llegar a la meta. La clave está en la práctica constante y el entrenamiento continuo.

Buena suerte a todos!

Top comments (0)