글쓰기 프리뷰
    강화학습(Reinforcement Learning) 기초 개념 정리

    강화학습(Reinforcement Learning) 기초 개념 정리

    (수정: 2026년 1월 1일 오전 08:21)

    강화학습(Reinforcement Learning) 기초 개념 정리

    인공지능의 세 가지 주요 학습 방법 중 하나인 강화학습(Reinforcement Learning, RL) 은 AlphaGo, ChatGPT, 자율주행 등 현대 AI의 핵심 기술입니다. 이 글에서는 강화학습의 기본 개념부터 핵심 알고리즘의 분류까지, AI 입문자도 이해할 수 있도록 상세히 설명합니다.


    1. 강화학습이란?

    세 가지 학습 패러다임

    학습 방법데이터목표예시
    지도학습입력-정답 쌍정답 예측이미지 분류, 번역
    비지도학습정답 없는 데이터패턴 발견클러스터링, 차원 축소
    강화학습환경과의 상호작용보상 최대화게임 AI, 로봇 제어

    강화학습은 에이전트(Agent)환경(Environment) 과 상호작용하며, 보상(Reward) 을 최대화하는 행동(Action) 을 스스로 학습하는 방법입니다.

    강화학습의 핵심 아이디어

    ┌─────────────────────────────────────────────────────┐ │ 환경 (Environment) │ │ │ │ 상태(State) ──────────────────────► 에이전트 │ │ ▲ │ │ │ │ │ │ │ │ ▼ │ │ 보상(Reward) ◄──────────────────── 행동(Action) │ │ │ └─────────────────────────────────────────────────────┘

    실생활 비유: 강아지 훈련

    • 강아지(에이전트)가 "앉아" 명령에 앉으면(행동) 간식(보상)을 받습니다
    • 강아지는 어떤 행동이 간식을 가져오는지 스스로 학습합니다
    • 시행착오를 통해 점점 더 좋은 행동을 선택하게 됩니다

    2. 핵심 개념과 용어

    MDP (Markov Decision Process)

    강화학습 문제는 대부분 마르코프 결정 과정(MDP) 으로 모델링됩니다.

    MDP의 구성 요소:

    • S: 상태 공간 (State Space) - 가능한 모든 상태의 집합
    • A: 행동 공간 (Action Space) - 가능한 모든 행동의 집합
    • P(s'|s,a): 전이 확률 - 상태 s에서 행동 a를 취했을 때 s'로 이동할 확률
    • R(s,a,s'): 보상 함수 - 전이에 대한 즉각적인 보상
    • γ (gamma): 할인율 - 미래 보상의 현재 가치 (0~1)

    마르코프 속성 (Markov Property)

    "미래는 현재에만 의존하고, 과거에는 의존하지 않는다"

    # 마르코프 속성 예시 P(s_{t+1} | s_t, a_t) = P(s_{t+1} | s_0, a_0, s_1, a_1, ..., s_t, a_t)

    현재 상태만 알면 미래를 예측할 수 있다는 가정입니다. 체스에서 현재 보드 상태만 보면 게임을 진행할 수 있는 것과 같습니다.

    정책 (Policy)

    정책 π(a|s) 는 상태 s에서 행동 a를 선택할 확률을 정의합니다.

    # 결정론적 정책 (Deterministic Policy) # 상태마다 하나의 행동을 확정적으로 선택 def deterministic_policy(state): if state == "배고픔": return "밥 먹기" elif state == "졸림": return "자기" else: return "공부하기" # 확률론적 정책 (Stochastic Policy) # 상태에서 여러 행동을 확률적으로 선택 def stochastic_policy(state): if state == "배고픔": return {"밥 먹기": 0.8, "간식 먹기": 0.2} else: return {"공부하기": 0.6, "게임하기": 0.4}

    가치 함수 (Value Function)

    상태 가치 함수 V(s): 상태 s에서 시작하여 정책 π를 따랐을 때 얻을 수 있는 기대 누적 보상

    Vπ(s)=Eπ[t=0γtRt+1S0=s]V^\pi(s) = \mathbb{E}_\pi \left[ \sum_{t=0}^{\infty} \gamma^t R_{t+1} \mid S_0 = s \right]

    행동 가치 함수 Q(s,a): 상태 s에서 행동 a를 취한 후 정책 π를 따랐을 때의 기대 누적 보상

    Qπ(s,a)=Eπ[t=0γtRt+1S0=s,A0=a]Q^\pi(s,a) = \mathbb{E}_\pi \left[ \sum_{t=0}^{\infty} \gamma^t R_{t+1} \mid S_0 = s, A_0 = a \right]

    할인율 (Discount Factor) γ

    할인율은 미래 보상의 현재 가치를 조절합니다.

    # 할인율의 효과 예시 gamma = 0.99 # 미래 보상을 거의 그대로 반영 # gamma = 0.9 # 미래 보상을 90%만 반영 # gamma = 0.5 # 미래 보상을 절반만 반영 # 시간에 따른 보상 가치 rewards = [1, 1, 1, 1, 1] # 매 시점 보상 1 discounted_sum = sum(gamma**t * r for t, r in enumerate(rewards)) # gamma=0.99: 약 4.9 # gamma=0.5: 약 1.9
    • γ ≈ 1: 장기적 보상 중시 (체스, 바둑)
    • γ ≈ 0: 즉각적 보상 중시 (긴급한 상황)

    3. 벨만 방정식 (Bellman Equation)

    가치 함수의 핵심인 벨만 방정식은 "현재 가치 = 즉각 보상 + 할인된 미래 가치" 관계를 나타냅니다.

    벨만 기대 방정식

    Vπ(s)=aπ(as)sP(ss,a)[R(s,a,s)+γVπ(s)]V^\pi(s) = \sum_a \pi(a|s) \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma V^\pi(s')]

    Qπ(s,a)=sP(ss,a)[R(s,a,s)+γaπ(as)Qπ(s,a)]Q^\pi(s,a) = \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma \sum_{a'} \pi(a'|s') Q^\pi(s',a')]

    벨만 최적 방정식

    최적 정책 π*에서의 가치 함수:

    V(s)=maxasP(ss,a)[R(s,a,s)+γV(s)]V^*(s) = \max_a \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma V^*(s')]

    Q(s,a)=sP(ss,a)[R(s,a,s)+γmaxaQ(s,a)]Q^*(s,a) = \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma \max_{a'} Q^*(s',a')]

    코드로 이해하기

    import numpy as np def bellman_update(V, state, actions, transitions, rewards, gamma=0.99): """ 벨만 최적 방정식을 이용한 가치 함수 업데이트 """ max_value = float('-inf') for action in actions: value = 0 for next_state, prob in transitions[state][action].items(): reward = rewards[state][action][next_state] value += prob * (reward + gamma * V[next_state]) max_value = max(max_value, value) return max_value # 예시: 간단한 그리드 월드 states = ['A', 'B', 'C', 'goal'] V = {s: 0 for s in states} # 값 반복(Value Iteration) 알고리즘 for iteration in range(100): new_V = {} for state in states: if state == 'goal': new_V[state] = 0 # 종료 상태 else: new_V[state] = bellman_update(V, state, ...) V = new_V

    4. 탐험과 활용 (Exploration vs Exploitation)

    강화학습의 가장 중요한 딜레마 중 하나입니다.

    딜레마 이해하기

    • 활용 (Exploitation): 현재까지 알고 있는 최선의 행동 선택
    • 탐험 (Exploration): 더 좋은 행동을 찾기 위해 새로운 시도

    예시: 새로운 도시에서 저녁 식사

    • 활용: 어제 맛있었던 식당에 다시 가기
    • 탐험: 새로운 식당 시도하기

    ε-greedy 전략

    가장 간단하고 널리 사용되는 방법:

    import random def epsilon_greedy(Q, state, epsilon=0.1): """ 확률 ε으로 무작위 행동, (1-ε)으로 최적 행동 선택 """ if random.random() < epsilon: # 탐험: 무작위 행동 return random.choice(list(Q[state].keys())) else: # 활용: Q값이 가장 높은 행동 return max(Q[state], key=Q[state].get) # ε 감소 스케줄 (학습이 진행될수록 탐험 줄임) def get_epsilon(episode, min_eps=0.01, decay=0.995): return max(min_eps, 1.0 * (decay ** episode))

    다른 탐험 전략들

    import numpy as np # 1. Boltzmann (Softmax) 탐험 def boltzmann_action(Q, state, temperature=1.0): """온도가 높으면 더 무작위, 낮으면 탐욕적""" q_values = np.array(list(Q[state].values())) exp_q = np.exp(q_values / temperature) probs = exp_q / exp_q.sum() actions = list(Q[state].keys()) return np.random.choice(actions, p=probs) # 2. UCB (Upper Confidence Bound) def ucb_action(Q, state, N, c=2): """불확실한 행동에 보너스 부여""" total = sum(N[state].values()) ucb_values = {} for action in Q[state]: if N[state][action] == 0: return action # 한 번도 시도 안 한 행동 우선 bonus = c * np.sqrt(np.log(total) / N[state][action]) ucb_values[action] = Q[state][action] + bonus return max(ucb_values, key=ucb_values.get)

    5. 강화학습 알고리즘 분류

    대분류: Model-Based vs Model-Free

    강화학습 알고리즘 ├── Model-Based (모델 기반) │ ├── 환경의 전이 확률 P(s'|s,a)를 알거나 학습 │ ├── 계획(Planning)이 가능 │ └── 예: Dynamic Programming, MCTS, MuZero └── Model-Free (모델 없음) ├── 환경 모델 없이 직접 경험으로 학습 ├── 더 범용적이지만 샘플 효율성 낮음 └── 예: Q-Learning, SARSA, Policy Gradient

    Model-Free의 세부 분류

    Model-Free ├── Value-Based (가치 기반) │ ├── 가치 함수를 학습하여 정책 도출 │ ├── 이산 행동 공간에 적합 │ └── 예: Q-Learning, DQN, Double DQN ├── Policy-Based (정책 기반) │ ├── 정책을 직접 학습 │ ├── 연속 행동 공간에 적합 │ └── 예: REINFORCE, PPO, TRPO └── Actor-Critic (결합) ├── 가치 함수(Critic)와 정책(Actor) 동시 학습 ├── 두 방법의 장점 결합 └── 예: A2C, A3C, SAC, TD3

    On-Policy vs Off-Policy

    특성On-PolicyOff-Policy
    학습 데이터현재 정책으로 수집다른 정책으로 수집 가능
    샘플 효율성낮음 (재사용 어려움)높음 (경험 재사용)
    안정성높음상대적으로 낮음
    예시SARSA, A2C, PPOQ-Learning, DQN, SAC
    # On-Policy: SARSA # 현재 정책이 선택한 다음 행동 a'를 사용 Q[s][a] += alpha * (r + gamma * Q[s_next][a_next] - Q[s][a]) # Off-Policy: Q-Learning # 다음 상태에서 최적 행동을 사용 (현재 정책과 무관) Q[s][a] += alpha * (r + gamma * max(Q[s_next]) - Q[s][a])

    6. 기초 알고리즘 구현

    Q-Learning 완전 구현

    import numpy as np import gymnasium as gym from collections import defaultdict class QLearningAgent: def __init__(self, action_space, learning_rate=0.1, gamma=0.99, epsilon=1.0, epsilon_min=0.01, epsilon_decay=0.995): self.action_space = action_space self.lr = learning_rate self.gamma = gamma self.epsilon = epsilon self.epsilon_min = epsilon_min self.epsilon_decay = epsilon_decay # Q-테이블 초기화 self.Q = defaultdict(lambda: np.zeros(action_space.n)) def get_action(self, state): """ε-greedy 정책으로 행동 선택""" if np.random.random() < self.epsilon: return self.action_space.sample() return np.argmax(self.Q[state]) def update(self, state, action, reward, next_state, done): """Q-Learning 업데이트""" current_q = self.Q[state][action] if done: target = reward else: target = reward + self.gamma * np.max(self.Q[next_state]) # Q-value 업데이트 self.Q[state][action] += self.lr * (target - current_q) # ε 감소 self.epsilon = max(self.epsilon_min, self.epsilon * self.epsilon_decay) def train_q_learning(env_name='FrozenLake-v1', episodes=10000): env = gym.make(env_name, is_slippery=False) agent = QLearningAgent(env.action_space) rewards_history = [] for episode in range(episodes): state, _ = env.reset() total_reward = 0 done = False while not done: action = agent.get_action(state) next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated agent.update(state, action, reward, next_state, done) state = next_state total_reward += reward rewards_history.append(total_reward) if episode % 1000 == 0: avg_reward = np.mean(rewards_history[-100:]) print(f"Episode {episode}, Avg Reward: {avg_reward:.3f}, ε: {agent.epsilon:.3f}") env.close() return agent, rewards_history if __name__ == "__main__": agent, history = train_q_learning() print(f"\n최종 평균 보상: {np.mean(history[-100:]):.3f}")

    SARSA 구현 (On-Policy)

    class SARSAAgent: def __init__(self, action_space, learning_rate=0.1, gamma=0.99, epsilon=1.0, epsilon_min=0.01, epsilon_decay=0.995): self.action_space = action_space self.lr = learning_rate self.gamma = gamma self.epsilon = epsilon self.epsilon_min = epsilon_min self.epsilon_decay = epsilon_decay self.Q = defaultdict(lambda: np.zeros(action_space.n)) def get_action(self, state): if np.random.random() < self.epsilon: return self.action_space.sample() return np.argmax(self.Q[state]) def update(self, state, action, reward, next_state, next_action, done): """SARSA 업데이트 - 다음 행동(next_action)을 사용""" current_q = self.Q[state][action] if done: target = reward else: # Q-Learning과의 차이: max 대신 next_action의 Q값 사용 target = reward + self.gamma * self.Q[next_state][next_action] self.Q[state][action] += self.lr * (target - current_q) self.epsilon = max(self.epsilon_min, self.epsilon * self.epsilon_decay) def train_sarsa(env_name='FrozenLake-v1', episodes=10000): env = gym.make(env_name, is_slippery=False) agent = SARSAAgent(env.action_space) for episode in range(episodes): state, _ = env.reset() action = agent.get_action(state) # 초기 행동 선택 done = False while not done: next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated next_action = agent.get_action(next_state) # 다음 행동 미리 선택 agent.update(state, action, reward, next_state, next_action, done) state = next_state action = next_action # SARSA의 핵심! return agent

    7. 간단한 환경 만들기

    커스텀 그리드 월드

    import numpy as np class SimpleGridWorld: """ 간단한 4x4 그리드 월드 - 시작: (0, 0) - 목표: (3, 3) - 함정: (1, 1), (2, 2) """ def __init__(self, size=4): self.size = size self.start = (0, 0) self.goal = (3, 3) self.traps = [(1, 1), (2, 2)] self.state = self.start def reset(self): self.state = self.start return self._get_obs() def _get_obs(self): return self.state[0] * self.size + self.state[1] def step(self, action): """ 행동: 0=상, 1=하, 2=좌, 3=우 """ x, y = self.state moves = {0: (-1, 0), 1: (1, 0), 2: (0, -1), 3: (0, 1)} dx, dy = moves[action] # 새 위치 계산 (경계 확인) nx = max(0, min(self.size - 1, x + dx)) ny = max(0, min(self.size - 1, y + dy)) self.state = (nx, ny) # 보상 및 종료 조건 if self.state == self.goal: return self._get_obs(), 10, True, False, {} elif self.state in self.traps: return self._get_obs(), -10, True, False, {} else: return self._get_obs(), -0.1, False, False, {} def render(self): grid = [['.' for _ in range(self.size)] for _ in range(self.size)] grid[self.goal[0]][self.goal[1]] = 'G' for trap in self.traps: grid[trap[0]][trap[1]] = 'X' grid[self.state[0]][self.state[1]] = 'A' print("\n".join([" ".join(row) for row in grid])) print() # 사용 예시 if __name__ == "__main__": env = SimpleGridWorld() state = env.reset() env.render() # 랜덤 에이전트 for _ in range(10): action = np.random.randint(4) next_state, reward, done, _, _ = env.step(action) env.render() if done: print(f"Episode ended with reward: {reward}") break

    8. 학습 시각화

    import matplotlib.pyplot as plt import numpy as np def plot_training_results(rewards, window=100): """학습 곡선 시각화""" fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) # 에피소드별 보상 ax1.plot(rewards, alpha=0.3, label='Episode Reward') # 이동 평균 if len(rewards) >= window: moving_avg = np.convolve(rewards, np.ones(window)/window, mode='valid') ax1.plot(range(window-1, len(rewards)), moving_avg, color='red', linewidth=2, label=f'{window}-Episode Moving Avg') ax1.set_xlabel('Episode') ax1.set_ylabel('Total Reward') ax1.set_title('Learning Curve') ax1.legend() ax1.grid(True, alpha=0.3) # 보상 분포 히스토그램 ax2.hist(rewards, bins=50, edgecolor='black', alpha=0.7) ax2.axvline(np.mean(rewards), color='red', linestyle='--', label=f'Mean: {np.mean(rewards):.2f}') ax2.set_xlabel('Reward') ax2.set_ylabel('Frequency') ax2.set_title('Reward Distribution') ax2.legend() ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('training_results.png', dpi=150) plt.show() def visualize_q_table(Q, size=4): """Q-테이블을 그리드로 시각화""" action_symbols = ['↑', '↓', '←', '→'] fig, axes = plt.subplots(2, 2, figsize=(12, 12)) axes = axes.flatten() for action_idx, ax in enumerate(axes): q_grid = np.zeros((size, size)) for state in range(size * size): row, col = state // size, state % size q_grid[row, col] = Q[state][action_idx] im = ax.imshow(q_grid, cmap='RdYlGn', aspect='equal') ax.set_title(f'Action: {action_symbols[action_idx]}') # 값 표시 for i in range(size): for j in range(size): ax.text(j, i, f'{q_grid[i, j]:.1f}', ha='center', va='center', fontsize=10) ax.set_xticks(range(size)) ax.set_yticks(range(size)) plt.colorbar(im, ax=ax) plt.suptitle('Q-Table Visualization', fontsize=14) plt.tight_layout() plt.savefig('q_table.png', dpi=150) plt.show()

    9. 다음 단계 로드맵

    강화학습 기초를 마스터했다면, 다음 순서로 학습을 이어가세요:

    입문자 추천 학습 순서

    1. Q-Learning / SARSA (테이블 기반) 2. DQN (신경망 + Q-Learning) 3. Double DQN, Dueling DQN, Rainbow 4. Policy Gradient (REINFORCE) 5. Actor-Critic (A2C/A3C) 6. PPO (현재 가장 인기) 7. MCTS, AlphaZero, MuZero (Model-Based) 8. RLHF (LLM 정렬)

    추천 도구 및 라이브러리

    목적라이브러리
    환경Gymnasium (OpenAI Gym 후속)
    알고리즘Stable-Baselines3, CleanRL, RLlib
    딥러닝PyTorch, JAX
    실험 관리Weights & Biases, TensorBoard

    10. 핵심 정리

    개념설명
    MDP상태, 행동, 보상, 전이로 구성된 강화학습 문제의 수학적 모델
    정책 (π)상태에서 행동을 선택하는 전략
    가치 함수 (V, Q)상태 또는 상태-행동의 장기적 가치
    벨만 방정식가치 함수의 재귀적 관계식
    탐험 vs 활용새로운 시도와 알려진 최선 사이의 균형
    할인율 (γ)미래 보상의 현재 가치 비율

    Quiz

    강화학습에서 할인율(γ)이 1에 가까울수록 어떤 특성을 가지나요?

    Dunde's Portfolio

    © 2026 Dunde. All rights reserved.

    Built with React, TypeScript, and Vite. Deployed on GitHub Pages.