Skip to main content
Summary

길을 외우는 것이 아니라 가치를 배운다

앞 장에서 우리는 작은 격자 세계를 만들었습니다. 에이전트는 출발점 S에서 시작하고, 상하좌우로 움직이며, 목표 지점 G에 도착하면 보상을 받습니다. 벽 #에 부딪히면 조금 손해를 보고, 실패 지점 X에 빠지면 큰 벌점을 받습니다.

이제 이 세계에 학습하는 에이전트를 넣어 보겠습니다. 이번 장의 주인공은 Q-learning입니다. Q-learning 에이전트는 사람이 알려 준 정답 경로를 외우지 않습니다. 대신 직접 움직여 보고, 보상을 받고, 그 경험을 바탕으로 '이 상태에서 이 행동은 좋은가?'를 조금씩 기록합니다.

처음에는 아무것도 모릅니다. 출발점에서 오른쪽으로 가는 것이 좋은지, 아래로 가는 것이 좋은지, 벽에 부딪히면 얼마나 나쁜지 알지 못합니다. 하지만 같은 세계를 수백 번, 수천 번 반복해서 경험하면 상황이 달라집니다. 에이전트의 머릿속에는 점점 이런 생각이 생깁니다.

'아, 이 칸에서는 오른쪽으로 가는 편이 좋구나.'

'여기서 아래로 가면 위험하구나.'

'목표 근처에서는 이 방향을 선택해야 하는구나.'

이 생각을 숫자로 저장한 표가 바로 Q-table입니다.

Q-learning으로 길 찾기

Q-table은 에이전트의 작은 지도다

Q-learning에서 Q는 상태와 행동의 조합이 얼마나 좋은지를 나타내는 값입니다. 상태가 (0, 0)이고 행동이 RIGHT라면, Q[(0, 0)][RIGHT]는 '출발점에서 오른쪽으로 가는 행동의 장기적인 가치'를 뜻합니다.

여기서 중요한 말은 '장기적인'입니다. 어떤 행동은 당장 보상이 작아 보여도 나중에 목표로 이어질 수 있습니다. 반대로 어떤 행동은 지금은 별문제가 없어 보여도 결국 실패 지점으로 이어질 수 있습니다. Q값은 단순히 이번 한 번의 보상만 보는 것이 아니라, 그 행동 이후에 이어질 미래까지 함께 고려하려고 합니다.

처음 Q-table은 아무것도 모르는 상태에서 시작합니다. 그래서 모든 값을 0.0으로 둡니다.

Q[(0, 0)] = [0.00, 0.00, 0.00, 0.00]
              UP   RIGHT DOWN LEFT

이 표만 보면 네 행동이 모두 똑같아 보입니다. 당연합니다. 아직 경험이 없기 때문입니다. Q-learning은 이 빈 표를 시행착오로 채워 나갑니다.

Q-table 초기화하기

이번 장에서는 앞 장에서 만든 GridWorld 클래스를 그대로 사용하겠습니다. 먼저 같은 격자 세계를 준비하고, 벽이 아닌 모든 칸에 대해 Q값을 저장할 공간을 만들겠습니다.

import random
from collections import deque

grid = [
    'S....',
    '.##..',
    '..X#.',
    '.#..G',
    '.....',
]

env = GridWorld(grid, max_steps=30)

ACTIONS = list(GridWorld.ACTIONS.keys())


def all_states(env):
    states = []

    for r in range(env.rows):
        for c in range(env.cols):
            pos = (r, c)
            if pos not in env.walls:
                states.append(pos)

    return states


def make_q_table(env):
    q_table = {}

    for state in all_states(env):
        q_table[state] = [0.0 for _ in ACTIONS]

    return q_table


q_table = make_q_table(env)

print(q_table[(0, 0)])

출력은 다음과 같습니다.

[0.0, 0.0, 0.0, 0.0]

리스트의 네 값은 각각 UP, RIGHT, DOWN, LEFT 행동의 Q값입니다. 앞 장에서 행동을 숫자로 정의했기 때문에 UP=0, RIGHT=1, DOWN=2, LEFT=3처럼 리스트 인덱스로 바로 접근할 수 있습니다.

state = (0, 0)
action = GridWorld.RIGHT

print(q_table[state][action])

