SlideShare a Scribd company logo
1 of 9
Download to read offline
Policy Gradient
Policy gradient는 DQN처럼 value function을 근사한 다음 주어진 상태의 value function 값에 따라 어떤 행동을
취할지 결정하는, 즉 implict poilcy를 가지는 다른 강화학습 알고리즘들과 달리 policy를 직접 함수로 나타낸 뒤 학
습하는 강화학습 알고리즘이다.
Policy Gradient 논문 을 따라가면서 이런 방식의 접근이 어떤 점에서 어려운지, 어떻게 해결했는지 알아보고
CartPole에 대해 구현한 코드를 살펴보자.
Policy Gradient를 적용하기 위한 제약조건은 단순하다. Policy를 근사하는 함수를 라고 할 때, 는 미분 가능해야
한다. Gradient 값을 근사해서 policy를 학습하는 것이 주 요지이므로 이는 당연한 제약이라 할 수 있다. Neural
Network 또한 미분 가능하기 때문에 Policy Gradient의 function approximator로 사용할 수 있다.
Policy function 는 상태 s, 행동 a, 매개변수 를 받아 상태 s에서 행동 a를 할 확률을 돌려주는 함수이다. 즉
이다. 실제로 어떤 행동을 할 지는 이 확률에 따라서 stochastic하게 고른다.
이제 우리는 policy가 얼마나 좋은지 측정할 수 있는 함수, 즉 loss function을 정의한 뒤 이를 통해 policy를 업데이
트 할 것이다. loss function에는 두가지 형태가 있는데, 여기서는 출발 상태가 로 고정 됐을 때의 loss function인
로 예시를 들겠다. 로 정의하면
가 성립한다. 직관적인 의미를 생각해보면 는 시간에 따른 비율을 고려한 보
상의 기대값의 합이고, 는 policy를 따라 행동했을 때 어떤 상태 s에서 머무르고 있을 확률을 뜻한다.
이제 해야 할 일은 값에 따라 를 gradient descent로 업데이트 해주면 된다. 하지만, 우리는 무한히 시뮬레이
션을 해 볼 수 없기 때문에 도 계산할 수 없고, 각 (상태, 행동) 쌍의 가치를 나타내는 도 알지 못한다. 이것을 알
고 있다면 그냥 Q-learning policy를 사용하면 될 일이다. 그러면 이걸 어떻게 계산해야 할까?
REINFORCE: Monte Carlo Policy Gradient
REINFORCE는 몬테카를로를 통해 위의 gradient 값을 근사하는 알고리즘 이름으로, Policy gradient 논문 이전에
발표된 것이다. 미분값이 , 즉 policy의 state distribution을 따르므로
라고 볼 수 있다. 여기서 expectation은 state, 즉 에 대해 취해진 것이다. 마찬가지로, 분자와 분모에 를 곱
해주면 가 되는 것을 알 수 있다.
그런데, 우리가 실제로 한번의 시뮬레이션을 통해 얻은 reward 합이 라고 하면, 함수의 정의에 의해
가 성립한다. 즉 로 고쳐 쓸 수 있고, 는 모두 우리가 직접 계산할 수
있는 값이기 때문에 충분한 샘플링을 거쳐 값을 근사할 수 있게 된다. 즉 다음 업데이트를 반복해주면 policy가
점점 좋아진다.
그런데 이렇게 업데이트를 진행할 경우, 이론상에선 문제가 없지만 실제 구현할 때에는 행동을 한 번 할 때마다 미
분값과 그 때 forward propagation 값을 저장해두어야 한다는 단점이 있다. 따라서 인 점을 활용해
loss function을 로 두면 기존 tensorflow나 pytorch 등의 라이브러리로 쉽게 구현할 수 있다.
알고리즘을 정리하면 다음과 같다.
 
