はじめに
前回の記事で、DeepRacer on AWSの環境構築(Launch Wizardでのデプロイからログインまでの)をまとめました。今回はその続きとして、実際にモデルを作成し、報酬関数を書いて訓練を開始するまでを振り返ります。
学生時代にDeepRacer Studentで日本3位を取った身としては「勝手が変わっているだろうな」という予想はあったのですが、想像より細かい部分まで進化していました。今回はその発見を中心に、初回でつまずいたポイントも含めて記録します。
モデル作成:Step 1 モデル名と環境
「Your models」→「Create model」から新規モデルの作成に入ります。
強化学習の基本構造(エージェント・環境・状態・行動・報酬)を説明する概要図が表示されました。DeepRacerの強化学習では、エージェント(車)は環境(トラック)と相互作用し、特定の行動を行うことで報酬を受け取ることで学習します。 訓練プロセスは、エージェントの現在の状態を運転判断にマッピングする関数(ポリシー)であって、エージェントが経験する平均総報酬を最大化するものを探索するために、車の走行を何度もシミュレートします。
ここは学生時代の記憶とほぼ変わらない概念だったので、懐かしさを感じました。モデル名を入力し、次のトラック選択に進みます。
トラック選択
トラック一覧の中に、re:Invent 2018を見つけました。Monzaにインスパイアされた、初のChampionship Cupトラックであり、この短いクラシックスピードウェイは長らく初心者お気に入りのコースとして定番になっています。
まさに当時、自分が走らせていたコースそのものだったので、迷わずこれを選びました。懐かしさ重視のプロジェクトとしては、ここは外せないポイントだと思います。
Step 2: センサーとハイパーパラメータ
センサーはカメラ(単眼)/ステレオカメラ/LIDARの3択でした。単眼カメラでは、物体はトラック上の2レーンに固定位置で均等に配置され、位置はエピソードごとに変化しません。訓練は固定位置の物体に対して速く収束する傾向がありますが、モデルは過学習しやすく、実世界のトラックに汎化しにくい可能性があります。 一方ステレオカメラでは物体がランダムな位置に配置され、収束にはより時間がかかりますが、より頑健なモデルになります。
今回はシンプルに単眼カメラのまま進めました。
アルゴリズム:PPO vs SAC
もう1つの大きな選択肢が訓練アルゴリズムでした。PPOは最先端のポリシー勾配アルゴリズムで、訓練中にポリシーネットワークと価値ネットワークという2つのニューラルネットワークを使用します。 一方SACは生涯報酬の最大化のみを追求するのではなく、探索を重視し、最適な方針を追求する過程でエントロピー(探索の多様性)にインセンティブを与えるアルゴリズムです。
DeepRacerでは昔からPPOが定番として使われてきた実績があるため、今回はPPOを選択しました。
ハイパーパラメータ
Gradient descent batch size、Number of epochs、Learning rate、Entropy、Discount factor、Loss type、Number of experience episodes between each policy-updating iteration、と項目自体は当時の記憶とほぼ一致していました。今回は初回訓練ということで、すべて既定値のまま進めています(既定値はDeepRacerコミュニティでも広く使われている値のため、まずはここを触らず報酬関数側に集中するのがセオリーです)。
Step 3: Action space(行動空間)の定義 — ここが一番の変化点
ここが今回、個人的に一番驚いたポイントでした。強化学習において、エージェントが環境と相互作用する際に取りうる有効な行動の集合をaction spaceと呼びます。DeepRacer on AWSコンソールでは、discrete(離散)またはcontinuous(連続)のaction spaceでエージェントを訓練できます。 モデルを訓練する際、action spaceはエージェントが取れる速度とステアリング角度の組み合わせを定義します。
学生時代のDeepRacerは基本的にdiscrete(離散)action spaceが主流で、「speed 1.0/angle -30度」のような固定の組み合わせリストから選ぶ形式でした。ところが今の既定選択はcontinuous(連続)action spaceになっていました。
連続的な範囲設定において、エージェントは提供された最小/最大の範囲から最適な速度とステアリング値を訓練を通じて選ぶことを学習します。値の範囲を提供することはより良い選択肢のように見えますが、エージェントが最適な行動を選択できるようになるまでより長い訓練が必要になります。
つまり、「決められた選択肢から選ぶ」時代から「範囲の中から自分で最適値を探す」時代に変わっていたわけです。これは学習の柔軟性が上がった反面、訓練にかかる時間・コストの感覚も変えていく必要がありそうです。
今回は、ステアリング角度を狭め(左右±20度、既定は±30度)、速度も抑えめ(1〜3m/s)に設定して、初回は安定性重視で進めることにしました。
Step 4: シェル選択(見た目のカスタマイズ)
車体シェルはパフォーマンスに影響を与えず、見た目だけのものです。 これは昔からある機能で、性能に一切影響しないお楽しみ要素です。DeepRacer公式のシェルを選びました。
Step 5: 報酬関数のカスタマイズ
いよいよ本題です。reward_functionの入力パラメータには、track_width、distance_from_center、all_wheels_on_track、speed、steering_angle、heading、waypoints、closest_waypointsなど多数の情報が含まれています。
初回は「まず安定して完走できる」ことを目標に、以下の3要素を組み合わせた報酬関数を書きました。
- 中心線からの距離に応じた減点(コースアウト対策)
- 進行方向と次waypoint方向の一致度(逆走・大回りの抑制)
- 中心線に近く、方向も合っている時だけ加速を評価する速度インセンティブ
import math
def reward_function(params):
'''
re:Invent 2018トラック向け 初回動作確認用の報酬関数
- 中心線追従 + 進行方向一致 + 速度インセンティブ
'''
track_width = params['track_width']
distance_from_center = params['distance_from_center']
all_wheels_on_track = params['all_wheels_on_track']
speed = params['speed']
steering = abs(params['steering_angle'])
heading = params['heading']
waypoints = params['waypoints']
closest_waypoints = params['closest_waypoints']
reward = 1.0
# 1. 中心線からの距離に応じた減点
marker_1 = 0.1 * track_width
marker_2 = 0.25 * track_width
marker_3 = 0.5 * track_width
if distance_from_center <= marker_1:
center_factor = 1.0
elif distance_from_center <= marker_2:
center_factor = 0.6
elif distance_from_center <= marker_3:
center_factor = 0.2
else:
center_factor = 1e-3
reward *= center_factor
# 2. 進行方向と次waypoint方向の一致度
next_point = waypoints[closest_waypoints[1]]
prev_point = waypoints[closest_waypoints[0]]
track_direction = math.atan2(
next_point[1] - prev_point[1],
next_point[0] - prev_point[0]
)
track_direction_deg = math.degrees(track_direction)
direction_diff = abs(track_direction_deg - heading)
if direction_diff > 180:
direction_diff = 360 - direction_diff
if direction_diff <= 10.0:
direction_factor = 1.0
elif direction_diff <= 30.0:
direction_factor = 0.5
else:
direction_factor = 0.1
reward *= direction_factor
# 3. 速度インセンティブ
SPEED_THRESHOLD = 2.0
if center_factor >= 0.6 and direction_factor >= 0.5:
if speed >= SPEED_THRESHOLD:
reward += 0.5
else:
reward += 0.1
# 4. ステアリング過剰使用へのペナルティ
if steering > 15:
reward *= 0.8
# 5. コースアウトへの厳格な罰則
if not all_wheels_on_track:
reward = 1e-3
return float(reward)
つまずいた話:PEP8のスタイルチェック
expected 2 blank lines, found 1
これはPythonのコーディング規約(PEP8)のチェックで、関数定義(def)の前には空行を2行入れる必要がある、という指摘でした。
正直初めて知りました。。。
地味なところですが、意外と引っかかるポイントだったので記録しておきます。
Step 5(続き):Stop condition
最後に訓練の停止条件(最大時間)を設定します。既定値は10分でしたが、continuous action spaceは離散に比べて収束が遅い傾向があるため、初回は少し伸ばして30分程度に設定しました。
まとめ(前編)
- Action space 離散→連続への既定変更が、自分が知っていたDeepRacerと一番大きく変わっていた点でした。
- モデル作成のフロー自体(センサー→アルゴリズム→action space→シェル→報酬関数)は、当時の構成をほぼ踏襲していて安心感がありました。
続き(訓練の実行、SageMakerのクォータ問題、評価、そしてレース提出まで)は後編にまとめます。