아직은 0.0입니다. 하지만 학습이 진행되면 출발점에서 오른쪽으로 가는 값은 점점 커지고, 벽이나 위험한 방향으로 가는 값은 상대적으로 낮아질 것입니다.

한 번의 경험이 Q값을 바꾸는 방식

Q-learning의 핵심 업데이트는 아주 짧은 문장으로 설명할 수 있습니다.

현재 알고 있는 값과 새로 경험한 값을 비교해서, Q값을 조금 고친다.

조금 더 구체적으로 쓰면 다음과 같습니다.

새 Q값 = 기존 Q값 + 학습률 × (목표값 - 기존 Q값)

여기서 목표값은 '이번 행동으로 받은 보상'과 '다음 상태에서 기대할 수 있는 가장 좋은 미래 가치'를 합친 값입니다.

목표값 = 방금 받은 보상 + 할인율 × 다음 상태의 최대 Q값

단, 목표 지점이나 실패 지점에 도착해서 에피소드가 끝났다면 미래는 더 이상 없습니다. 이때 목표값은 그냥 방금 받은 보상입니다.

이제 이 아이디어를 코드로 옮겨 보겠습니다.

def update_q_value(q_table, state, action, reward, next_state, done, alpha, gamma):
    current = q_table[state][action]

    if done:
        target = reward
    else:
        target = reward + gamma * max(q_table[next_state])

    q_table[state][action] = current + alpha * (target - current)

alpha는 학습률입니다. 새 정보를 얼마나 빠르게 반영할지 정합니다. alpha가 크면 Q값이 빠르게 바뀌지만 흔들릴 수 있고, 작으면 안정적이지만 오래 걸립니다.

gamma는 할인율입니다. 미래 보상을 얼마나 중요하게 볼지 정합니다. gamma가 0에 가까우면 에이전트는 눈앞의 보상만 봅니다. gamma가 1에 가까우면 먼 미래의 보상도 중요하게 생각합니다. Grid World처럼 목표까지 여러 칸을 이동해야 하는 문제에서는 보통 미래 보상을 어느 정도 크게 봐야 합니다.

이 업데이트가 Q-learning의 심장입니다. 에이전트는 한 칸 움직일 때마다 이 함수를 호출하며 자신의 작은 지도를 고쳐 나갑니다.

ε-greedy로 탐험과 활용을 섞기

Q-table을 업데이트하려면 먼저 행동을 골라야 합니다. 그런데 여기서 문제가 생깁니다. 처음에는 모든 Q값이 0.0이므로 어떤 행동이 좋은지 모릅니다. 이때 무작정 현재 Q값이 가장 큰 행동만 고르면, 우연히 선택된 방향만 계속 반복할 수 있습니다.

그래서 필요한 것이 ε-greedy입니다. 읽을 때는 '입실론 그리디'라고 부릅니다. 규칙은 단순합니다. 일정 확률 epsilon으로는 무작위 행동을 하고, 나머지 확률로는 현재 Q-table에서 가장 좋아 보이는 행동을 합니다.

epsilon이 크다  → 탐험을 많이 한다
epsilon이 작다  → 배운 것을 더 많이 활용한다

학습 초반에는 많이 돌아다녀야 합니다. 벽도 부딪혀 보고, 실패 지점에도 빠져 보고, 우연히 목표에도 도착해 봐야 합니다. 학습 후반에는 이미 배운 좋은 길을 더 자주 따라가면 됩니다.

def best_action(q_table, state):
    values = q_table[state]
    best_value = max(values)
    candidates = [action for action in ACTIONS if values[action] == best_value]
    return random.choice(candidates)


def select_action(q_table, state, epsilon):
    if random.random() < epsilon:
        return random.choice(ACTIONS)

    return best_action(q_table, state)

여기서 best_action은 Q값이 가장 큰 행동을 고릅니다. 만약 같은 값이 여러 개라면 그중 하나를 무작위로 고릅니다. 이 부분이 의외로 중요합니다. 학습 초반에는 모든 값이 같으므로, 항상 첫 번째 행동인 UP만 고르면 에이전트가 이상하게 굳어질 수 있습니다. 같은 점수라면 공평하게 뽑아 주는 편이 좋습니다.

한 에피소드 실행하기

