はじめに
※本記事は、Keras-RLの基本的な使い方を理解している人向けに記載しています
Pythonの強化学習ライブラリであるKeras-RLを使っていると、
細かいところで独自にカスタマイズしたくなることがあります。
ここでは例として強化学習の**ポリシー(方策)**について考えたいと思います。
ポリシーとは、複数の行動の選択肢があった場合に、どの行動を選択するかを決めるルールです。
例えば、
q_values = [1.3, 0.8, 1.2, 0.1]
という行動の選択肢に対応するQ値のリストがあったとしましょう。
(行動1のQ値が1.3、行動2のQ値が0.8、・・)
単純に考えれば最大のQ値である行動1を選択するのがよさそうですが、
もしかすると行動3を選んだほうが最終的にはよい報酬が得られるかもしれません。
そこで、たまにランダムに行動を選択したりすることが考えられるわけですが、
このルールがポリシーと呼ばれています。
また、強化学習をある現実的な問題に当てはめたときに、
そのドメインの知識を利用することも効果的と考えられます。
(例えば消費電力や機器の性能の制約により「ある時間内に行動はN回までしか変更できない」など)
そこで、本記事ではKeras-RLで独自のポリシーを作成して実行する手順を紹介していきます。
なお、Keras-RLの各パラメータ設定については以下記事をご参照ください。
Pythonの強化学習ライブラリKeras-RLのパラメータ設定
デフォルトのポリシー
Keras-RLには今のところ以下のポリシーが用意されています。
- LinearAnnealedPolicy
- SoftmaxPolicy
- EpsGreedyQPolicy
- GreedyQPolicy
- BoltzmannQPolicy
- MaxBoltzmannQPolicy
- BoltzmannGumbelQPolicy
よくサンプルで見るのはEpsGreedyQPolicyですが、これは
「確率εでランダムな行動を選択し、確率1-εでグリーディに行動を選択する」
というポリシーです。
「グリーディに行動を選択する」というのはQ値のリストが与えられたときに、
単にQ値が最大となる行動を選択するというだけのことです。
最初の例で言うと行動1を選択するということになります。
それほど長くないコードなので、実際に見てみましょう。
https://github.com/keras-rl/keras-rl/blob/master/rl/policy.py
以下、コードにコメントをはさみながら解説していきます。
class EpsGreedyQPolicy(Policy):
# カスタムポリシーを作るには、Policyクラスを継承すればよい
"""Implement the epsilon greedy policy
Eps Greedy policy either:
- takes a random action with probability epsilon
- takes current best action with prob (1 - epsilon)
"""
def __init__(self, eps=.1):
super(EpsGreedyQPolicy, self).__init__()
self.eps = eps
# ここで必要なパラメータを初期化する
def select_action(self, q_values):
# q_valuesにQ値のリストがセットされている
"""Return the selected action
# Arguments
q_values (np.ndarray): List of the estimations of Q for each action
# Returns
Selection action
"""
assert q_values.ndim == 1
nb_actions = q_values.shape[0] # 行動の選択肢の数
if np.random.uniform() < self.eps: # epsの確率で
action = np.random.randint(0, nb_actions) # ランダムに行動を選択
else: # 1- epsの確率で
action = np.argmax(q_values) # Q値が最大となる行動を選択
return action # 行動(のインデックス)を返却する
カスタムポリシーを作成する
一から作るのも手間なので、EpsGreedyQPolicyをベースに作ってみます。
ここでは簡単な例として、
「確率εでランダムに行動を選択する。ただし、その場合は、Q値が閾値q_thを超えるものから選択する」
というポリシー(EpsGreedyCustomQPolicy)を実装します。
from rl.policy import Policy
import numpy as np
import random
class EpsGreedyCustomQPolicy(Policy):
def __init__(self, eps=.1, q_th=0):
super(EpsGreedyCustomQPolicy, self).__init__()
self.eps = eps
self.q_th = q_th # パラメータを初期化
def select_action(self, q_values):
assert q_values.ndim == 1
if np.random.uniform() < self.eps:
# リストから条件(q_thよりもQ値が大きいこと)を満たすアクションをランダムに選択
action = np.random.choice(np.where(q_values > self.q_th)[0])
else:
action = np.argmax(q_values)
return action
ここでは、例外処理を入れていないことに注意してください。
では、カスタマイズしたポリシーとEpsGreedyQPolicyで選択する行動に違いがあるか確認しましょう。
from rl.policy import EpsGreedyQPolicy
from mypolicy import EpsGreedyCustomQPolicy
import numpy as np
eps = 0.99
q_th = 1.0
policy_orig = EpsGreedyQPolicy(eps=eps)
policy_custom = EpsGreedyCustomQPolicy(eps=eps, q_th=q_th)
q_values = np.array([1.2, 0.8, 1.1, 0.2, 0.5])
action_orig = policy_orig.select_action(q_values)
action_custom = policy_custom.select_action(q_values)
print("Q-values: " + str(q_values))
print("EpsGreedy: " + str(action_orig))
print("Custom: " + str(action_custom))
何度かやってみると、EpsGreedyQPolicyはランダムに選んでいるのに対し、
EpsGreedyCustomQPolicyは0か2のインデックスしか選ばないことがわかると思います。
Q-values: [1.2 0.8 1.1 0.2 0.5]
EpsGreedy: 3
Custom: 2
おわりに
「カスタムポリシーを作成する」といっても、
「Q値のリストであるq_valuesから行動のインデックスにどう変換するか?」
という非常にシンプルな問題です。
強化学習を適用する際に最初からポリシーに頭を悩ませる必要はないと思いますが、
うまく学習ができない時などに検討してみるのもよいでしょう。