「世界初、来店不要のフルオーダーメイド靴」のプレスリリースを行いました。

9-19. Google ColaboratoryでOpenAI Gym “CarRacing-v0″を実行できるように改造した

メロスは激怒した

「Google Colaboratory」で OpenAI Gym のゲーム環境の一つである「CarRacing-v0」をやろうとした人の10人に11人は挫折したことと思います。なにせ「Colab CarRacing-v0」で検索しても情報がほとんど出てきません。

なぜこんなにも情報がないのか。よく検証してみると3段階のハードルがありました。

問題1.ColabのプリインストールのBox2Dがぶっ壊れている(壁度★★☆☆☆)

Colab には Gym も box2d も入っているのですが、壊れています。

import gym
env = gym.make('CarRacing-v0')
AttributeError: module 'gym.envs.box2d' has no attribute 'CarRacing'

というエラーが出ます。ググるといろんな解決策が出ますが、眉唾ものもありどれが良いのかはっきりしません。

問題2.env.render()で画面が立ち上がれないのでエラーが出る(壁度★★★☆☆)

Colab では新規に画面がポップアップするような表示はできず、HTML経由でインライン表示(?)するしかありません。CarRacing-v0 はこれに対応していませんので、env.render() でエラーが出ます。

env.render()
NameError: name 'base' is not defined

環境によっては次の場合もあるでしょう

NotImplementedError: abstract

gym 関係ではよくこの base と abstract に阻まれます。しかし、通常はこのエラーにたどり着くことはありません。その前に3のエラーが出るからです。

問題3.env.reset()でエラーが出る(壁度★★★★★)

これが致命的です。最初の一歩である環境のリセットでエラーが出ます。

state = env.reset()
NameError: name 'base' is not defined

エラーをよく見ると、env.reset() 内の env.step() 内の env.render() でエラーが出ているので、問題2が原因であることが分かります。env.step() がダメということは何もできないということです。

さて、表示系だけがダメなのであれば「Colabで学習だけして、表示はローカルで行う」という方針で良いのですが、問題3のエラーがあるためにColabでは何一つ動作ができません。

ということで、今回は「car_racing.py」を書き換えて Colab で動作するように改造しました。また、おまけとして遺伝的アルゴリズム(GA)でプレイを最適化した結果もお見せします。

書き換えた後のライセンス関係がよく分からないのですが、権利は元の作者に帰属し、ここの筆者はなんら権利を主張しません。また、改造によって生じたいかなる損害も補償しません。大人の方、よろしくお願いします。

実行環境

WinPython3.6 のようなローカル環境では、適切に pip install したあと、~/gym/envs/box2d/car_racing.py を開いて「実行>ファイルごとの設定>外部システムターミナルで実行」するとゲームのプレイができます。また、本記事の改造によって内部コンソールでの実行もできるようになります

WinPython - Browse /WinPython_3.6/3.6.7.0 at SourceForge.net
Portable Scientific Python 2/3 32/64bit Distribution for Windows

Google Colaboratory ではゲームのプレイはできませんが、本記事の改造によって実行ができるようになります

Google Colaboratory

Colab での実行方法

まず、プリインストールの gym を葬ります。

#========================================================
# プリインストールのgymを葬る
#========================================================
!pip uninstall gym
Proceed (y/n)?

と聞かれるので「y」で進みます。

新たに gym と box2d をインストールします。

#========================================================
# 新たにgymとbox2dをインストール
#========================================================
!pip install gym
!pip install box2d-py

これで問題1は解決しました。

次に、こちらの改造済みファイル「car_racing.py」を Colab 画面左のカレントディレクトリにアップロードします。

アップしたファイルで、インストールしたBox2Dフォルダ内の同ファイルを上書きします。

#========================================================
# 改造済みのcar_racing.pyを./にアップして、gym内の該当ファイルにコピー(=上書き)
#========================================================
!cp ./car_racing.py ../usr/local/lib/python3.6/dist-packages/gym/envs/box2d/car_racing.py

動作確認コードです。問題1のエラーは出ないと思います。(Warningが出ても気にしないでください)

#========================================================
# importとCarRacingの選択
#========================================================
#以上の手順の後に初めてgymをimportすること!
import gym
import numpy as np
import numpy.random as nr
import matplotlib.pyplot as plt

#ゲームの選択
env = gym.make('CarRacing-v0')

#デフォルトのactionsの確認(steer, gas, brakeの3次元連続値)
print('env.action_space: {}'.format(env.action_space))

#アクションを離散値で用意しておく
actions = np.array([[ 0, 0, 0],  #actions[0]:何もしない(=等速直線運動)
                    [ 0, 1, 0],  #actions[1]:加速
                    [ 0, 0, 1],  #actions[2]:減速
                    [ 1, 0, 0],  #actions[3]:右旋回
                    [-1, 0, 0]]) #actions[4]:左旋回
