見出し画像

棒のバランスをとるゲームCartPoleを機械学習で攻略してみた

こんにちは!

ぷもんです!


今まで、
機械学習を理解するためにリサーチしてみた!
機械学習ができるscikit-learnって何なのか?
Anacondaをダウンロードして jupyter notebookを使えるようにして scikit-learnが使えるようにするまで!
OpenAI Gymの強化学習はANACONDA NAVIGATOR→Jupyter Notebookでもできるのか?
強化学習でバランスゲームを攻略! ステップとエピソードとは?
棒のバランスを取るCartPoleの動かし方 強化学習を始める前に
離散値ってなんや?
強化学習に必要な「Qテーブル」と「離散値で表す関数」をつくるには?
Q学習の式を理解する
強化学習 Q学習をコードにすると?
強化学習 探索と利用のジレンマを解決する ε-greedyアルゴリズムとは?
ε-greedyアルゴリズムをさらに改善
罰則の追加
の13回にわたって強化学習を勉強してきました。


今回は今までやってきたことを1つのコードにまとめて
きちんと動くのかやっていきます。


動いたコードがこちらです!


①ちゃんと動くのかテストする用の1番シンプルなもの

→これで動かなければそもそも準備が整っていない可能性が高いです。

import gym   
env = gym.make('CartPole-v0')  
env.reset()  
for _ in range(1000):  
  env.render()  
  env.step(env.action_space.sample())
env.close()  

僕はAnacondaからjupyter notebookを開いてコードを書いているのですが
Anacondaやjupyter notebookがわからない人は
Anacondaをダウンロードして jupyter notebookを使えるようにして scikit-learnが使えるようにするまで!
OpenAI Gymの強化学習はANACONDA NAVIGATOR→Jupyter Notebookでもできるのか?
などを参考にして動かす環境を作ったり

OpenAI Gymをインストールしてない場合は

pip install gym

でインストールする必要があります。


これで動いたとしてもこのままでは全くCartPoleの棒のバランスが取れません。


②強化学習がどれくらい進んだかを知るための機能を追加

→ステップ数が表示されるようにするなど
 強化学習がどう進んでいるか?がわかるような仕組みを作れる

ここら辺から本格的に難しくなるので1行ずつ理解していきます。
強化学習でバランスゲームを攻略! ステップとエピソードとは?
棒のバランスを取るCartPoleの動かし方 強化学習を始める前に
なども参考にしてみてください。

import gym
import numpy as np
env = gym.make('CartPole-v0')
goal_average_steps = 195
max_number_of_steps = 200
num_consecutive_iterations = 100
num_episodes = 5000
last_time_steps = np.zeros(num_consecutive_iterations)
for episode in range(num_episodes):
  
  observation = env.reset()
  episode_reward = 0
  for t in range(max_number_of_steps):
      
      env.render()
      action = np.random.choice([0, 1])
      observation, reward, done, info = env.step(action)
      episode_reward += reward
      if done:
          print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1,
              last_time_steps.mean()))
          last_time_steps = np.hstack((last_time_steps[1:], [episode_reward]))
          break
  if (last_time_steps.mean() >= goal_average_steps): 
      print('Episode %d train agent successfuly!' % episode)
      break

このままではランダムな動きしかしてくれないので
強化学習で知識を蓄積して利用する機能を作らないといつまでも
上手くなりません。


③Q学習の仕組みを追加

→Qテーブルと言う今までの記録を表にして知識としてまとめるものや
 保存しやすい数値の離散値に変換する機能を追加します。

難しいですがここの理解をしていないと機械学習の理解が進みません。
離散値ってなんや?
強化学習に必要な「Qテーブル」と「離散値で表す関数」をつくるには?
Q学習の式を理解する
強化学習 Q学習をコードにすると?
でできるだけわかりやすく書いたつもりなので参考にして見てください。

q_table = np.random.uniform(low=-1, high=1, size=(4 ** 4, env.action_space.n))
def bins(clip_min, clip_max, num):
  return np.linspace(clip_min, clip_max, num + 1)[1:-1]
