ライントレーサーをDeep Q Learningで教育する - Chainer - Qiitaで取り扱った環境をOpenAI gymライクに扱えるように環境とAIを分離・整備しました。分離したといっても、renderのところを当初wxPythonと描画・ロジック一体で作りこんでしまったので、おかしな状態になっていますが、動くので良しという段階です。
この単眼ライントレーサはPOMDPの例ともいえますが、(最初にコース上に乗せる前提で)ロジックベースで動かしてみている人は少なからずいると思います。
POMDPってなんぞというかたは、@okdshinさんが最近親切な説明を書かれていたので、参考にするといいと思います。
⇒ 外部メモリ(External Memory)を利用した強化学習 - Qiita
先述の以前のチャレンジでは、過去4ステップ分のステートを状態としてDQNにくれてやっていました。このほか、特徴量エンジニアリングの要領で”線”に対してどのくらいの位置に自分がいるかを表現するパラメータを入れてやっても割とよくなります。
(ま、信念みたいなものですね)
で、OpenAI Gymについては@icoxfog417さんがハンズオンを2016/11/16に企画されているそうで、事前準備資料としてgithubに上がっているのが分かりやすいかなと思います。
icoxfog417/techcircle_openai_handson: Tech-Circle OpenAI Handson Repository
(Windows 7だと導入が難しいみたいですけどね、本件はWindwos7、CPU ONLYでも動きます)
(他人の褌で相撲取りまくり)
リポジトリ
Gym_LineFollower: Simple Open AI gym like Environment
ダウンロードしていただいて、
python exapmle.py
で、ランダムに動き回るエージェントが1000ステップだけ計算します。
ここの中を書き換えて、DQNやそのほかの方式のAIで動かせます。
- actionは(左輪速度, 右輪速度)で、それぞれ[-1.0,1.0]の値域を取れます。DQNのように離散値を使いたい場合は、actionlist=[[1.0,1.0],[-1.0,1.0],...]のようにした挙句に、actionlist[0]を渡すようなイメージですかね。
- observationは単眼の光センサで中身は(検知・不検知)、つまり(1 or 0)です。
- infoにはデバッグなどように、ライントレーサの位置や角度が入っていますので、必要でしたら使ってみてね!
- rewardは後述の通り考え中!
def run(self):
observation = env.reset()
for t in range(1000):
env.render()
print(observation)
action = agent.act()
observation, reward, done, info = env.step(action)
if done:
print("Episode finished after {} timesteps".format(t+1))
break
wx.Yield()
env.monitor.close()
print("Simulation completed\nWaiting for closing window..")
サンプルエージェントは@ugo-namaさんのtorcs gymからパクリベースです。
gym_torcs/sample_agent.py
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
class Agent(object):
def __init__(self, dim_action):
self.dim_action = dim_action
def act(self):
return np.tanh(np.random.randn(self.dim_action)) # random action
工事中のところ/宿題(ここをメモするための投稿ともいえる)
1. コースが決め打ち
AIの研究というからにはいろんな環境で走れたほうがいいですよね。ですが、オーバルで決め打ちです。
2. 報酬設計
ライントレーサーをDeep Q Learningで教育するで使った報酬とは違うものを考えていますが、今は仮のものを入れています。
if not done:
# 生き残っていればコースにいなくてもご褒美
reward = 1.0
elif self.steps_beyond_done is None:
# Robot just went out over the boundary
self.steps_beyond_done = 0
reward = 1.0
else:
if self.steps_beyond_done == 0:
logger.warn("You are calling 'step()' even though this environment has already returned done = True. You should always call 'reset()' once you receive 'done = True' -- any further steps are undefined behavior.")
self.steps_beyond_done += 1
reward = 0.0
以前は無理にコースに乗せるために色々ハックしていましたが、これも美しくないです。
# Reward
# コースにどれだけ沿っているかのご褒美
proximity_reward = 0.0
if self.Course.adLines([ag.pos_x, ag.pos_y], 5.0):
proximity_reward = 1.0
elif self.Course.adLines([ag.pos_x, ag.pos_y], 10.0):
proximity_reward = 0.5
# 壁にぶつかったら罰則
if self.Box.adLines([ag.pos_x, ag.pos_y], 3.0):
proximity_reward -= 1.0
# 直進を選好するようにご褒美
forward_reward = 0.0
if(action[0] == action[1] and proximity_reward > 0.75):
forward_reward = 0.1 * proximity_reward
# センサがコースを検知し続けるようなインセンティブ
eye_reward = 1.0 if ag.EYE.obj == 1 else 0.0
# ご褒美の合計
reward = proximity_reward + forward_reward + eye_reward
3. 描画とロジックの分離
困った挙句にマルチスレッドにしてしまいましたが、グローバル変数使ったり美しくない。OpenAI gymに寄せるか、ugonamaさんみたいに描画サーバ方式に寄せるか…。
以上