[ 32. Actor-Critic 실습2 ]
( 아래 내용은 SKplanet Tacademy의 강화학습 강좌를 참고하여 학습한 내용입니다 )
1. Introduction
이전 포스트에서 우리는 위 그림을 확인했었다. Actor Critic은, Value-Based에서 사용하는 Value Function도 사용하고, Policy-Based에서 사용하는 Policy를 둘 다 사용한다. 최근에 많이 사용되는 RL 방법들도 모두 이 Actor Critic에 기반을 둔 것이다.
1 ) Actor
- 어떤 행동을 해야할지 결정해주는 역할
- Policy가 이 역할을 한다
2 ) Critic
- Actor가 내린 결정을 평가
- Value Function이 이 역할을 한다
2. \(\bigtriangledown_{\theta} U(\theta)\) of Actor Critic
우리는 이전에 \(U(\theta)\)의 기울기를 구했었다. 이것에 Value-Function을 사용하기 위해, 우리는 다음과 같이 \(Q_{\pi_{\theta}}(s,a)\)를 사용하여 다음과 같이 나타낼 수 있다.
\(\begin{align*} \bigtriangledown_{\theta}U(\theta) &= E_{\tau}[\sum_{t=0}^{\tau}\bigtriangledown_{\theta}log\pi_{\theta}(a_t \mid s_t) G_t]\\ &= E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) Q_{\pi_{\theta}}(s,a)]\\ \end{align*}\),
그리고 위 식의 \(Q_{\pi_{\theta}}(s,a)\)를 우리는 잘 알지 못하기 때문에, 다음과 같이 근사할 것이다. (모델은 Neural Net으로 사용할 것이다)
- \(Q_{\pi_{\theta}}(s,a) \approx Q_{w}(s,a)\).
따라서, \(\bigtriangledown_{\theta} U(\theta)\)는 다음과 같다.
- $\bigtriangledown_{\theta} U(\theta) \approx E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) Q_{w}(s,a)]$.
3. A2C ( Advantage Actor Critic)
How to reduce variance?
결론부터 이야기하자면, 우리는 다음과 같은 식을 통해 위 식의 분산을 줄일 수 있다.
\(\begin{align*} \bigtriangledown_{\theta}U(\theta) &= E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) Q_{\pi_{\theta}}(s,a)]\\ &= E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) (Q_{\pi_{\theta}}(s,a)-V_{\pi_{\theta}}(s))]\\ &= E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) A_{\pi_{\theta}}(s,a)] \end{align*}\),
여기서 \(A_{\pi_{\theta}}(s,a)\) 는 \(Q_{\pi_{\theta}}(s,a)-V_{\pi_{\theta}}(s)\) 를 의미하는 것으로, Advantage를 의미한다.
“특정 정책 하에서, 어떠한 state에서 어떠한 action을 했을 때 얻게되는 value” ( \(Q_{\pi_{\theta}}(s,a)\) ) 에서,
“그 state에서의 value” ( \(V_{\pi_{\theta}}(s)\) )를 빼는 것으로, 그 행동을 했을 때의 “이득”(Advantage)라고 볼 수 있다.
근데, 어떻게 마음대로 \(Q_{\pi_{\theta}}(s,a)\) 대신 \((Q_{\pi_{\theta}}(s,a)-V_{\pi_{\theta}}(s))\)를 사용할 수 있을까?
Proof
state s에 관한 임의의 함수 \(B(s)\)에 대해서, 다음은 항상 성립한다!
\(\begin{align*} E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(s,a) B(s)] &= \sum_{s \in S}d^{\pi_{\theta}}(s) \sum_{a}\pi_{\theta}(s,a) \bigtriangledown_{\theta} log \pi_{\theta}(s,a)B(s)\\ &= \sum_{s \in S}d^{\pi_{\theta}}(s) \sum_{a}\bigtriangledown_{\theta} \pi_{\theta}(s,a)B(s)\\ &= \sum_{s \in S}d^{\pi_{\theta}}(s) B(s)\sum_{a}\bigtriangledown_{\theta} \pi_{\theta}(s,a)\\ &= 0 \end{align*}\).
(위 식에서 \(d^{\pi_{\theta}}(s)\) 는, 특정 정책 \(\pi_{\theta}\) 하에서, 해당 state \(s\)에서 머물게 되는 (상대적) 점유율/시간이라고 보면 된다. 총 합은 1이된다 )
따라서 우리는 state에 관한 임의의 함수인 \(V_{\pi_{\theta}}(s)\)를 뺴줘도 되고, 따라서 \(\bigtriangledown_{\theta}U(\theta)\)를 다음과 같이 나타내도 되는 것이다!
- \(\bigtriangledown_{\theta}U(\theta) = E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) A_{\pi_{\theta}}(s,a)]\).
따라서 위의 gradient를 이용하여 학습하는 방법을 우리는 “Advantage Actor-Critic”, 혹은 A2C라고 부른다.
4. TD Actor-Critic
A2C를 훈련시킨다고 해보자. \(\bigtriangledown_{\theta}U(\theta) = E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) A_{\pi_{\theta}}(s,a)]\)를 보면 알겠지만, 우리는 총 3종류의 parameter를 train해야 한다.
- 1 ) Q function
- 2 ) Value Function
- 3 ) Policy
하지만 만약 우리가 TD-error를 사용한다면, 우리는 Q-function을 굳이 훈련시키지 않고 Value-Function ( \(V(s)\) )만 훈련시키면 된다. 어떻게 그것이 가능한지 수식을 통해 확인해보자.
( 복습 )
TD-error : \(\delta_{\pi_{\theta}} = ( r + \gamma\;V_{\pi_{\theta}}(s')) - V_{\pi_{\theta}}(s)\)
TD-error \(\delta_{\pi_{\theta}}\) 는 \(A_{\pi_{\theta}}(s,a)\)에 대한 unbiased estimate이기 때문에, 우리는 이를 이용하여 Policy Gradient를 계산할 수 있다.
\(\begin{align*} E_{\pi_{\theta}}[\delta_{\pi_{\theta}} \mid s,a] &= E_{\pi_{\theta}}[r + \gamma\;V_{\pi_{\theta}}(s') - V_{\pi_{\theta}}(s)] \\ &= Q_{\pi_{\theta}}(s,a) -V_{\pi_{\theta}}(s))\\ &= A_{\pi_{\theta}}(s,a) \end{align*}\).
따라서, 다음과 같은 approximate TD-error를 사용할 경우, 우리는 value function \(V(s)\)만 학습하면 된다!
- \(\delta_{V} = r + \gamma V_{V}(s') - V_V(s)\).
정리하면, TD Actor-Critic에서 사용하는 \(\bigtriangledown_{\theta}U(\theta)\) 는 다음과 같다.
- \(\bigtriangledown_{\theta}U(\theta) = E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) \delta]\).
5. Summary
지난 두 포스트에서 우리는 Policy Gradient에 대해서 배웠다. ( Policy Gradient의 다양한 형태인 REINFORCE, A2C, TD Actor-Critic 등 )
각각의 \(\bigtriangledown_{\theta}U(\theta)\)를 정리하면 다음과 같다.
-
1 ) REINFORCE : \(E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) v_t]\)
-
2 ) Q Actor-Critic : \(E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) Q^{w}(s,a)]\)
-
3 ) Advantage Actor-Critic : \(E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) A^{w}(s,a)]\)
-
4 ) TD Actor-Critic : \(E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) \delta]\)
-
5 ) REINFORCE : \(E_{\pi_{\theta}}[\bigtriangledown_{\theta}log\pi_{\theta}(a \mid s) v_t]\)
6. Implementation with Pytorch
( 아래 코드는 SKplanet Tacademy의 강화학습 강좌를 참고로 하여 작성된 코드입니다. )
1) Import Libraries
import gym
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Categorical
2) Set Hyperparameters
lr = 0.0002
gamma = 0.98
3) Actor Critic
def main():
env = gy.make('CartPole-v1')
model = ActorCritic() # 이 부분이 Policy-Based와 다른 점이다
n_rollout = 5 # 5번의 step마다 update 진행
print_int = 20
score = 0
for episode in range(2000):
done = False
state = env.reset()
while not done:
for t in range(n_rollout):
prob = model.pi(torch.from_numpy(s).float()) # into probability
actions = Categorical(prob).sample() # random sample ( prob에 따라 )
state_, returns, done, info = env.step(actions.item())
model.put_data((state,actions,returns,state_,done))
state = state_ # 다음 state로 넘어감
score += returns # return을 누적해서 더함
if done:
break
model.train()
if episode%print_int==0 & episode!=0:
print('episode : {}, score : {}'.format(episode, score/print_int))
score= 0
env.close()
class ActorCritic(nn.Module):
def __init__(self):
super(ActorCritic, self).__init__()
self.data = []
self.fc_common = nn.Linear(4,128)
self.fc_pi = nn.Linear(128,2)
self.fc_v = nn.Linear(128,1) # hidden layer의 unit 개수 : 128개
self.opt = optim.Adam(self.parameters(), lr=lr) # Adam Optimizer 사용
## REINFORCE와는 다르게, 훈련시켜야하는 network가 2개(pi & v)다
def pi(sef,x,dim):
x = F.relu(self.fc_common(x))
x = self.fc_pi(x)
pi = F.softmax(x,dim=dim) # 각 행동에 대한 probability 반환
return pi
def v(self,x):
x = F.relu(self.fc_common(x))
v = self.fc_v(x)
return v
def put_data(self,item):
self.data.append(item)
def batch(self):
S,A,R,S_,Done = [],[],[],[],[]
for item in self.data:
s,a,r,s_,done = item
S.append(s)
A.append([a])
R.append([r/100.0])
S_.append(s_)
if done:
d = 0
else :
d = 1
D.append([d])
s_batch = torch.tensor(S, dtype=torch.float)
a_batch = torch.tensor(A, dtype=torch.float),
r_batch = torch.tensor(R, dtype=torch.float),
s2_batch = torch.tensor(S_, dtype=torch.float),
d_batch = torch.tensor(D, dtype=torch.float),
self.data= []
return s_batch,a_batch,r_batch,s2_batch,d_batch
def train(self):
s,a,r,s_,done = self.batch()
TD_error = (r+gamma*self.v(s_)*done) - self.v(s)
pi = self.pi(s,dim=1)
pi_a = pi.gather(1,a)
loss = - torch.log(pi_a)*TD_error.detach() + F.smooth_l1_loss(self.v(s), TD_error.detach()) # detach : gradient 계산 안되는 상수 취급위해!
self.opt.zero_grad()
loss.mean().backward()
self.opt.step()