독학 연구소
공부한 내용을 정리합니다.
Dueling DQN (1)
DQN(Deep Q-Networks) (2)

Double DQN

논문 Deep Reinforcement Learning with Double Q-learning 으로 소개되었습니다.

 

DQN 에서 $Q$ 의 가치가 과대 평가되는 문제를 해결하기 위한 아이디어입니다. 타겟을 계산하는데 있어 DQN 과 차이가 있는데 Double DQN 은 일반 네트워크 $\theta$ 에서 행동 가치가 가장 높은 행동을 찾고 그 행동에 대한 가치는 타겟 네트워크 $\theta^-$ 에서 구합니다.

 

$Y_t^{\text{DQN}} \equiv R_{t+1} + \gamma {\underset{a} {\text{max}}}Q(S_{t+1}, a; \boldsymbol{\theta}_t^-)$

 

$Y_t^{\text{DoubleDQN}} \equiv R_{t+1} + \gamma Q(S_{t+1}, {\underset{a} {\text{argmax}}} Q(S_{t+1}, a; \boldsymbol{\theta}_t); \boldsymbol{\theta}_t^-)$

 

 

이는 DQN 과 DDQN 의 $Q$ 에 대해 최적 가치 $V_*(s)$ 와의 오차를 비교한 것입니다.

 

DQN 은 취할 수 있는 행동의 수가 증가함에 따라 최대 행동 가치는 더욱 과대 평가되어 오차는 증가합니다.

 

 

Dueling DQN

논문 Dueling Network Architectures for Deep Reinforcement Learning 으로 소개되었습니다.

 

행동 가치 함수 $Q$ 를 가치 함수 $V$ 와 어드밴티지 함수 $A$ 로 나눈다는 아이디어입니다. $A$ 를 이용하여 상태별 행동 가치에 대해 평균을 제하고 $V$ 를 더함으로써 행동 가치 함수 $Q$ 를 나타내는 것입니다.

 

$Q(s, a) = V(s) + A(s, a) - \frac{1}{|\mathcal{A}|}\sum_{a} A(s, a)$

 

DQN vs Dueling DQN

 

 

구현

Double DQN 과 Dueling DQN 을 모두 포함하는 DDDQN 알고리즘을 구현합니다.

 

알고리즘 테스트 환경은 OpenAI GymLunarLander-v2 입니다.

 

 

LunarLander 는 드론을 깃발안에 안정적으로 착륙하는 문제입니다. 드론이 안정적인 착륙에 성공하면 좋은 보상을 받을 수 있으며 깃발 안쪽에 착륙한다면 더 큰 보상을 받습니다. 반대로 화면에서 사라지거나 착륙에 실패하면 나쁜 보상을 받게 됩니다.

 

 

환경으로부터 받을 수 있는 정보를 출력합니다.

import gym

env = gym.make('LunarLander-v2')

print('obs:', env.observation_space.shape[0])
print('act:', env.action_space.n)

 

상태 정보는 8차원의 벡터로 연속적인 공간을 갖으며 에이전트가 취할 수 있는 행동은 4개의 범위를 갖는 이산적인 공간입니다.

obs: 8
act: 4

 

신경망을 정의합니다.

class Network:
    def __init__(self, scope):
        with tf.variable_scope(scope):
            with tf.name_scope('input'):
                self.s = tf.placeholder(tf.float32, [None, N_S])
                self.q_target = tf.placeholder(tf.float32, [None, N_A])
            
            with tf.variable_scope('layer'):
                self.fc1 = tf.layers.dense(self.s, 256, tf.nn.relu)
                self.fc2 = tf.layers.dense(self.fc1, 128, tf.nn.relu)
                self.v = tf.layers.dense(self.fc2, 1)
                self.adv = tf.layers.dense(self.fc2, N_A)
                
            with tf.variable_scope('output'):
                self.q = self.v + (self.adv - tf.reduce_mean(self.adv, axis=1, keep_dims=True))
                
            with tf.name_scope('loss'):
                self.loss = tf.losses.mean_squared_error(self.q_target, self.q)
                
            with tf.name_scope('optimizer'):
                self.train_op = tf.train.RMSPropOptimizer(LR).minimize(self.loss)
        
            self.params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope)

 

Dueling DQN 에서 제안하는 구조로 층을 구성합니다.

with tf.variable_scope('layer'):
    self.fc1 = tf.layers.dense(self.s, 256, tf.nn.relu)
    self.fc2 = tf.layers.dense(self.fc1, 128, tf.nn.relu)
    self.v = tf.layers.dense(self.fc2, 1)
    self.adv = tf.layers.dense(self.fc2, N_A)

 

두 번째 층에서 $V$ 와 $A$ 의 층으로 연결되는 것을 볼 수 있습니다. 또한 $A$ 층의 출력이 행동의 크기입니다.

 

