작은 세계를 직접 만들어 보는 이유
지금까지 우리는 강화학습을 여러 각도에서 바라보았습니다. 에이전트가 있고, 환경이 있고, 상태를 보고, 행동을 고르고, 보상을 받고, 다음 상태로 이동한다는 구조를 반복해서 다뤘습니다. 하지만 이 구조는 머릿속으로만 생각하면 조금 추상적입니다. '상태'가 정확히 무엇인지, '행동'이 환경에 어떻게 전달되는지, '보상'이 언제 계산되는지, '종료'는 누가 판단하는지 흐릿하게 느껴질 수 있습니다.
그래서 이제 아주 작은 세계를 직접 만들겠습니다. 이름은 Grid World입니다. 말 그대로 격자로 된 세계입니다. 바둑판처럼 나뉜 칸 위에 에이전트가 하나 있고, 에이전트는 위, 오른쪽, 아래, 왼쪽으로 움직입니다. 어떤 칸은 벽이라 지나갈 수 없고, 어떤 칸은 목표 지점이며, 어떤 칸은 빠지면 실패하는 위험 지점입니다.
이 작은 세계는 단순하지만 강화학습의 뼈대를 거의 모두 담고 있습니다. 상태는 에이전트의 좌표입니다. 행동은 상하좌우 이동입니다. 보상은 목표에 도착했는지, 실패 지점에 빠졌는지, 벽에 부딪혔는지에 따라 달라집니다. 종료 조건은 목표에 도착하거나 실패하거나 너무 오래 헤맬 때 발생합니다.
이 장에서 만드는 Grid World는 다음 장에서 Q-learning 에이전트를 학습시키는 무대가 됩니다. 아직은 똑똑한 에이전트를 만들지 않습니다. 먼저 에이전트가 뛰어놀 수 있는 작은 운동장을 만드는 것이 목표입니다.