이제 환경에서 한 에피소드를 실행하는 함수를 만들겠습니다. 한 에피소드는 출발점에서 시작해 목표에 도착하거나, 실패 지점에 빠지거나, 최대 스텝 수에 도달할 때까지 이어지는 한 번의 시도입니다.

에피소드가 진행되는 동안 에이전트는 다음 과정을 반복합니다.

  • 현재 상태를 본다.

  • ε-greedy로 행동을 고른다.

  • 환경에 행동을 전달하고 보상을 받는다.

  • Q-table을 업데이트한다.

  • 다음 상태로 이동한다.

코드로 쓰면 다음과 같습니다.

def run_episode(env, q_table, epsilon, alpha=0.2, gamma=0.95, training=True):
    state = env.reset()
    total_reward = 0.0
    path = [state]
    final_event = None

    while True:
        action = select_action(q_table, state, epsilon)
        next_state, reward, done, info = env.step(action)

        if training:
            update_q_value(
                q_table=q_table,
                state=state,
                action=action,
                reward=reward,
                next_state=next_state,
                done=done,
                alpha=alpha,
                gamma=gamma,
            )

        total_reward += reward
        path.append(next_state)
        state = next_state
        final_event = info['event']

        if done:
            break

    return {
        'total_reward': total_reward,
        'steps': env.steps,
        'path': path,
        'event': final_event,
    }

training=True일 때만 Q-table을 업데이트합니다. 나중에 학습이 끝난 에이전트를 평가할 때는 training=False로 두고, Q-table을 더 이상 바꾸지 않은 채 행동만 관찰하면 됩니다.

여러 번 반복해서 학습시키기

한 번 움직여서는 아무것도 배울 수 없습니다. 강화학습은 반복의 기술입니다. 같은 세계를 여러 번 경험하면서 조금씩 더 나은 행동을 찾아갑니다.

이번에는 1000번의 에피소드를 실행해 보겠습니다. 처음에는 epsilon=1.0으로 시작합니다. 즉, 거의 완전히 무작위로 움직입니다. 이후 에피소드가 지날수록 epsilon을 조금씩 줄입니다. 마지막에는 0.05 아래로 내려가지 않게 하겠습니다. 끝까지 아주 작은 탐험은 남겨 두는 셈입니다.

random.seed(42)

q_table = make_q_table(env)

num_episodes = 1000
alpha = 0.2
gamma = 0.95

epsilon = 1.0
epsilon_min = 0.05
epsilon_decay = 0.995

history = []

for episode in range(1, num_episodes + 1):
    result = run_episode(
        env=env,
        q_table=q_table,
        epsilon=epsilon,
        alpha=alpha,
        gamma=gamma,
        training=True,
    )

    history.append(result)
    epsilon = max(epsilon_min, epsilon * epsilon_decay)

    if episode % 100 == 0:
        recent = history[-100:]
        avg_reward = sum(item['total_reward'] for item in recent) / len(recent)
        avg_steps = sum(item['steps'] for item in recent) / len(recent)
        goal_rate = sum(1 for item in recent if item['event'] == 'goal') / len(recent)

        print(
            f'episode={episode:4d} '
            f'epsilon={epsilon:.3f} '
            f'avg_reward={avg_reward:6.2f} '
            f'avg_steps={avg_steps:5.1f} '
            f'goal_rate={goal_rate:5.1%}'
        )

실행 결과는 무작위성 때문에 매번 조금씩 달라질 수 있습니다. 하지만 대체로 다음과 비슷한 흐름을 기대할 수 있습니다.

episode= 100 epsilon=0.606 avg_reward= -0.38 avg_steps= 25.4 goal_rate=23.0%
episode= 200 epsilon=0.367 avg_reward=  0.23 avg_steps= 18.7 goal_rate=58.0%
episode= 300 epsilon=0.222 avg_reward=  0.62 avg_steps= 12.1 goal_rate=82.0%
episode= 400 epsilon=0.135 avg_reward=  0.80 avg_steps=  9.4 goal_rate=94.0%
episode= 500 epsilon=0.082 avg_reward=  0.88 avg_steps=  8.1 goal_rate=98.0%
episode= 600 epsilon=0.050 avg_reward=  0.92 avg_steps=  7.4 goal_rate=100.0%
episode= 700 epsilon=0.050 avg_reward=  0.93 avg_steps=  7.2 goal_rate=100.0%
episode= 800 epsilon=0.050 avg_reward=  0.93 avg_steps=  7.2 goal_rate=100.0%
episode= 900 epsilon=0.050 avg_reward=  0.93 avg_steps=  7.1 goal_rate=100.0%
episode=1000 epsilon=0.050 avg_reward=  0.93 avg_steps=  7.1 goal_rate=100.0%

