Exploration vs Exploitation 딜레마 완벽 정리: ε-greedy부터 UCB, Thompson Sampling, RND까지 탐색 전략 비교와 구현

Updated Feb 6, 2026

강화학습의 영원한 딜레마: Exploration vs Exploitation

강화학습 에이전트가 마주하는 가장 근본적인 문제 중 하나가 바로 탐색(Exploration)활용(Exploitation) 사이의 균형입니다. 현재 알고 있는 최선의 행동을 계속 선택할 것인가(활용), 아니면 더 나은 행동을 찾기 위해 미지의 선택을 시도할 것인가(탐색)?

핵심 질문: 지금 당장의 보상을 최대화할 것인가, 장기적으로 더 나은 전략을 찾을 것인가?

특히 희소 보상(Sparse Reward) 환경에서는 무작위 탐색만으로는 의미 있는 보상을 발견하기 어렵습니다. 이 글에서는 다양한 탐색 전략을 체계적으로 비교하고 실전 구현 방법을 알아봅니다.


기본 탐색 전략: ε-greedy와 Boltzmann Exploration

ε-greedy: 가장 단순하지만 효과적인 전략

ε-greedy는 확률 ϵ\epsilon로 무작위 행동을, 1ϵ1-\epsilon 확률로 현재 최선의 행동을 선택합니다.

import numpy as np

def epsilon_greedy(Q_values, epsilon=0.1):
    """ε-greedy 행동 선택

    Args:
        Q_values: 각 행동의 Q값 배열
        epsilon: 탐색 확률 (0~1)
    """
    if np.random.random() < epsilon:
        return np.random.randint(len(Q_values))  # 탐색
    else:
        return np.argmax(Q_values)  # 활용

장점: 구현이 간단하고 계산 비용이 낮음
단점: 모든 미지의 행동을 동등하게 취급 (비효율적 탐색)

Decaying ε-greedy: 학습 단계에 따른 탐색 감소

학습 초기에는 높은 탐색률, 후반에는 활용 중심으로 전환:

def get_epsilon(episode, epsilon_start=1.0, epsilon_end=0.01, decay_rate=0.995):
    return max(epsilon_end, epsilon_start * (decay_rate ** episode))

Boltzmann Exploration (Softmax)

Q값에 비례하는 확률로 행동을 선택합니다. 온도 파라미터 τ\tau로 탐색 정도를 조절:

P(a)=eQ(a)/τaeQ(a)/τP(a) = \frac{e^{Q(a)/\tau}}{\sum_{a'} e^{Q(a')/\tau}}

  • τ0\tau \to 0: 거의 greedy (활용 중심)
  • τ\tau \to \infty: 균등 분포 (탐색 중심)
def boltzmann_exploration(Q_values, temperature=1.0):
    exp_Q = np.exp(Q_values / temperature)
    probs = exp_Q / np.sum(exp_Q)
    return np.random.choice(len(Q_values), p=probs)

불확실성 기반 탐색: UCB와 Thompson Sampling

Upper Confidence Bound (UCB)

UCB는 “낙관적 초기화” 원리를 활용합니다. 각 행동의 가치를 추정할 때 불확실성을 보너스로 추가:

UCB(a)=Q(a)+clntN(a)UCB(a) = Q(a) + c \sqrt{\frac{\ln t}{N(a)}}

  • Q(a)Q(a): 행동 aa의 평균 보상 (활용)
  • clntN(a)c \sqrt{\frac{\ln t}{N(a)}}: 탐색 보너스 (불확실성)
  • tt: 전체 시간 스텝
  • N(a)N(a): 행동 aa를 선택한 횟수
  • cc: 탐색 강도 파라미터 (보통 1~2)

의미: 적게 시도한 행동일수록(N(a)N(a) 작음) 보너스가 커져서 선택 확률 증가

class UCB:
    def __init__(self, n_actions, c=2.0):
        self.Q = np.zeros(n_actions)
        self.N = np.zeros(n_actions)
        self.c = c
        self.t = 0

    def select_action(self):
        self.t += 1
        # 한 번도 선택 안 한 행동이 있으면 우선 선택
        if np.any(self.N == 0):
            return np.argmin(self.N)

        ucb_values = self.Q + self.c * np.sqrt(np.log(self.t) / self.N)
        return np.argmax(ucb_values)

    def update(self, action, reward):
        self.N[action] += 1
        self.Q[action] += (reward - self.Q[action]) / self.N[action]

