강화학습/RL 강의 정리

모두를 위한 RL강좌 정리하기(Lecture 6 ~ Lab 6)

APinCan 2020. 8. 28. 20:12

이전까지는 강화학습 에이전트를 학습시킬 때 큐테이블을 사용해서 학습시켰다. 이전의 Frozen Lake는 state는 16개, 각각의 action은 4개가 있는 테이블로 만들 수가 있었다. 근데 과연 큐테이블을 Frozen Lake뿐만 아니라 좀 더 어려운 예제에 적용시킬 수가 있을까

 

답은 힘들다.

이 큐테이블은 실생활의 더 어려운 예제에는 적용하기가 힘들다. 예를 들어 100x100의 미로는 100x100x4의 큐테이블로 표현할 수 있지만 만약 그림같은 게임이라면 이는 엄청나게 커지기 때문에 큐 테이블로는 표현하기가 힘들다. 그렇다면 큐테이블을 안쓰고 어떻게 큐함수들을 표현할 수 있을까?

 

그래서 신경망을 이용해 큐테이블을 근사한다. 이 신경망은 마치 큐테이블처럼 다음과 같이 어떤 state가 input으로 들어오면 output으로 각 action의 큐값이 나온다.

 

이렇게 신경망을 구축을 한 다음에는 마치 큐테이블에서 큐값을 업데이트했던 것처럼 신경망도 업데이트를 해야한다. 이때 이 신경망들의 weights를 W라고 하고 input state를 s라고 하면 이때 나오는 output은 Ws라고 표현할 수 있다. 이 값이 신경망이 예측한 큐값이 되고, 에이전트의 목표는 optimal한 Q값을 찾는 것이기 때문에 이 optimal Q를 label로 정한다. 그렇다면 이 신경망의 loss function은 위와 같이 쓸 수 있다. 그리고 이 loss function을 이제 우리가 아는 형태로 다시 써야한다.

 

label, 즉 y는 optimal Q라고 했다. 그리고 이 optimal Q는 위와같이 표현한다.

 

이제 이 Ws도 조금 다르게 표현할텐데 Ws는 큐에 대한 예측값이므로 \(\hat{Q} \)으로 표현한다. 조건부확률로 표현되어 있는데 이말은 어떤 weight(신경망)일때 state s, action a에서 큐값(예측값)이다. 그리고 이 예측값은 학습을 거쳐 optimal Q값으로 같아지게 하는 것이 이 신경망의 목적이다.
그래서 위와 같이 predict - label을 최소화하는 \(\theta \)를 찾는 식으로 표현할 수 있다.

 

다음은 전체적인 큐러닝 알고리즘이다.

이를 보면 에피소드를 M번 진행하는데 각 에피소드마다 처음 얻는 state에 전처리를 거친다. 그 다음 state에서 action을 선택해야 하는데  E-greedy 방법으로 action을 선택하거나, 현재 신경망, state에서 고를 수 있는 action 중 가장 높은 큐값인 action을 선택한다. 그러면 환경은 state와 action을 돌려주는데 그 것을 가지고 이제 신경망을 학습시킨다.

 

신경망을 학습시키는 법은 위에서 배웠고 label인 y는 조건에 따라 조금 다른 식을 쓴다. 만약 다음 state가 terminal state(목표지점)이라면 다음 큐값이 없기 때문에 그냥 거기서 얻은 reward를 y로 설정한다. 만약 terminal state가 아니라면 discount facotr를 적용한 큐값과 reward를 더해 y로 설정한다. 그리고 설정한 y값과 이번에 네트워크에서 얻은 큐값의 차이를 계속 좁히게 gradient descent로 학습시킨다.

 

여기서 non-terminal state일 때 y를 설정하는 것을 보면 이 식은 deterministic할 때의 식이였다. 하지만 왜 아래와 같은 stochastic한 식을 쓰지 않을까? 그건 신경망에서는 이걸 쓰나 저걸 쓰나 별로 상관하지 않아도 되서 더 간단한 식을 쓴다고 한다.

 

이런 네트워크에서도 동일하게 Q값은 수렴할까? 신경망에서는 테이블과 다르게 수렴하지 않는다. 이유는 각 샘플들간의 상관관계와 target이 유동적이라서인데 이에 대한 상세한 이유는 나중에 DQN 강의에서 다룰 것 같다.

 

이제 Frozen Lake 문제를 신경망을 이용해 코드로 구현해본다. 강의와는 조금 다르게 tensorflow 2.0으로 업데이트 됐으므로 keras를 적극적으로 이용한다.

import gym
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

env = gym.make('FrozenLake-v0')

늘 그렇듯 똑같은 환경 생성

 

learning_rate = 0.1
input_size = env.observation_space.n
output_size = env.action_space.n
dis=.99
num_episodes=2000

이것저것 파라미터 설정

 

model = tf.keras.Sequential([
    tf.keras.layers.Dense(output_size, input_shape=[input_size],
                         kernel_initializer=tf.random_uniform_initializer(minval=0, maxval=0.01))
])