def digitize_state(observation):
  cart_pos, cart_v, pole_angle, pole_v = observation
  digitized = [np.digitize(cart_pos, bins=bins(-2.4, 2.4, 4)),
               np.digitize(cart_v, bins=bins(-3.0, 3.0, 4)),
               np.digitize(pole_angle, bins=bins(-0.5, 0.5, 4)),
               np.digitize(pole_v, bins=bins(-2.0, 2.0, 4))]
  return sum([x * (4 ** i) for i, x in enumerate(digitized)])
def get_action(state, action, observation, reward):
  next_state = digitize_state(observation)
  next_action = np.argmax(q_table[next_state])
  
  alpha = 0.2
  gamma = 0.99
  q_table[state, action] = (1 - alpha) * q_table[state, action] +\
          alpha * (reward + gamma * q_table[next_state, next_action])
  return next_action, next_state
import gym
import numpy as np
env = gym.make('CartPole-v0')
goal_average_steps = 195
max_number_of_steps = 200
num_consecutive_iterations = 100
num_episodes = 5000
last_time_steps = np.zeros(num_consecutive_iterations)
for episode in range(num_episodes):
 
  observation = env.reset()
  state = digitize_state(observation)
  action = np.argmax(q_table[state])
  episode_reward = 0
  for t in range(max_number_of_steps):
      
      env.render()
      
      observation, reward, done, info = env.step(action)
      
      action, state = get_action(state, action, observation, reward)
      episode_reward += reward
      if done:
         print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1,
             last_time_steps.mean()))
         last_time_steps = np.hstack((last_time_steps[1:], [episode_reward]))
         break
  if (last_time_steps.mean() >= goal_average_steps): 
     print('Episode %d train agent successfuly!' % episode)
     break

これでやっと機械学習をできるようになったわけですが
このままでは効率が悪く上手く行くまでにめっちゃ時間がかかってしまいます。

ここからは機械学習が上手く行くような機能を追加していきます。


④ε-greedyアルゴリズムを追加

→強化学習を上手く進めるには
 知識を得るために新しい行動をする探索と
 得た知識を使って行動する利用のバランスが重要です。
 それを調整するのがε-greedyアルゴリズムです。

こちらのnoteではそのε-greedyアルゴリズムについて書かれています。
強化学習 探索と利用のジレンマを解決する ε-greedyアルゴリズムとは?
ε-greedyアルゴリズムをさらに改善
名前は難しそうですがQ学習の理解と比べたら意外と簡単です。

q_table = np.random.uniform(low=-1, high=1, size=(4 ** 4, env.action_space.n))
def bins(clip_min, clip_max, num):
  return np.linspace(clip_min, clip_max, num + 1)[1:-1]
def digitize_state(observation):
  cart_pos, cart_v, pole_angle, pole_v = observation
  digitized = [np.digitize(cart_pos, bins=bins(-2.4, 2.4, 4)),
               np.digitize(cart_v, bins=bins(-3.0, 3.0, 4)),
               np.digitize(pole_angle, bins=bins(-0.5, 0.5, 4)),
               np.digitize(pole_v, bins=bins(-2.0, 2.0, 4))]
  return sum([x * (4 ** i) for i, x in enumerate(digitized)])
def get_action(state, action, observation, reward, episode):
  next_state = digitize_state(observation)
  epsilon = 0.5 * (0.99 ** episode)
  if  epsilon <= np.random.uniform(0, 1):
      next_action = np.argmax(q_table[next_state])
  else:
      next_action = np.random.choice([0, 1])
  alpha = 0.2
  gamma = 0.99
  q_table[state, action] = (1 - alpha) * q_table[state, action] +\
          alpha * (reward + gamma * q_table[next_state, next_action])
  return next_action, next_state
