soccerAIのプログラムレベルのお話(α0.1.0)
soccerai α0.1.0を公開したので解説。
注意
今回から、sexpdataとtensorflowというライブラリを使います。 実行前に
pip install -r requirement.txt
を実行してください。 sexpdataについてはこちら。
大きな変更箇所
- rewardの表示方法をmatplotlibから、tensorboardに変更
matplotlibが使いづらいと感じたので変更(メモリ使って少し重かったし)。 こちらのサイトを参考にしてclassを改造した。
- hearメッセージを表示しないようにした
邪魔だったので止めました。
- 引数を追加
いろいろ便利な引数を追加した。 説明は以下の通り。
名前 | 既定値 | 説明 |
---|---|---|
special_division | True | Q tableのindexをボールの距離に応じて変更するかどうか。普通はキックできる場所になったときのみ変更するシステムにしている。 |
special_division_value | 1 | 上記のボールの距離を設定する |
damage_reset_ball_distance | 5 | 今回から追加されたdamageシステムのリセットするボールの距離を指定。不具合あり。 |
num | 11 | 登録する選手数を設定して、報酬が同じになるようにするためのもの。 |
actions | 6 | actionの数を指定。 |
- goal情報を入手するようにした
メモリ使用量がかなり減ったのでgoal情報も取得するようにしてゴール報酬を設定した。
- repeat関数を削除
前回のバージョンで首がもげる問題が解消されていたらしいのでなくした。
- action数を大幅削減、reward関数を一から作成
今回のバージョンから、ボールが見えるようになって、前のreward関数が非効率になったため変更。不具合あり。
- ボールキックの判定を良くした
解説
rewardの表示方法をmatplotlibから、tensorboardに変更
自分のプログラムの該当部分はこちら。
class TensorBoardLogger(object): def __init__(self, log_dir, session=None): self.log_dir = log_dir self.writer = tf.summary.FileWriter(self.log_dir) self.episode = 0 self.session = session or tf.get_default_session() or tf.Session() def log(self, logs=None): # scalar logging if logs is None: logs = {} for name, value in logs.items(): summary = tf.Summary() summary_value = summary.value.add() summary_value.simple_value = value summary_value.tag = name self.writer.add_summary(summary, self.episode) self.writer.flush() self.episode += 1
上記のサイトのhistgram部分を消して、短くした。
self.logger1 = TensorBoardLogger(log_dir='./logdir/reward') self.logger2 = TensorBoardLogger(log_dir='./logdir/see') self.logger3 = TensorBoardLogger(log_dir='./logdir/action') self.logger4 = TensorBoardLogger(log_dir='./logdir/damage')
ここで初期設定をして(ログの保存先を指定)
self.logger1.log(logs=dict(reward=args[0])) self.logger2.log(logs=dict(reward=args[1])) self.logger3.log(logs=dict(reward=args[2])) self.logger4.log(logs=dict(reward=args[3]))
ここで、ログを実際に書き込みしている。
tensorboardはコマンドで呼び出すのでstart.shにtensorboard --logdir=./logdir
を入れている。
goal情報を入手するようにした
goal_info = self.see_analysis(rec_raw, ["g", self.enemy])
実際に使っている場所
# ゴールが見えているか if goal_info is None: goal_info = [self.max_goal_distance + 1, self.max_goal_angle + 1] else: goal_info = list(map(lambda n: float(n), goal_info)) if goal_info[0] > self.max_goal_distance: goal_info = [self.max_goal_distance, goal_info[1]]
see_analysisの使い方は以下のwikiを参照してください。
action数を大幅削減、reward関数を一から作成
def step(self, action): # actionから移動をして報酬を計算するプログラム # self.goal_damage += 0.01 reward = 0 reward_see = 0 reward_action = 0 tmp = False self.ball_damage += 11 / self.num * 0.1 if random.random() < self.noise: end_action = action else: action = random.randrange(0, self.actions) end_action = action if action == 0: self.send_dash(100) elif action == 1: self.send_turn(10) elif action == 2: self.send_turn(-10) elif action == 3: self.send_turn(30) elif action == 4: self.send_turn(-30) elif action == 5: self.send_kick(100, 0) """==========reward setting==========""" # 見えているものを確認する rec = {"type": None} rec_raw = [] while rec["type"] != "see": rec_raw = self.recieve_msg() rec = self.msg_analysis(rec_raw) if rec["type"] == "hear": # ゴールしたときの報酬(できたらめっちゃ褒める) if rec["contents"][:6] == "goal_" + self.team: self.send_move(-10, -10) reward_action += 1000 * int(rec["contents"][7]) self.up_reward += 100 print("GOAL!", reward_action, "time:", rec["time"]) tmp = True elif rec["type"] == "sense_body": if self.kickcount < int(rec["value"][4][1]): print("kick!!!!", "time:", rec["time"]) reward_action += 100 self.kickcount = int(rec["value"][4][1]) ball_info = self.see_analysis(rec_raw, "b") goal_info = self.see_analysis(rec_raw, ["g", self.enemy]) # ボールが見えてるか if ball_info is None: ball_info = [self.max_ball_distance + 1, self.max_ball_angle + 1] else: ball_info = list(map(lambda n: float(n), ball_info)) if ball_info[0] > self.max_ball_distance: ball_info = [self.max_ball_distance, ball_info[1]] elif ball_info[0] < self.damage_reset_ball_distance: self.ball_damage = 0 # 距離に応じた報酬をもらう reward_see += 80 - (ball_info[0] * 2) if ball_info[0] < self.special_division_value: ball_info = [self.special_division, ball_info[1]] # reward_see += 25 if action == 5: if ball_info[0] < self.special_division_value: reward_action += 100 else: reward_action -= 100 # ゴールが見えているか if goal_info is None: goal_info = [self.max_goal_distance + 1, self.max_goal_angle + 1] else: goal_info = list(map(lambda n: float(n), goal_info)) if goal_info[0] > self.max_goal_distance: goal_info = [self.max_goal_distance, goal_info[1]] # ログの追加 reward_see += self.up_reward reward_action += self.up_reward reward += reward_see + reward_action - self.ball_damage self._update(reward, reward_see, reward_action, self.ball_damage * -1) """=========ここまで===========""" return (int(ball_info[0] / self.ball_distance_interval), int(ball_info[1] / self.ball_angle_interval), int(goal_info[0] / self.goal_distance_interval), int(goal_info[1] / self.goal_angle_interval) ), reward, end_action
詳しい説明は割愛。
ボールキックの判定を良くした
elif rec["type"] == "sense_body": if self.kickcount < int(rec["value"][4][1]): print("kick!!!!", "time:", rec["time"]) reward_action += 100 self.kickcount = int(rec["value"][4][1])
sense_bodyメッセージの中のkickcountが前回と変わっていたらキックしたということにするようにした。
最後に
今回は報酬設定関係がメイン。
個人的な質問等はこちらまで。
https://forms.gle/V6NRhoTooFw15hJdA
また、自分が参加しているRobocup soccer シミュレーションリーグのチームでは参加者募集中です!活動の見学、活動に参加したい方、ご連絡お待ちしております!
詳しくはこちら