今回はマルチエージェント強化学習アルゴリズムの1つであるWoLF PHCについて紹介します。
Win or Learn Fast(WoLF)
まずWin or Learn Fast(WoLF)についてお話します。
WoLFはマルチエージェント強化学習における重要な学習原理です。
この原理は、名前にある通り、勝っている時(Win)はゆっくり学習。負けている時(Lose)は早く学習するといった原理(principle)です。
2つの学習率\(\delta_w, \delta_l (\delta_w > \delta_l)\)を用意します。勝っている時には\(\delta_w\)を用いて、負けている時は\(\delta_l\)用いて学習します。
ここで、言う勝つというのは、
現在の戦略(方策)\(\pi\)による期待報酬値が、ナッシュ均衡戦略をとった時の期待報酬値 を上回っていることをいいます。
この手法は2人2行動ゲームにおいて、各エージェントがWoLFを用いて利己的に学習する時に、ナッシュ均衡に収束することが
証明されています*1。そのため、マルチエージェント強化学習では重要な原理の1つになっています。
WoLF-PHC
WoLF-PHCとは、前回紹介したPolicy Hill Climbing アルゴリズムにWoLFの原理を適用したものになります。*2
Policy Hill ClimbingはQ Learning のQ値を用いて、方策を修正していく手法です。
www.tcom242242.net
WoLF-PHCでは、PHCアルゴリズムの方策を更新する速度を、WoLFの原理を用いて調整します。
この時に勝つをどのように定義するかがポイントです。 通常のマルチエージェント強化学習で、各エージェントが各々の行動や状態を観測できない場合には、
各エージェントがナッシュ均衡戦略を知る(or 計算する)ことは基本的には不可能です。
そのため、ナッシュ均衡方策以外の方策を用いてWoLFの原理を適用しなければなりません。
WoLF-PHCでは、平均方策 をナッシュ均衡方策の代わりに用いています。 この平均方策というのは単純にこれまでの方策の平均です。
この平均方策より現在の方策を用いた時の期待報酬が高ければWin、
低ければLoseとして、WoLFを適用します。
つまり、平均方策より現在の方策を用いた時の期待報酬が高ければ、ゆっくり学習し、
低ければ早く学習します。
以下にアルゴリズムを示します。
実験
問題設定
前回と同様にMatching Pennies を用いて実験します。
以下がMatching Penniesの利得表
1,2 | Heads | Tails |
---|---|---|
Heads | 1, -1 | -1,1 |
Tails | -1,1 | 1,-1 |
ソースコード
ソースコードはgithubにあげました。
プログラムの構成は以下のようになっています。
run.pyを実行すれば実験が始まります。
├── matrix_game.py # Matching Pennies ├── run.py # 実験実行用プログラム └── wolf_agent.py # Win or Learn Fast Agent
wolf_agent.py
import numpy as np class WoLFAgent(): """ Policy hill-climbing algorithm(PHC) http://www.cs.cmu.edu/~mmv/papers/01ijcai-mike.pdf """ def __init__(self, alpha=0.1, delta=0.0001, actions=None, high_delta=0.004, low_delta=0.002): self.alpha = alpha self.actions = actions self.last_action_id = None self.q_values = self._init_q_values() self.pi = [(1.0/len(actions)) for idx in range(len(actions))] self.pi_average = [(1.0/len(actions)) for idx in range(len(actions))] self.high_delta = high_delta self.row_delta = low_delta self.pi_history = [self.pi[0]] self.reward_history = [] self.conter = 0 def _init_q_values(self): q_values = {} q_values = np.repeat(0.0, len(self.actions)) return q_values def act(self, q_values=None): action_id = np.random.choice(np.arange(len(self.pi)), p=self.pi) self.last_action_id = action_id action = self.actions[action_id] return action def observe(self, reward): self.reward_history.append(reward) self.q_values[self.last_action_id] = ((1.0 - self.alpha) * self.q_values[self.last_action_id]) + (self.alpha * reward) self._update_pi_average() self._update_pi() def _update_pi_average(self): self.conter += 1 for aidx, _ in enumerate(self.pi): self.pi_average[aidx] = self.pi_average[aidx] + (1/self.conter)*(self.pi[aidx]-self.pi_average[aidx]) if self.pi_average[aidx] > 1: self.pi_average[aidx] = 1 if self.pi_average[aidx] < 0: self.pi_average[aidx] = 0 def _update_pi(self): delta = self.decide_delta() max_action_id = np.argmax(self.q_values) for aidx, _ in enumerate(self.pi): if aidx == max_action_id: update_amount = delta else: update_amount = ((-delta)/(len(self.actions)-1)) self.pi[aidx] = self.pi[aidx] + update_amount if self.pi[aidx] > 1: self.pi[aidx] = 1 if self.pi[aidx] < 0: self.pi[aidx] = 0 self.pi_history.append(self.pi[0]) def decide_delta(self): """ comfirm win or lose """ expected_value = 0 expected_value_average = 0 for aidx, _ in enumerate(self.pi): expected_value += self.pi[aidx]*self.q_values[aidx] expected_value_average += self.pi_average[aidx]*self.q_values[aidx] if expected_value > expected_value_average: # win return self.row_delta else: # lose return self.high_delta
matrix_game.py
import numpy as np class MatrixGame(): def __init__(self): self.reward_matrix = self._create_reward_table() def step(self, action1, action2): r1, r2 = self.reward_matrix[action1][action2] return None, r1, r2 def _create_reward_table(self): reward_matrix = [ [[1, -1], [-1, 1]], [[-1, 1], [1, -1]] ] return reward_matrix
run.py
import numpy as np import matplotlib.pyplot as plt from wolf_agent import WoLFAgent from matrix_game import MatrixGame import pandas as pd if __name__ == '__main__': nb_episode = 1000 actions = np.arange(2) agent1 = WoLFAgent(alpha=0.1, actions=actions, high_delta=0.0004, low_delta=0.0002) agent2 = WoLFAgent(alpha=0.1, actions=actions, high_delta=0.0004, low_delta=0.0002) game = MatrixGame() for episode in range(nb_episode): action1 = agent1.act() action2 = agent2.act() _, r1, r2 = game.step(action1, action2) agent1.observe(reward=r1) agent2.observe(reward=r2) print(agent1.pi) print(agent2.pi) plt.plot(np.arange(len(agent1.pi_history)),agent1.pi_history, label="agent1's pi(0)") plt.plot(np.arange(len(agent2.pi_history)),agent2.pi_history, label="agent2's pi(0)") plt.ylim(0, 1) plt.xlabel("episode") plt.ylabel("pi(0)") plt.legend() plt.savefig("result.jpg") plt.show()
実験結果
単純に何回か学習行動を行い、 方策が収束するかを見てみます。
PHCとは異なり、Headsを選択する確率が0.5で収束していることがわかります。
この問題でのナッシュ均衡方策は0.5なので、うまく学習できていることがわかります。
参考文献
Multiagent learning using a variable learning rate
Rational and Convergent Learning in Stochastic Games
コメント