경험 리플레이(Replay Buffer), ε-탐욕적(epsilon-greedy) 정책, 타겟 네트워크(Target Network)의 개념을 이해
[DQN 알고리즘 개요]
DQN은 Q-러닝과 Neural Network을 결합하여 복잡한 환경에서 Reinforcement Learning을 수행하는 알고리즘
Q-네트워크를 사용하여 Q-함수를 근사
DQN의 주요 구성 요소:
경험 리플레이 (Replay Buffer)
ε-탐욕적 (Epsilon-Greedy) 정책
타겟 네트워크 (Target Network)
[경험 리플레이 (Replay Buffer)]
과거 경험을 메모리에 저장하고 무작위로 샘플링하여 학습 데이터 간의 상관 관계를 줄여 학습 안정성을 향상
Transition: 형태의 경험을 저장
: 현재 상태
: 행동
: 보상
: 다음 상태
: 에피소드 종료 여부
샘플링: 저장된 경험들로부터 무작위로 미니배치를 구성하여 학습에 사용
[ε-탐욕적 (Epsilon-Greedy) 정책]
탐험(Exploration)과 활용(Exploitation)의 균형을 맞춤
ε 확률로 무작위 행동을 선택하고, 1 - ε 확률로 현재 Q-값이 가장 높은 행동을 선택
ε 값은 학습이 진행됨에 따라 점차 감소시켜 탐험을 줄이고 활용을 늘림
[타겟 네트워크 (Target Network)]
Q-값 업데이트의 안정성을 향상
메인 네트워크와 타겟 네트워크를 분리하여 사용
메인 네트워크: Q값을 예측하고 업데이트하는 데 사용
타겟 네트워크: 일정 주기마다 메인 네트워크의 파라미터를 복사하여 Q값을 계산하는 데 사용
타겟 네트워크를 고정함으로써 TD-오차의 변동성을 줄여 학습을 안정화
[DQN 학습 업데이트]
Loss Function (Loss Function): TD (Temporal Difference) 오차를 최소화하는 방향으로 학습
TD 타겟 (TD Target):
: 즉각적인 보상
: 할인율
: 다음 상태
: 다음 상태에서 타겟 네트워크를 사용하여 선택된 최적 행동
: 타겟 네트워크의 파라미터
TD 오차 (TD Error):
: 메인 네트워크의 파라미터
Loss Function:
업데이트: Loss Function을 최소화하는 방향으로 메인 네트워크의 파라미터를 업데이트
[ε-탐욕적 정책 액션 선택]
의 확률로 무작위 행동 선택
의 확률로 현재 Q-값이 최대인 행동 선택
[실습: DQN (Deep Q-Network) 구현 (CartPole)]
1. 환경 설정
gym: CartPole 환경 제공
torch: Deep Learning 모델 및 연산 처리
numpy: 배열 및 수치 연산
collections.deque: 경험 리플레이 버퍼 구현
import gymnasium as gym
import random
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import deque
env = gym.make('CartPole-v1', render_mode='rgb_array')
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
2. DQN 모델 정의
DQNAgent 클래스: Neural Network 모델 정의
3개의 완전 연결 계층 (nn.Linear) 사용
ReLU Activation Function 적용 (torch.relu)
입력: 상태, 출력: 각 행동의 Q값
class DQNAgent(nn.Module):
def __init__(self, state_size, action_size):
super(DQNAgent, self).__init__()
self.fc1 = nn.Linear(state_size, 128) # 입력층
self.fc2 = nn.Linear(128, 128) # 은닉층
self.fc3 = nn.Linear(128, action_size) # 출력층
def forward(self, x):
x = torch.relu(self.fc1(x)) # 활성화 함수 적용 (ReLU)
x = torch.relu(self.fc2(x))
return self.fc3(x) # Q-값 반환
3. 하이퍼파라미터 설정
학습률 (learning_rate), 할인율 (gamma), 탐험 확률 (epsilon) 등 설정
미니배치 크기 (batch_size), 메모리 크기 (memory_size) 설정
타겟 네트워크 업데이트 주기 (target_update_frequency) 설정
learning_rate = 0.0003
gamma = 0.99 # 할인율
epsilon = 1.0 # 초기 탐험 확률
epsilon_min = 0.01
epsilon_decay = 0.995
batch_size = 128
memory_size = 30000
target_update_frequency = 5 # 타겟 네트워크 업데이트 주기
메인 네트워크를 사용하여 행동을 선택하고, 타겟 네트워크를 사용하여 선택된 행동의 Q값을 평가
예측 Q값과 타겟 Q값 비교하여 Loss Function 계산
Backpropagation 및 가중치 업데이트
def train(batch_size):
# 경험 리플레이 버퍼에서 미니배치 샘플링
minibatch = random.sample(memory, batch_size)
# 미니배치로부터 상태, 행동, 보상, 다음 상태, 종료 여부 추출
states = torch.tensor(np.array([transition[0] for transition in minibatch]), dtype=torch.float32)
actions = torch.tensor(np.array([transition[1] for transition in minibatch]), dtype=torch.long)
rewards = torch.tensor(np.array([transition[2] for transition in minibatch]), dtype=torch.float32)
next_states = torch.tensor(np.array([transition[3] for transition in minibatch]), dtype=torch.float32)
dones = torch.tensor(np.array([transition[4] for transition in minibatch]), dtype=torch.float32)
# 타겟 Q값 계산 (Target Network 사용)
target_q_values = target_agent(next_states).max(1)[0].detach() # 타겟 네트워크에서 최대 Q값 가져오기
targets = rewards + (gamma * target_q_values * (1 - dones)) # done = 1 이면 다음 상태의 Q값 0
# 예측 Q값 계산
predicted_q_values = agent(states).gather(1, actions.unsqueeze(1)) # 학습된 네트워크에서 계산된 Q값 가져오기
# 손실 함수 계산 및 역전파
loss = criterion(predicted_q_values.squeeze(), targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
6. 학습 루프
episodes만큼 에피소드 반복
각 에피소드마다 환경 초기화 및 상태 설정
ε-탐욕적 정책으로 행동 선택
환경에서 행동 실행 후 다음 상태, 보상, 종료 여부 얻음
경험 리플레이 버퍼에 경험 추가
버퍼 크기가 batch_size 이상이면 학습 함수 호출
타겟 네트워크 업데이트 및 탐험 확률 감소
에피소드 결과 출력
# 학습 루프
episodes = 100
for e in range(episodes):
state, _ = env.reset()
state = torch.tensor(state, dtype=torch.float32)
done = False
total_reward = 0
while not done:
# ε-탐욕적 정책으로 행동 선택
if random.random() < epsilon:
action = env.action_space.sample()
else:
action = agent(state).argmax().item()
# 환경에서 행동 실행 후 다음 상태, 보상, 종료 여부 얻음
next_state, reward, terminated, truncated, _ = env.step(action)
next_state = torch.tensor(next_state, dtype=torch.float32)
total_reward += reward
done = terminated or truncated
memory.append((state, action, reward, next_state, done))
if len(memory) >= batch_size:
train(batch_size)
state = next_state
# 타겟 네트워크 업데이트
if e % target_update_frequency == 0:
target_agent.load_state_dict(agent.state_dict())
# 탐험 확률 감소
if epsilon > epsilon_min:
epsilon *= epsilon_decay
# 중간 결과 출력
if (e+1) % 20 == 0:
print(f"Episode {e + 1}/{episodes}, Total Reward: {total_reward}, Epsilon: {epsilon:.3f}")
Episode 20/100, Total Reward: 11.0, Epsilon: 0.220
Episode 40/100, Total Reward: 24.0, Epsilon: 0.067
Episode 60/100, Total Reward: 118.0, Epsilon: 0.010
Episode 80/100, Total Reward: 158.0, Epsilon: 0.010
Episode 100/100, Total Reward: 199.0, Epsilon: 0.010
7. 학습된 에이전트 검증 (렌더링 with GIF)
import imageio
import os
frames = []
state, _ = env.reset()
state = torch.tensor(state, dtype=torch.float32)
done = False
for i in range(3000): # 3000 steps simulation
action = agent(state).argmax().item()
next_state, reward, terminated, truncated, _ = env.step(action)
next_state = torch.tensor(next_state, dtype=torch.float32)
state = next_state
done = terminated or truncated
frames.append(env.render())
if done:
break
imageio.mimsave('cartpole_simulation.gif', frames, duration=33) # save to gif file
print("GIF saved as cartpole_simulation.gif")