14wk-1: 강화학습 (2) – 4x4 Grid World (AgentRandom)

Author

최규빈

Published

June 3, 2024

1. 강의영상

2. Imports

#!pip install gymnasium
#---#
import gymnasium as gym
#---#
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import IPython

3. 4x4 Grid World

- 문제설명: 4x4 그리드월드에서 상하좌우로 움직이는 에이전트가 목표점에 도달하도록 학습하는 방법

- GridWorld에서 사용되는 주요변수

  1. State: 각 격자 셀이 하나의 상태이며, 에이전트는 이러한 상태 중 하나에 있을 수 있음.
  2. Action: 에이전트는 현재상태에서 다음상태로 이동하기 위해 상,하,좌,우 중 하나의 행동을 취할 수 있음.
  3. Reward: 에이전트가 현재상태에서 특정 action을 하면 얻어지는 보상.
  4. Terminated: 하나의 에피소드가 종료되었음을 나타내는 상태.

4. 예비학습

A. gym.spaces

- 예시1

action_space = gym.spaces.Discrete(4) 
action_space 
Discrete(4)
[action_space.sample() for _ in range(5)]
[1, 1, 2, 1, 2]
0 in action_space
True
4 in action_space
False

- 예시2

state_space = gym.spaces.MultiDiscrete([4,4])
state_space
MultiDiscrete([4 4])
[state_space.sample() for _ in range(5)]
[array([3, 3]), array([3, 0]), array([3, 3]), array([3, 3]), array([2, 0])]
np.array([0,1]) in state_space
True
np.array([3,3]) in state_space
True
np.array([3,4]) in state_space
False

B. 시각화

def show(states):
    fig = plt.Figure()
    ax = fig.subplots()
    ax.matshow(np.zeros([4,4]), cmap='bwr',alpha=0.0)
    sc = ax.scatter(0, 0, color='red', s=500)  
    ax.text(0, 0, 'start', ha='center', va='center')
    ax.text(3, 3, 'end', ha='center', va='center')
    # Adding grid lines to the plot
    ax.set_xticks(np.arange(-.5, 4, 1), minor=True)
    ax.set_yticks(np.arange(-.5, 4, 1), minor=True)
    ax.grid(which='minor', color='black', linestyle='-', linewidth=2)
    state_space = gym.spaces.MultiDiscrete([4,4])
    def update(t):
        if states[t] in state_space:
            s1,s2 = states[t]
            states[t] = [s2,s1]
            sc.set_offsets(states[t])
        else:
            s1,s2 = states[t]
            s1 = s1 + 0.5 if s1 < 0 else (s1 - 0.5 if s1 > 3 else s1)
            s2 = s2 + 0.5 if s2 < 0 else (s2 - 0.5 if s2 > 3 else s2)
            states[t] = [s2,s1]       
            sc.set_offsets(states[t])
    ani = FuncAnimation(fig,update,frames=len(states))
    display(IPython.display.HTML(ani.to_jshtml()))
show([[0,0],[1,0],[2,0],[3,0],[4,0]])

5. Env 클래스 구현

action_to_direction = {
    0 : np.array([1, 0]), # row+, down
    1 : np.array([0, 1]), # col+, right
    2 : np.array([-1 ,0]), # row-, up
    3 : np.array([0, -1]) # col-, left
}
action_to_direction2 = {0: 'down', 1: 'right', 2: 'up', 3: 'left'} # 당장쓰진 않지만 하는김에 
action = action_space.sample()
direction = action_to_direction[action]
current_state = state_space.sample()
next_state = current_state + direction
current_state, direction, next_state
(array([3, 0]), array([-1,  0]), array([2, 0]))

- Class 구현: 아래와 같은 느낌의 클래스를 구현해보자.

class GridWorld:
    def __init__(self):
        self.state_space = gym.spaces.MultiDiscrete([4,4])
        self.action_space = gym.spaces.Discrete(4) 
        self._action_to_direction = {
            0 : np.array([1, 0]), # row+, down
            1 : np.array([0, 1]), # col+, right
            2 : np.array([-1 ,0]), # row-, up
            3 : np.array([0, -1]) # col-, left
        }
        self.reset()
        self.state = None 
        self.reward = None 
        self.termiated = None
    def step(self,action):
        direction = self._action_to_direction[action]
        self.state = self.state + direction
        if np.array_equal(self.state,np.array([3,3])): 
            self.reward = 100 
            self.terminated = True
        elif self.state not in self.state_space:
            self.reward = -10
            self.terminated = True
        else:
            self.reward = -1 
        return self.state, self.reward, self.terminated
    def reset(self):
        self.state = np.array([0,0])
        self.terminated = False   
        return self.state 
env = GridWorld()
state = env.reset()
states = [] 
states.append(state)
for t in range(50):
    action = env.action_space.sample() 
    state,reward,terminated = env.step(action)
    states.append(state)
    if terminated: break 
show(states)
  • 처음에 바로 죽는 경우가 많아 몇번 시도하고 위의 애니메이션을 얻음

6. AgentRandom

A. 에이전트 클래스 설계

- 우리가 구현하고 싶은 기능

  • .act(): 액션을 결정 –> 여기서는 그냥 랜덤액션
  • .save_experience(): 데이터를 저장 –> 여기에 일단 초점을 맞추자
  • .learn(): 데이터로에서 학습 –> 패스
