강화학습/OpenAI gym

OpenAI gym을 이용해 Q-Learning 구현해보기

APinCan 2020. 3. 21. 23:59

큐러닝의 경우 살사와 너무 유사해서 이번에는 다른 예제를 가지고 큐러닝을 해보자. 이번의 예제는 OpenAI gym의 Taxi-v1.

일단 Taxi-v1의 깃허브 코드를 보며 state와 action 등에 대해서 파악.

 

Description

에피소드가 시작하면 택시는 랜덤한 위치에서 시작하고 승객은 랜덤한 location(위치, R,G,B,Y)에서 시작한다. 택시 드라이버는 승객의 위치로 가서 승객을 태우고 승객의 목적지로 간다. 여기서 목적지는 승객이 출발하는 위치를 제외한 나머지 위치 중 하나이다. 승객이 목적지에서 내리면 에피소드는 끝난다.

observations

500개의 이산적인 states들이 있는데 25개의 taxi 위치, 승객의 위치(택시 안에 있는것도 포함) 5개 그리고 4개의 목적지를 포함해 총 500개의 state들이 있다.

 

승객의 위치

0 : R

1 : G

2 : Y

3 : B

4 : 택시 안

 

목적지

0 : R

1 : G

2 : Y

3 : B

 

Actions

0 : 아래로

1 : 위로

2 : 오른쪽으로

3 : 왼쪽으로

4 : 승객 태우기

5 : 승객 내리기

 

Rewards

각 action마다 -1을 reward로 받는데 승객을 목적지에 내려주면 +20을 reward로 받는다. 단 승객을 잘못 태우거나 잘못 내리면 -10을 reward로 받는다.

 

그리고 state space는  (택시의 행, 택시의 열, 승객의 위치, 목적지)로 이루어져있다는데 이게 0~500의 스칼라값으로 변환된 것임.

 

일단 위와 같이 택시문제에 대해서 파악을 하고 이제 코드를 보자

 

 

import gym
import numpy as np
import random
import os
import time

ALPHA=0.4
GAMMA=0.9
EPSILON=0.1
TIMES = 10000000

선언부 부분.

타임스텝은 총 천만번을 실행했고 os와 time은 큐러닝의 결과를 보기 위해 임포트 시켰음.

 

env = gym.make("Taxi-v1")

위에서 언급한 택시 예제를 환경으로 설정.

 

taxi의 action개수
taxi의 state개수

이 state들을 그래픽하게 나타내면 테이블로 나타내기 좀 힘듬. 이걸 대충 그래도 표현해보자면 아래와 같은 state들의 집합을 육면체로 표시한게 될 것이다. 그리고 각 state마다 할 수 있는 action이 6개가 있는 것은 덤.

 

def action_select(state, q_function, mode="s"):
    actions = q_function[state]
    rand_prob = random.random()
    action = 0
    
    max_val = np.max(actions)
    # max인 인덱스 저장
    max_list = [idx for idx, i in enumerate(actions) if i==max_val]
    # greedy 또는 큐러닝의 경우 가장 큰 큐함수값의 idx
    if rand_prob >= EPSILON or mode=="q":
        action = random.choice(max_list)
    # non-greedy
    else:
        actions_idx= np.delete([0,1,2,3, 4, 5], max_list)
        if len(actions_idx)==0:
            action = 0
        else:
            action = random.choice(actions_idx)
        
    return action

\( \epsilon \)-greedy aciton 선택 부분을 구현한 코드.

앞의 살사와 다른 부분은 인자에 모드를 하나 추가 했다는 것. 기본값 S는 sarsa의 s이고 q(큐러닝)을 준다면 무조건 action들의 큐함수값 중 가장 높은 것을 선택함.

나머지 부분의 구현은 살사와 완전 동일.

 

q_function = [[0, 0, 0, 0, 0, 0] for i in range(env.observation_space.n)]
observation = env.reset()

for t in range(TIMES):
    action = action_select(observation, q_function)
    current_q = q_function[observation][action]

    next_observation, reward, done, info = env.step(action)
    
    next_action = action_select(next_observation, q_function, "q")   
    next_q = q_function[next_observation][next_action]
    q_function[observation][action] = current_q + ALPHA*(reward + GAMMA*next_q - current_q)
    
    if done:
        observation = env.reset()
        pass
        
    observation = next_observation

앞의 살사와 거의 완전 동일한 코드인데 action의 개수가 4개에서 6개로 증가했으므로 큐함수의 초기값을 담은 리스트도 6개가 됨. 현재 state에서의 action이 아닌 다음 state에서 action을 선택하는 부분도 인자로 q를 넣어줌. 이렇게 구현이 끝.

그 다음 결과를 보자.

 

각 state마다 학습한 큐함수를 출력. 이렇게만 봐서는 뭐가 뭔지 모름.

 

얻은 큐함수를 가지고 실제로 실행을 시켜보자. 그러기 위해서 코드를 하나 대충 더 짬.

observation = env.reset()
done = False

while not done:
    action = action_select(observation, q_function, "q")
    observation, _, done, _ = env.step(action)
    
    os.system('cls')
    env.render()
    time.sleep(0.7)

내가 얻은 큐함수를 가지고 action을 수행하면서 실제로 어떻게 이동하는지 보는 코드.

이 결과는 아래와 같음.

 

 

 

 

 

 

 

 

 

 

여기에서 G는 승객의 위치, B는 목적지, 노란색은 택시.

 

택시는 승객을 태우기 위해 밑에 보이는 action들을 수행하며 승객의 위치로 감. 이게 학습을 했으니까 택시가 승객의 위치로 바로 가는 것이지 학습 안하면 무진장 헤맴.

 

승객의 위치까지 가서 Pickup action을 통해 승객을 태우면 택시의 색깔은 초록색으로 바뀜. 이제 여기서부터 승객의 목적지인 B까지 가보자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

G에서 승객을 태운택시는 승객의 목적지까지 알아서 찾아감.

 

그리고 목적지에서 승객을 내려주면 에피소드 끝.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Taxi-v1 예제를 통해서 에이전트가 큐러닝을 통해 학습하고 문제를 해결하는 것을 보았음.