Thompson Sampling: 베이지안 접근

Thompson Sampling은 각 행동의 가치 분포를 유지하고, 매번 샘플링하여 행동을 선택합니다.

class ThompsonSampling:
    def __init__(self, n_actions):
        self.alpha = np.ones(n_actions)  # 성공 횟수 + 1
        self.beta = np.ones(n_actions)   # 실패 횟수 + 1

    def select_action(self):
        # 각 행동의 보상 분포에서 샘플링
        samples = np.random.beta(self.alpha, self.beta)
        return np.argmax(samples)

    def update(self, action, reward):
        if reward > 0:
            self.alpha[action] += 1
        else:
            self.beta[action] += 1

장점: 자연스럽게 불확실성을 고려하며, 멀티암드 밴딧 문제에서 이론적으로 최적


희소 보상 환경을 위한 내재적 동기: Curiosity와 RND

전통적 탐색 전략은 희소 보상 환경에서 실패합니다. 보상 신호가 거의 없으면 탐색할 방향을 찾지 못하기 때문입니다.

Intrinsic Curiosity Module (ICM)

ICM은 예측 오류를 내재적 보상으로 사용합니다:

rti=ηϕ(st+1)ϕ^(st+1)2r^i_t = \eta |\phi(s_{t+1}) – \hat{\phi}(s_{t+1})|^2

  • ϕ(s)\phi(s): 상태 특징 인코더
  • ϕ^(st+1)\hat{\phi}(s_{t+1}): 현재 상태 sts_t와 행동 ata_t로부터 예측한 다음 상태 특징
  • η\eta: 내재적 보상 가중치

원리: 에이전트가 예측하지 못한 새로운 상태를 만나면 높은 보상 → 탐색 유도

import torch
import torch.nn as nn

class ICM(nn.Module):
    def __init__(self, state_dim, action_dim, feature_dim=256):
        super().__init__()
        # Feature encoder
        self.encoder = nn.Sequential(
            nn.Linear(state_dim, 256),
            nn.ReLU(),
            nn.Linear(256, feature_dim)
        )

        # Forward model: (s_t, a_t) -> s_{t+1} 예측
        self.forward_model = nn.Sequential(
            nn.Linear(feature_dim + action_dim, 256),
            nn.ReLU(),
            nn.Linear(256, feature_dim)
        )

    def compute_intrinsic_reward(self, state, action, next_state):
        feat = self.encoder(state)
        next_feat_pred = self.forward_model(
            torch.cat([feat, action], dim=-1)
        )
        next_feat_true = self.encoder(next_state).detach()

        # 예측 오류 = 내재적 보상
        intrinsic_reward = 0.5 * ((next_feat_pred - next_feat_true) ** 2).mean(dim=-1)
        return intrinsic_reward

Random Network Distillation (RND)

RND는 고정된 랜덤 네트워크를 타겟으로, 학습 네트워크가 이를 모방하도록 훈련합니다:

rti=ftarget(st)fpredictor(st)2r^i_t = |f_{\text{target}}(s_t) – f_{\text{predictor}}(s_t)|^2

  • ftargetf_{\text{target}}: 고정된 랜덤 네트워크 (학습 안 함)
  • fpredictorf_{\text{predictor}}: 학습하는 예측 네트워크

핵심 아이디어: 자주 방문한 상태는 예측이 잘 되고(낮은 보상), 새로운 상태는 예측 오류가 크므로(높은 보상) 탐색 유도

class RND(nn.Module):
    def __init__(self, state_dim, feature_dim=256):
        super().__init__()
        # 고정된 타겟 네트워크
        self.target_net = nn.Sequential(
            nn.Linear(state_dim, 256),
            nn.ReLU(),
            nn.Linear(256, feature_dim)
        )
        # 학습 네트워크
        self.predictor_net = nn.Sequential(
            nn.Linear(state_dim, 256),
            nn.ReLU(),
            nn.Linear(256, feature_dim)
        )

        # 타겟 네트워크 가중치 고정
        for param in self.target_net.parameters():
            param.requires_grad = False

    def compute_intrinsic_reward(self, state):
        target_feat = self.target_net(state)
        pred_feat = self.predictor_net(state)
        intrinsic_reward = ((target_feat - pred_feat) ** 2).mean(dim=-1)
        return intrinsic_reward