class AgentRandom: 
    def __init__(self,env):
        #--# define spaces 
        self.action_space = env.action_space
        self.state_space = env.state_space
        #--# replay buffer 
        self.action = None 
        self.actions = [] 
        self.current_state =  None 
        self.current_states = [] 
        self.reward = None 
        self.rewards = [] 
        self.next_state =  None 
        self.next_states = [] 
        self.terminated = None 
        self.terminations = []
        #--# other information
        self.n_episodes = 0         
        self.n_experiences = 0
        self.score = 0        
        self.playtimes = [] 
        self.scores = []    
    def act(self):
        self.action = self.action_space.sample()
    def learn(self):
        pass 
    def save_experience(self):
        self.current_states.append(self.current_state)        
        self.actions.append(self.action)
        self.rewards.append(self.reward)  
        self.next_states.append(self.next_state)
        self.terminations.append(self.terminated)
        #--#
        self.n_experiences = self.n_experiences + 1 
        self.score = self.score + self.reward

B. 환경과 상호작용

env = GridWorld()
agent = AgentRandom(env)
#--#
for _ in range(50):
    agent.current_state = env.reset()
    agent.score = 0 
    for t in range(100):
        # step1: 행동
        agent.act()
        # step2: 보상
        agent.next_state, agent.reward, agent.terminated = env.step(agent.action)
        # step3: 저장 & 학습
        agent.save_experience()
        agent.learn()
        # step4: 
        agent.current_state = agent.next_state
        if agent.terminated: break
    agent.scores.append(agent.score) 
    agent.playtimes.append(t+1)
    agent.n_episodes = agent.n_episodes + 1 
    #---#
    print(
        f"에피소드: {agent.n_episodes} \t"
        f"점수(에피소드): {agent.scores[-1]} \t" 
        f"게임시간(에피소드): {agent.playtimes[-1]}\t"
        f"경험수: {agent.n_experiences}"
    )
에피소드: 1     점수(에피소드): -12   게임시간(에피소드): 3   경험수: 3
에피소드: 2     점수(에피소드): -15   게임시간(에피소드): 6   경험수: 9
에피소드: 3     점수(에피소드): -11   게임시간(에피소드): 2   경험수: 11
에피소드: 4     점수(에피소드): -10   게임시간(에피소드): 1   경험수: 12
에피소드: 5     점수(에피소드): -10   게임시간(에피소드): 1   경험수: 13
에피소드: 6     점수(에피소드): -12   게임시간(에피소드): 3   경험수: 16
에피소드: 7     점수(에피소드): -11   게임시간(에피소드): 2   경험수: 18
에피소드: 8     점수(에피소드): -18   게임시간(에피소드): 9   경험수: 27
에피소드: 9     점수(에피소드): -10   게임시간(에피소드): 1   경험수: 28
에피소드: 10    점수(에피소드): 91    게임시간(에피소드): 10  경험수: 38
에피소드: 11    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 39
에피소드: 12    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 40
에피소드: 13    점수(에피소드): -11   게임시간(에피소드): 2   경험수: 42
에피소드: 14    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 43
에피소드: 15    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 44
에피소드: 16    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 45
에피소드: 17    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 46
에피소드: 18    점수(에피소드): -15   게임시간(에피소드): 6   경험수: 52
에피소드: 19    점수(에피소드): -11   게임시간(에피소드): 2   경험수: 54
에피소드: 20    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 55
에피소드: 21    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 56
에피소드: 22    점수(에피소드): -12   게임시간(에피소드): 3   경험수: 59
에피소드: 23    점수(에피소드): -11   게임시간(에피소드): 2   경험수: 61
에피소드: 24    점수(에피소드): -11   게임시간(에피소드): 2   경험수: 63
에피소드: 25    점수(에피소드): -13   게임시간(에피소드): 4   경험수: 67
에피소드: 26    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 68
에피소드: 27    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 69
에피소드: 28    점수(에피소드): -11   게임시간(에피소드): 2   경험수: 71
에피소드: 29    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 72
에피소드: 30    점수(에피소드): -13   게임시간(에피소드): 4   경험수: 76
에피소드: 31    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 77
에피소드: 32    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 78
에피소드: 33    점수(에피소드): -18   게임시간(에피소드): 9   경험수: 87
에피소드: 34    점수(에피소드): -13   게임시간(에피소드): 4   경험수: 91
에피소드: 35    점수(에피소드): -18   게임시간(에피소드): 9   경험수: 100
에피소드: 36    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 101
에피소드: 37    점수(에피소드): -15   게임시간(에피소드): 6   경험수: 107
에피소드: 38    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 108
에피소드: 39    점수(에피소드): -13   게임시간(에피소드): 4   경험수: 112
에피소드: 40    점수(에피소드): -17   게임시간(에피소드): 8   경험수: 120
에피소드: 41    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 121
에피소드: 42    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 122
에피소드: 43    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 123
에피소드: 44    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 124
에피소드: 45    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 125
에피소드: 46    점수(에피소드): -17   게임시간(에피소드): 8   경험수: 133
에피소드: 47    점수(에피소드): -10   게임시간(에피소드): 1   경험수: 134
에피소드: 48    점수(에피소드): -13   게임시간(에피소드): 4   경험수: 138
에피소드: 49    점수(에피소드): -12   게임시간(에피소드): 3   경험수: 141
에피소드: 50    점수(에피소드): -11   게임시간(에피소드): 2   경험수: 143

C. 상호작용결과 시각화

[np.array([0,0])] + agent.next_states[28:38] # 에피소드10
[array([0, 0]),
 array([0, 1]),
 array([0, 2]),
 array([0, 3]),
 array([1, 3]),
 array([2, 3]),
 array([2, 2]),
 array([3, 2]),
 array([3, 1]),
 array([3, 2]),
 array([3, 3])]
show([np.array([0,0])] + agent.next_states[28:38]) # 에피소드5