강화학습 아키텍처 실무 가이드 — DQN·PPO·Actor-Critic·MARL·AlphaGo
DQN, PPO, Actor-Critic, Multi-Agent RL, AlphaGo 아키텍처의 메커니즘 차이와 실무 적용 절차, 안정화 기법을 정리한다.
2026-08-12 · 최초 발행 2025-12-11
정책 하나를 학습시키는 코드는 며칠이면 짤 수 있다. 그 학습이 발산하지 않고, 배포 후에도 안전하게 굴러가게 만드는 일이 진짜 작업이다. 강화학습(Reinforcement Learning)은 에이전트가 환경과의 상호작용을 통해 보상 극대화를 학습하는 최적화 기법이고, 산업 제어·추천·로보틱스·게임 AI까지 적용 범위가 넓어지면서 DQN, PPO, Actor-Critic, Multi-Agent RL, AlphaGo 아키텍처 각각의 메커니즘과 실무 적용 절차를 구분해서 이해할 필요가 커졌다.
MDP, 그리고 무엇이 발산을 막는가
강화학습의 기본 틀은 상태·행동·전이·보상·감가율로 구성되는 Markov Decision Process이고, 목적은 장기 누적 보상(기대 반환)의 최대화다. 이 위에서 정책 π(a|s), 가치 함수 V(s)·Q(s,a), 환경 동역학 모델 P(s'|s,a)을 구분해서 다루며, 현재 정책을 평가·개선하는 On-policy와 다른 정책이 만든 데이터로 학습하는 Off-policy를 나눈다.
딥러닝으로 함수를 근사하기 시작하면 발산 위험이 따라붙는다. 그래서 타깃 네트워크, 경험 재현, Advantage 정규화, 클리핑, Entropy 보너스 같은 안정화 기법이 거의 필수로 붙는다.
아키텍처마다 서로 다른 문제를 푼다
DQN(Deep Q-Network)은 Q(s,a)를 CNN·MLP로 근사하고 경험 재현(Replay Buffer)과 타깃 네트워크로 학습을 안정화한다. Off-policy이기 때문에 데이터를 재사용할 수 있어 효율적이고, 이산 행동 공간이나 픽셀 입력에서 특히 효과적이다. Double DQN, Dueling, Prioritized Replay 같은 개선 기법을 함께 적용하는 것이 권장된다.
PPO(Proximal Policy Optimization)는 확률비 r(θ)에 클리핑 손실을 걸어 정책 업데이트 범위를 제한한다. On-policy이면서도 배치 기반 업데이트로 안정성과 실용성을 동시에 확보했고, 연속 행동이나 대규모 병렬 수집 환경에 적합하다. KL 타깃, 엔트로피 보너스, GAE(λ)와 함께 튜닝하는 것이 일반적이다.
Actor-Critic(A2C/A3C 등)은 정책(Actor)과 가치함수(Critic)를 동시에 학습하며 Advantage로 분산을 줄인다. 다양한 환경에 범용으로 적용 가능하고, A3C는 비동기 업데이트로 수렴을 가속하며 A2C는 동기화로 안정성을 높인다.
Multi-Agent RL(MARL)은 다중 에이전트가 상호작용하면서 비정상성(non-stationarity) 문제가 생기는 영역이다. CTDE(Centralized Training, Decentralized Execution), QMIX·MADDPG 같은 구조를 활용하며, 협력·경쟁·혼합 시나리오마다 크레딧 할당, 통신(communication), 공정성·안정화 기법이 따로 필요하다.
AlphaGo 아키텍처는 정책 네트워크(지도학습·자기대국 강화학습)와 가치 네트워크를 MCTS와 결합한 구조다. 롤아웃·가치 추정으로 탐색 품질을 높이며, 규칙 기반 완전정보 게임과 대규모 연산 자원을 전제로 하고 데이터 생성·탐색·학습 파이프라인의 공학적 최적화가 요구된다.
학습 루프가 실제로 도는 방식
안정성 체크에서 NaN이나 발산, KL 초과가 감지되면 롤백·학습률 감소·그라디언트 클리핑·가중치 재초기화로 되돌아가고, 수렴 기준을 못 채우면 다시 관측 단계로 순환하는 구조다.
알고리즘마다 무엇이 다른가
| 알고리즘 | 성능(벤치마크) | 확장성(병렬/분산) | 일관성(수렴/분산) | 안정성(발산 리스크) | 운영 편의(튜닝/배포) |
|---|---|---|---|---|---|
| DQN | 이산·비주얼 태스크 강점, 샘플 비효율 | 데이터 병렬 및 리플레이 확장 용이 | 중간, 하이퍼파라미터 민감 | 타깃·더블·듀얼링 없으면 취약 | 중간, 모델 단순·리플레이 운영 필요 |
| PPO | 연속/이산 전반 강건, 실무 표준 | 환경 병렬 수집·배치 업데이트 우수 | 높음, 클리핑·KL 제약 | 클리핑·Entropy로 높음 | 높음, 하이퍼파라미터 합리적 범위 |
| A2C/A3C | 범용, 고속 수렴 | A3C 비동기·A2C 동기 분산 양호 | 중간, 온폴리시 분산 다소 큼 | 중간, 학습률/엔트로피 의존 | 중간, 구현 단순·안정화 필요 |
| MARL | 과제 의존, 협력/경쟁 시 강점 | 상호작용 증가로 난이도 높음 | 낮음~중간, 비정상성 이슈 | 낮음~중간, 크레딧 할당 문제 | 낮음, 프레임워크·로깅 복잡 |
| AlphaGo | 특정 도메인 최고 수준 | 매우 높음(대규모 병렬·분산) | 높음(MCTS로 안정적 탐색) | 높음(비용 크나 안정) | 낮음, 인프라·데이터 파이프라인 복잡 |
실무에서는 이런 식으로 쓰인다
대규모 추천·광고 실험에서는 오프폴리시 학습으로 정책을 만들고 배포 전 안전성을 확인하는데, IPS/DR 추정과 안전한 정책 개선(SPIBB 등)을 함께 쓴다. 절차는 로그 수집 → 시뮬레이터/모델로 OPE → DQN/BCQ/CRR 학습 → 점진적 트래픽 전환 순서다.
로보틱스·제어에서는 연속 제어에 PPO나 SAC(참고) 계열을 선호하고, Domain randomization으로 시뮬레이션과 현실의 갭을 줄인다. 물리 시뮬 설정 → 병렬 롤아웃 → 정책 업데이트 → 실기 검증/안전 제약 순서로 진행된다.
운영 자동화(스케일링·캐시·스케줄링)에서는 상태 관측량을 표준화하고 드리프트를 모니터링하는 게 중요하며, SLA 기반으로 보상을 설계한다. KPI를 보상으로 설계 → A2C/PPO 에이전트 학습 → A/B 보호대책과 함께 점진 배포로 이어진다.
게임 AI·전략 최적화에서는 MCTS와 정책/가치를 결합(AlphaGo 유사)해 탐색을 강화하고 셀프플레이 파이프라인을 구축한다. 셀프플레이 생성 → 정책/가치 업데이트 → 평가 리그 → 최적 정책 롤아웃 순서로 돈다.
데이터 파이프라인 관점에서는 온폴리시가 샘플 신선도를 확보하고 대규모 환경 병렬화에 유리한 대신 오프폴리시보다 샘플 재사용이 제한되고, 오프폴리시는 재현성·재사용성이 높고 분산 수집이 쉬운 대신 분포 이동과 과적합 위험이 따라온다. 안정화·모니터링은 학습률 스케줄, 그라디언트 클리핑, 정책 엔트로피, KL 타깃을 공통으로 쓰고 평균 리턴, 에피소드 길이, KL, value loss, explained variance, crash rate를 지표로 삼아 NaN·발산을 감지하면 자동 롤백한다. 배포는 Shadow/Canary와 안전 제약 정책(Safe RL)을 적용하고 운영 중 데이터 쉬프팅을 감시하는데, 탐험을 줄이면 학습이 정체되고 늘리면 서비스 리스크가 올라가는 트레이드오프가 늘 따라붙는다.
AlphaGo는 무엇으로 이루어지는가
정책 네트워크(지도학습으로 초기화)와 정책 강화학습(자기대국), 가치 네트워크가 핵심 구성이고, MCTS는 정책 priors로 분기를 줄이고 가치 예측으로 롤아웃을 대체하며 UCT 변형으로 탐색 균형을 잡는다.
절차는 보드 상태와 합법 수 priors를 입력받아 MCTS(시뮬레이션: 선택→확장→평가→역전파)를 돌리고 정책·가치를 업데이트하는 처리 단계를 거쳐, 선택된 수와 최종 정책 파라미터를 출력한다. 대규모 분산 학습과 재현 가능한 데이터 파이프라인이 이 절차 전체를 뒷받침한다.
운영 관점에서는 자원 비용과 엔지니어링 복잡도가 높아 재현성과 실험 관리(데이터 버전·체크포인트)가 필수이며, AlphaZero·MuZero 같은 최신 변형과의 차이는 계속 확인이 필요하다.
PyTorch로 짜보는 DQN 최소 골격
Python 3.10, PyTorch 2.2+, Gymnasium 0.29+, CUDA 선택 환경을 전제로 하고, 재현성을 위해 시드를 고정하는 것이 좋다. 이산 행동 환경(CartPole-v1 등)에 Adam 옵티마이저, 타깃 네트워크 동기화 주기 τ를 전제조건으로 한다.
# pip install gymnasium torch numpy
import gymnasium as gym
import torch, torch.nn as nn, torch.optim as optim
import random, numpy as np
from collections import deque
env = gym.make("CartPole-v1")
obs_dim = env.observation_space.shape[0]
act_dim = env.action_space.n
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class QNet(nn.Module):
def __init__(self, in_dim, out_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, 128), nn.ReLU(),
nn.Linear(128, 128), nn.ReLU(),
nn.Linear(128, out_dim)
)
def forward(self, x): return self.net(x)
q, q_tgt = QNet(obs_dim, act_dim).to(device), QNet(obs_dim, act_dim).to(device)
q_tgt.load_state_dict(q.state_dict())
opt = optim.Adam(q.parameters(), lr=1e-3)
buf = deque(maxlen=50000)
gamma, eps, eps_min, eps_decay = 0.99, 1.0, 0.05, 0.995
batch, sync_every = 64, 200
def act_fn(s):
global eps
if random.random() < eps:
return env.action_space.sample()
with torch.no_grad():
qs = q(torch.tensor(s, dtype=torch.float32, device=device)).cpu().numpy()
return int(np.argmax(qs))
def train_step():
if len(buf) < batch: return
batch_s = random.sample(buf, batch)
s, a, r, s2, d = zip(*batch_s)
s = torch.tensor(s, dtype=torch.float32, device=device)
a = torch.tensor(a, dtype=torch.int64, device=device).unsqueeze(1)
r = torch.tensor(r, dtype=torch.float32, device=device).unsqueeze(1)
s2 = torch.tensor(s2, dtype=torch.float32, device=device)
d = torch.tensor(d, dtype=torch.float32, device=device).unsqueeze(1)
q_sa = q(s).gather(1, a)
with torch.no_grad():
max_a2 = q(s2).argmax(1, keepdim=True) # Double DQN
tgt = r + gamma * (1 - d) * q_tgt(s2).gather(1, max_a2)
loss = nn.functional.smooth_l1_loss(q_sa, tgt)
opt.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(q.parameters(), 10.0)
opt.step()
episodes = 300
step = 0
for ep in range(episodes):
s, _ = env.reset()
ep_ret = 0
done = False
while not done:
a = act_fn(s)
s2, r, term, trunc, _ = env.step(a)
done = term or trunc
buf.append((s, a, r, s2, float(done)))
s = s2; ep_ret += r; step += 1
train_step()
if step % sync_every == 0:
q_tgt.load_state_dict(q.state_dict())
eps = max(eps_min, eps * eps_decay)
print(f"ep={ep} return={ep_ret:.1f} eps={eps:.2f}")
env.close()
타깃 네트워크, Double DQN, 그라디언트 클리핑이 안정성을 확보하는 세 축이고, 버퍼 용량·배치·동기화 주기가 성능을 좌우한다. NaN이 발생하면 학습률·클리핑·초깃값부터 점검하는 게 순서다.
도입 효과와 진입 순서
평균 리턴·성공률·Regret 감소 같은 지표가 개선되고, 동일 자원 대비 작업 자동화율을 30% 이상 가속하는 목표를 설정할 수 있다(업무·환경 의존). 분산 수집·학습을 쓰면 병렬 환경 수가 늘어날수록 벽시계 시간이 준선형으로 단축되지만 통신 병목은 별도로 고려해야 한다. 정성적으로는 복잡한 동적 의사결정을 정책화하고 재현 가능하게 만들며, 시뮬레이션 기반 실험으로 위험을 줄이고, 실시간 적응(온라인 업데이트·Bandit 하이브리드)으로 운영 탄력성을 높인다.
DQN은 이산 행동·오프폴리시 효율, PPO는 안정·범용성, Actor-Critic은 구조 단순성과 분산, MARL은 상호작용 복잡계, AlphaGo는 탐색과 학습 결합의 정점 — 이렇게 포지션이 갈린다. 파일럿은 PPO나 DQN의 표준 레시피로 시작해 모니터링·안전 제약·점진 배포 체계를 먼저 갖추고, MARL이나 AlphaGo 유사 구조는 과제 난이도와 자원에 맞춰 단계적으로 진입하는 편이 현실적이다. 라이브러리·논문의 버전·API·SOTA는 계속 바뀌므로 최신 동향은 수시로 확인해야 한다.