장점: ICM보다 안정적이며, Montezuma’s Revenge 같은 어려운 환경에서 뛰어난 성능


전략 비교: 언제 무엇을 사용할까?

전략 계산 비용 희소 보상 대응 적합한 환경 단점
ε-greedy 매우 낮음 약함 밀집 보상, 간단한 문제 비효율적 탐색
Boltzmann 낮음 약함 Q값 분포가 의미 있는 환경 온도 튜닝 필요
UCB 낮음 중간 밴딧 문제, 행동 공간 작음 연속 행동에 부적합
Thompson Sampling 중간 중간 불확실성 중요한 문제 분포 가정 필요
ICM 높음 강함 희소 보상, 복잡한 환경 예측 모델 학습 비용
RND 높음 매우 강함 극도로 희소한 보상 메모리/연산 집약적

실전 선택 가이드

  1. 간단한 환경 (CartPole, Mountain Car)
    → Decaying ε-greedy면 충분

  2. 밴딧 문제 (Multi-Armed Bandit)
    → UCB 또는 Thompson Sampling

  3. 보상이 드문 환경 (Montezuma’s Revenge, 미로 탐색)
    → RND + PPO 조합

  4. 로봇 제어 (연속 행동 공간)
    → Boltzmann + Entropy Regularization


실전 구현 팁

1. 하이브리드 접근: 외재적 + 내재적 보상

# 총 보상 = 환경 보상 + 내재적 보상
total_reward = extrinsic_reward + beta * intrinsic_reward
  • β\beta: 내재적 보상 가중치 (0.01~1.0)
  • 학습 진행에 따라 β\beta 감소시키기도 함

2. Observation Normalization

RND/ICM 사용 시 상태 정규화가 필수:

class RunningMeanStd:
    def __init__(self, epsilon=1e-4, shape=()):
        self.mean = np.zeros(shape)
        self.var = np.ones(shape)
        self.count = epsilon

    def update(self, x):
        batch_mean = np.mean(x, axis=0)
        batch_var = np.var(x, axis=0)
        batch_count = x.shape[0]

        delta = batch_mean - self.mean
        total_count = self.count + batch_count

        self.mean += delta * batch_count / total_count
        m_a = self.var * self.count
        m_b = batch_var * batch_count
        M2 = m_a + m_b + delta**2 * self.count * batch_count / total_count
        self.var = M2 / total_count
        self.count = total_count

    def normalize(self, x):
        return (x - self.mean) / np.sqrt(self.var + 1e-8)

3. 탐색 스케줄링

def get_exploration_bonus(episode, max_episodes):
    # 학습 초반: 높은 탐색, 후반: 낮은 탐색
    progress = episode / max_episodes
    return 1.0 * (1 - progress) + 0.1 * progress

마무리

강화학습에서 탐색 전략은 학습 성패를 좌우하는 핵심 요소입니다. 이 글에서 다룬 내용을 정리하면:

  • ε-greedy/Boltzmann: 간단한 환경에서 빠른 프로토타이핑에 적합
  • UCB/Thompson Sampling: 불확실성을 체계적으로 다루는 밴딧 문제 최적화
  • ICM/RND: 희소 보상 환경에서 게임 체인저, 내재적 동기로 탐색 유도

실전 조언: 문제의 복잡도와 보상 밀집도에 따라 전략을 선택하되, 간단한 방법부터 시작해 점진적으로 복잡한 기법을 도입하세요.

다음 단계: 여러분의 환경에 맞는 탐색 전략을 실험해보고, 하이퍼파라미터(ϵ\epsilon, cc, β\beta 등)를 튜닝하며 최적의 균형점을 찾아보세요!

Did you find this helpful?

☕ Buy me a coffee

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

TODAY 390 | TOTAL 2,613