PPO(Proximal Policy Optimization)는 강화학습에서 정책(Policy)을 안정적으로 업데이트하기 위해 제안된 정책 기반(Policy-based) 알고리즘입니다. 구현이 비교적 간단하면서도 안정성과 성능이 뛰어나 OpenAI Baselines, Stable-Baselines, RLlib 등 다양한 강화학습 프레임워크에서 기본 알고리즘으로 채택하고 있습니다.
1. PPO (Proximal Policy Optimization)
기존의 정책 경사(Policy Gradient) 방법은 정책을 한 번에 크게 업데이트할 경우 학습이 불안정해지는 치명적인 단점이 있었습니다. PPO는 정책이 이전 정책에서 너무 멀리 벗어나지 않도록 제한(Clip)하여 이 문제를 해결합니다.
1.1 PPO가 등장한 이유: TRPO의 한계
정책이 한 번 업데이트될 때 기존 정책과 너무 많이 달라지면 학습이 붕괴되고 성능이 급격히 하락할 수 있습니다. 이를 방지하기 위해 먼저 등장한 알고리즘이 TRPO(Trust Region Policy Optimization)입니다.
TRPO는 정책이 이전 정책에서 너무 멀어지지 않도록 KL Divergence를 사용하여 정책 변화량을 엄격하게 제한했습니다. 하지만 다음과 같은 명확한 한계가 있었습니다.
- 구현이 매우 복잡함.
- 2차 미분인 헤시안 행렬(Hessian Matrix)을 사용하여 계산량이 지나치게 많음.
PPO는 TRPO의 '안정적인 업데이트'라는 목적은 유지하면서, 1차 미분만을 사용하여 계산을 단순화한 알고리즘입니다.
1.2 PPO의 핵심 아이디어
PPO는 Advantage Actor-Critic 구조를 기반으로 합니다. Actor는 행동 정책을 학습하고, Critic은 상태 가치(Value)를 추정하여 Advantage를 계산합니다.
1) 비율(Ratio) 계산
PPO는 정책의 변화량을 측정하기 위해 현재 정책과 이전 정책의 확률 비율을 계산합니다.
- $\pi(a|s)$: 상태 $s$에서 행동 $a$를 할 확률 (현재 정책)
- $\pi_{old}(a|s)$: 이전 정책
2) Advantage 계산
Advantage는 특정 행동이 해당 상태에서의 평균적인 기대치보다 '얼마나 더 좋은가'를 의미합니다. 즉, 실제로 얻은 가치와 Critic이 예측한 상태 가치의 차이입니다.
3) PPO Clipped Objective (핵심 손실 함수)
PPO는 확률 비율(ratio)이 일정 범위를 벗어나면 업데이트를 강제로 제한하는 Clipped Objective Function을 사용합니다.
($\epsilon$은 보통 $0.1$ ~ $0.2$로 설정합니다.)
4) Clip의 작동 원리
PPO의 지상 목표는 "정책이 한 번에 너무 많이 바뀌지 않게 하자"입니다.
- $\epsilon = 0.2$ 일 때, ratio의 허용 범위는 $0.8$ ~ $1.2$가 됩니다.
[정상 업데이트의 경우]
- 이전 정책 확률 = $0.4$
- 새 정책 확률 = $0.42$
- $ratio = 0.42 / 0.4 = 1.05$
- 허용 범위($0.8$ ~ $1.2$) 안이므로 그대로 업데이트를 진행합니다.
[Clip이 적용되는 경우]
- 만약 ratio 계산 결과가 $1.5$가 나왔다면, 허용 범위를 초과한 것입니다.
- 업데이트 크기가 너무 크다고 판단하여, ratio를 상한선인 $1.2$로 강제 제한(Clip)합니다. 이를 통해 정책이 파괴되는 것을 막습니다.
1.3 PPO 전체 알고리즘 흐름
- 초기 정책 $\pi_\theta$ 설정
- 반복(Iteration):
- 환경에서 데이터를 수집 (상태, 행동, 보상)
- Advantage 계산
- 여러 Epoch 동안 반복:
- ratio 계산
- Clip 적용하여 Loss 계산
- Gradient 계산 및 정책 업데이트
1.4 PPO PyTorch 구현 코드
import gymnasium as gym
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Categorical
# Hyperparameters
learning_rate = 0.0005
gamma = 0.98
# GAE(Generalized Advantage Estimation):
# Monte Carlo의 높은 분산과 TD의 높은 편향(bias) 사이의 균형을 맞추기 위해 TD 오차를 여러 스텝 섞어 계산
lmbda = 0.95
eps_clip = 0.1
K_epoch = 3
T_horizon = 20
class PPO(nn.Module):
def __init__(self):
super(PPO, self).__init__()
self.data = []
self.fc1 = nn.Linear(4, 256)
self.fc_pi = nn.Linear(256, 2)
self.fc_v = nn.Linear(256, 1)
self.optimizer = optim.Adam(self.parameters(), lr=learning_rate)
def pi(self, x, softmax_dim=0):
x = F.relu(self.fc1(x))
x = self.fc_pi(x)
prob = F.softmax(x, dim=softmax_dim)
return prob
def v(self, x):
x = F.relu(self.fc1(x))
v = self.fc_v(x)
return v
def put_data(self, transition):
self.data.append(transition)
def make_batch(self):
s_lst, a_lst, r_lst, s_prime_lst, prob_a_lst, done_lst = [], [], [], [], [], []
for transition in self.data:
s, a, r, s_prime, prob_a, done = transition
s_lst.append(s)
a_lst.append([a])
r_lst.append([r])
s_prime_lst.append(s_prime)
prob_a_lst.append([prob_a])
done_mask = 0 if done else 1
done_lst.append([done_mask])
s = torch.tensor(np.array(s_lst), dtype=torch.float)
a = torch.tensor(np.array(a_lst), dtype=torch.long)
r = torch.tensor(np.array(r_lst), dtype=torch.float)
s_prime = torch.tensor(np.array(s_prime_lst), dtype=torch.float)
done_mask = torch.tensor(np.array(done_lst), dtype=torch.float)
prob_a = torch.tensor(np.array(prob_a_lst), dtype=torch.float)
self.data = []
return s, a, r, s_prime, done_mask, prob_a
def train_net(self):
s, a, r, s_prime, done_mask, prob_a = self.make_batch()
for _ in range(K_epoch):
td_target = r + gamma * self.v(s_prime) * done_mask
delta = td_target - self.v(s)
delta = delta.detach().cpu().numpy()
advantage_lst = []
advantage = 0.0
# GAE 계산: 보상 리스트를 뒤에서부터 역순으로 순회하며 누적 연산
for delta_t in delta[::-1]:
advantage = gamma * lmbda * advantage + delta_t[0]
advantage_lst.append([advantage])
advantage_lst.reverse()
advantage = torch.tensor(advantage_lst, dtype=torch.float)
pi = self.pi(s, softmax_dim=1)
pi_a = pi.gather(1, a)
# ratio 계산: exp(log(pi) - log(prob_old)) = pi / prob_old
ratio = torch.exp(torch.log(pi_a + 1e-8) - torch.log(prob_a + 1e-8))
surr1 = ratio * advantage
surr2 = torch.clamp(ratio, 1 - eps_clip, 1 + eps_clip) * advantage
loss = -torch.min(surr1, surr2) + F.smooth_l1_loss(self.v(s), td_target.detach())
self.optimizer.zero_grad()
loss.mean().backward()
self.optimizer.step()
def main():
env = gym.make('CartPole-v1')
model = PPO()
score = 0.0
print_interval = 20
for n_epi in range(10000):
s, _ = env.reset()
done = False
while not done:
for _ in range(T_horizon):
prob = model.pi(torch.from_numpy(s).float())
m = Categorical(prob)
a = m.sample().item()
s_prime, r, terminated, truncated, info = env.step(a)
done = terminated or truncated
model.put_data((s, a, r / 100.0, s_prime, prob[a].item(), done))
s = s_prime
score += r
if done:
break
model.train_net()
if n_epi % print_interval == 0 and n_epi != 0:
print("# of episode :{}, avg score : {:.1f}".format(n_epi, score / print_interval))
score = 0.0
env.close()
if __name__ == '__main__':
main()
2. 최신 강화학습 트렌드 및 주요 알고리즘
최근 강화학습은 환경 내에서 단순히 상호작용하는 것을 넘어, 시뮬레이션 활용, 오프라인 데이터 활용, 트랜스포머 아키텍처 도입 등 다양한 방식으로 진화하고 있습니다.
2.1 SAC (Soft Actor-Critic)
- 핵심 개념: 최대 엔트로피(Maximum Entropy) 기반의 Off-policy Actor-Critic 알고리즘입니다.
- 특징: 단순히 주어지는 보상(Reward)을 최대화하는 것을 넘어, 정책의 무작위성(엔트로피)도 함께 최대화하도록 학습합니다.
- 장점: 에이전트가 특정 행동에 과도하게 수렴하는(Local Optima) 현상을 방지하고, 더 다양한 행동 공간을 탐험하게 하여 학습 안정성을 대폭 끌어올립니다. 두 개의 Q-네트워크를 사용하는 Double Q-learning 구조를 채택하여 연속적인 행동 공간(Continuous Action Space) 제어 환경(예: 로봇 제어)에서 매우 강력한 성능을 발휘합니다.
- 참고: 추후 블로그 작성 예정.
2.2 MuZero
- 핵심 개념: 환경의 규칙이나 모델을 미리 알지 못해도 스스로 학습하는 모델 기반(Model-based) 강화학습입니다. DeepMind에서 제안했습니다.
- 특징: 전작인 AlphaZero는 바둑이나 체스처럼 룰이 완벽히 정의된 환경에서만 동작했지만, MuZero는 환경 자체를 모델링하는 신경망(Representation, Dynamics, Prediction Network)을 학습합니다.
- 의사결정: 내재된 모델을 통해 미래 상태를 예측하고, 이를 바탕으로 MCTS(Monte Carlo Tree Search)를 수행하여 최적의 행동을 찾아냅니다.
2.3 MCTS (Monte Carlo Tree Search)
- 핵심 개념: 가능한 경우의 수를 트리(Tree) 구조로 탐색하며, 여러 번의 무작위 시뮬레이션을 통해 최적의 경로를 찾는 탐색 알고리즘입니다. AlphaGo의 핵심 엔진입니다.
- 4단계 프로세스: 1. 선택(Selection): 현재 상태에서 유망한 노드를 골라 내려갑니다.3. 시뮬레이션(Simulation): 해당 노드에서 끝까지 랜덤 플레이를 돌려봅니다.
- 4. 역전파(Backpropagation): 시뮬레이션 결과(승/패, 보상)를 거슬러 올라가며 트리의 통계값을 업데이트합니다.
- 2. 확장(Expansion): 탐색하지 않은 새로운 행동 노드를 추가합니다.
2.4 Dreamer
- 핵심 개념: 환경과 직접 상호작용하며 데이터를 모으는 대신, 학습된 세계 모델(World Model) 내부에서 상상(Imagination)하며 학습하는 모델 기반 강화학습 알고리즘입니다.
- 특징: 에이전트가 현실 경험을 바탕으로 잠재 공간(Latent Space) 기반의 동적 모델을 구축합니다. 정책 업데이트는 현실 데이터가 아닌, 이 세계 모델이 생성한 가상의 미래 시나리오(Trajectory) 안에서 이루어집니다. 고차원 이미지 입력이 필요한 환경에서 샘플 효율성(Sample Efficiency)을 극대화합니다.
2.5 Decision Transformer
- 핵심 개념: 강화학습을 최적화 제어 문제가 아닌 시퀀스 모델링(Sequence Modeling) 문제로 치환한 혁신적인 접근법입니다.
- 특징: Transformer 아키텍처를 사용하여 과거의 (상태, 행동, 보상) 시퀀스를 입력받아 다음 행동을 예측합니다.
- 오프라인 지도학습: 환경과 상호작용하며 정책을 업데이트하는 기존 방식이 아닙니다. 이미 수집된 거대한 오프라인 데이터셋을 지도학습 방식으로 학습합니다. 특히, 목표로 하는 총 보상값인 **"Return-to-Go"**를 입력에 조건으로 달아주어, 원하는 성능 수준의 행동을 생성하도록 유도할 수 있습니다.
'개념 정리 step2 > 강화 학습' 카테고리의 다른 글
| [강화학습] A3C, A2C, ACER 알고리즘 핵심 정리 및 구현 (0) | 2026.03.14 |
|---|---|
| [강화학습] Policy Gradient부터 Actor-Critic까지 정리 (0) | 2026.03.10 |
| [강화학습] Q-learning의 개념부터 Gym을 활용한 DQN 구현까지 (0) | 2026.03.04 |
| [강화학습] Deep Reinforcement Learning 개념 (0) | 2026.03.03 |
| [강화학습] TD Learning (시간차 학습) 개념, 랜덤 벽 GridWorld 실습 (0) | 2026.03.01 |