구현 (tensorflow)
텐서플로우를 사용해 policy gradient를 CartPole에 대해 구현한 코드를 살펴보자.
pg라는 클래스를 만들어 CartPole 환경과 텐서플로우 세션, 그리고 policy를 나타낼 neural network를 만들어준
다.
pg의 멤버 변수를 모두 세팅한 후, 실험은 위와 같은 코드로 진행할 것이다.
class pg:
def __init__(self):
self.env = gym.make('CartPole-v0')
self.sess = tf.Session()
self.net = self.build_policy()
self.sess.run(tf.global_variables_initializer())
def main():
agent = pg()
agent.train()
if __name__ == '__main__':
main()
'''
CartPole의 observation을 state로 받아서
각 행동의 unnormalized prob.을 내보내는 네트워크.
[N, 4] -> [N, 2]
reward : Sutton book의 REINFORCE 중 각 time step 별 G를 모은 리스트. reward[t] == time step t
에서의 G
action : 각 time step 별 선택됐던 action은 1, 아니면 0
'''
def build_policy(self):
state = tf.placeholder(tf.float32, shape=(None, 4))
위의 코드는 실제 policy를 나타낼 neural network를 구성하는 코드다. 각 라인별로 살펴보면,
한 에피소드 동안 쌓인 상태/보상/취했던 행동을 입력으로 받아서
64개의 유닛을 가진 히든레이어 하나 짜리 DNN을 만들고, 출력을 softmax 한 값을 각 행동을 취할 확률로 삼는다.
reward = tf.placeholder(tf.float32, shape=(None, 1))
action = tf.placeholder(tf.float32, shape=(None, 2))
layer = tf.layers.dense(state, 64, activation=tf.nn.relu,
kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG))
logit = tf.layers.dense(layer, 2, activation=None,
kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG))
log_prob = tf.nn.log_softmax(logit)
prob = tf.nn.softmax(logit)
loss = tf.multiply(log_prob, action)
loss = tf.reduce_sum(loss, axis=1, keepdims=True)
loss = tf.multiply(loss, reward)
loss = -tf.reduce_sum(loss) +
tf.reduce_sum(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES))
optimize = tf.train.AdamOptimizer(LR).minimize(loss) # 1e-2 / 1e-4
return {
'optimize': optimize, 'loss': loss, 'log_prob': log_prob,
'state': state, 'reward': reward, 'action': action,
'prob': prob
}
state = tf.placeholder(tf.float32, shape=(None, 4))
reward = tf.placeholder(tf.float32, shape=(None, 1))
action = tf.placeholder(tf.float32, shape=(None, 2))
layer = tf.layers.dense(state, 64, activation=tf.nn.relu,
kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG))
logit = tf.layers.dense(layer, 2, activation=None,
kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG))
log_prob = tf.nn.log_softmax(logit)
prob = tf.nn.softmax(logit)
loss = tf.multiply(log_prob, action)
loss = tf.reduce_sum(loss, axis=1, keepdims=True)
loss = tf.multiply(loss, reward)
loss = -tf.reduce_sum(loss) +
tf.reduce_sum(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES))
위의 식에 따라 loss를 계산한다. 이 때 주의할 점은 우리가 정의한 는 최소화의 대상이 아니라 최대화의 대상이기
때문에, tf.reduce_sum(loss)에 -1을 곱해줘야 한다.
이제 optimizer를 설정해주고 각 오퍼레이터를 사용할 수 있게 리턴해준다.
이제 실제 학습을 어떻게 하는 지 알아보자.
optimize = tf.train.AdamOptimizer(LR).minimize(loss) # 1e-2 / 1e-4
return {
'optimize': optimize, 'loss': loss, 'log_prob': log_prob,
'state': state, 'reward': reward, 'action': action,
'prob': prob
}
def train(self):
gamma = 0.9
history = []
for episode in range(1, 10000 + 1):
#render = True if episode % 100 == 1 else False
render = False
obs, done, state, reward, action, G = self.env.reset(), False, [], [], [], []
while not done:
if render:
self.env.render()
prob = self.sess.run(self.net['prob'], feed_dict={
self.net['state']: np.array(obs).reshape((-1, 4))
})[0]
act = np.random.choice(list(range(2)), p=prob)
state.append(obs)
obs, rew, done, _ = self.env.step(act)
rew = 0.0 if abs(rew) < 1e-5 else (1.0 if rew > 0 else -1.0)
action.append(act)
reward.append(rew)
for t in range(len(reward)):
reward[t] = reward[t] * pow(gamma, t)
for t in range(len(reward)):
G.append(np.sum(reward[t:]))
state = np.reshape(np.array(state), (-1, 4))
reward = np.reshape(np.array(G), (-1, 1))
reward = (reward - np.mean(reward)) / (np.std(reward) + 1e-5)
action = np.eye(2)[np.array(action).reshape(-1)] # one-hot
action = np.reshape(np.array(action), (-1, 2))
self.sess.run(self.net['optimize'], feed_dict={
self.net['state']: state, self.net['reward']: reward, self.net['action']: action
})
history.append(reward.shape[0])
if episode % 100 == 0:
총 10000번의 에피소드(한 번 죽거나 성공하는게 한 에피소드)동안 학습을 진행한다.
각 에피소드가 시작될 때마다 초기화를 해주고,
현재 상태에 대한 확률 계산 -> 확률에 따른 행동 선택 -> 행동 수행 -> 결과 관찰 을 반복한다.
그리고 저장한 reward 값에 discount factor를 적용시켜준다. 즉 시간에 따른 감가상각을 반영해준다.
이제 python list로 저장된 상태 / 보상 / 행동을 numpy 로 가공한 뒤, optimize에 인자로 넘겨줘 policy를 업데이트
한다.
구현 (pytorch)
print('%dth try : %d step, avg %s' % (episode, reward.shape[0],
np.mean(history[-200:])))
render = False
obs, done, state, reward, action, G = self.env.reset(), False, [], [], [], []
while not done:
if render:
self.env.render()
prob = self.sess.run(self.net['prob'], feed_dict={
self.net['state']: np.array(obs).reshape((-1, 4))
})[0]
act = np.random.choice(list(range(2)), p=prob)
state.append(obs)
obs, rew, done, _ = self.env.step(act)
rew = 0.0 if abs(rew) < 1e-5 else (1.0 if rew > 0 else -1.0)
action.append(act)
reward.append(rew)
for t in range(len(reward)):
reward[t] = reward[t] * pow(gamma, t)
for t in range(len(reward)):
G.append(np.sum(reward[t:]))
state = np.reshape(np.array(state), (-1, 4))
reward = np.reshape(np.array(G), (-1, 1))
reward = (reward - np.mean(reward)) / (np.std(reward) + 1e-5)
action = np.eye(2)[np.array(action).reshape(-1)] # one-hot
action = np.reshape(np.array(action), (-1, 2))
self.sess.run(self.net['optimize'], feed_dict={
self.net['state']: state, self.net['reward']: reward, self.net['action']: action
})
pytorch로 구현한 버전도 살펴보자.
import random
import 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
L2_REG = 1e-2
LR = 1e-2
'''
CartPole의 observation을 state로 받아서
각 행동의 unnormalized prob.을 내보내는 네트워크.
[N, 4] -> [N, 2]
reward : Sutton book의 REINFORCE 중 각 time step 별 G를 모은 리스트. reward[t] == time step t에서
의 G
action : 각 time step 별 선택됐던 action은 1, 아니면 0
'''
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(4, 32)
self.fc2 = nn.Linear(32, 2)
self.history = []
def forward(self, x):
state, reward, action = x
n = state.size()[0]
layer = F.relu(self.fc1(state))
logit = self.fc2(layer)
log_prob = F.log_softmax(logit, dim=1)
loss = log_prob * action
loss = torch.sum(loss, 1, True)
loss = loss * reward
loss = torch.sum(loss)
return -loss
def predict(self, x):
with torch.no_grad():
x = F.relu(self.fc1(x))
x = self.fc2(x)
res = F.softmax(x, dim=1)
return res
#return log_prob
class pg:
def __init__(self):
self.env = gym.make('CartPole-v0')
self.net = Net()
self.optimizer = optim.RMSprop(self.net.parameters(), lr=LR, weight_decay=L2_REG)
self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(self.optimizer, mode='max')
def train(self):
gamma = 0.99
history = []
for episode in range(1, 10000 + 1):
render = True if episode % 100 == 0 and episode else False
obs, done, state, reward, action, G = self.env.reset(), False, [], [], [], []
while not done:
if render:
self.env.render()
prob = self.net.predict(torch.from_numpy(np.array(obs).reshape((-1, 4))
).float()).detach().numpy()[0]
act = np.random.choice(list(range(2)), p=prob)
state.append(obs)
obs, rew, done, _ = self.env.step(act)
action.append(act)
reward.append(rew)
G = [0.0 for _ in range(len(reward))]
Gr = 0.0
for i in range(len(reward)-1, -1, -1):
G[i] = Gr = reward[i] + gamma * Gr
state = np.reshape(np.array(state), (-1, 4))
reward = np.reshape(np.array(G), (-1, 1))
reward = (reward - np.mean(reward)) / (np.std(reward) + 1e-5)
action = np.eye(2)[np.array(action).reshape(-1)] # one-hot
action = np.reshape(np.array(action), (-1, 2))
state = torch.from_numpy(state).float()
reward = torch.from_numpy(reward).float()
action = torch.from_numpy(action).float()
self.optimizer.zero_grad()
loss = self.net([state, reward, action])
loss.backward()
self.optimizer.step()
if episode and episode % 50 == 0:
self.scheduler.step(np.mean(history[-200:]))
history.append(reward.shape[0])
if episode % 50 == 0:
print('%dth try : %d step, avg %s, lr %s' % (episode, reward.shape[0],
np.mean(history[-200:]), self.optimizer.param_groups[0]['lr']))
def main():
텐서플로우 버전과 구조는 거의 비슷하다. 다른 점은
pytorch는 learning rate scheduling이 간편하기 때문에 적용했다는 점과,
optimize를 operator로 빼는 것이 아닌 gradient flush -> back propagation -> apply optimizer를 명시적으로 해
준다는 점이다. Learning rate scheduler의 mode가 max인 이유는 metric, 즉 평균 에피소드 길이를 최대화 하는
방향으로 학습해야하기 때문이다.
실제로 pytorch 버전을 실행시키면
900 에피소드 만에 학습이 완료되는 것을 볼 수 있다.
https://imgur.com/a/bkdaytw 에서 실제 학습 결과를 볼 수 있다.
 