import gym
import numpy as np
env = gym.make('CartPole-v0')
goal_average_steps = 195
max_number_of_steps = 200
num_consecutive_iterations = 100
num_episodes = 5000
last_time_steps = np.zeros(num_consecutive_iterations)
for episode in range(num_episodes):
 
  observation = env.reset()
  state = digitize_state(observation)
  action = np.argmax(q_table[state])
  episode_reward = 0
  for t in range(max_number_of_steps):
      
      env.render()
      
      observation, reward, done, info = env.step(action)
      
      action, state = get_action(state, action, observation, reward, episode)
      episode_reward += reward
      if done:
         print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1,
             last_time_steps.mean()))
         last_time_steps = np.hstack((last_time_steps[1:], [episode_reward]))
         break
  if (last_time_steps.mean() >= goal_average_steps): 
     print('Episode %d train agent successfuly!' % episode)
     break

これでもまでなかなか上手くいってくれません。

次でラストです。


⑤罰則を追加する

→上手くいってない行動には罰則を与えます。
 今まで上げていた報酬のマイナス版を与えるだけなのですが
 罰則で急激に行動の精度が上がった感じがしました。

人も機械学習も飴と鞭のバランスが重要だと言うことですね?

罰則の追加のnoteも参考にしてみてください。

q_table = np.random.uniform(low=-1, high=1, size=(4 ** 4, env.action_space.n))
def bins(clip_min, clip_max, num):
  return np.linspace(clip_min, clip_max, num + 1)[1:-1]
def digitize_state(observation):
  cart_pos, cart_v, pole_angle, pole_v = observation
  digitized = [np.digitize(cart_pos, bins=bins(-2.4, 2.4, 4)),
               np.digitize(cart_v, bins=bins(-3.0, 3.0, 4)),
               np.digitize(pole_angle, bins=bins(-0.5, 0.5, 4)),
               np.digitize(pole_v, bins=bins(-2.0, 2.0, 4))]
  return sum([x * (4 ** i) for i, x in enumerate(digitized)])
def get_action(state, action, observation, reward, episode):
  next_state = digitize_state(observation)
  epsilon = 0.5 * (0.99 ** episode)
  if  epsilon <= np.random.uniform(0, 1):
      next_action = np.argmax(q_table[next_state])
  else:
      next_action = np.random.choice([0, 1])
  alpha = 0.2
  gamma = 0.99
  q_table[state, action] = (1 - alpha) * q_table[state, action] +\
          alpha * (reward + gamma * q_table[next_state, next_action])
  return next_action, next_state
import gym
import numpy as np
env = gym.make('CartPole-v0')
goal_average_steps = 195
max_number_of_steps = 200
num_consecutive_iterations = 100
num_episodes = 5000
last_time_steps = np.zeros(num_consecutive_iterations)
for episode in range(num_episodes):
 
  observation = env.reset()
  state = digitize_state(observation)
  action = np.argmax(q_table[state])
  episode_reward = 0
  for t in range(max_number_of_steps):
      
      env.render()
      
      observation, reward, done, info = env.step(action)
      
      action, state = get_action(state, action, observation, reward, episode)
      episode_reward += reward
      if done:
          reward = -200
      action, state = get_action(state, action, observation, reward, episode)
      if done:
          print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1,
              last_time_steps.mean()))
          last_time_steps = np.hstack((last_time_steps[1:], [t + 1]))
          break
  if (last_time_steps.mean() >= goal_average_steps): 
     print('Episode %d train agent successfuly!' % episode)
     break


結論から言うと最後のコードを打ち込めばできてしまうので
機械学習に興味はあるけど
最初から詰まると挫折しそうと言う方は
最後のコードでどんな動きをするのか理解してモチベーションをあげてから
勉強を始めてもいいかもしれません。


一番初めの
機械学習を理解するためにリサーチしてみた!を書いたのが
4/11なのでかなり時間はかかりましたが
無事機械学習をやってみることができました。

と言っても打ちしながら書いて理解できるようになった程度なので
これを利用して使えるようにこれからさらに勉強していきます。


最後まで読んでいただきありがとうございました。

ぷもんでした!

noteを日々投稿してます! もしいいなと思ってもらえたら サポートしてもらえるとありがたいです。 VRやパソコンの設備投資に使わせていただきます。 ご意見、質問等ありましたらコメントください。 #ぷもん でつぶやいてもらえると励みになります。 一緒に頑張りましょう!