DQN
핵심
- remove high correlation -> experience replay
high correlation이 발생하는 이유
전체 데이타의 correlation에 비해서 짧은 시간 내 에서는 높은 correlation을 갖게 되기 때문
- 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)]
...
Leave a comment