agent = pg()
agent.train()
if __name__ == '__main__':
main()
self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(self.optimizer, mode='max')
self.optimizer.zero_grad()
loss = self.net([state, reward, action])
loss.backward()
self.optimizer.step()
$ python torch_pg.py
?[33mWARN: gym.spaces.Box autodetected dtype as <class 'numpy.float32'>. Please provide explicit
dtype.?[0m
50th try : 200 step, avg 97.68, lr 0.01
100th try : 200 step, avg 137.47, lr 0.01
150th try : 200 step, avg 149.85333333333332, lr 0.01
200th try : 200 step, avg 161.53, lr 0.01
250th try : 200 step, avg 184.48, lr 0.01
300th try : 200 step, avg 188.565, lr 0.01
350th try : 178 step, avg 176.955, lr 0.01
400th try : 143 step, avg 172.125, lr 0.01
450th try : 200 step, avg 171.25, lr 0.01
500th try : 200 step, avg 171.86, lr 0.01
550th try : 200 step, avg 189.815, lr 0.01
600th try : 200 step, avg 183.51, lr 0.01
650th try : 200 step, avg 187.015, lr 0.01
700th try : 200 step, avg 177.83, lr 0.01
750th try : 200 step, avg 177.83, lr 0.01
800th try : 200 step, avg 189.825, lr 0.01
850th try : 200 step, avg 189.825, lr 0.01
900th try : 200 step, avg 200.0, lr 0.01
참고문헌
[1] Richard S. Sutton, David McAllester, Satinder Singh, Yishay Mansou. Policy Gradient Methods for
Reinforcement Learning with Function Approximation.
[2] Sutton, R. S., Barto, A. G. Reinforcement Learning: An Introduction. 2nd edition draft.

More Related Content

Similar to Policy gradient

PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기찬희 이
 
Reinforcement learning
Reinforcement learningReinforcement learning
Reinforcement learningTae Young Lee
 
Introduction to SAC(Soft Actor-Critic)
Introduction to SAC(Soft Actor-Critic)Introduction to SAC(Soft Actor-Critic)
Introduction to SAC(Soft Actor-Critic)Suhyun Cho
 
React Hooks 마법. 그리고 깔끔한 사용기
React Hooks 마법. 그리고 깔끔한 사용기React Hooks 마법. 그리고 깔끔한 사용기
React Hooks 마법. 그리고 깔끔한 사용기NAVER SHOPPING
 
Reinforcement learning basic
Reinforcement learning basicReinforcement learning basic
Reinforcement learning basicJicheol Woo
 
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련Haesun Park
 
목적이 부여된 에이전트 행동
목적이 부여된 에이전트 행동목적이 부여된 에이전트 행동
목적이 부여된 에이전트 행동Hyosung Jeon
 
Coursera Machine Learning (by Andrew Ng)_강의정리
Coursera Machine Learning (by Andrew Ng)_강의정리Coursera Machine Learning (by Andrew Ng)_강의정리
Coursera Machine Learning (by Andrew Ng)_강의정리SANG WON PARK
 
Unity ml agent quick guide
Unity ml agent quick guideUnity ml agent quick guide
Unity ml agent quick guideKyoungman Lee
 
Reinforcement learning v0.5
Reinforcement learning v0.5Reinforcement learning v0.5
Reinforcement learning v0.5SANG WON PARK
 
13. Application - Tensorflow Autoencoder
13. Application - Tensorflow Autoencoder 13. Application - Tensorflow Autoencoder
13. Application - Tensorflow Autoencoder merry7
 
Survey of activation functions
Survey of activation functionsSurvey of activation functions
Survey of activation functions창기 문
 
Direct Sparse Odometry (DSO) Review
Direct Sparse Odometry (DSO) ReviewDirect Sparse Odometry (DSO) Review
Direct Sparse Odometry (DSO) ReviewEdwardIm1
 
안드로이드App개발 09.task
안드로이드App개발 09.task안드로이드App개발 09.task
안드로이드App개발 09.taskDaeHee Jang
 
강화학습의 흐름도 Part 1
강화학습의 흐름도 Part 1강화학습의 흐름도 Part 1
강화학습의 흐름도 Part 1Dongmin Lee
 
ML + 주식 phase 2
ML + 주식  phase 2ML + 주식  phase 2
ML + 주식 phase 2HoChul Shin
 
함수형 사고 - Functional thinking
함수형 사고 - Functional thinking함수형 사고 - Functional thinking
함수형 사고 - Functional thinking재문 심
 
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It종빈 오
 

Similar to Policy gradient (20)

PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기PySpark 배우기 Ch 06. ML 패키지 소개하기
PySpark 배우기 Ch 06. ML 패키지 소개하기
 
Reinforcement learning
Reinforcement learningReinforcement learning
Reinforcement learning
 
Introduction to SAC(Soft Actor-Critic)
Introduction to SAC(Soft Actor-Critic)Introduction to SAC(Soft Actor-Critic)
Introduction to SAC(Soft Actor-Critic)
 
React Hooks 마법. 그리고 깔끔한 사용기
React Hooks 마법. 그리고 깔끔한 사용기React Hooks 마법. 그리고 깔끔한 사용기
React Hooks 마법. 그리고 깔끔한 사용기
 
Reinforcement learning basic
Reinforcement learning basicReinforcement learning basic
Reinforcement learning basic
 
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
[홍대 머신러닝 스터디 - 핸즈온 머신러닝] 4장. 모델 훈련
 
목적이 부여된 에이전트 행동
목적이 부여된 에이전트 행동목적이 부여된 에이전트 행동
목적이 부여된 에이전트 행동
 
Coursera Machine Learning (by Andrew Ng)_강의정리
Coursera Machine Learning (by Andrew Ng)_강의정리Coursera Machine Learning (by Andrew Ng)_강의정리
Coursera Machine Learning (by Andrew Ng)_강의정리
 
Unity ml agent quick guide
Unity ml agent quick guideUnity ml agent quick guide
Unity ml agent quick guide
 
Reinforcement learning v0.5
Reinforcement learning v0.5Reinforcement learning v0.5
Reinforcement learning v0.5
 
13. Application - Tensorflow Autoencoder
13. Application - Tensorflow Autoencoder 13. Application - Tensorflow Autoencoder
13. Application - Tensorflow Autoencoder
 
Survey of activation functions
Survey of activation functionsSurvey of activation functions
Survey of activation functions
 
Direct Sparse Odometry (DSO) Review
Direct Sparse Odometry (DSO) ReviewDirect Sparse Odometry (DSO) Review
Direct Sparse Odometry (DSO) Review
 
안드로이드App개발 09.task
안드로이드App개발 09.task안드로이드App개발 09.task
안드로이드App개발 09.task
 
강화학습의 흐름도 Part 1
강화학습의 흐름도 Part 1강화학습의 흐름도 Part 1
강화학습의 흐름도 Part 1
 
ML + 주식 phase 2
ML + 주식  phase 2ML + 주식  phase 2
ML + 주식 phase 2
 
함수형 사고 - Functional thinking
함수형 사고 - Functional thinking함수형 사고 - Functional thinking
함수형 사고 - Functional thinking
 
함수적 사고 2장
함수적 사고 2장함수적 사고 2장
함수적 사고 2장
 
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
 
Redux
ReduxRedux
Redux
 

Policy gradient

  • 1. Policy Gradient Policy gradient는 DQN처럼 value function을 근사한 다음 주어진 상태의 value function 값에 따라 어떤 행동을 취할지 결정하는, 즉 implict poilcy를 가지는 다른 강화학습 알고리즘들과 달리 policy를 직접 함수로 나타낸 뒤 학 습하는 강화학습 알고리즘이다. Policy Gradient 논문 을 따라가면서 이런 방식의 접근이 어떤 점에서 어려운지, 어떻게 해결했는지 알아보고 CartPole에 대해 구현한 코드를 살펴보자. Policy Gradient를 적용하기 위한 제약조건은 단순하다. Policy를 근사하는 함수를 라고 할 때, 는 미분 가능해야 한다. Gradient 값을 근사해서 policy를 학습하는 것이 주 요지이므로 이는 당연한 제약이라 할 수 있다. Neural Network 또한 미분 가능하기 때문에 Policy Gradient의 function approximator로 사용할 수 있다. Policy function 는 상태 s, 행동 a, 매개변수 를 받아 상태 s에서 행동 a를 할 확률을 돌려주는 함수이다. 즉 이다. 실제로 어떤 행동을 할 지는 이 확률에 따라서 stochastic하게 고른다. 이제 우리는 policy가 얼마나 좋은지 측정할 수 있는 함수, 즉 loss function을 정의한 뒤 이를 통해 policy를 업데이 트 할 것이다. loss function에는 두가지 형태가 있는데, 여기서는 출발 상태가 로 고정 됐을 때의 loss function인 로 예시를 들겠다. 로 정의하면 가 성립한다. 직관적인 의미를 생각해보면 는 시간에 따른 비율을 고려한 보 상의 기대값의 합이고, 는 policy를 따라 행동했을 때 어떤 상태 s에서 머무르고 있을 확률을 뜻한다. 이제 해야 할 일은 값에 따라 를 gradient descent로 업데이트 해주면 된다. 하지만, 우리는 무한히 시뮬레이 션을 해 볼 수 없기 때문에 도 계산할 수 없고, 각 (상태, 행동) 쌍의 가치를 나타내는 도 알지 못한다. 이것을 알 고 있다면 그냥 Q-learning policy를 사용하면 될 일이다. 그러면 이걸 어떻게 계산해야 할까? REINFORCE: Monte Carlo Policy Gradient REINFORCE는 몬테카를로를 통해 위의 gradient 값을 근사하는 알고리즘 이름으로, Policy gradient 논문 이전에 발표된 것이다. 미분값이 , 즉 policy의 state distribution을 따르므로 라고 볼 수 있다. 여기서 expectation은 state, 즉 에 대해 취해진 것이다. 마찬가지로, 분자와 분모에 를 곱 해주면 가 되는 것을 알 수 있다. 그런데, 우리가 실제로 한번의 시뮬레이션을 통해 얻은 reward 합이 라고 하면, 함수의 정의에 의해 가 성립한다. 즉 로 고쳐 쓸 수 있고, 는 모두 우리가 직접 계산할 수 있는 값이기 때문에 충분한 샘플링을 거쳐 값을 근사할 수 있게 된다. 즉 다음 업데이트를 반복해주면 policy가 점점 좋아진다. 그런데 이렇게 업데이트를 진행할 경우, 이론상에선 문제가 없지만 실제 구현할 때에는 행동을 한 번 할 때마다 미 분값과 그 때 forward propagation 값을 저장해두어야 한다는 단점이 있다. 따라서 인 점을 활용해 loss function을 로 두면 기존 tensorflow나 pytorch 등의 라이브러리로 쉽게 구현할 수 있다.
  • 2. 알고리즘을 정리하면 다음과 같다.   구현 (tensorflow) 텐서플로우를 사용해 policy gradient를 CartPole에 대해 구현한 코드를 살펴보자. pg라는 클래스를 만들어 CartPole 환경과 텐서플로우 세션, 그리고 policy를 나타낼 neural network를 만들어준 다. pg의 멤버 변수를 모두 세팅한 후, 실험은 위와 같은 코드로 진행할 것이다. class pg: def __init__(self): self.env = gym.make('CartPole-v0') self.sess = tf.Session() self.net = self.build_policy() self.sess.run(tf.global_variables_initializer()) def main(): agent = pg() agent.train() if __name__ == '__main__': main() ''' CartPole의 observation을 state로 받아서 각 행동의 unnormalized prob.을 내보내는 네트워크. [N, 4] -> [N, 2] reward : Sutton book의 REINFORCE 중 각 time step 별 G를 모은 리스트. reward[t] == time step t 에서의 G action : 각 time step 별 선택됐던 action은 1, 아니면 0 ''' def build_policy(self): state = tf.placeholder(tf.float32, shape=(None, 4))
  • 3. 위의 코드는 실제 policy를 나타낼 neural network를 구성하는 코드다. 각 라인별로 살펴보면, 한 에피소드 동안 쌓인 상태/보상/취했던 행동을 입력으로 받아서 64개의 유닛을 가진 히든레이어 하나 짜리 DNN을 만들고, 출력을 softmax 한 값을 각 행동을 취할 확률로 삼는다. reward = tf.placeholder(tf.float32, shape=(None, 1)) action = tf.placeholder(tf.float32, shape=(None, 2)) layer = tf.layers.dense(state, 64, activation=tf.nn.relu, kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG)) logit = tf.layers.dense(layer, 2, activation=None, kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG)) log_prob = tf.nn.log_softmax(logit) prob = tf.nn.softmax(logit) loss = tf.multiply(log_prob, action) loss = tf.reduce_sum(loss, axis=1, keepdims=True) loss = tf.multiply(loss, reward) loss = -tf.reduce_sum(loss) + tf.reduce_sum(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)) optimize = tf.train.AdamOptimizer(LR).minimize(loss) # 1e-2 / 1e-4 return { 'optimize': optimize, 'loss': loss, 'log_prob': log_prob, 'state': state, 'reward': reward, 'action': action, 'prob': prob } state = tf.placeholder(tf.float32, shape=(None, 4)) reward = tf.placeholder(tf.float32, shape=(None, 1)) action = tf.placeholder(tf.float32, shape=(None, 2)) layer = tf.layers.dense(state, 64, activation=tf.nn.relu, kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG)) logit = tf.layers.dense(layer, 2, activation=None, kernel_regularizer=tf.contrib.layers.l2_regularizer(L2_REG)) log_prob = tf.nn.log_softmax(logit) prob = tf.nn.softmax(logit) loss = tf.multiply(log_prob, action) loss = tf.reduce_sum(loss, axis=1, keepdims=True) loss = tf.multiply(loss, reward) loss = -tf.reduce_sum(loss) + tf.reduce_sum(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES))
  • 4. 위의 식에 따라 loss를 계산한다. 이 때 주의할 점은 우리가 정의한 는 최소화의 대상이 아니라 최대화의 대상이기 때문에, tf.reduce_sum(loss)에 -1을 곱해줘야 한다. 이제 optimizer를 설정해주고 각 오퍼레이터를 사용할 수 있게 리턴해준다. 이제 실제 학습을 어떻게 하는 지 알아보자. optimize = tf.train.AdamOptimizer(LR).minimize(loss) # 1e-2 / 1e-4 return { 'optimize': optimize, 'loss': loss, 'log_prob': log_prob, 'state': state, 'reward': reward, 'action': action, 'prob': prob } def train(self): gamma = 0.9 history = [] for episode in range(1, 10000 + 1): #render = True if episode % 100 == 1 else False render = False obs, done, state, reward, action, G = self.env.reset(), False, [], [], [], [] while not done: if render: self.env.render() prob = self.sess.run(self.net['prob'], feed_dict={ self.net['state']: np.array(obs).reshape((-1, 4)) })[0] act = np.random.choice(list(range(2)), p=prob) state.append(obs) obs, rew, done, _ = self.env.step(act) rew = 0.0 if abs(rew) < 1e-5 else (1.0 if rew > 0 else -1.0) action.append(act) reward.append(rew) for t in range(len(reward)): reward[t] = reward[t] * pow(gamma, t) for t in range(len(reward)): G.append(np.sum(reward[t:])) state = np.reshape(np.array(state), (-1, 4)) reward = np.reshape(np.array(G), (-1, 1)) reward = (reward - np.mean(reward)) / (np.std(reward) + 1e-5) action = np.eye(2)[np.array(action).reshape(-1)] # one-hot action = np.reshape(np.array(action), (-1, 2)) self.sess.run(self.net['optimize'], feed_dict={ self.net['state']: state, self.net['reward']: reward, self.net['action']: action }) history.append(reward.shape[0]) if episode % 100 == 0:
  • 5. 총 10000번의 에피소드(한 번 죽거나 성공하는게 한 에피소드)동안 학습을 진행한다. 각 에피소드가 시작될 때마다 초기화를 해주고, 현재 상태에 대한 확률 계산 -> 확률에 따른 행동 선택 -> 행동 수행 -> 결과 관찰 을 반복한다. 그리고 저장한 reward 값에 discount factor를 적용시켜준다. 즉 시간에 따른 감가상각을 반영해준다. 이제 python list로 저장된 상태 / 보상 / 행동을 numpy 로 가공한 뒤, optimize에 인자로 넘겨줘 policy를 업데이트 한다. 구현 (pytorch) print('%dth try : %d step, avg %s' % (episode, reward.shape[0], np.mean(history[-200:]))) render = False obs, done, state, reward, action, G = self.env.reset(), False, [], [], [], [] while not done: if render: self.env.render() prob = self.sess.run(self.net['prob'], feed_dict={ self.net['state']: np.array(obs).reshape((-1, 4)) })[0] act = np.random.choice(list(range(2)), p=prob) state.append(obs) obs, rew, done, _ = self.env.step(act) rew = 0.0 if abs(rew) < 1e-5 else (1.0 if rew > 0 else -1.0) action.append(act) reward.append(rew) for t in range(len(reward)): reward[t] = reward[t] * pow(gamma, t) for t in range(len(reward)): G.append(np.sum(reward[t:])) state = np.reshape(np.array(state), (-1, 4)) reward = np.reshape(np.array(G), (-1, 1)) reward = (reward - np.mean(reward)) / (np.std(reward) + 1e-5) action = np.eye(2)[np.array(action).reshape(-1)] # one-hot action = np.reshape(np.array(action), (-1, 2)) self.sess.run(self.net['optimize'], feed_dict={ self.net['state']: state, self.net['reward']: reward, self.net['action']: action })
  • 6. pytorch로 구현한 버전도 살펴보자. import random import 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 L2_REG = 1e-2 LR = 1e-2 ''' CartPole의 observation을 state로 받아서 각 행동의 unnormalized prob.을 내보내는 네트워크. [N, 4] -> [N, 2] reward : Sutton book의 REINFORCE 중 각 time step 별 G를 모은 리스트. reward[t] == time step t에서 의 G action : 각 time step 별 선택됐던 action은 1, 아니면 0 ''' class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(4, 32) self.fc2 = nn.Linear(32, 2) self.history = [] def forward(self, x): state, reward, action = x n = state.size()[0] layer = F.relu(self.fc1(state)) logit = self.fc2(layer) log_prob = F.log_softmax(logit, dim=1) loss = log_prob * action loss = torch.sum(loss, 1, True) loss = loss * reward loss = torch.sum(loss) return -loss def predict(self, x): with torch.no_grad(): x = F.relu(self.fc1(x)) x = self.fc2(x) res = F.softmax(x, dim=1) return res #return log_prob class pg:
  • 7. def __init__(self): self.env = gym.make('CartPole-v0') self.net = Net() self.optimizer = optim.RMSprop(self.net.parameters(), lr=LR, weight_decay=L2_REG) self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(self.optimizer, mode='max') def train(self): gamma = 0.99 history = [] for episode in range(1, 10000 + 1): render = True if episode % 100 == 0 and episode else False obs, done, state, reward, action, G = self.env.reset(), False, [], [], [], [] while not done: if render: self.env.render() prob = self.net.predict(torch.from_numpy(np.array(obs).reshape((-1, 4)) ).float()).detach().numpy()[0] act = np.random.choice(list(range(2)), p=prob) state.append(obs) obs, rew, done, _ = self.env.step(act) action.append(act) reward.append(rew) G = [0.0 for _ in range(len(reward))] Gr = 0.0 for i in range(len(reward)-1, -1, -1): G[i] = Gr = reward[i] + gamma * Gr state = np.reshape(np.array(state), (-1, 4)) reward = np.reshape(np.array(G), (-1, 1)) reward = (reward - np.mean(reward)) / (np.std(reward) + 1e-5) action = np.eye(2)[np.array(action).reshape(-1)] # one-hot action = np.reshape(np.array(action), (-1, 2)) state = torch.from_numpy(state).float() reward = torch.from_numpy(reward).float() action = torch.from_numpy(action).float() self.optimizer.zero_grad() loss = self.net([state, reward, action]) loss.backward() self.optimizer.step() if episode and episode % 50 == 0: self.scheduler.step(np.mean(history[-200:])) history.append(reward.shape[0]) if episode % 50 == 0: print('%dth try : %d step, avg %s, lr %s' % (episode, reward.shape[0], np.mean(history[-200:]), self.optimizer.param_groups[0]['lr'])) def main():
  • 8. 텐서플로우 버전과 구조는 거의 비슷하다. 다른 점은 pytorch는 learning rate scheduling이 간편하기 때문에 적용했다는 점과, optimize를 operator로 빼는 것이 아닌 gradient flush -> back propagation -> apply optimizer를 명시적으로 해 준다는 점이다. Learning rate scheduler의 mode가 max인 이유는 metric, 즉 평균 에피소드 길이를 최대화 하는 방향으로 학습해야하기 때문이다. 실제로 pytorch 버전을 실행시키면 900 에피소드 만에 학습이 완료되는 것을 볼 수 있다. https://imgur.com/a/bkdaytw 에서 실제 학습 결과를 볼 수 있다.   agent = pg() agent.train() if __name__ == '__main__': main() self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(self.optimizer, mode='max') self.optimizer.zero_grad() loss = self.net([state, reward, action]) loss.backward() self.optimizer.step() $ python torch_pg.py ?[33mWARN: gym.spaces.Box autodetected dtype as <class 'numpy.float32'>. Please provide explicit dtype.?[0m 50th try : 200 step, avg 97.68, lr 0.01 100th try : 200 step, avg 137.47, lr 0.01 150th try : 200 step, avg 149.85333333333332, lr 0.01 200th try : 200 step, avg 161.53, lr 0.01 250th try : 200 step, avg 184.48, lr 0.01 300th try : 200 step, avg 188.565, lr 0.01 350th try : 178 step, avg 176.955, lr 0.01 400th try : 143 step, avg 172.125, lr 0.01 450th try : 200 step, avg 171.25, lr 0.01 500th try : 200 step, avg 171.86, lr 0.01 550th try : 200 step, avg 189.815, lr 0.01 600th try : 200 step, avg 183.51, lr 0.01 650th try : 200 step, avg 187.015, lr 0.01 700th try : 200 step, avg 177.83, lr 0.01 750th try : 200 step, avg 177.83, lr 0.01 800th try : 200 step, avg 189.825, lr 0.01 850th try : 200 step, avg 189.825, lr 0.01 900th try : 200 step, avg 200.0, lr 0.01
  • 9. 참고문헌 [1] Richard S. Sutton, David McAllester, Satinder Singh, Yishay Mansou. Policy Gradient Methods for Reinforcement Learning with Function Approximation. [2] Sutton, R. S., Barto, A. G. Reinforcement Learning: An Introduction. 2nd edition draft.