- Authors

- Name
- Youngju Kim
- @fjvbn20031
価値ベース vs 方策ベース
これまで扱ったDQN系の方法は**価値ベース(value-based)**アプローチでした。Q関数を学習し、Q値が最も高い行動を選択する間接的な方式です。
**方策ベース(policy-based)**方法は方策を直接パラメータ化して最適化します。方策ネットワーク pi(a|s; theta) が各状態で行動の確率分布を出力します。
価値ベースの限界
- 離散行動に限定: DQNは連続行動空間に直接適用が困難
- 決定的方策: Q値のargmaxを取るため確率的方策を自然に表現しにくい
- 収束不安定性: 価値関数の小さな変化が方策の急激な変化を引き起こす可能性
方策ベースの長所
- 連続行動空間: ガウシアン方策などで自然に連続行動を扱える
- 確率的方策: 探索が方策に内蔵されており、別途のイプシロンスケジュールが不要
- 収束保証: 局所最適解への収束が理論的に保証(適切な学習率下で)
- 部分観測環境: 確率的方策が部分観測問題でより自然
方策の表現(Policy Representation)
離散行動:ソフトマックス方策
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
class DiscretePolicyNetwork(nn.Module):
"""이산 행동 공간을 위한 정책 네트워크"""
def __init__(self, obs_size, n_actions, hidden_size=128):
super().__init__()
self.net = nn.Sequential(
nn.Linear(obs_size, hidden_size), nn.ReLU(),
nn.Linear(hidden_size, hidden_size), nn.ReLU(),
nn.Linear(hidden_size, n_actions),
)
def forward(self, x):
return self.net(x)
def get_action_prob(self, state):
logits = self.forward(state)
probs = F.softmax(logits, dim=-1)
return probs
def select_action(self, state):
probs = self.get_action_prob(state)
dist = torch.distributions.Categorical(probs)
action = dist.sample()
log_prob = dist.log_prob(action)
return action.item(), log_prob
連続行動:ガウシアン方策
class ContinuousPolicyNetwork(nn.Module):
"""연속 행동 공간을 위한 가우시안 정책 네트워크"""
def __init__(self, obs_size, action_size, hidden_size=128):
super().__init__()
self.shared = nn.Sequential(
nn.Linear(obs_size, hidden_size), nn.ReLU(),
nn.Linear(hidden_size, hidden_size), nn.ReLU(),
)
self.mean_head = nn.Linear(hidden_size, action_size)
self.log_std_head = nn.Linear(hidden_size, action_size)
def forward(self, x):
features = self.shared(x)
mean = self.mean_head(features)
log_std = self.log_std_head(features).clamp(-20, 2)
std = log_std.exp()
return mean, std
def select_action(self, state):
mean, std = self.forward(state)
dist = torch.distributions.Normal(mean, std)
action = dist.sample()
log_prob = dist.log_prob(action).sum(dim=-1)
return action.detach().numpy(), log_prob
Policy Gradient導出
目的関数
方策ベース方法の目標は期待累積報酬を最大化することです:
J(theta) = E_pi[ sum_t gamma^t * r_t ]
この目的関数をthetaについて微分して勾配を求める必要があります。
Policy Gradient定理
核心的な結果は以下の通りです:
grad J(theta) = E_pi[ sum_t grad log pi(a_t | s_t; theta) * G_t ]
ここでG_tは時点tからの割引累積報酬です。
この定理の意味を直感的に説明すると:
- 高い報酬を受けた行動: log piの勾配方向にパラメータを更新し、その行動の確率を高める
- 低い報酬を受けた行動: 反対方向に更新し、その行動の確率を下げる
導出の核心
導出の核心トリックは「対数微分トリック(log-derivative trick)」です:
grad pi(a|s; theta) = pi(a|s; theta) * grad log pi(a|s; theta)
これにより期待値形式に変換でき、サンプリングによる近似が可能になります。
REINFORCEアルゴリズム
REINFORCEは最も基本的なPolicy Gradientアルゴリズムです。モンテカルロ方式で全エピソードを収集した後更新します。
アルゴリズム疑似コード
1. 정책 네트워크 pi(a|s; theta) 초기화
2. 반복:
a. 현재 정책으로 에피소드 하나를 수집
- 각 스텝에서 (s_t, a_t, r_t, log pi(a_t|s_t))를 기록
b. 각 시점의 할인 누적 보상 G_t를 계산
c. 정책 그래디언트 계산:
loss = -sum_t log pi(a_t|s_t) * G_t
d. 역전파로 theta 업데이트
CartPole REINFORCE実装
import gymnasium as gym
import torch
import torch.optim as optim
def train_reinforce_cartpole():
"""REINFORCE로 CartPole 학습"""
env = gym.make("CartPole-v1")
obs_size = env.observation_space.shape[0]
n_actions = env.action_space.n
policy = DiscretePolicyNetwork(obs_size, n_actions, hidden_size=128)
optimizer = optim.Adam(policy.parameters(), lr=0.001)
gamma = 0.99
rewards_history = []
for episode in range(1000):
log_probs = []
rewards = []
obs, _ = env.reset()
while True:
obs_tensor = torch.tensor([obs], dtype=torch.float32)
action, log_prob = policy.select_action(obs_tensor)
next_obs, reward, terminated, truncated, _ = env.step(action)
log_probs.append(log_prob)
rewards.append(reward)
obs = next_obs
if terminated or truncated:
break
returns = []
G = 0
for r in reversed(rewards):
G = r + gamma * G
returns.insert(0, G)
returns = torch.tensor(returns, dtype=torch.float32)
if len(returns) > 1:
returns = (returns - returns.mean()) / (returns.std() + 1e-8)
log_probs_tensor = torch.stack(log_probs)
policy_loss = -(log_probs_tensor * returns).sum()
optimizer.zero_grad()
policy_loss.backward()
optimizer.step()
total_reward = sum(rewards)
rewards_history.append(total_reward)
if episode % 50 == 0:
mean_reward = np.mean(rewards_history[-50:])
print(f"에피소드 {episode}: 보상={total_reward:.0f}, 평균={mean_reward:.1f}")
if mean_reward >= 475:
print(f"에피소드 {episode}에서 해결!")
break
env.close()
return policy, rewards_history
# policy, history = train_reinforce_cartpole()
ベースラインによる分散削減
問題:高い分散
基本REINFORCEの勾配推定は分散が非常に高いです。1つのエピソードから計算した勾配が非常にノイジーで学習が不安定です。
解決:ベースライン関数
勾配から定数ベースラインbを引いても期待値は変わりませんが分散は減ります:
grad J(theta) = E_pi[ sum_t grad log pi(a_t|s_t; theta) * (G_t - b) ]
最も一般的なベースラインは**状態価値関数V(s)**です。
class PolicyWithBaseline(nn.Module):
"""베이스라인이 있는 정책 네트워크"""
def __init__(self, obs_size, n_actions, hidden_size=128):
super().__init__()
self.shared = nn.Sequential(nn.Linear(obs_size, hidden_size), nn.ReLU())
self.policy_head = nn.Sequential(nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, n_actions))
self.value_head = nn.Sequential(nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1))
def forward(self, x):
features = self.shared(x)
return self.policy_head(features), self.value_head(features)
def select_action(self, state):
logits, value = self.forward(state)
probs = F.softmax(logits, dim=-1)
dist = torch.distributions.Categorical(probs)
action = dist.sample()
return action.item(), dist.log_prob(action), value
探索問題とエントロピーボーナス
早期収束問題
方策ベース方法は高い報酬を受けた行動の確率を素早く高めるため、十分に探索する前に次善の方策に収束することがあります。
エントロピーボーナス
方策のエントロピーを損失関数に追加すると探索を促進できます:
total_loss = policy_loss + value_loss_coef * value_loss - entropy_coef * entropy
エントロピーが高いということは行動確率が均等であることを意味するので、エントロピーを最大化すると探索が促進されます。
def compute_entropy_loss(logits):
"""정책 엔트로피 계산"""
probs = F.softmax(logits, dim=-1)
log_probs = F.log_softmax(logits, dim=-1)
entropy = -(probs * log_probs).sum(dim=-1)
return entropy.mean()
分散削減技法の比較
| 技法 | 説明 | 分散削減効果 |
|---|---|---|
| リターン正規化 | G_tを平均0、分散1に正規化 | 中程度 |
| ベースライン | アドバンテージ G_t - V(s_t) を使用 | 高い |
| 時間依存ベースライン | 未来の報酬のみ考慮 | 高い |
| GAE | 複数ステップのアドバンテージの加重平均 | 非常に高い |
まとめ
- 方策ベース方法: 方策を直接パラメータ化して最適化するアプローチ
- Policy Gradient定理: 期待報酬の勾配を対数確率とリターンの積で表現
- REINFORCE: 最も基本的なモンテカルロPolicy Gradientアルゴリズム
- ベースライン: 価値関数をベースラインとして使用して分散を大きく削減
- エントロピーボーナス: 方策のエントロピーを高めて早期収束を防止
- 限界: 高い分散、オンポリシー学習による低いデータ効率
REINFORCEはCartPoleのような簡単な環境ではうまく動作しますが、Pongのような複雑な環境では学習が非常に遅いです。次の記事ではこの問題を解決するActor-Critic方法を扱います。