여기서 세 가지 숫자를 눈여겨보면 됩니다.

avg_reward는 최근 100개 에피소드의 평균 총보상입니다. 처음에는 실패 지점에 빠지거나 오래 헤매기 때문에 낮습니다. 학습이 진행되면 목표에 더 자주 도착하고, 이동 횟수도 줄어들면서 평균 보상이 올라갑니다.

avg_steps는 목표나 종료까지 걸린 평균 이동 횟수입니다. 좋은 길을 찾을수록 이 값은 줄어듭니다. Grid World에서는 이동 비용이 있기 때문에, 짧은 길로 목표에 도착할수록 총보상이 좋아집니다.

goal_rate는 최근 100번 중 목표에 도착한 비율입니다. 초반에는 낮지만, 후반에는 거의 100%에 가까워지는 것이 좋습니다.

학습 초반과 후반의 행동은 다르다

학습 초반의 에이전트는 호기심 많은 초보자와 같습니다. 오른쪽으로 가다가 왼쪽으로 돌아오고, 벽에 부딪히고, 목표와 상관없는 곳을 빙빙 돕니다. 겉보기에는 답답하지만 이 시간이 필요합니다. 실패한 경험도 Q-table을 채우는 재료가 되기 때문입니다.

학습 후반의 에이전트는 달라집니다. epsilon이 작아지면서 무작위 행동보다 Q-table에 기반한 행동을 더 많이 선택합니다. 이제 에이전트는 '예전에 해 보니 이쪽이 좋았다'는 기억을 이용합니다.

학습이 끝난 뒤에는 탐험을 끄고, 현재 Q-table이 말하는 최선의 행동만 따라가 보겠습니다.

greedy_result = run_episode(
    env=env,
    q_table=q_table,
    epsilon=0.0,
    training=False,
)

print('총보상:', round(greedy_result['total_reward'], 2))
print('이동 횟수:', greedy_result['steps'])
print('마지막 사건:', greedy_result['event'])
print(env.render(path=greedy_result['path']))

학습이 잘 되었다면 대략 이런 결과를 볼 수 있습니다.

총보상: 0.94
이동 횟수: 7
마지막 사건: goal
S * * * *
. # # . *
. . X # *
. # . . A
. . . . .

에이전트가 위쪽 길을 따라 오른쪽으로 이동한 뒤, 아래로 내려가 목표에 도착했습니다. 사람이 최단 경로를 직접 알려 주지 않았는데도, 보상만으로 이 길을 찾아낸 것입니다.

이 장면이 Q-learning의 매력입니다. 정답 행동을 하나하나 라벨링하지 않았습니다. 우리는 그저 세계의 규칙과 보상만 정했습니다. 나머지는 에이전트가 직접 부딪히며 배웠습니다.

Q-table에서 최종 정책 추출하기

학습된 Q-table은 숫자의 표입니다. 하지만 숫자만 보면 에이전트가 실제로 어떤 방향을 선호하는지 한눈에 들어오지 않습니다. 그래서 각 상태에서 Q값이 가장 큰 행동을 화살표로 바꿔 보겠습니다. 이것이 최종 정책입니다.

ACTION_SYMBOLS = {
    GridWorld.UP: '↑',
    GridWorld.RIGHT: '→',
    GridWorld.DOWN: '↓',
    GridWorld.LEFT: '←',
}


def print_policy(env, q_table):
    for r in range(env.rows):
        row = []

        for c in range(env.cols):
            pos = (r, c)

            if pos in env.walls:
                cell = '#'
            elif pos == env.goal:
                cell = 'G'
            elif pos in env.failures:
                cell = 'X'
            else:
                action = best_action(q_table, pos)
                cell = ACTION_SYMBOLS[action]

            row.append(cell)

        print(' '.join(row))


print_policy(env, q_table)

예를 들어 다음과 비슷한 정책이 나올 수 있습니다.

→ → → → ↓
↑ # # → ↓
↑ ← X # ↓
↑ # → → G
↑ → → → ↑

