ichou1のブログ

主に音声認識、時々、データ分析のことを書く

Kerasメモ(強化学習)その3

前回の続き。

DQN(Deep Q Learning)の中身について見ていく。

AgentとしてDQNAgentを使う場合、指定しなければデフォルトで「Double DQN」が有効になる。

rl/agents/dqn.py
class DQNAgent(AbstractDQNAgent):
    def __init__(self,
                 model,
                 policy=None,
                 test_policy=None,
                 enable_double_dqn=True,  # <---
                 enable_dueling_network=False,
                 dueling_type='avg', *args, **kwargs):

「Double DQN」は2つのQ-networkを使う。

役割 学習の有無 ソースコード
target network estimate the Q value 学習しない sefl.target_model
online network predicts the actions 学習する self.model
rl/agents/dqn.py
class DQNAgent(AbstractDQNAgent):

    def compile(self, optimizer, metrics=[]):
        # We never train the target model
        self.target_model = clone_model(self.model, {})
        self.target_model.compile(optimizer='sgd', loss='mse')
        self.model.compile(optimizer='sgd', loss='mse')
        ...
        self.trainable_model = trainable_model  # 後述
trainable_modelの組み上げ
        # 「online network」
        ins = [self.model.input] 
        y_pred = self.model.output  # Q value

        y_true = Input(name='y_true', shape=(self.nb_actions,))
        mask = Input(name='mask', shape=(self.nb_actions,))

        loss_out = Lambda(clipped_masked_error,
                          output_shape=(1,),
                          name='loss')([y_true, y_pred, mask])

        trainable_model = Model(inputs=ins + [y_true, mask],
                                outputs=[loss_out, y_pred])
インプット
  • ins: 状態(Observation)
  • y_true: 実際にとった行動に対する報酬
  • mask: 行動のmaskデータ(「実際にとった行動」は「1」、とらなかった行動は「0」)
アウトプット
  • Huber損失(mask=1の行動に対する「y_true」と「y_pred」)
  • y_pred: 「online network」のアウトプット(Q値)

報酬が低い行動は、Q値が低くなるように学習される。
「online network」の学習結果を「target network」に反映していく。

        # self.target_model_update: 0.01 (soft-update)
        # Soft update with `0.99 * old(self.target_model) + 0.01 * new(self.model)`.
        updates = get_soft_target_model_updates(self.target_model, self.model, 0.01)
        optimizer = AdditionalUpdatesOptimizer(optimizer, updates)



順伝播

「online network」からQ値を得る。
Q値は「その行動の選びやすさ」を示すので、値が大きいほど、選択される確率が高くなるが、あくまでも「選ばれる確率が高い」ということ。
行動の選択はポリシーに従う。
偶然の発見のために、ランダム性を持たせる。

rl/agents/dqn.py
class DQNAgent(AbstractDQNAgent):

    def forward(self, observation):
        # Select an action.
        state = self.memory.get_recent_state(observation)
        q_values = self.compute_q_values(state)  # self.model.predict_on_batch()
        if self.training:
            action = self.policy.select_action(q_values=q_values)

        return action
rl/policy.py
class BoltzmannQPolicy(Policy):

    # Example q_values: [-0.01126872, -0.00710144]
    def select_action(self, q_values):
        exp_values = np.exp(q_values)
        # --> [0.98879454, 0.99292371]

        probs = exp_values / np.sum(exp_values)
        # --> [0.49895818, 0.50104182]

        action = np.random.choice([0, 1], p=probs)
        # --> 0(move to the left) or 1(move to the right)

実際のコードは、numpy.exp()コール前に値をclipしているが省略

逆伝播

経過となるデータは「Experience Replay」に蓄積する。

class DQNAgent(AbstractDQNAgent):

    def backward(self, reward, terminal):
        self.memory.append(self.recent_observation,
                           self.recent_action,
                           reward,
                           terminal,
                           training=self.training)

データの取り出し。

        experiences = self.memory.sample(self.batch_size)

データの中身の一例。

[Experience(state0=[array([0.02735838, -0.2061478 , -0.0381168 ,  0.20968898])],
            action=1,
            reward=1.0,
            state1=[array([ 0.02323542, -0.01050215, -0.03392302, -0.09476996])],
            terminal1=False)]

報酬を計算する。

            # 「online network」から、s_t+1における最も高いQ値のactionを選ぶ
            q_values = self.model.predict_on_batch(state1_batch)
            actions = np.argmax(q_values, axis=1)

            # 「target network」から、s_t+1におけるQ値(対象は上のaction)を得る
            target_q_values = self.target_model.predict_on_batch(state1_batch)
            # self.batch_size: 32
            q_batch = target_q_values[range(32), actions]

            # 「target network」から得たQ値を使ってrewardを計算する
            # self.gamma: 0.99
            discounted_reward_batch = self.gamma * q_batch

            # ゲームオーバ(terminal1=True)なら「0」
            # Set discounted reward to zero for all states that were terminal.
            discounted_reward_batch *= terminal1_batch

            # R: reward_batch + discounted_reward_batch
            # Rが「y_true」に該当する
            # targets[idx][action] = R
            # dummy_targets[idx] = R

            # train_on_batch()でlabelを与えているが、ダミー
            # We use a dummy target 
            # since the actual loss is computed in a Lambda layer
            metrics = self.trainable_model.train_on_batch(ins + [targets, masks],
                                              [dummy_targets, targets])

train_on_batch()でインプットとして渡すデータの中身の一例。

ins:           [0.02735838, -0.2061478 , -0.0381168 ,  0.20968898]
targets:       [0.       , 1.0051814]
masks:         [0., 1.]



今回は「ゲームオーバを避ける」ように行動を学習した。
時刻tにおいてアクション「a_t」をとり、時刻t+1における報酬が低かったとする。
その場合、時刻tにおけるアクション「a_t」に対するQ値は低く設定される。


ハイスコアを競うようなゲームなら報酬が高くなるように行動を学習する。
時刻tにおいてアクション「a_t」をとり、時刻t+1における報酬が高かったとする。
その場合、時刻tにおけるアクション「a_t」に対するQ値は高く設定される。