본문 바로가기

트렌드 한눈에 보기/학계 트렌드

[CS234] Assignment 2 풀이

CS234 Assignment 2는 Tensorflow에 대한 기본적인 이해가 필요하다.

과제를 풀기 위해 Tensorflow Wiki를 정독하는 것은 

너무 비효율적으로 느껴졌고, 사실 Wiki를 이해하기도 쉽지 않았다.

 

그래서 과제가 어떻게 구성되어 있는지를 먼저 익혀보고자 한다.

Imitation Learning의 일종이라고 생각하면 될 것 같다.

전문가가 짜놓은 Deep Q Network 구조를 학습함으로써

RL에 대한 감을 좀 익히고....는 사실 과제 해답을 먼저 보지 않으면

과제 자체를 풀 자신이 없다.


Assignment 2의 코딩 과제는 총 다섯 문제(Q1 ~ Q5)이다.

각 문제 당 여러 개의 함수를 작성해야 하니, 실제 양은 그보다 많다.

모든 문제를 풀면 Mnih 교수의 Atari DQN 을 구현할 수 있게끔 되어 있다.

 

Q1은 $\epsilon-greedy$를 구현하는 문제이다.

어떤 행동을 취할지 결정하는 과정을 한 번의 step이라고 했을 때,

Atari DQN 논문에서 $\epsilon-greedy$는

초기 100만 step동안 $\epsilon$이 1에서 0.1로 감소한다.

 

그러므로 Q1의 Linear Schedule 함수는 1에서 0.1로 감소하는 과정을 구현하면 되고

Linear Exploration 함수는 해당 과정동안 epsilon의 확률로 

랜덤한 행동을 취하고, (1-epsilon)의 확률로 최적의 행동을 취하는 과정을 구현하면 된다.


Q2는 Q value를 Linear VFA로 구현하는 과정이다.

강의를 들을 때는 $Q = x(s) * w$ 하는 정도로 간단하게 이해했지만

tensorflow로 구현할 때는 사정이 다르다. 

알고리즘을 구현하는 tensorflow만의 방법이 있기에

그 규칙들을 따라주면서 코드를 짜야하기 때문이다.

 

우선 placeholder라는 함수를 구현해줘야 한다.

Tensorflow에서 placeholder의 역할은, 

이를 테면 휴게소 매점에서 줄을 대신 서주는 사람과도 유사하다.

돈을 가진 사람은 언제 올 지 모르니까 일단 줄을 서있다가,

계산할 차례가 오면 돈가진 사람이 값을 치르게끔 하는 것이다.

이와 마찬가지로 placeholder를 통해 일단 c = a + b 처럼 형식을 만들어 놓고는

a = 1, b = 2 하는 식으로 값을 나중에 부여해줄 수 있다. 

 

다양한 데이터에 대해 같은 종류의 연산을 수행할 때는(딥러닝처럼)

이렇게 접근하는 것이 좀 더 간편할 수는 있을 것 같다.

 

두 번째로 Q value를 VFA로 구현하면 되는데

이 부분은  $Q = x(s) * w$ 와 동일하다. 

하지만 역시 tensorflow 문법을 알아야한다. 

문제는 tensorflow가 버전 업그레이드를 거치면서

문법이 상당히 달라졌다는 것이다.

일단은 호환이 되기 때문에, 어느 버전이든 상관없다만 

또 언제 "더 이상 지원하지 않습니다" 따위의 에러를 낼 지 모른다.

input_state = tf.layers.flatten(state = state, scope = scope)
out = tf.layers.dense(input_state, num_actions, scope = scope, reuse = reuse)

 

세 번째는 Target Update 과정이다.

강의에서는 몬테카를로를 이용할지, TD Learning을 이용할지 한참 설명했는데,

이 단계에서는 단순히 변수들을 묶어주기만 하면 되는 듯하다.

q_scope와 target_q_scope 를 입력받아서 해당하는 변수들을 

tensorflow의 get_collection 함수로 모아준 뒤에 

assign, group 함수로 묶어주는 듯하다.

q_scope_variable = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, q_scope)
target_q_scope_variable = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, target_q_scope)
assgin_op = [tf.assign(target_q_scope_variable[i], q_scope_variable[i]) for i in range(len(q_scope_variable))]
self.update_target_op = tf.group(*assgin_op)

 

네 번째 Loss 계산 함수가 되어서야 

세 번째에서 작성해준 변수들을 가지고 진정한 target update를 진행해준다.

여기서는 TD Learning을 사용해서 Q를 반영해준 것으로 보인다.

궁극적인 목표는 아래 알고리즘을 만족시켜주는 것이다.

                Q_samp(s) = r if done
                          = r + gamma * max_a' Q_target(s', a')
                loss = (Q_samp(s) - Q(s, a))^2 

이를 위해 Tensorflow의 where, one_hot 등등 함수를 사용하는데

도대체 무슨 용도인지 와닿질 않는다.

q_samp = tf.where(self.done_mask, self.r, self.r + self.config.gamma * tf.reduce_max(target_q, axis=1))
actions = tf.one_hot(self.a, num_actions)
q = tf.reduce_sum(tf.multiply(q, actions), axis=1)
self.loss = tf.reduce_mean(tf.squared_difference(q_samp, q))

다섯 번째, Q2의 마지막 함수인데

네 번째에서 구한 loss 를 활용해서 

Q를 나타내는 파라미터들을 업데이트 해주는 과정으로 보인다. 

optimizer = tf.train.AdamOptimizer()
var_in_scope = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope)
gradients, variables = zip(*optimizer.compute_gradients(self.loss, var_list=var_in_scope))
gradients = [tf.clip_by_norm(gradient, self.config.clip_val) for gradient in gradients]
self.train_op = optimizer.apply_gradients(zip(gradients, variables))
self.grad_norm = tf.global_norm(gradients)

Q3에서는 본격적으로 DQN 학습용 layer를 꾸려준다.

Q2가 linear VFA라면, Q3가 DNN을 활용해서 Q를 만들어주는 과정이다.

        with tf.variable_scope(scope, reuse=reuse) as _:
            conv1 = layers.conv2d(state, num_outputs=32, kernel_size=(8, 8), stride=4, activation_fn=tf.nn.relu)
            conv2 = layers.conv2d(conv1, num_outputs=64, kernel_size=(4, 4), stride=2, activation_fn=tf.nn.relu)
            conv3 = layers.conv2d(conv2, num_outputs=64, kernel_size=(3, 3), stride=1, activation_fn=tf.nn.relu)
            full_inputs = layers.flatten(conv3)
            full_layer = layers.fully_connected(full_inputs, num_outputs=512, activation_fn=tf.nn.relu)
            out = layers.fully_connected(full_layer, num_outputs=num_actions)

이후 Q4, Q5는 코드를 돌리기만 하면 되는 문제들이다.

사실 코드를 이해하려고 이렇게 글로 풀어봤지만

아무리 봐도 제대로 이해한 것 같지가 않아서 굉장히 초조하다.

도대체 어떻게 풀으라고 이런 과제를 내준 것인지,

스탠포드 수강생들은 다들 혼자서 풀고 있었는지 궁금하다.