$V$ 와 평균을 제한 $A$ 를 합하여 $Q$ 를 나타냅니다.

with tf.variable_scope('out'):
    self.q = self.v + (self.adv - tf.reduce_mean(self.adv, axis=1, keep_dims=True))

 

손실 함수를 정의합니다.

with tf.name_scope('loss'):
    self.loss = tf.losses.mean_squared_error(self.q_target, self.q)

 

타겟과 예측 사이의 평균 제곱 오차(mean squared error, MSE)로 $Q$ 타겟과 추정 $Q$ 간의 계산입니다.

 

손실값을 최소화하는 방향으로 학습하도록 설정합니다.

with tf.name_scope('optimizer'):
    self.train_op = tf.train.RMSPropOptimizer(LR).minimize(self.loss)

 

$\epsilon$-greedy 정책을 정의합니다.

self.epsilons = np.linspace(self.epsilon, 0.1, 200000)

...

def decay_epsilon(self):
    self.epsilon = self.epsilons[min(self.train_cnt, 200000-1)]
        
def get_action(self, s):
    if np.random.rand() < self.epsilon:
        action = np.random.randint(N_A)
    else:
        q = self.sess.run(self.main.q, {self.main.s: s[np.newaxis, :]})
        action = np.argmax(q)
    return action
    
def train(self):
    ...

    self.train_cnt += 1

    self.decay_epsilon()

 

일반 신경망 업데이트 횟수가 200000번까지 $\epsilon$ 을 감소시키며 그 이후부터는 $\epsilon$ 을 0.1로 유지합니다.

 

 

일반 신경망을 업데이트합니다.

def train(self):
    minibatch = random.sample(self.replay_memory, BATCH_SIZE)
    minibatch = np.array(minibatch)

    q = self.sess.run(self.main.q, {self.main.s: minibatch[:, 4].tolist()})
    q_a = np.argmax(q, axis=1)

    target_q = self.sess.run(self.target.q, {self.target.s: minibatch[:, 4].tolist()})
    update_value = minibatch[:, 2] + GAMMA * target_q[range(len(minibatch)), q_a] * ~minibatch[:, 3].astype(np.bool)


    q_target = self.sess.run(self.main.q, {self.main.s: minibatch[:, 0].tolist()})
    q_target[range(len(minibatch)), minibatch[:, 1].tolist()] = update_value

    _, loss = self.sess.run([self.main.train_op, self.main.loss], {self.main.s: minibatch[:, 0].tolist(),
                                                                   self.main.q_target: q_target})

    self.train_cnt += 1

    self.decay_epsilon()

    return loss

 

Double DQN 에서 제안한 식으로 타겟을 계산합니다.

q = self.sess.run(self.main.q, {self.main.s: minibatch[:, 4].tolist()})
q_a = np.argmax(q, axis=1)

target_q = self.sess.run(self.target.q, {self.target.s: minibatch[:, 4].tolist()})
update_value = minibatch[:, 2] + GAMMA * target_q[range(len(minibatch)), q_a] * ~minibatch[:, 3].astype(np.bool)

 

일반 신경망의 $Q_\theta$ 에서 최대의 행동 가치를 갖는 행동 시퀀스를 구하고 이를 이용하여 타겟 신경망의 $Q_{\theta^-}$ 에서 행동 가치를 구하여 계산합니다.

 

$Y_t^{\text{DoubleDQN}} \equiv R_{t+1} + \gamma Q(S_{t+1}, {\underset{a} {\text{argmax}}} Q(S_{t+1}, a; \boldsymbol{\theta}_t); \boldsymbol{\theta}_t^-)$

 

 

1000번의 에피소드 동안 학습한 결과입니다.

 

 

저장된 모델을 불러와 테스트합니다.

INFO:tensorflow:Restoring parameters from ./tmp/dddqn_lunarlander/model/model
episode: 0 / reward 276.7014293397136
episode: 1 / reward 22.280288275624372
episode: 2 / reward 231.75701431710345
episode: 3 / reward 215.13574884255914
episode: 4 / reward 235.63575783693358
episode: 5 / reward 283.90393140514414
episode: 6 / reward 285.0946617743626
episode: 7 / reward -62.710477314100785
episode: 8 / reward -28.79188154959367
episode: 9 / reward 34.11406993609501

 

 

 

'머신러닝 > 강화학습' 카테고리의 다른 글

액터-크리틱(Actor-Critic)  (0) 2020.11.27
정책 그래디언트(Policy Gradient)  (0) 2020.11.24
DQN(Deep Q-Networks) (1)  (0) 2020.11.20
함수 근사(Function Approximation)  (0) 2020.11.18
Model-Free Control  (0) 2020.11.13
  Comments,     Trackbacks