今回は強化学習とはどのようなものなのかを解説した後に,バンディットタスク 迷路 三目並べ カートポール問題を通して強化学習を体験してみようと思います.
強化学習を体験する前に強化学習の概要について簡単に説明します. 強化学習のキーワードとして以下のようなものがあります.
それでは早速,強化学習を使って問題解決をしてみましょう.
2本の腕A,Bを持つ機械を考えます. 一方の腕は引くと平均して45円が出てきます.もう一方の腕は引くと平均して60円が出てきます. 私たちはこの機械の腕を30回引くことができます. たくさんのお金を得るためにはどうすればいいでしょうか? まずは,実際にプレイしてみましょう.
# バンディットタスクに関わる関数
# ランダムに行動するエージェント
def random_agent():
action = random.choice(["A","B"])
return action
# 人間がプレイする場合に使う関数
def human_choice():
action = input("A or B:")
if action == "A" or action == "B":
return action
else:
action = random.choice(["A","B"])
return action
# 強化学習エージェント
def RL_agent():
####### ここで強化学習エージェントを設計する #######
pass
####### ここで強化学習エージェントを設計する #######
# 2本腕バンディットマシーン
def bandit_machine(action,A,B):
if action == "A":
reward = np.random.normal(A,20)
elif action == "B":
reward = np.random.normal(B,20)
return int(reward)
# ランダムに腕を引かせた場合のバンディットタスク
import random
import numpy as np
# A,B の平均を決める
rnd = random.random()
if rnd <= 0.5:
A,B = 45,60
else:
A,B = 60,45
# 報酬のリストを初期化
reward_list = np.zeros(30)
for i in range(30):
###### ここで行動選択 ########
action = random_agent()
###### ここで行動選択 ########
reward = bandit_machine(action,A,B)
reward_list[i] = reward
print(sum(reward_list/30))
# 自分で腕を引く場合のバンディットタスク 30回やってみる
import random
import numpy as np
# A,B の平均を決める
rnd = random.random()
if rnd <= 0.5:
A,B = 45,60
else:
A,B = 60,45
# 報酬のリストを初期化
reward_list = np.zeros(30)
for i in range(30):
###### ここで行動選択 ########
action = human_choice()
###### ここで行動選択 ########
reward = bandit_machine(action,A,B)
print(reward)
reward_list[i] = reward
print(f"A:{A} B:{B}")
print("average:",sum(reward_list/30))
自分でプレイしてみた場合はどうでしょうか? ぜひ1度試してみてください. 回数が少ないうえに排出量に乱数が絡むためランダムより平均報酬が少なくなる場合もあるかもしれませんが, おおむね50より大きな報酬が得られたと思います.(回数が少ないので正しいほうを多く選んでいても少なくなることがありますが,気にしないでください) なぜ,私たちはランダムより大きな報酬を得ることができたのでしょうか? 実際にプレイされた方は,今までにAを引いて出た報酬とBを引いて出た報酬を比べ 経験(予想される報酬)に基づいて「多く出そうな方」を選択されたのではないでしょうか? また,「もしかしたらこっちの方が多く出るかもしれない」とあえて違う方の腕を引いたりもしたのではないでしょうか? 強化学習では経験に基づいて良さそうな行動を選択することを経験の「活用」 そうではない行動を取ってみることを「探索」と言います. 私たちは「探索」と「活用」を行ってよりよい報酬を得たのです. それでは,強化学習の方法を使ってコンピュータにバンディットタスクを解かせてみましょう.
import random
import numpy as np
# 予測報酬が大きいほうを選ぶエージェント
def RL_agent(V_a,V_b):
if V_a < V_b:
action = "B"
elif V_a >= V_b:
action = "A"
return action
# A,B の平均を決める
rnd = random.random()
if rnd <= 0.5:
A,B = 45,60
else:
A,B = 60,45
# 報酬のリストを初期化
reward_list = np.zeros(30)
count_A = 0 # Aを選んだ回数
count_B = 0 # Bを選んだ回数
# A,Bの期待値を記憶する
V_a = 20
V_b = 20
for i in range(30):
###### ここで行動選択 ########
action = RL_agent(V_a,V_b)
###### ここで行動選択 ########
reward = bandit_machine(action,A,B)
if action == "A":
V_a += 0.5*(reward - V_a) # 予測報酬を誤差を使って更新する
count_A += 1
if action == "B":
V_b += 0.5*(reward - V_b) # 予測報酬を誤差を使って更新する
count_B += 1
reward_list[i] = reward
print(f"A:{A} B:{B}")
print(f"V_a:{V_a:.1f} V_b:{V_b:.1f}")
print(f"count_A:{count_A} count_B:{count_B}")
print(f"average: {sum(reward_list/30):.1f}")
このような期待報酬の高い行動を取る戦略のことをグリーディな戦略と言います. このコードでは探索を行わないため多くの場合どちらか一方の行動しか選ばれません. 鋭い方には「初期値を20にしているんだからそれはそうだ」と見抜かれてしまうかもしれません. 確かにこの問題の場合は事前に報酬が45,60であることが分かっているため初期値を高めに80(楽観的初期値)などと設定しておけばいいのですが,多くの問題は強化学習エージェントを設計する段階では プログラムを書く私たちにもどの程度の期待値で報酬を返すのかが分かりません. そのため,このような事故が起きてしまいました. 解決するために一定の割合で探索をするように改良してみましょう.
# epsilon(0<=epsilon<=1)の確率で探索するエージェント
def epsilon_greedy(V_a,V_b,epsilon):
rnd = random.random()
# epsilonの確率で期待報酬の低いほうを選び探索する
if rnd < epsilon:
if V_a < V_b:
action = "A"
elif V_a >= V_b:
action = "B"
# 1-epsilonの確率で期待報酬の高いほうを選ぶ
else:
if V_a < V_b:
action = "B"
elif V_a >= V_b:
action = "A"
return action
行動選択の部分をepsilon_greedyに変えて何回か実行してみてください. 今回は探索を行ったため期待値の大きい場合を選ぶことが多そうです. 探索の割合は高すぎると無駄が多くなり,低すぎると正しいほうを選べなくなります. 様々な割合や学習率で試してみてください. 確率εで探索を,確率1-εで活用を行うような戦略をε-グリーディな戦略と言います.
強化学習エージェントが探索と予測報酬の更新によって学習を進めていく様子を見ていただきました. 次回は強化学習エージェントが迷路を学習する様子を見ていきましょう.