이 화살표들은 각 칸에서 에이전트가 가장 좋다고 생각하는 행동을 뜻합니다. 출발점 근처의 화살표가 목표를 향해 이어지고 있다면 학습이 잘 된 것입니다.

다만 모든 칸의 정책이 완벽하게 예쁘게 나오지 않아도 너무 걱정하지 않아도 됩니다. 에이전트가 자주 방문하지 않은 칸은 Q값이 충분히 업데이트되지 않았을 수 있습니다. 강화학습에서 중요한 것은 전체 표가 미술 작품처럼 깔끔한가가 아니라, 실제로 시작 상태에서 목표까지 안정적으로 가는가입니다.

최단 경로와 비교하기

이번 Grid World는 작기 때문에 사람이 보기에도 최단 경로를 짐작할 수 있습니다. 하지만 코드를 통해 최단 경로를 계산해 보면 학습된 경로가 얼마나 좋은지 더 분명하게 확인할 수 있습니다.

여기서는 너비 우선 탐색, 즉 BFS를 사용하겠습니다. BFS는 출발점에서 가까운 칸부터 차례대로 확인하므로, 모든 이동 비용이 같을 때 최단 경로를 찾는 데 적합합니다.

def shortest_path(env, avoid_failures=True):
    start = env.start
    goal = env.goal

    queue = deque([(start, [start])])
    visited = {start}

    while queue:
        pos, path = queue.popleft()

        if pos == goal:
            return path

        for action, (dr, dc) in GridWorld.ACTIONS.items():
            nr = pos[0] + dr
            nc = pos[1] + dc
            next_pos = (nr, nc)

            if not env._inside(next_pos):
                continue
            if next_pos in env.walls:
                continue
            if avoid_failures and next_pos in env.failures:
                continue
            if next_pos in visited:
                continue

            visited.add(next_pos)
            queue.append((next_pos, path + [next_pos]))

    return None


learned_path = greedy_result['path']
optimal_path = shortest_path(env)

print('학습 경로 길이:', len(learned_path) - 1)
print('최단 경로 길이:', len(optimal_path) - 1)
print('학습 경로:', learned_path)
print('최단 경로:', optimal_path)

학습이 잘 되었다면 다음처럼 두 경로 길이가 같게 나올 수 있습니다.

학습 경로 길이: 7
최단 경로 길이: 7
학습 경로: [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 4), (2, 4), (3, 4)]
최단 경로: [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 4), (2, 4), (3, 4)]

이 결과는 꽤 근사합니다. 에이전트는 최단 경로 알고리즘을 배운 적이 없습니다. 그래프 탐색도 하지 않았습니다. 그저 행동하고, 보상받고, Q-table을 업데이트했을 뿐입니다. 그런데 결과적으로 최단 경로와 같은 길을 찾았습니다.

물론 모든 환경에서 Q-learning이 항상 최단 경로를 보장하는 것은 아닙니다. 학습 횟수가 부족하거나, 탐험이 충분하지 않거나, 학습률과 할인율이 맞지 않거나, 보상 설계가 이상하면 좋지 않은 정책에 머무를 수 있습니다. 강화학습은 강력하지만 예민한 기술입니다.

값이 전파된다는 감각

Q-learning을 처음 볼 때 가장 신기한 점은 목표 보상이 출발점까지 거꾸로 전파된다는 것입니다.

목표 바로 옆 칸에서 목표로 들어가는 행동은 금방 좋은 행동이라는 사실이 드러납니다. 목표에 들어가면 +1.0을 받기 때문입니다. 그다음에는 목표 바로 옆 칸으로 이동하게 해 주는 이전 칸의 행동이 좋아집니다. 다시 그 이전 칸의 행동이 좋아집니다.

마치 어두운 미로 끝에 불이 하나 켜져 있고, 그 빛이 통로를 따라 조금씩 번져 나오는 것과 같습니다. 처음에는 목표 근처만 밝습니다. 에피소드가 반복될수록 그 밝음이 출발점 쪽으로 전파됩니다. 결국 출발점에서도 어느 방향으로 가야 밝은 길로 이어지는지 알게 됩니다.

이것이 벨만 방정식의 직관이 코드로 살아나는 순간입니다. 현재의 좋은 행동은 '지금 받는 보상'과 '다음 상태의 좋은 가능성'으로 결정됩니다. Q-learning은 이 생각을 수없이 반복하면서 표를 고쳐 나갑니다.

