AWS DeepRacerを強化する 報酬関数実装パターンあれこれ

DeepRacer League Virtual Circuitで優勝してAWS re:Invent 2019に招待されたいDI部の貞松です。

DeepRacer Leagueで勝利を収めるためには、DeepRacerの学習モデルを改善して強化していく必要があります。 現状DeepRacerコンソールのモデル作成画面から編集可能な要素としては主に以下の3つです。

  • 行動パターン
  • 報酬関数
  • ハイパーパラメータ

基本的にハイパーパラメータは(強化学習アルゴリズムに対する理解に基づく理由がない限り)デフォルトの状態で学習するのが良いと考えられます。 あとは行動パターンと報酬関数ですが、行動パターンと報酬関数の実装にはある程度の依存関係があるので、同時に設計する必要があります。 本エントリーでは、行動パターンの設定について軽く触れつつ、DeepRacerの学習に対して特定の意図を組み込んだ報酬関数の実装パターンを幾つかご紹介します。

行動パターンの設定

DeepRacerの行動パターンはハンドルの操作角とスピードの最大値および数値の刻み幅(行動の数)を入力することで設定します。

最終的に行動パターンはハンドルの操作角とスピードのパターンの掛け合わせで設定されます。

ここで設定した行動パターンによって、報酬関数の実装時に判定基準の閾値を調整したり、そもそもハンドルの操作角やスピードに関する報酬設定をする必要が無くなる可能性がある為、行動パターンと報酬関数の設計については併せて考えることが重要です。

報酬関数の実装

報酬関数の実装では、DeepRacerの現在の状態パラメータ(params)を元にして、どういった状態であれば良いとみなして報酬を与える(もしくは悪いとみなしてペナルティを与える)かを考えます。 状態パラメータとそれを用いた報酬関数のサンプルについては、下記のページに記載されているので、基本的にそれに沿った内容を以下で解説します。

Input Parameters of the AWS DeepRacer Reward Function

パターン1:センターラインに沿って走行させる

センターラインからの距離を用いて、センターラインに近いほど高い報酬を与えます。
センターラインに沿って走行させることで、コースアウトせずコースを1周完走できるように学習させようという意図です。

def reward_function(params):
    # トラックの幅とセンターラインからの距離をparamsから取得
    track_width = params['track_width']
    distance_from_center = params['distance_from_center']

    # センターラインからの距離に応じて報酬を与える
    marker_1 = 0.1 * track_width
    marker_2 = 0.5 * track_width

    if distance_from_center <= marker_1:
        reward = 1.0
    elif distance_from_center <= marker_2:
        reward = 0.5
    else:
        reward = 1e-3  # センターラインからの距離がmarker_2を超えている場合はコースアウトしている

    return reward

パターン2:早いスピードで走行させる

現在のスピード(m/s)を取得して、スピードが速い程高い報酬を与えます。 スピードが速くてもコースアウトしてしまうと意味がないのでコースアウト判定も必要です。
コース1周を通して、出来るだけスピードに乗った状態をキープしてラップタイムを短縮しようという意図です。

※ 行動パターンの設定でスピードのパターンが1種類のみの場合は、この報酬関数は意味を成さないので注意してください。

define reward_function(params):
    # 全てのタイヤがトラック上にあるか否か(コースアウト判定)とスピード(m/s)をparamsから取得
    all_wheels_on_track = params['all_wheels_on_track']
    speed = params['speed']

    # 高速と低速を判別する閾値を定義(行動パターンの設定によって変動する)
    SPEED_THRESHOLD = 3.0 

    if not all_wheels_on_track:
        # コースアウト
        reward = 1e-3
    elif speed < SPEED_THRESHOLD:
        # 低速走行の為、報酬は少なめ
        reward = 0.5
    else:
        # 高速走行の為、報酬は多め
        reward = 1.0

    return reward

パターン3:急ハンドルやジグザグ走行を抑制する

ハンドルの操作角を取得して、操作角が大きすぎる場合はペナルティを与えます。
急ハンドルを切ったり、細かくハンドルを切ってジグザグに走行するのを抑制する意図です。

def reward_function(params):
    # ハンドルの操作角(-30°〜30°)をparamsから取得
    # 操作角の絶対値を計算(右旋回、左旋回問わず角度の大きさで判断する)
    steering = abs(params['steering_angle']) 

    # 規定の報酬を設定する
    reward = 1.0

    # 急ハンドルを判定する為の閾値を定義して、それ以上の操作角だった場合にペナルティを与える
    # 閾値は行動パターンの設定によって変動する
    STEERING_THRESHOLD = 20.0
    if steering > ABS_STEERING_THRESHOLD:
        reward *= 0.8

    return reward

パターン4:コース上に設定されたポイントに向かって走るように制御する

DeepRacerで用意されているコースにはwaypoint(コース上に配置された次に向かうべき地点の指標)が設定されています。

車体が次に向かうべきwaypointに向いた状態で走行できるように、現在の車体の向きと直近のwaypointを繋いだ方向の差分を計算し、 差分が大きい場合はペナルティを与えます。

def reward_function(params):
    import math

    # トラック上のwaypointと直近のwaypoint、およびコース上の基準軸に対する車体の向きをparamsから取得
    waypoints = params['waypoints']
    closest_waypoints = params['closest_waypoints']
    heading = params['heading']

    # 規定の報酬を設定する
    reward = 1.0

    # 現在の位置から最も近い次のwaypointと前のwaypointを取得する
    next_point = waypoints[closest_waypoints[1]]
    prev_point = waypoints[closest_waypoints[0]]

    # 前のwaypointから次のwaypointに向かう角度(radian)を計算する
    track_direction = math.atan2(next_point[1] - prev_point[1], next_point[0] - prev_point[0]) 
    # degreeに変換
    track_direction = math.degrees(track_direction)

    # コース上の基準軸に対する車体の向きと直近のwaypointを繋ぐ向きの差分を取る
    direction_diff = abs(track_direction - heading)

    # 算出した方向の差分から車体の向きが大きくズレている場合にペナルティを与える
    # 閾値の設定はコースの種類によって調整が必要
    DIRECTION_THRESHOLD = 10.0
    if direction_diff > DIRECTION_THRESHOLD:
        reward *= 0.5

    return reward

まとめ

DeepRacerの学習モデルを強化する為の報酬関数の実装パターンについてご紹介しました。

実際には、これらを組み合わせたり、閾値を調整したり、はたまた状態に対して報酬を与えるのかペナルティを与えるのか等、対象のコース特性に合わせて様々な実装を試すことになります。試行錯誤を繰り返しながら、DeepRacerを強化して勝利を掴み取りましょう。