Q-learningで倒立振子を振り回す
はじめに
最近kaggleでも強化学習系のお題が増えてきたように思うので(実際に解法に強化学習が使用されているかは別として)、 手をつけていなかった強化学習について、本を読みながら試してみたことを書きます。
参考資料
実施したこと
openAIのgymのcart-poleの環境を使って強化学習の一種であるQ-learningを実行します。 本来これは下の図のような棒を乗せた車を左右にうまく動かして棒を倒立している状態に保つというのがタスクになりますが、 普通にやっても面白くないので今回は画面外に出ないように棒の角速度が最大になるような動作を強化学習により実施することを目的とします。
手法については、TD法のQ-learningを使います。Q-learningはQテーブルという状態ごとにどうactionをとるべきかというテーブルを作成することに強化学習を実現する方法であり、式としては以下のような形でステップ毎に行動価値関数であるQテーブルを更新していくような形となります。
ここで、が状態、が行動、は学習率、は割引率を示し、での状態を元に、の時のQテーブルをパラメータに従い更新します。
コードは基本的には参考文献のものを参考にしているのでここでは割愛し、 今回の問題設定に伴い修正を行った点についてのみ記載します。
一応実装全文はこちらです。
今回は棒の速度をとにかくあげたいので、報酬の部分はこのような形としています。
if abs(state[0]) > 2.4: # カートが範囲外 reward = -1000 else: reward = state[3] #角速度をそのまま報酬に
state[0]はカートに位置、state[3]は棒の角速度で、カートが画面外に出るような場合は大きなマイナス報酬を与え、あとは角速度 をそのまま報酬として与えています。角速度のレンジは設定では無制限となっていますが、実験上では20を超える数値が出ることはほとんどありませんでした。
またcartpoleのdefaultの設定だと角度が12 * 2 * math.pi / 360を越えると棒が倒れたと判定されて終了フラグであるdoneが1となってしまい動画の保存が失敗するので、それを無制限に変更し、最大ステップ数も変更します(defaultは200)。 また1000episode毎に動画を保存するために下記の設定を入れます。
env = gym.make('CartPole-v0') env.env.theta_threshold_radians = float('inf') env._max_episode_steps = 1000 env = wrappers.Monitor(env, directory='movie', force=True, video_callable=( lambda ep: ep % 1000 == 0)) # 1000epsodeごとに動画を保存
結果
実験を行い、収束を確認するために100試行平均の最高角速度の推移を確認すると、おおよそ4000試行程度で性能が頭打ちになりました。平均しているのは試行ごとの振れ幅が大きいためです。
4000試行後のサンプルをいくつか見てみます。
- 4000試行目
最初あまりうまくいっていないか?と思わせて途中から覚醒するパターン。
- 5000試行目
すぐに画面外に出るという典型的なうまくいっていないサンプル。
- 6000試行目
悪くはないが4000試行の例に比べると速度が物足りない。
所感
倒立振子で角速度が最大になることを目的に強化学習の一種であるQ-learningを行いました。結果としては、指標的には学習自体はそれなりにうまくいき、良い感じのサンプルも得られたのですが、サンプル毎のブレ幅が大きく安定した結果を得ることはできませんでした。理想としては画面中央で左右に小刻みに動いて一定の速度を維持し続けて欲しかったのですが、なかなか難しいなという印象です。 ここで記載した結果は一例であり、実際は探索と活用の割合、報酬関数の数値設定、学習率、Qテーブルの離散化数などの多数のパラメータを変更しながら実験を行ったわけですが、パラメータサーチにそれなりに時間もかかりますし、より高度なDQNを使おうとするとこの時間が更に跳ね上がるので、NNの層作りに近い難しさを感じています。