격자 세계의 규칙 정하기
우리가 만들 세계는 문자로 표현할 수 있습니다. 예를 들어 다음과 같은 격자가 있다고 해 보겠습니다.
S . . . .
. # # . .
. . X # .
. # . . G
. . . . .여기서 각 문자는 세계의 규칙을 나타냅니다.
S는 출발점입니다.G는 목표 지점입니다.X는 실패 지점입니다.#는 벽입니다..은 자유롭게 이동할 수 있는 빈칸입니다.
좌표는 파이썬에서 다루기 쉽도록 (행, 열) 형태로 표현하겠습니다. 맨 왼쪽 위가 (0, 0)입니다. 아래로 내려가면 행 번호가 커지고, 오른쪽으로 가면 열 번호가 커집니다.
예를 들어 출발점 S가 맨 왼쪽 위에 있다면 상태는 (0, 0)입니다. 목표 지점 G가 네 번째 행, 다섯 번째 열에 있다면 파이썬 좌표로는 (3, 4)입니다. 사람은 보통 '위쪽, 아래쪽'으로 공간을 생각하지만, 코드에서는 행 번호가 아래로 갈수록 커진다는 점을 기억해야 합니다.
행동은 네 가지로 정의하겠습니다.
0: 위로 이동
1: 오른쪽으로 이동
2: 아래로 이동
3: 왼쪽으로 이동나중에 Q-learning을 구현할 때는 Q[state][action]처럼 상태와 행동의 조합마다 값을 저장해야 합니다. 그래서 행동을 문자열이 아니라 숫자로 표현해 두면 편합니다. 물론 사람이 읽을 때는 UP, RIGHT, DOWN, LEFT라는 이름을 붙여 주면 훨씬 보기 좋습니다.
환경은 무엇을 책임져야 할까
강화학습 코드에서 환경은 심판이자 세계 그 자체입니다. 에이전트는 행동을 선택할 뿐입니다. 그 행동이 가능한지, 어디로 이동하는지, 보상이 얼마인지, 게임이 끝났는지는 환경이 판단합니다.
이 구분이 중요합니다. 에이전트가 '오른쪽으로 가면 목표에 가까워질 것 같아'라고 생각할 수는 있습니다. 하지만 실제로 오른쪽 칸에 벽이 있는지, 목표가 있는지, 위험 지점이 있는지는 환경이 알려 줍니다. 에이전트는 환경의 내부 규칙을 직접 바꾸면 안 됩니다.
우리가 만들 환경은 크게 세 가지 기능을 가져야 합니다. 먼저 reset은 에피소드를 처음 상태로 되돌립니다. 다음으로 step은 에이전트의 행동을 받아 세계를 한 칸 진행시킵니다. 마지막으로 render는 현재 세계를 사람이 볼 수 있게 출력합니다.
강화학습에서 가장 중요한 함수는 step입니다. 이 함수는 행동 하나를 입력받아 보통 다음과 같은 정보를 반환합니다.
next_state, reward, done, info = env.step(action)next_state는 행동 후 에이전트가 도착한 상태입니다. reward는 방금 행동으로 받은 보상입니다. done은 에피소드가 끝났는지 나타내는 값입니다. info는 디버깅이나 로그 확인을 위한 추가 정보입니다. 예를 들어 벽에 부딪혔는지, 목표에 도착했는지 같은 사건을 넣을 수 있습니다.
이 구조가 강화학습의 심장입니다. 상태를 보고, 행동하고, 보상을 받고, 다음 상태로 이동합니다. 이 한 줄의 함수 호출 안에 우리가 앞에서 배운 거의 모든 개념이 압축되어 있습니다.
Grid World 클래스 만들기
이제 파이썬 코드로 환경을 만들어 보겠습니다. 외부 강화학습 라이브러리를 쓰지 않고, 순수 파이썬만 사용하겠습니다. 그래야 환경이 어떤 원리로 움직이는지 투명하게 볼 수 있습니다.
class GridWorld:
UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3
ACTIONS = {
UP: (-1, 0),
RIGHT: (0, 1),
DOWN: (1, 0),
LEFT: (0, -1),
}
ACTION_NAMES = {
UP: 'UP',
RIGHT: 'RIGHT',
DOWN: 'DOWN',
LEFT: 'LEFT',
}
def __init__(self, grid, max_steps=50):
self.grid = [list(row) for row in grid]
self.rows = len(self.grid)
self.cols = len(self.grid[0])
self.max_steps = max_steps
self.start = None
self.goal = None
self.walls = set()
self.failures = set()
for r in range(self.rows):
for c in range(self.cols):
cell = self.grid[r][c]
pos = (r, c)
if cell == 'S':
self.start = pos
elif cell == 'G':
self.goal = pos
elif cell == '#':
self.walls.add(pos)
elif cell == 'X':
self.failures.add(pos)
if self.start is None:
raise ValueError('출발점 S가 필요합니다.')
if self.goal is None:
raise ValueError('목표 지점 G가 필요합니다.')
self.reset()
def reset(self):
self.agent_pos = self.start
self.steps = 0
self.done = False
return self.agent_pos
def _inside(self, pos):
r, c = pos
return 0 <= r < self.rows and 0 <= c < self.cols
def _is_wall(self, pos):
return pos in self.walls
def step(self, action):
if self.done:
return self.agent_pos, 0.0, True, {'event': 'already_done'}
if action not in self.ACTIONS:
raise ValueError('알 수 없는 행동입니다.')
dr, dc = self.ACTIONS[action]
r, c = self.agent_pos
candidate = (r + dr, c + dc)
self.steps += 1
event = 'move'
if not self._inside(candidate) or self._is_wall(candidate):
next_pos = self.agent_pos
reward = -0.05
event = 'blocked'
else:
next_pos = candidate
self.agent_pos = next_pos
reward = -0.01
if self.agent_pos == self.goal:
reward = 1.0
self.done = True
event = 'goal'
elif self.agent_pos in self.failures:
reward = -1.0
self.done = True
event = 'failure'
elif self.steps >= self.max_steps:
self.done = True
event = 'timeout'
info = {
'event': event,
'steps': self.steps,
}
return self.agent_pos, reward, self.done, info
def render(self, path=None):
path = set(path or [])
lines = []
for r in range(self.rows):
chars = []
for c in range(self.cols):
pos = (r, c)
if pos == self.agent_pos:
chars.append('A')
elif pos == self.start:
chars.append('S')
elif pos == self.goal:
chars.append('G')
elif pos in self.failures:
chars.append('X')
elif pos in self.walls:
chars.append('#')
elif pos in path:
chars.append('*')
else:
chars.append('.')
lines.append(' '.join(chars))
return 'n'.join(lines)코드가 조금 길어 보이지만, 핵심은 단순합니다. 생성자 __init__에서는 격자를 읽으며 출발점, 목표 지점, 벽, 실패 지점을 찾아 둡니다. reset은 에이전트를 출발점으로 되돌립니다. _inside는 좌표가 격자 안에 있는지 확인하고, _is_wall은 해당 좌표가 벽인지 확인합니다.
가장 중요한 step은 행동을 받아 후보 위치를 계산합니다. 후보 위치가 격자 밖이거나 벽이면 에이전트는 움직이지 않습니다. 대신 작은 벌점 -0.05를 받습니다. 정상적으로 이동하면 기본 이동 비용 -0.01을 받습니다. 목표에 도착하면 +1.0, 실패 지점에 도착하면 -1.0을 받습니다.
왜 이동할 때마다 -0.01을 줄까요? 에이전트가 너무 오래 돌아다니지 않게 만들기 위해서입니다. 목표 보상만 있고 이동 비용이 없다면, 에이전트는 빨리 가든 늦게 가든 같은 보상을 받을 수 있습니다. 하지만 매번 작은 비용이 붙으면 짧은 경로가 더 좋은 경로가 됩니다.
첫 번째 환경 실행하기
이제 실제 격자를 하나 만들고 환경을 실행해 보겠습니다.
grid = [
'S....',
'.##..',
'..X#.',
'.#..G',
'.....',
]
env = GridWorld(grid, max_steps=30)
state = env.reset()
print('초기 상태:', state)
print(env.render())출력은 다음과 비슷합니다.
초기 상태: (0, 0)
A . . . .
. # # . .
. . X # .
. # . . G
. . . . .S가 있어야 할 자리에 A가 보입니다. A는 현재 에이전트의 위치입니다. 출발점과 에이전트가 같은 칸에 있으므로, 화면에는 에이전트를 우선 표시했습니다.
이제 에이전트를 오른쪽으로 한 칸 움직여 보겠습니다.
next_state, reward, done, info = env.step(GridWorld.RIGHT)
print('다음 상태:', next_state)
print('보상:', reward)
print('종료:', done)
print('추가 정보:', info)
print(env.render())예상 출력은 다음과 같습니다.
다음 상태: (0, 1)
보상: -0.01
종료: False
추가 정보: {'event': 'move', 'steps': 1}
S A . . .
. # # . .
. . X # .
. # . . G
. . . . .상태가 (0, 0)에서 (0, 1)로 바뀌었습니다. 목표에 도착하지 않았고 실패하지도 않았으므로 done은 False입니다. 한 번 이동했기 때문에 작은 이동 비용 -0.01을 받았습니다.
이 작은 결과를 가볍게 넘기면 안 됩니다. 이것이 강화학습 데이터의 가장 기본 단위입니다. 방금 우리는 다음 경험을 하나 얻었습니다.
상태 (0, 0)에서 RIGHT 행동을 했더니,
보상 -0.01을 받고,
다음 상태 (0, 1)로 이동했다.다음 장에서 Q-learning은 바로 이런 경험을 이용해 이 상태에서 이 행동은 얼마나 좋은가?를 조금씩 업데이트합니다.
벽에 부딪히면 어떻게 될까
환경에는 규칙이 있어야 합니다. 에이전트가 벽을 뚫고 지나갈 수 있다면, 우리가 만든 미로는 의미가 없어집니다. 이번에는 벽에 부딪히는 상황을 확인해 보겠습니다.
현재 에이전트가 (0, 1)에 있다고 합시다. 아래쪽 (1, 1)에는 벽 #이 있습니다. 이 상태에서 아래로 이동하면 어떻게 될까요?
next_state, reward, done, info = env.step(GridWorld.DOWN)
print('다음 상태:', next_state)
print('보상:', reward)
print('종료:', done)
print('추가 정보:', info)
print(env.render())출력은 다음과 비슷합니다.
다음 상태: (0, 1)
보상: -0.05
종료: False
추가 정보: {'event': 'blocked', 'steps': 2}
S A . . .
. # # . .
. . X # .
. # . . G
. . . . .행동은 했지만 위치는 그대로입니다. 벽에 막혔기 때문입니다. 보상은 일반 이동 비용보다 조금 더 나쁜 -0.05입니다. 이런 벌점이 있으면 에이전트는 나중에 벽에 부딪히는 행동을 덜 선호하게 됩니다.
여기서 중요한 점은 '움직이지 않았어도 행동은 행동'이라는 사실입니다. 강화학습에서는 실패한 시도도 학습 데이터입니다. 벽에 부딪힌 경험은 에이전트에게 이 상태에서 아래로 가는 행동은 별로 좋지 않다는 단서를 줍니다.
목표와 실패 지점 만들기
이번에는 목표까지 가는 정해진 행동 목록을 실행해 보겠습니다. 아직 학습은 하지 않습니다. 사람이 직접 행동을 정해 주고, 환경이 그 행동에 어떻게 반응하는지만 확인합니다.
env.reset()
actions = [
GridWorld.RIGHT,
GridWorld.RIGHT,
GridWorld.RIGHT,
GridWorld.RIGHT,
GridWorld.DOWN,
GridWorld.DOWN,
GridWorld.DOWN,
]
total_reward = 0.0
path = [env.agent_pos]
for action in actions:
next_state, reward, done, info = env.step(action)
total_reward += reward
path.append(next_state)
action_name = GridWorld.ACTION_NAMES[action]
event = info['event']
print(f'action={action_name:>5} state={next_state} reward={reward:>5.2f} done={done} event={event}')
if done:
break
print('총보상:', round(total_reward, 2))
print(env.render(path=path))예상 로그는 다음과 비슷합니다.
action=RIGHT state=(0, 1) reward=-0.01 done=False event=move
action=RIGHT state=(0, 2) reward=-0.01 done=False event=move
action=RIGHT state=(0, 3) reward=-0.01 done=False event=move
action=RIGHT state=(0, 4) reward=-0.01 done=False event=move
action= DOWN state=(1, 4) reward=-0.01 done=False event=move
action= DOWN state=(2, 4) reward=-0.01 done=False event=move
action= DOWN state=(3, 4) reward= 1.00 done=True event=goal
총보상: 0.94마지막 위치는 목표 지점 G입니다. 목표에 도착하는 순간 보상은 +1.0이 되고, done이 True가 됩니다. 에피소드가 끝난 것입니다.
이제 실패 지점도 확인해 보겠습니다. 실패 지점 X는 (2, 2)에 있습니다. 출발점에서 아래쪽과 오른쪽을 적절히 움직이면 그 칸에 도착할 수 있습니다.
env.reset()
actions = [
GridWorld.DOWN,
GridWorld.DOWN,
GridWorld.RIGHT,
GridWorld.RIGHT,
]
for action in actions:
next_state, reward, done, info = env.step(action)
action_name = GridWorld.ACTION_NAMES[action]
event = info['event']
print(f'action={action_name:>5} state={next_state} reward={reward:>5.2f} done={done} event={event}')
if done:
break예상 로그는 다음과 같습니다.
action= DOWN state=(1, 0) reward=-0.01 done=False event=move
action= DOWN state=(2, 0) reward=-0.01 done=False event=move
action=RIGHT state=(2, 1) reward=-0.01 done=False event=move
action=RIGHT state=(2, 2) reward=-1.00 done=True event=failure실패 지점에 도착하는 순간 큰 벌점 -1.0을 받고 에피소드가 끝납니다. 같은 종료라도 목표 도착과 실패는 완전히 다른 의미를 갖습니다. 목표 도착은 좋은 종료이고, 실패 지점 도착은 나쁜 종료입니다.
강화학습에서는 이런 차이가 매우 중요합니다. 에피소드가 끝났다는 사실만으로는 충분하지 않습니다. 어떻게 끝났는지가 중요합니다. 보상이 바로 그 차이를 숫자로 표현합니다.
너무 오래 헤매는 에이전트를 멈추기
목표에도 도착하지 않고 실패도 하지 않으면서 계속 돌아다니는 에이전트가 있을 수 있습니다. 예를 들어 오른쪽으로 갔다가 왼쪽으로 돌아오고, 다시 오른쪽으로 갔다가 왼쪽으로 돌아오는 행동을 반복할 수 있습니다.
그래서 환경에는 최대 스텝 수가 필요합니다. 우리가 만든 코드의 max_steps가 바로 그 역할을 합니다. max_steps=30이라면 한 에피소드에서 최대 30번까지만 행동할 수 있습니다. 그 안에 목표나 실패 지점에 도착하지 못하면 timeout으로 끝납니다.
이 종료 조건은 작은 실습에서도 중요하지만, 실제 강화학습에서는 더 중요합니다. 에피소드가 무한히 계속되면 학습 데이터를 정리하기 어렵고, 총보상을 계산하기도 애매해집니다. 물론 모든 문제가 명확한 에피소드 구조를 갖는 것은 아니지만, 처음 실습할 때는 시작과 끝이 분명한 환경이 훨씬 이해하기 쉽습니다.
env = GridWorld(grid, max_steps=3)
env.reset()
actions = [
GridWorld.RIGHT,
GridWorld.LEFT,
GridWorld.RIGHT,
GridWorld.LEFT,
]
for action in actions:
next_state, reward, done, info = env.step(action)
print(next_state, reward, done, info)
if done:
break예상 출력은 다음과 비슷합니다.
(0, 1) -0.01 False {'event': 'move', 'steps': 1}
(0, 0) -0.01 False {'event': 'move', 'steps': 2}
(0, 1) -0.01 True {'event': 'timeout', 'steps': 3}세 번째 행동 후 timeout으로 에피소드가 끝났습니다. 이처럼 종료 조건은 목표와 실패뿐 아니라 시간 제한으로도 정의할 수 있습니다.
경로를 눈으로 확인하기
로그는 정확하지만, 숫자만 보면 에이전트가 어디를 지나갔는지 한눈에 들어오지 않을 때가 있습니다. 그래서 render 함수에 path를 넘기면 지나간 칸을 *로 표시하도록 만들어 두었습니다.
env = GridWorld(grid, max_steps=30)
env.reset()
path = [env.agent_pos]
actions = [
GridWorld.RIGHT,
GridWorld.RIGHT,
GridWorld.RIGHT,
GridWorld.RIGHT,
GridWorld.DOWN,
GridWorld.DOWN,
GridWorld.DOWN,
]
for action in actions:
next_state, reward, done, info = env.step(action)
path.append(next_state)
if done:
break
print(env.render(path=path))출력은 다음과 비슷합니다.
S * * * *
. # # . *
. . X # *
. # . . A
. . . . .A는 현재 위치이고, *는 지나온 경로입니다. 목표 지점에 도착했기 때문에 원래 G가 있던 자리에 A가 표시됩니다. 이런 간단한 시각화만 있어도 디버깅이 훨씬 쉬워집니다.
강화학습 실습을 하다 보면 보상 합계만 보고는 문제가 잘 보이지 않을 때가 많습니다. 총보상이 낮은 이유가 벽에 자주 부딪혀서인지, 실패 지점에 빠져서인지, 목표 근처에서 빙빙 돌기 때문인지 로그와 시각화를 함께 봐야 알 수 있습니다.
무작위 에이전트로 환경 테스트하기
환경을 만들었으면, 학습 알고리즘을 넣기 전에 먼저 무작위 행동으로 테스트해 보는 것이 좋습니다. 무작위 에이전트는 아무 생각 없이 네 방향 중 하나를 고릅니다. 똑똑하지는 않지만 환경이 제대로 작동하는지 확인하기에는 충분합니다.
import random
def run_random_episode(env, seed=None):
if seed is not None:
random.seed(seed)
state = env.reset()
total_reward = 0.0
path = [state]
logs = []
while True:
action = random.choice(list(GridWorld.ACTIONS.keys()))
next_state, reward, done, info = env.step(action)
total_reward += reward
path.append(next_state)
logs.append({
'state': state,
'action': GridWorld.ACTION_NAMES[action],
'next_state': next_state,
'reward': reward,
'done': done,
'event': info['event'],
})
state = next_state
if done:
break
return total_reward, path, logs
env = GridWorld(grid, max_steps=30)
total_reward, path, logs = run_random_episode(env, seed=7)
print('총보상:', round(total_reward, 2))
print('스텝 수:', len(logs))
print('마지막 사건:', logs[-1]['event'])
print(env.render(path=path))무작위 에이전트는 대부분 비효율적으로 움직입니다. 벽에 부딪히기도 하고, 같은 칸을 여러 번 오가기도 합니다. 운이 좋으면 목표에 도착하지만, 운이 나쁘면 실패 지점에 빠지거나 시간 초과로 끝납니다.
이 모습은 답답해 보이지만 아주 중요합니다. 학습 전 에이전트는 원래 이런 상태입니다. 아직 어떤 행동이 좋은지 모르기 때문입니다. 다음 장에서 Q-learning을 적용하면 에이전트는 같은 환경을 여러 번 경험하면서 점점 더 좋은 행동을 선택하게 됩니다.
상태, 행동, 보상, 종료 조건 다시 보기
이제 우리가 만든 환경을 강화학습의 기본 구성 요소와 다시 연결해 보겠습니다.
상태는 에이전트의 현재 좌표입니다. 이 실습에서는 (행, 열) 튜플 하나만으로 상태를 표현했습니다. 실제 게임에서는 캐릭터 위치, 적 위치, 체력, 남은 시간 같은 정보가 상태에 포함될 수 있습니다. 로봇이라면 관절 각도, 속도, 센서 값이 상태가 될 수 있습니다.
행동은 상하좌우 네 방향 이동입니다. 지금은 이산 행동 공간입니다. 즉, 선택지가 몇 개로 딱 나뉘어 있습니다. 반대로 로봇 팔을 움직이는 문제에서는 각 관절에 어느 정도 힘을 줄지 정해야 하므로 연속 행동 공간이 될 수 있습니다.
보상은 행동의 결과를 숫자로 평가한 값입니다. 목표에 도착하면 +1.0, 실패하면 -1.0, 벽에 부딪히면 -0.05, 그냥 이동하면 -0.01입니다. 이 숫자들은 정답이 아니라 설계입니다. 보상 설계를 어떻게 하느냐에 따라 에이전트의 행동은 크게 달라질 수 있습니다.
종료 조건은 에피소드가 끝나는 규칙입니다. 목표 도착, 실패 지점 도착, 시간 초과가 종료 조건입니다. 종료 조건이 명확하면 한 번의 시도가 어디서 끝나는지 알 수 있고, 여러 에피소드의 성과를 비교하기 쉬워집니다.
이 네 가지가 모이면 강화학습 환경의 기본 틀이 완성됩니다. 복잡한 게임 AI나 로봇 제어 문제도 결국 이 구조를 확장한 것입니다. 화면이 이미지가 되고, 행동이 조이스틱 입력이 되고, 보상이 게임 점수가 되고, 종료 조건이 게임 오버가 될 뿐입니다.
좋은 환경은 학습을 쉽게 만든다
강화학습에서 환경은 단순한 배경이 아닙니다. 환경을 어떻게 설계하느냐가 학습의 난이도를 결정합니다. 같은 알고리즘을 쓰더라도 상태 표현이 너무 복잡하거나 보상이 너무 드물거나 종료 조건이 애매하면 학습이 잘 되지 않습니다.
이번 Grid World는 일부러 작고 투명하게 만들었습니다. 에이전트가 어디에 있는지 바로 보이고, 행동 결과가 한 칸 이동으로 나타나며, 보상이 왜 주어졌는지도 쉽게 확인할 수 있습니다. 이 투명함 덕분에 알고리즘이 잘못된 것인지, 환경 설계가 잘못된 것인지, 보상 값이 이상한 것인지 구분할 수 있습니다.
처음 강화학습을 실습할 때는 거대한 게임부터 시작하고 싶어질 수 있습니다. 하지만 처음부터 화면 이미지, 물리 엔진, 복잡한 행동 공간을 다루면 무엇이 문제인지 알기 어렵습니다. 작은 격자 세계에서 시작하면 강화학습의 기본 흐름을 손으로 만지듯 이해할 수 있습니다.
직접 바꿔 볼 실험
이 환경은 아주 작지만 여러 실험을 해 볼 수 있습니다.
벽의 위치를 바꾸어 더 어려운 미로를 만들어 봅니다.
실패 지점
X를 여러 개 추가해 위험한 경로를 만들어 봅니다.이동 비용을
-0.01에서-0.1로 키워 짧은 경로를 더 강하게 선호하게 만들어 봅니다.벽에 부딪혔을 때의 벌점을 없애거나 더 크게 만들어 행동 변화를 관찰합니다.
max_steps를 줄여 에이전트가 더 빨리 목표를 찾아야 하는 환경으로 바꿔 봅니다.
이 실험들은 단순한 장난처럼 보이지만, 실제 강화학습 연구와 개발에서 매일 마주치는 질문과 닮아 있습니다. 보상을 어떻게 줄 것인가? 실패를 얼마나 강하게 벌할 것인가? 에이전트에게 얼마나 많은 시간을 줄 것인가? 상태를 어떻게 표현할 것인가? 이런 질문에 대한 답이 달라지면 학습 결과도 달라집니다.
다음 장으로 가는 다리
우리는 이제 강화학습 에이전트가 활동할 작은 세계를 만들었습니다. 이 세계는 상태를 좌표로 제공하고, 행동을 받아 다음 상태로 이동시키며, 보상과 종료 여부를 반환합니다. 다시 말해 Q-learning을 적용할 준비가 끝났습니다.
다음 장에서는 이 Grid World 위에 Q-table을 올려 보겠습니다. 처음에는 모든 행동의 가치를 모르는 상태에서 출발합니다. 에이전트는 움직이고, 벽에 부딪히고, 실패 지점에 빠지고, 가끔 목표에 도착할 것입니다. 그리고 그 경험을 바탕으로 각 상태에서 어떤 행동이 더 좋은지 조금씩 배워 갈 것입니다.
강화학습의 재미는 바로 여기서 시작됩니다. 우리가 정답 경로를 직접 알려 주지 않았는데도, 에이전트는 보상만 보고 더 나은 길을 찾아갑니다. 작은 격자 위에서 벌어지는 이 시행착오가 게임 AI, 로봇, 에이전트, 그리고 언어 모델 정렬까지 이어지는 행동 학습의 출발점입니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.