머신러닝 딥러닝

[강화학습-10] 액터-크리틱(Actor-Critic)

Qcoding 2025. 5. 28. 17:34
반응형
10. Actor-Critic (A2C·A3C) & Advantage

10. Actor–Critic 구조 (A2C·A3C) & Advantage 함수

Actor–Critic(AC)정책(Actor) 과 가치 추정(Critic)을 동시에 학습해 정책 기반 방법의 높은 표현력TD 부트스트랩의 샘플 효율을 결합합니다.

Actor–Critic architecture
그림 1. Actor–Critic 상호작용 ⟶ TD 오차 δ가 정책을 곧바로 갱신한다.

10-1. A2C (Advantage Actor-Critic)

  • 동기식 A2C : N개의 워커가 동시에 n-step Return을 계산, 미니배치로 정책/가치 네트워크를 업데이트.
  • Advantage $A_t = R_t^{(n)} - V_\phi(S_t)$ 를 이용해 baseline 분산↓.
  • 목표 손실 $$\mathcal{L} = \underbrace{\bigl(\!R_t^{(n)} - V_\phi(S_t)\bigr)^2}_{\text{Critic}} \;-\;\beta\;A_t\;\log\pi_\theta(A_t\!\mid\!S_t) \;+\;\alpha\;\mathcal{H}\!\bigl[\pi_\theta(\cdot\mid S_t)\bigr]$$ 여기서 $\mathcal{H}$는 엔트로피 보너스(탐험 유도).

10-2. A3C (Asynchronous A2C)

멀티쓰레드 워커가 비동기로 환경을 탐험 → 각 워커 gradients를 글로벌 네트워크에 lock-free로 더하면서 학습 속도 크게 ↑.

특징A2C (동기)A3C (비동기)
병렬성GPU 벡터화CPU 멀티쓰레드
업데이트 간격고정 $n$ step워커마다 가변
장점안정적 배치 학습빠른 탐험·수렴
단점GPU 메모리↑Thread 경합 조정 필요

10-3. Advantage 함수 & GAE(λ)

Advantage $A_t$ 는 “행동 $a$의 상대적 우수성”을 측정해 Policy Gradient 분산을 낮춘다.

  • 1-step TD 오차 : $\delta_t = r_t + \gamma V(S_{t+1}) - V(S_t)$
  • GAE(λ) : $$A_t^{(\lambda)}=\sum_{k=0}^{\infty}(\gamma\lambda)^k\,\delta_{t+k}$$ λ ↑ ⇒ Bias ↓ · Variance ↑ (REINFORCE에 가까움)

10-4. 간단 구현 : A2C로 LunarLander-v2

OpenAI Gym LunarLander 예시
그림 2. LunarLander 환경 (랜덤 정책)

PyTorch 200줄 내 코어 코드 (다중 워커 생략 — 단일 인스턴스 n-step A2C):

"""
pip install gymnasium torch numpy
"""
import gymnasium as gym, numpy as np, torch, torch.nn as nn, torch.optim as optim
from collections import deque

env_id = "LunarLander-v2"
env    = gym.make(env_id)
gamma, n_step = 0.99, 5

class ActorCritic(nn.Module):
    def __init__(self, s_dim, a_dim):
        super().__init__()
        self.shared = nn.Sequential(
            nn.Linear(s_dim, 256), nn.ReLU(),
            nn.Linear(256, 128),    nn.ReLU())
        self.pi = nn.Sequential(nn.Linear(128, a_dim))
        self.v  = nn.Linear(128, 1)
    def forward(self, s):
        h = self.shared(s)
        return self.pi(h), self.v(h)

net   = ActorCritic(env.observation_space.shape[0], env.action_space.n)
opt   = optim.Adam(net.parameters(), lr=3e-4)
buffer= deque(maxlen=4096)

def get_action(state):
    with torch.no_grad():
        logits, _ = net(torch.tensor(state, dtype=torch.float32))
    probs = torch.softmax(logits, dim=-1)
    return int(torch.multinomial(probs, 1))

def compute_returns(rewards, next_v, dones):
    R = next_v
    returns = []
    for r, d in zip(reversed(rewards), reversed(dones)):
        R = r + gamma * R * (1 - d)
        returns.insert(0, R)
    return returns

for ep in range(800):
    s, _ = env.reset()
    traj_s,traj_a,traj_r,traj_d = [],[],[],[]
    done, ep_ret = False, 0
    while not done:
        a = get_action(s)
        s2, r, term, trunc, _ = env.step(a)
        d = term or trunc
        traj_s.append(s); traj_a.append(a); traj_r.append(r); traj_d.append(d)
        s, done, ep_ret = s2, d, ep_ret+r

        # n-step rollout
        if len(traj_r) >= n_step or done:
            with torch.no_grad():
                _, next_v = net(torch.tensor(s, dtype=torch.float32))
            R = compute_returns(traj_r, next_v.item(), traj_d)
            for i in range(len(R)):
                buffer.append((traj_s[i], traj_a[i], R[i]))
            traj_s=traj_a=traj_r=traj_d=[]

        # 학습
        if len(buffer) >= 1024:
            batch = [buffer.popleft() for _ in range(1024)]
            states = torch.tensor([b[0] for b in batch], dtype=torch.float32)
            actions= torch.tensor([b[1] for b in batch])
            targets= torch.tensor([b[2] for b in batch], dtype=torch.float32)

            logits, values = net(states)
            logp   = torch.log_softmax(logits, dim=-1)
            adv    = targets - values.squeeze()

            loss_pi= -(logp[range(len(actions)), actions] * adv.detach()).mean()
            loss_v =  0.5*adv.pow(2).mean()
            entropy= -(torch.softmax(logits,dim=-1)*logp).sum(1).mean()

            loss = loss_pi + loss_v - 0.01*entropy
            opt.zero_grad(); loss.backward(); opt.step()

    if (ep+1)%20==0:
        print(f"[{ep+1:4d}] Return = {ep_ret:7.1f}")
  • 성공 기준 : 평균 Score ≥ 200 (OpenAI Gym).
  • 멀티 워크 (A3C) 를 사용하면 CPU만으로 수렴 속도 ↑ & 샘플 효율 ↑.

10-5. 요약 & 다음 편 예고

  • Actor + Critic 조합은 Advantage로 Policy Gradient를 안정화한다.
  • A2C는 GPU 벡터화, A3C는 비동기 쓰레드로 학습 가속.
  • LunarLander 같은 복합 box2d 미션도 n-step A2C만으로 해결 가능.

다음 글 : 신뢰 영역으로 정책 업데이트를 제어하는 PPO(Clip/TRPO)Soft Actor-Critic 의 엔트로피 정책 최적화를 다루겠습니다.


참고 자료

  • Mnih et al., “Asynchronous Methods for Deep RL,” ICML 2016
  • Sutton & Barto, RL: An Introduction, Ch. 13
  • Schulman et al., “High-Dimensional Continuous Control with GAE,” ICLR 2016
반응형