【機械学習】DeepRacerGuruでAWS DeepRacerのログを解析してみた

## はじめに
AWS DeepRacerは、AWSが提供する学習用ツールで、強化学習を活用した機械学習モデルのトレーニングや評価を行うことができます。このツールでは、自律走行する1/18スケールのレーシングカーを用いて、実践的な学習を楽しく体験できます。また、作成したモデルを世界中のユーザーと競わせることができる点も魅力のひとつです。
  
ひょんなことからDeepracerのレースに参加する事になったので、その際に行ったログ解析の手法をまとめます。  
本業優先のため、簡単かつ無料で利用できるDeepRacerGuruを利用しました。
## 使用するログと概要
解析に使用するログはTrainingとEvaluationです。  
概要を簡単に説明すると以下の通り。  
  
- **Trainingログ** 
機械学習モデルが学習中に収集されるデータを記録します。
モデルのページを開き、Traingingタブのすぐ下にあるボタンから取得できます。

![73a49c57721ee05df8f0ceb3fc175df4df85d15c76ec5fef3cc9e52499561fb0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/64437827-ed4f-4cd3-b295-ab6f7bed041f.png)

- **Evaluationログ**  
トレーニング後にモデルのパフォーマンスを評価するために使用されます。
モデルのページを開き、Evaluationタブの少し下にある赤枠のボタンから取得できます。

![ebe9ee7193899ca6fdbe62ae0e4495dcdc0a5656b0bee2c9d25dc71bdb73ac2e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/c52393f5-837a-4cdb-8940-6cee563284ed.png)

### ログの準備
.tar.gzファイルがダウンロードされるので、2回解凍してください。
解凍後に現れるフォルダを潜っていくと「logs」フォルダがあるので、そこからさらに2階層潜ると以下のファイルが格納されています。

- **Trainingの場合**: `～robomaker.log`
- **Evaluationの場合**: `～sagemaker.log`

これらのログを、用途に応じてそれぞれ別フォルダに格納すると管理がしやすくなります。

## DeepRacerGuruの使い方

![8e7706188ac1fd348e81bc0ae522275184b16cb39e9f8c6bd09bef4d23ec9674.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/e95324d9-3aac-4df2-ae40-fed20a8131bd.png)

1. **DeepRacerGuruを起動し、「Track」を押して対象コース名を選択します**。
2. **[File] → [Switch Directory]** をクリックし、解析したいログが入ったフォルダを指定します。
3. **[File] → [New File(s)]** を選択してインポートを開始します。「Import Succeeded」と表示されればインポート成功です。
4. 自動生成されるJSONファイルの`model_name`の値を、使用するモデル名に変更しておくと後の解析作業がスムーズです。
5. **[File] → [Open File]** を選び、解析対象のモデルを開きます。
 
## トレーニングログの解析
インポートが成功すると、以下のような画面が表示されます。この画面で、モデルがどのように学習したかを詳細に確認できます。

![06532735eced3f5dedc325b6567ed995f8969488df036676ec1715c357ed1d1c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/070eb88f-004e-47c7-b2c1-051d28e5f139.png)

トレーニングログの解析はツールバーのAnalyze→Heatmap + Episode Routeで行いました。

![d9dea80874f64a94008280f97a065915f0f8f5d259f16b3916c8feca87bf5f5c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/cf2ac00c-53ae-4e93-8574-10adb5683b7c.png)

Heatmapは、モデルがトラック上のどの位置を頻繁に通過したかを示すマップです。色が濃いほど頻繁に通過しています。
Episode Routeは、各トレーニングでモデルが通ったルートを緑の点で示します。これにより、モデルの動きを視覚的に追跡できます。
 
Episode Routeはコースアウトするか一周した時点で1つとし、各エピソードは右下のEpisodeブロック内のボタンで切り替え可能です。
2時間のトレーニングだと約200エピソードになります。
 
また、ツールバーのEpisodesである程度絞り込みが可能で、例えばEpisodes→Fast Lapsではとレーニング中最速のエピソードを表示可能です。
 
エピソードポイントをクリックすればその時点でのマシン情報を詳細に確認可能です。
報酬関数でprintを設定していれば、Log File Debugでreward等、パラメータの確認が可能です。
※SpeedやHeading等、State内に表示される変数も小数点以下まで表示されるので必要であれば出力します。
※どのようなパラメータが設定されているかは以下URLを参照してください。
　https://docs.aws.amazon.com/ja_jp/deepracer/latest/developerguide/deepracer-reward-function-input.html

![482115a99a562272c184a338bde63d85f9a31e41eef0275ea254f9ce576b7266.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/1cba6dcc-2709-47ed-8869-a36376a1c378.png)

ちなみにAnalyze→Heatmap + Exit Pointsではコースアウト箇所の確認が可能です。

![23280e4e424155846c1b875577c677a2e4f97dd8b0f13cc21366e7e23919c3ab.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/70c80ec3-b7e9-4306-aee2-e555c4ada454.png)

![aeee0b15778f5d307702f00f6254a08a6f5ae4d9bb6b57989523d7f76a54a244.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/af9898aa-8952-4b0a-83d0-cbd69151da1a.png)

まず最後の直線をなるべく真っすぐ走らせたかったので、以下の報酬関数で学習を行いました。
報酬関数を全て載せるのは規約違反となるので、一部抜粋します。
 
## 報酬関数
```python
if 44 <= closest_waypoints[1] <= 55:
    reward = max(0.001, 1.0 - (distance_from_center / (track_width / 2.0)))
    
    # headingが180に近いほど高報酬を与える
    target_heading = 180.0
    heading_diff = abs(heading - target_heading)
    if heading_diff < 5.0:  # ヘディングの許容範囲を狭める
        reward += 10.0 - (heading_diff / 5.0)  # 報酬を増加
    else:
        reward *= 0.5  # 許容範囲外の場合のペナルティ
    
    # headingが-の場合は-180に近いほど高報酬を与える。左下へのふらつきを抑制
    if heading < 0:
        target_heading = -180.0
        heading_diff = abs(heading - target_heading)
    if heading_diff < 5.0:
        reward += 10.0 - (heading_diff / 5.0)
    else:
        reward *= 0.5
    
    if speed >= 3.5 and speed <= 4:  # スピードの許容範囲を狭める
        reward += 10.0  # 報酬を増加
    else:
        reward *= 0.5  # 許容範囲外の場合のペナルティ
    
    print(f"WP 44-55: WP: {closest_waypoints[1]}")
    print(f"Spd: {speed}")
    print(f"Head: {heading}")
    print(f"R: {reward}")
```

ある程度ふらつきが抑制され、直線終盤では安定して最高スピードが出るようになりました。


![9bf7ebf3e6e5dd96ab9e9c24fcb10fd2288a143aba1caa3674279e48acefaded.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265059/626d9963-f88a-4661-8879-498848a691a5.png)

カーブの部分の走行についてはより複雑に思考してトライエラーを繰り返す必要があります。

実際の自動運転システムが作成される過程では天文学的な数の試行錯誤が繰り返されていると思うので、自動運転技術に携わる方々の努力は言葉に表せないくらい凄いなと思うことしかできません。

この記事が未来の技術者の役に立つことを願い、ここに締めさせていただきます。
お読みいただきありがとうございました！