작은 실험으로 감각 익히기

이제 몇 가지 설정을 바꿔 보면서 Q-learning의 성격을 관찰해 볼 수 있습니다.

  • epsilon_decay를 더 작게 만들어 탐험을 빨리 줄이면, 에이전트가 초반에 우연히 발견한 길에 너무 빨리 고정될 수 있습니다.

  • epsilon_min을 0.0으로 두면 학습 후반에 탐험이 완전히 사라집니다. 단순한 환경에서는 괜찮을 수 있지만, 복잡한 환경에서는 아직 발견하지 못한 좋은 길을 놓칠 수 있습니다.

  • gamma를 낮추면 에이전트는 미래 목표보다 당장의 벌점을 더 크게 의식합니다. 너무 낮으면 목표가 멀리 있을 때 길을 잘 찾지 못할 수 있습니다.

  • alpha를 크게 하면 Q값이 빠르게 변하지만 학습이 흔들릴 수 있습니다. 반대로 너무 작게 하면 많은 에피소드가 필요합니다.

  • 이동 비용을 -0.01에서 -0.1로 키우면 에이전트는 짧은 경로를 더 강하게 선호합니다.

이 실험들은 단순한 숫자 놀이가 아닙니다. 실제 강화학습에서도 이런 설정 하나가 학습 성공과 실패를 가릅니다. 알고리즘은 같아도 탐험 방식, 보상 크기, 할인율, 학습률이 달라지면 전혀 다른 행동이 나올 수 있습니다.

표 형태 Q-learning의 의미와 한계

이번 장에서 구현한 Q-learning은 표 형태의 Q-learning입니다. 상태와 행동의 수가 작을 때는 아주 직관적이고 강력합니다. Grid World에서는 상태가 몇 개 되지 않고 행동도 네 개뿐이므로 Q-table을 직접 만들 수 있습니다.

하지만 세상이 조금만 커져도 문제가 생깁니다. 예를 들어 격자가 1000×1000이라면 상태가 백만 개입니다. 각 상태마다 네 행동의 값을 저장해야 하므로 표가 매우 커집니다. 게임 화면처럼 상태가 이미지라면 가능한 상태 수는 사실상 셀 수 없을 정도로 많습니다. 로봇 팔처럼 관절 각도와 속도가 연속적인 값이면 Q-table로 모든 상태를 저장하는 방식은 거의 불가능합니다.

그래서 나중에는 신경망으로 Q값을 근사하는 DQN이나, 정책 자체를 직접 학습하는 Policy Gradient, Actor-Critic 같은 방법이 필요해집니다. 하지만 그 복잡한 방법들도 지금 본 핵심에서 완전히 벗어나지는 않습니다.

상태를 본다. 행동을 선택한다. 보상을 받는다. 다음 상태로 이동한다. 그리고 장기 보상을 더 크게 만드는 방향으로 자신을 고친다.

이 단순한 루프가 강화학습의 중심입니다.

다음 장으로 가는 다리

우리는 Grid World에서 Q-learning 에이전트를 직접 구현했습니다. Q-table을 만들고, ε-greedy로 탐험과 활용을 섞고, 에피소드별 총보상과 이동 횟수를 기록했습니다. 마지막에는 학습된 정책을 화살표로 출력하고 최단 경로와 비교했습니다.

작은 격자 세계였지만, 여기에는 강화학습의 중요한 감각이 모두 들어 있습니다. 보상은 길을 비추는 신호가 되고, Q값은 그 신호를 상태와 행동의 표에 저장합니다. 탐험은 모르는 길을 열어 주고, 활용은 이미 배운 길을 안정적으로 따라가게 합니다.

다음 장에서는 조금 더 유명한 강화학습 실습 문제인 CartPole을 다루겠습니다. 막대가 쓰러지지 않도록 카트를 움직이는 문제입니다. Grid World처럼 칸이 딱딱 나뉘어 있지 않고, 상태가 위치와 속도 같은 숫자로 주어집니다. 이제 우리는 작은 표에서 출발해, 더 연속적이고 현실에 가까운 문제로 한 걸음 나아갈 준비가 되었습니다.

책 목록

처음 만나는 강화학습: 보상으로 행동을 배우는 AI

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기