DeepRacerの走行ログを Matplotlibで可視化する #AWSDeepRacer #AWSDeepRacerJP

はじめに

コンサルティング部の川原です。

DeepRacer 楽しいですよね。 知らず知らずのうちに機械学習の各種用語にふれることができるので、 入門としてもってこいだなと思ってます。

そして今月から私も DeepRacer リーグ の 仮想サーキットに参戦しています。 ※DeepRacerリーグの 仮想サーキットの概要・参加方法は以下を参照ください。

ついにオープンしたAWS DeepRacer League Virtual Circuitに参加しよう!

こちらの仮想サーキット、 モデルの評価 (submit) は 30分に1回可能です。 1 submitで、サーキット上での走行が 5回試行され、 1回も完走できなかった場合は Unable to finish 1 lap. と結果が返され、 1回以上完走できた場合は、その中の最速タイムが リーダーボードに提出されます。

↓2019/07/21 14:00 時点リーダーボード (私は11位!)

5回の走行ログは CloudWatchのロググループ /aws/deepracer/leaderboard/SimulationJobs に格納されます。

この走行ログを分析・可視化していきます。 今回はお勉強も兼ねて、データ分析・可視化でよく使用される Pythonの PandasライブラリMatplotlibライブラリ を使っていきます。

※Tableau を使ったより詳細な分析記事はこちら。

DeepRacerの走行ログをTableauで分析してみた #AWSDeepRacer #AWSDeepRacerJP

実行環境

  • OS version: macOS Mojave, version 10.14.5
  • Python version: 3.7.3
  • pandas version: 0.24.2
  • matplotlib version: 3.1.1

準備: 走行ログを取得

準備としてCloudWatch Logs Insights で走行ログのCSVをダウンロードします。

▼ 仮想サーキットのページの Latest model submitted から Evaluation Logs を選択

sim- から始まる番号をメモ

インサイト を選択

インサイトのページでロググループを /aws/deepracer/leaderboard/SimulationJobs を選択します。 下記クエリを記載し クエリを実行 します。※ sim-xxx には先程メモしていた番号を入れます。

filter @logStream like /^sim-xxx/
| fields @message
| filter @message like /^SIM_TRACE_LOG/
| parse @message "SIM_TRACE_LOG:*,*,*,*,*,*,*,*,*,*,*,*,*,*,*" as episodes,steps,x,y,heading,steering,speed,action_taken,reward,done,all_wheels_on_track,current_progress,closest_waypoint_index,track_length,time
| sort by time

下に表示されているクエリ結果を確認して アクション → クエリ結果をダウンロード (CSV) を選択します。

logs-insights-results.csv という名前でCSVがダウンロードされます。 次章でこの走行ログCSVを分析・可視化していきます。

分析・可視化

以下、Pythonです。 19秒台で完走したときのログを分析してみます。

ライブラリインポート、CSVの読み込み

matplotlib, pandas ライブラリをインポートして、 次にダウンロードしたCSVを pandas.Dataframe オブジェクトとして読み込みます。

import matplotlib.pyplot as plt
import pandas as pd

df = pd.read_csv('./logs-insights-results.csv')

CloudWatch Logs Insights で取得したCSVの列構成を表示してみます。

>>> for c in list(df.columns): print(c)
... 
@message
episodes
steps
x
y
heading
steering
speed
action_taken
reward
done
all_wheels_on_track
current_progress
closest_waypoint_index
track_length
time

以降では、 episodes, x, y, steering, speed あたりを使っていきます。

軌跡の可視化

x, y 列を使って 走行の軌跡を表示してみます。

plt.scatter(df.x, df.y, marker='o', alpha=0.3)

plt.show()

▼(参考) 仮想サーキット: Empire City Circuit

中盤・後半にかけてジグザグ走行や、 曲がりすぎ・曲がりきれずのコースアウトが確認できますね。

速度も可視化

次は speed 列の情報も含めます。 速度ごとにプロットする点の色を変化させます。

from matplotlib import cm

plt.scatter(df.x, df.y, marker='o', alpha=0.3, c=df.speed, cmap=cm.rainbow)
plt.colorbar()

plt.show()