print('actions:\n{}'.format(actions))

動作確認コードです。問題2と3のエラーは出ないと思います。

#========================================================
# 動作テスト
#========================================================
#ステージを固定して初期化
env.seed(1)
state = env.reset()

#初期画像を表示
plt.figure()
plt.imshow(env.render(mode='get_img'))
plt.show()

#ランダムにアクション(1アクションは4フレーム継続)
for i in range(10):
    for _ in range(4):
        #アクションの選択
        rand = nr.randint(5)

        #状態の更新
        state, reward, done, goal = env.step(actions[rand])
        print('reward:{}'.format(reward))
        
    #1アクション(=4フレーム)毎に画像を表示
    plt.figure()
    plt.imshow(env.render(mode='get_img'))
    plt.show()

改造で追加された env.render(mode=’get_img’) によってこんな画像が表示されるはずです。

改造したところ

表示まわり

車体の主観視点(車体が常に上を向くようにコースが回転する)をやめ、コースが回らないように変更
→目に優しいうえ、コースを把握しやすい
→人間がプレイするのは難しくなった

道路(踏むと報酬がもらえるパネル?)の色を濃くした
→すでに踏んだかどうかが分かりやすくなった

赤白の縁石をなくした
→表示速度を少し向上

赤白の縁石

デフォルトを verbose=0 に変更
→学習時に「Track generation…」がたくさん表示されて邪魔なので、print類はすべてOFFに

env.step() 内の env.render() を廃止問題2・問題3
→普通にいらない。表示したければ都度 env.render() すればいい。(これにより env.step() を繰り返すような学習の速度が50倍速になった)

env.render(mode=’get_img’) を追加
→コース全体の画像情報がndarray形式で返ってくるので、matplotlib等でColab上に表示できる。学習中の確認に必要

報酬まわり

1フレーム毎に-0.1点されるのを廃止
→強化学習向けの設定と思うが、進行度が分かりづらくなるのでいらない。必要ならば手動で-0.1点/フレームすればよい。

画面外にアウトで-1000点に変更
→元は-100点だったが、累計報酬が負になったほうが処理上助かるので

env.step() の4つ目の返り値を「ゴールしたかどうか」に変更
→元は空だったが、とりあえずこうした。本当はもっといろいろな情報を辞書形式で返すべきだが、それは今後のやる気次第で・・・

外部システムターミナルで実行した場合

開始時1秒間のズーム演出をなくした
→いらない

ズーム率を下げ、より広くコースが見渡せるようにした
→コースを把握しやすい

CarRacing-v0 の仕様を確認

赤字は改造による仕様です。

self.action_space = spaces.Box( np.array([-1,0,0]), np.array([+1,+1,+1]), dtype=np.float32)  # steer, gas, brake

から読み取れるように、env.step() に入れる入力値は3次元の連続値で、ステアリングが-1~1、アクセルが0~1、ブレーキが0~1の範囲です。明記されていませんが、[0, 0, 0] など意思のない入力をしたときはほぼ等速直線運動をします。

道路は3色のしましまになっており、1周に約250~300個のしましまパネルがあります。パネルを一つ踏むたびに約3~4点の報酬が返ってきて、全部でちょうど1000点になります。したがって累計報酬が1000点になったらクリアと判断できます(実際には丸め誤差で999.999くらいですが)。画面外に到達すると-1000点が返ってきます(オリジナルは-100点)。

状態の更新は次のようにします。

#状態の更新
state, reward, done, finish = env.step(3次元の連続値)

state:None
reward:このステップでの報酬。通常は0、道路を新規に踏むと3~4点、コースアウトすると-1000点
done:ゲームが終了したかどうか。True であれば「コースアウト」か「クリア」のどちらか
goal:クリアしたかどうか。True であれば「クリア」

ですから、3番目と4番目の返り値で「コースアウト」か「クリア」を判断できます。

画像のインライン表示は次のようにします。

#初期画像を表示
plt.figure()
plt.imshow(env.render(mode='get_img'))
plt.show()

ステージを毎回固定したい場合は以下のように乱数シードを固定します。

#ステージを固定して初期化
env.seed(1)
state = env.reset()

やりのこし

env.step() の4つ目の返り値には、

  • 車の位置、速度、加速度
  • コースの情報
  • まだ踏んでいない道路の情報
  • 前方の景色

など、機械学習に有益な情報を辞書形式で詰め込むべきなのですが、とりあえず保留とします。

おまけ

おまけと言いつつこれが目的だったのですが、特定のステージのプレイを遺伝的アルゴリズム(GA)で最適化してみました。

Colab での出力はこんな感じです。

ローカル環境で外部システムコンソールから実行した場合です。

最適化アルゴリズムはこちらの記事のものを踏襲しています。

タイトルとURLをコピーしました