강화 학습(Reinforcement Learning)

강화학습 Deterministic FrozenLake-v3 예제 게임규칙

coding art 2022. 1. 6. 16:22
728x90

헤더 영역에서 gym, numpy, matolotlib, random, register 를 불러 들인디.

register 라이브러리는 FrozenLake-v3에 특화하여 NxN  캔버스 구성 및 여러가지 파라메터 설정을 지원하며 이미 GYM 설치 시에 함께 설치된 듯하다.

random 은 단순히 최대값을 구하는  argmax 에 random 성을 부여할 수 있도록 별도 함수 rargmax()로 구성하는데 사용된다. 랜덤성이 없을 경우에는 deterministic하게 방향 설정하는대로 정확하게 움직이지만 랜덤성이 주어지면 어느 방향으로 움직일지 모르게 되는 것이다. 이와같이 움직임이 불확실해지는 경우 stochastic이라 한다. 만약 학습이 많이 진행되게 되면 랜덤성을 점차 줄이고 deterministic 한 특성이 커지도록 할 필요가 있을 것이다.

 

함수 rargmax 를 실행하면 indices 값을 반환하며 출력해 보면 이 값은 0,1=우측,2=아래쪽,3에 해당한다.

 

FrozenLake 버전을 설정하고 캔버스 사이즈를 4x4로, 'is_slipery'를 False 로 두면 deterministic 하게 실행된다. 반면에 'is_slipery'를 True 로 두면 stochastic 하게 실행된다.

FrozenLake-v3을 설정하고 각 상태 Q를 zeros 함수에 의해 (15, 4) shape 으로 내용 값은 0 으로 초기화하자.

Q 함수 또는 Q 테이블을 도입하면 매번 루프를 돌면서 업데이트 되는 Q 를 다시 action에 사용할 수 있게 된다.

Q 는16개의 state 와 4개의 action들 좌(0),우(1),상(3),하(2) ->Q(state, action) 으로 구성된다.

에피소드 값을 충분히 크게 2000으로 설정한다. 실제로 200 안밖에서 성공률이 높아지므로 작게 두고 변수 값들울 출력해 보자.

gamma는 향 후 Q 테이블 코드의 성공률을 높이기 위한 파라메터로서 1.0 이하로 설정하면 된다. 첫번째 goal에 도달하게되면 그 앞 셀에서 max Q값이 1.0 이 된다. 이 후에 인접 셀에 도착하여 max Q 값을 받아오게 되면 gamma 만큼 곱하게 되면 그 값이 1.0 보다 적어지게 된다. 즉 Frozen_Lake 를 탈출하게 되는 경로에 따라 Q 값의 분포들이 달라지게 된다.

done 이 불리언 값 False 로 선언되었으므로 아래의 while 제어문에서 env.step() 이 제공하는 done 의 값이 불리언 True가 될때까지 루푸를 돈다.  만약 done=True 로 선언되었다면 while not done: 루프가 돌지 않으므로 이 코드에서는 stochastic 처리를 할 수 없음에 유의하자. done=True 는 stochastic  한 경우이므로 계산이 더욱 어려우므로 다음 블로그에서 다루어보기로 한다.

한편 각 에피소드에 있어서 필요하다면 env.render()  명령을 실행하여 에이전트의 이동 상황을 모니터링해 보면 코드 이해에 도움이 된다.

에피소드 190 이면 goal 도착 확률이 높으므로 예를 들어서 출력 결과를 한 번 보도록 하자.

만약 gamma가 1.0 이라면 한번 성공한 경로만 계속 찾게 된다. 하지만 출발지에서 목적지까지의 경로가 하나 이상일 수가 있기 때문에 1.0 보다 작은 gamma 값을 사용하면 결국 goal에 인접한 cell 에서 maxQ가 최대값이 되는 경로를 압축하여 찾아갈 수 있게 된다. 이 gamma 를 한편 discount rate(금융에서 말하는 현재기준 할인율) 이라고도 한다. 아울러 에피소드 수가 점차 커지게 되면 결국 Q는 일정한 값들을 가지는 Q 테이블값에 수렴하게 될 것이다.

 

열전도 공학적으로 생각하자면 출발점에서 goal까지 열이 홀(Hole) 을 피해서 두 방향으로 흘러 갈 수 있으며 각 셀에서 온도 값 T 가 서로 다를 수 있다. 가장 단거리에 해당하는 열 전도 흐름에 대해서는 T 값들의 차이가 커질 것이며 먼 거리를 통한 열전도에서는 셀간의 T 값들 차이가 적어질 것이다. gamma 도 유사하게 생각할 수 있을 것이다.

 

에피소드가 2000 일때 성공률이 0.962 로 상당히 높은 편이다. 반면에 에피소드 200회 이전에는 실패 율이 상당히 높다. 따라서 에피소드 수를 200으로 설정하여 실행하면 좀더 세밀한 그래프가 얻어진다. 그래도 80회가 넘어가면 성공률이 높아지는 경향을 보여준다.

여기서 과연 무엇을 학습하는지 다시 주목해 보기로 한다.각 i 값에 해당하는 에피소드 별로 Q 테이블이 계산되어 업데이트 되면 그 다음 에피소드 계산에서도 업데이트 되어 가면서 사용됨을 알 수 있다. 즉 출발점부터 목적지에 이르기까지 어느정도 랜덤성이 포함된 경우에도 Q 테이블이 점차 학습되어 에피소드가 200회를 넘어가게 되면 Q 테이블이 거의 정답에 가까워졌으므로 거의 100% 성공적으로 경로를 찾아갈 수 있게 되는 것이다.

 

#Deterministic_Frozen_Lake.py

import gym
import numpy as np
import matplotlib.pyplot as plt
import random as pr
from gym.envs.registration import register

def rargmax(vector):
    m = np.amax(vector)
    indices = np.nonzero(vector == m)[0]
    return pr.choice(indices)  
      

register(
    id = 'FrozenLake-v3', 
    entry_point = 'gym.envs.toy_text:FrozenLakeEnv',
    kwargs = {'map_name': '4x4',
              'is_slippery':False})

env = gym.make('FrozenLake-v3')
Q = np.zeros([env.observation_space.n,env.action_space.n])

num_episodes = 200
gamma = 1.0

rList = []
for i in range(num_episodes):
    state = env.reset()
    rAll = 0
    done = False
    
    while not done:
        if i ==190: 
            env.render()
        action = rargmax(Q[state, :])
        if i == 190:
            print("action =: ", action)
        new_state, reward, done, _ = env.step(action)
        Q[state, action] = reward + gamma * np.max(Q[new_state, :])
        rAll += reward
        state = new_state
    if 190 < i < 251:
        print(i, ":  ", rAll)
    rList.append(rAll)

print("Sucess Rate: ", (sum(rList)/num_episodes))
print("Final Q-Table Values")
print("LEFT DOWN RIGHT UP")
print(Q)
plt.bar(range(len(rList)), rList, color="blue")
plt.show()