直線を低速で走っていることが多い一方で、終盤のコーナーは高速で曲がることができています。 全体的に低速が多く、更に改善の余地があることが分かりました。

ステアリング角度も可視化

軌跡可視化の時点で、ある程度ステアリングの傾向はつかめますが 念の為 steering 列の情報も含めてみます。 ステアリング角度の大きさ毎にプロットする点の種類を変化させます。

今回のログの場合、ステアリング角度大きさは 0度(0 rad), 12.5度(0.22 rad), 25度(0.44 rad) の三種類です。 ( df.steering.abs().unique() などで確認できます)

from matplotlib import cm

speed_min=df.speed.min()
speed_max=df.speed.max()

# ステアリング角度の大きさによってマーカー種類を変える
#   -  0.0度(0.00 rad)のときは "○"
#   - 12.5度(0.22 rad)のときは "△"
#   - 25.0度(0.44 rad)のときは "×

df_st0 = df[df['steering'] == 0.0]
df_st1 = df[df['steering'].abs() == 0.22]
df_st2 = df[df['steering'].abs() == 0.44]

plt.scatter(df_st0.x, df_st0.y, marker='$○$', alpha=0.3, c=df_st0.speed, cmap=cm.rainbow, vmin=speed_min, vmax=speed_max)
plt.scatter(df_st1.x, df_st1.y, marker='$△$', alpha=0.3, c=df_st1.speed, cmap=cm.rainbow, vmin=speed_min, vmax=speed_max)
plt.scatter(df_st2.x, df_st2.y, marker='$×$', alpha=0.3, c=df_st2.speed, cmap=cm.rainbow, vmin=speed_min, vmax=speed_max)

plt.colorbar()

plt.show()

直線で高速かつステアリング0度 ( 赤色 "○" プロット) のパターンを多くする必要があることが分かりますね。

おわりに

Pandas, Matplotlib を使ったDeepRacer走行ログの可視化でした。 Pandas はデータのフィルタリング機能や関数の豊富さなど、 使っていてとても便利だなと感じました。

以下に ライブラリインポート〜可視化までのサンプルスクリプトを載せます。 今までの可視化に加え、 5回の走行それぞれの Time, Progress, Estimated goal time もついでに記載します。

import matplotlib.pyplot as plt
import pandas as pd
from matplotlib import cm

df = pd.read_csv('./logs-insights-results.csv')

speed_min=df.speed.min()
speed_max=df.speed.max()

plt.figure(figsize=(9.0, 5.0))

# ステアリング角度の大きさによってマーカー種類を変える
#   -  0.0度(0.00 rad)のときは "○"
#   - 12.5度(0.22 rad)のときは "△"
#   - 25.0度(0.44 rad)のときは "×

df_st0 = df[df['steering'] == 0.0]
df_st1 = df[df['steering'].abs() == 0.22]
df_st2 = df[df['steering'].abs() == 0.44]

plt.scatter(df_st0.x, df_st0.y, marker='$○$', alpha=0.3, c=df_st0.speed, cmap=cm.rainbow, vmin=speed_min, vmax=speed_max)
plt.scatter(df_st1.x, df_st1.y, marker='$△$', alpha=0.3, c=df_st1.speed, cmap=cm.rainbow, vmin=speed_min, vmax=speed_max)
plt.scatter(df_st2.x, df_st2.y, marker='$×$', alpha=0.3, c=df_st2.speed, cmap=cm.rainbow, vmin=speed_min, vmax=speed_max)

plt.colorbar()

# 5回の走行それぞれの Time, Progress, Estimated goal time を記載する
for i in range(0,5):
    time_start = df[df['episodes'] == i].time.min()
    time_end = df[df['episodes'] == i].time.max()
    time = time_end - time_start
    progress = df[df['episodes'] == i].current_progress.max()
    estimated_time = time * 100.0 / progress
    text = '#%d. Time:%1.2f Progress:%1.2f, Estimated time:%1.2f' %(i, time, progress, estimated_time)
    plt.text(0.5, 3.0 - 0.3*i, text, fontsize=9)

plt.show()

※最初の試行 (#0... )の走行時間はログから正しく取得できませんでした・・・

参考