DQN

참조
pseudo-code

핵심

  1. remove high correlation -> experience replay

high correlation이 발생하는 이유
전체 데이타의 correlation에 비해서 짧은 시간 내 에서는 높은 correlation을 갖게 되기 때문 correlation

  1. relieve non-stationary target -> fixed target Q

1. experience replay

버퍼 사이즈 만큼 채우고 난뒤는 맨왼쪽을 지우고 맨뒤 삽입 -> FIFO -> deque 구조사용

In [1]:
1
2
3
4
5
6
7
8
9
class ReplayBuffer(object):
    ...
    def add(self, s, a, r, s_):
        if self.buffer_pointer < self.buffer_size:
            self.buffer.append((s, a, r, s_))
            self.buffer_pointer += 1
        else:
            self.buffer.popleft()
            self.buffer.append((s, a, r, s_))

주의해야 될 점: 처음에 buffer가 채우져 있지 않으면 안된다.

In [2]:
1
2
3
4
5
6
7
8
class Agent_DQN:
    def learn(self):
        '''
        인공신경망의 업데이트가 이루어지는 함수
        '''
        # 메모리를 적당히 채우면 learn 하고 그렇지 않으면 learn을 생략한다.
        if self.learning_iteration >= self.memory_size:
            ...                    

2.fixed target Q

2.1 evaluation_network

In [3]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Agent_DQN:  
    ...    
    def build_evaluation_network(self):
        '''
        eval net을 만들 땐 target net과는 다르게 loss를 구하는 net이 추가되어야 함.
        target net 은 fixed Q-target을 위해서 쓰는 것이지 업데이트를 하지 않는다.
        때문에 이 eval net만 tarinable = Ture 로 설정되어야 함.
        :return:
        '''
        ...

        #  실제 네트워크
        with tf.variable_scope('en'):
            hidden1 = tf.layers.dense(self.eval_input, 10, activation=tf.nn.relu,
                                      kernel_initializer=tf.random_normal_initializer(0., 0.5),
                                      bias_initializer=tf.random_normal_initializer(0., 0.1), name='layer1',
                                      trainable=True)
            self.q_eval = tf.layers.dense(hidden1, self.action_size, activation=tf.nn.relu,
                                      kernel_initializer=tf.random_normal_initializer(0., 0.5),
                                      bias_initializer=tf.random_normal_initializer(0., 0.1), name='layer2',
                                      trainable=True)

        # loss를 구하는 부분
        with tf.variable_scope('loss'):
            self.a_one_hot = tf.one_hot(self.a, depth=self.action_size)
            self.q_predict = tf.reduce_sum(tf.multiply(self.q_eval, self.a_one_hot), axis=1) # 가능한 action에 대한 Q를 갖음
            self.loss = tf.reduce_mean(tf.squared_difference(self.y, self.q_predict))
        with tf.variable_scope('train'):
            self._train_op = tf.train.RMSPropOptimizer(self.learning_rate)\
                .minimize(self.loss, global_step=self.global_step)

2.2 target_network

loss 구하는 graph가 없음

In [4]:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Agent_DQN:
    ...
    def build_target_network(self):
        self.target_input = tf.placeholder(tf.float32, [None, self.state_size], name = 'target_input')
        with tf.variable_scope('tn'):
            hidden1 = tf.layers.dense(self.target_input, 10, activation=tf.nn.relu,
                                      kernel_initializer=tf.random_normal_initializer(0., 0.5),
                                      bias_initializer=tf.random_normal_initializer(0., 0.1), name='layer1',
                                      trainable=False)
            self.get_q_target = tf.layers.dense(hidden1, self.action_size, activation=tf.nn.relu,
                                          kernel_initializer=tf.random_normal_initializer(0., 0.5),
                                          bias_initializer=tf.random_normal_initializer(0., 0.1), name='layer2',
                                          trainable=False)

target y를 구하고 mse loss를 계산

In [5]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Agent_DQN:
    ...    
    def learn(self):
        '''
        인공신경망의 업데이트가 이루어지는 함수
        '''
        ...
        # 메모리를 적당히 채우면 learn 하고 그렇지 않으면 learn을 생략한다
        if self.learning_iteration >= self.memory_size:
            # eval_net 과 fixed_q_target을 적절한 비율로 교체해준다.
            self.sess.run(self.replace_target_op)

            # q_eval 은 현재 Q함수값을 구하기 위해, get_q_target은 max함수에 포함되어있는 Q값을 구하기 위해 사용한다.
            get_q_target, q_eval = self.sess.run(
                [self.get_q_target, self.q_eval],
                feed_dict={
                    self.target_input: batch_s_,  # fixed params
                    self.eval_input: batch_s,  # newest params
                })

            # action 은 배치 메모리에서 state가 저장된 다음부분부터가 action이므로 그 값을 가져오면 된다.
            a = batch_a
            # reward는 action 다음에 저장했으므로 그 다음 값을 가져오면 된다.
            reward = batch_r
            # self.y placeholder에 넣어줄 값을 위에서 구한 값으로 적절히 만들어서 넣는다.
            _, self.loss_out = self.sess.run([self._train_op, self.loss],
                                         feed_dict={self.eval_input: batch_s,
                                                    self.y: reward + self.gamma * np.max(get_q_target, axis=1),
                                                    self.a: a
                                                    })

            # epsilon -greedy 탐험을 하기 위해 epsilon 값을 주기적으로 낮춰주어야한다.
            self.epsilon = self.epsilon * self.epsilon_decay
            ...

논문에는 없지만 구현상 변형

처음에는 hard copy를 핮만 학습할땐 soft copy로 효율적인 학습가능

In [6]:
1
2
3
4
5
6
7
8
9
10
11
class Agent_DQN:
    def __init__(self, arguments):
        ...
        # target net과 eval net의 파라미터를 모아준다. scope의 상위 directory를 이용해서 모아줄 수 있다.
        t_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='tn')
        self.t_params = t_params # hard copy
        e_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='en')
        
        # tf assign을 이용하면 특정 텐서 변수값에 다른 하나를 넣을 수 있다. t와 e의 함수로 만들어서 assign을 하면 효율적으로 파라미터를 옮길 수 있다.
        self.replace_target_op = [tf.assign(t, (1 - 0.03) * t + 0.03 * e) for t, e in zip(t_params, e_params)]
        ...

Tags:

Categories:

Updated:

Leave a comment