model.compile(optimizer=tf.train.GradientDescentOptimizer(learning_rate=learning_rate), loss='mse')

model.summary()

코드는 상당히 다르지만 신경망은 차이가 없다. 신경망의 input과 output을 설정해주고 초기값은 강의에서 나온 것처럼 random_uniform을 사용한다. 신경망의 optimizer는 강의에서 나온것처럼 GradientDescent를 사용하고 loss function은 mse를 사용했는데 강의에 나온 loss 값이랑 별 차이가 없다고 생각해 mse를 사용했다.

 

with tf.device('/GPU:0'):
    rList=[]
    for i in range(num_episodes):
        s = env.reset()
        e = 1.0 / ((i/50)+10)
        rAll = 0
        done = False
        local_loss = []

        while not done:
            Qs = model.predict(one_hot(s))
            if np.random.rand(1) < e:
                a = env.action_space.sample()
            else:
                a = np.argmax(Qs)

            s1, reward, done, _ = env.step(a)
            if done:
                Qs[0, a] = reward
            else:
                Qs1 = model.predict(one_hot(s1))
                Qs[0, a] = reward + dis*np.max(Qs1)

            model.fit(x=one_hot(s), y=Qs)

            rAll += reward
            s= s1
        rList.append(rAll)

강의에서 나온 코드와 조금 다르지만 하는 일은 똑같다. 만약 gpu가 아닌 cpu로 학습시킨다면 tf.device부분만 바꿔주면 된다.

 

print("Percent of successful episode: "+str(sum(rList)/num_episodes)+"%")
plt.bar(range(len(rList)), rList, color='blue')
plt.show()

학습이 어떻게 됐나 보니 그렇게 썩 만족스럽지는 않다. 목표지점을 제대로 찾아간 경우는 54%정도뿐이다

 

이제 다음으로 CartPole 예제에 큐네트워크를 적용시켜본다.

카트폴 예제는 그림과 같이 카트와 폴대가 있는데 카트를 좌우로 움직여 최대한 폴대를 쓰러트리지 않게 학습시키는 것이 목적이다. 근데 이 카트폴 문제도 앞의 Frozen Lake와 구현은 큰 차이가 없다.

 

import gym
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

라이브러리 임포트는 위와 별 차이가 없다

 

 

env = gym.make('CartPole-v0')

learning_rate = 1e-1
input_size = env.observation_space.shape[0]
output_size = env.action_space.n

num_episodes = 2000
dis = 0.9

여기서는 CartPole 환경을 생성한다. 

 

model = tf.keras.Sequential([
    tf.keras.layers.Dense(output_size, input_shape=[input_size],
                         kernel_initializer=tf.keras.initializers.glorot_uniform())
])
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate), loss='mse')

model.summary()

역시 위의 Frozen Lake에서 신경망과 별 차이는 없다. 다만 강의에서 xavier_initializer사용했기 때문에 여기서도 xavier initializer를 사용했다. glorot은 xavier와 동일하다. 이름이 바뀐 듯 하다.

 

with tf.device('/GPU:0'):
    rList=[]
    for i in range(num_episodes):
        e = 1. / ((i/10)+1)
        rAll=0
        step_count = 0
        s = env.reset()
        done = False

        while not done:
            step_count+=1
            x = np.reshape(s, [1, input_size])
            Qs = model.predict(x)
            if np.random.rand(1) < e:
                a = env.action_space.sample()
            else:
                a = np.argmax(Qs)

            s1, reward, done, _ = env.step(a)
            if done:
                Qs[0, a] = -100
            else:
                x1 = np.reshape(s1, [1, input_size])
                Qs1 = model.predict(x)
                Qs[0, a] = reward + dis*np.max(Qs1)

            model.fit(x=x, y=Qs)
            s = s1

            print(step_count)

        rList.append(step_count)
        print("Episode: {} step: {}".format(i, step_count))
        if len(rList) > 10 and np.mean(rList[-10:]) > 500:
            break

Frozen Lake 예제의 코드와 별로 차이는 없다. 여기서 done에서 -100을 해주는 이유는 폴대를 넘어지게 해서는 안되는데 done이라는 것은 넘어진 것이기 때문에 -100을 해주는 것이다. 그리고 각 에피소드마다 몇번의 스텝을 진행했는지를 출력한다.

에피소드를 2000번 반복하는데 조기 종료되려면 최근 스텝들의 평균이 500을 넘으면 된다. 즉 좌 또는 우로 500번 이상 왔다갔다했는데도 넘어지지 않았다는 것이다.

 

강의에 있는 학습결과를 그대로 가져왔다. 내가 학습시킨 것도 저것과 별 차이가 없어서 그냥 가져왔다. 이렇게 학습이 되지 않은 이유는 앞에서 봤듯 각 샘플들 간의 상관관계와 target이 유동적이라는 것이다. 다음 강의에서는 이를 해결하는 법을 배워본다.

 

Reference:

[1]: hunkim.github.io/ml/

[2]: youtu.be/w9GwqPx7LW8

[3]: youtu.be/Fcmgl8ow2Uc

[4]: youtu.be/MF_Wllw9VKk