티스토리 뷰

Study/AI

[RL] (Spinning Up) Intro to Policy Optimization

생각많은 소심남 2019. 5. 22. 05:11

( 본 글은 OpenAI Spinning Up을 개인적으로 정리한 글입니다. 원본)

 

Part 3: Intro to Policy Optimization — Spinning Up documentation

In this section, we’ll discuss the mathematical foundations of policy optimization algorithms, and connect the material to sample code. We will cover three key results in the theory of policy gradients: In the end, we’ll tie those results together and desc

spinningup.openai.com

 이번 글에서는 Policy Optimization 알고리즘에 대한 수학적 기초에 대해서 다뤄보고, 샘플코드와 논문을 연결시켜보고자 한다. 여기서 policy gradient에 대한 세가지 핵심 결론을 다루려는데,

이 되겠다. 결론적으로 이 결론들을 묶고, policy gradient에 대한 advantage 기반의 표현을 설명해보고자 한다. 이 내용은 추후에 다룰 Vanila Policy Gradient 구현시 사용되는 것과 같다.

Deriving the Simplest Policy Gradient

 우선 stochastic하고 parameterize한 policy \(\pi_{\theta}\)의 경우를 가정해보자. 이 policy가 지향하는 것은 expected return \(J(\pi_{\theta}) = E_{\tau \sim \pi_{\theta}}[R(\tau)] \)를 극대화 하는 것이다. 이의 derivation을 이끌어내기 위해서 \(R(\tau)\)가 finite-horizon undiscounted return (discount factor가 없는 reward의 합) 를 줄 거라고 가정할 것이지만, infinite-horizon discounted return에 대한 미분도 거의 동일하다.

이제 policy를 최적화할텐데, 아래와 같이

$$ \theta_{k+1} = \theta_{k} + \alpha \nabla_{\theta} J(\pi_{\theta})|_{\theta_{k}} $$

gradient ascent를 취할 것이다. (deep learning에서는 loss를 minimize하기 위해 gradient descent를 취하지만, Reinforcement Learning에서는 reward를 maximize해야 하기 때문에 gradient ascent를 취한다.) 이때 policy performance에 대한 gradient \( \nabla_{\theta} J(\pi_{\theta}) \) 를 policy gradient라고 부르고, 이렇게 policy를 최적화하는 알고리즘을 policy gradient algorithm이라고 부른다. (Vanila Policy Gradient와 TRPO가 이 범주에 포함된다. PPO도 약간 부정확하긴 하지만, policy gradient algorithm이라 종종 표현하기도 한다.)

 이 알고리즘을 사용하기 위해서는, 수학적으로 연산이 가능한 policy gradient의 표현식이 필요하다. 여기에는 두가지 과정이 필요하다. 
 1) policy performance에 대한 분석이 가능한 gradient로 도출해서, 이를 expected value의 형태로 변환시킨 후,
 2) 해당 expected value에 대한 sample estimate를 형성해서, agent와 environemt간 일정시간동안 interaction을 하면서 생성되는 데이터를 활용해 계산할 수 있어야 한다.

 이 파트에서는 해당 표현식의 가장 간단한 형식을 도출하고, 다음 파트에서는 일반적으로 많이 사용되는 policy gradient 를 사용하면서 해당 형식을 어떻게 개선시킬 수 있는지 보여줄 것이다.

1. Probability of a Trajectory
 : 주어진 policy \(\pi_{\theta}\)에서 도출된 action으로 생성된, T step까지의 Trajectory \(\tau = (s_{0}, s_{1}, ..., s_{T})\)가 나올 수 있는 확률은 다음과 같다.

$$ P(\tau | \theta) = \rho_{0} (s_{0}) \prod_{t=0}^{T} P(s_{t+1} | s_{t}, a_{t}) \pi_{\theta}(a_{t}|s_{t}) $$
(참고로 \(\rho_{0}(s_{0})\)는 init state \(s_{0}\)가 random하게 생성되는 분포를 표현한 것이다.)

2. The Log-Derivative Trick
 : log-derivative trick은 미분학에서 온 간단한 규칙에 기반한 것이다. \(\log x\)에 대한 \(x\)의 미분값은 \(\frac{1}{x}\) 이다. 그래서 식을 정리하고, chain rule과 묶게 되면, 아래와 같은 식을 도출할 수 있다.

$$ \nabla_{\theta}P(\tau|\theta) = P(\tau|\theta)\nabla_{\theta}\log P(\tau|\theta) $$

(이해가 안되면, 위 식에서 우변의 \(\nabla_{\theta}\log P(\tau|\theta)\)를 미분해보면 log 미분에도 나오는 것처럼 분모가 생기는데, 이로 인해 상쇄되어, 결국 좌변과 우변이 같아지게 된다.)

3. Log-Probability of a Trajectory
: 그럼 1번에서 구한 Trajectory에 대한 Log-Probability는 다음과 같게 된다.

$$ \log P(\tau|\theta) = \log \rho_{0}(s_{0}) + \sum_{t=0}^{T} \left( \log P(s_{t+1} | s_{t}, a_{t}) + \log \pi_{\theta}(a_{t} | s_{t})\right) $$

(양변에 \(\log\)를 취하면 되는데, 알다시피 일반 수의 곱셈 형태는 \(\log\)에선 덧셈 형태로 표현되게 된다.)

4. Gradients of Environment Functions
 : environment는 \(\theta\)에 대해서는 상관관계가 없기 때문에, \(\rho_{0}(s_{0}), P(s_{t+1}|s_{t}, a_{t}), R(\tau)\)는 모두 0이 된다.

5. Grad-Log-Prob of a Trajectory
 : 그러면 남는 Trajactory에 대한 Log Probability의 Gradient는 다음과 같이 정의된다.

$$ \require{cancel} \begin{align} \nabla_{\theta} \log P(\tau|\theta) & = \cancel{ \nabla_{\theta} \log \rho_{0}(s_{0})} + \sum_{t=0}^{T} \left( \cancel{\nabla_{\theta} \log P( s_{t+1} | s_{t}, a_{t})} + \nabla_{\theta} \log \pi_{\theta}(a_{t}|s_{t})\right) \\ & = \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t})  \end{align} $$

 이걸 모두 적용해보면, 다음과 같이 식을 나열해볼 수 있다.

Basic Policy Gradient의 미분
$$ \begin{align} \nabla_{\theta} J(\pi_{\theta}) & = \nabla_{\theta} E_{\tau \sim \pi_{\theta}} [R(\tau)] \\ & = \nabla_{\theta} \int_{\tau} P(\tau | \theta)R(\tau) & \scriptstyle{\text{Expand Expectation}} \\ & = \int_{\tau} \nabla_{\theta} P(\tau|\theta)R(\tau) & \scriptstyle{\text{Bring Gradient under integral}} \\ & =  \int_{\tau} P(\tau|\theta) \nabla_{\theta} \log P(\tau|\theta) R(\tau) & \scriptstyle{\text{Log-derivative trick}} \\ & = E_{\tau \sim \pi_{\theta}}[\nabla_{\theta} \log P(\tau|\theta) R(\tau) ] & \scriptstyle{\text{Return to  expectation form}} \\ \therefore \nabla_{\theta} J(\pi_{\theta})  & = E_{\tau \sim \pi_{\theta}} [\sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta}(a_{t} | s_{t})R(\tau)] & \scriptstyle{\text{Expansion for grad-log-prob}} \end{align} $$

 결국 gradient는 expectation이 되는데, 이를 sample mean을 이용해서 estimate할 수 있다는 것을 의미하는 것이다. 만약 N개의 episode 동안 \(D = \{\tau_{i}\}_{i=1,...,N} \) 만큼 trajectory를 수집하고, 각 trajectory가 policy \(\pi_{\theta}\) 를 agent가 environment내에서 모은 데이터라고 가정하면, policy gradient는 다음과 같이 estimate할 수 있게 된다.

$$ \hat{g} =  \frac{1}{|D|} \sum_{\tau \in D} \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t}) R(\tau) $$

 여기서 \(|D|\)는 \(D\) 내에 있는 trajectory의 갯수인데, 앞의 예제에서 가져오면 \(N\)이 되겠다.

 마지막 식이 바로 우리가 원하던 계산 가능한 표현식 중 가장 간단한 형태이다. 만약 지금 수행하고 있는 policy를 \( \nabla_{\theta} \log_{\pi_{\theta}}(a|s) \)를 계산할 수 있도록 나타낸다고 가정하고, trajectory dataset을 모을 수 있게끔 agent가 environment상에서 policy를 수행할 수 있다면, 위 과정을 통해서 policy gradient를 구할 수 있고, update를 수행할 수 있다.

Implementing the Simplest Policy Gradient

 여기 spinup/examples/pg_math/1_simple_pg.py (github) Tensorflow로 구현한 가장 간단한 policy gradient algorithm을 공유했다. 약 122줄 정도 되는데, 한번 깊게 분석해보기를 권한다. 코드를 하나하나씩 다 다루지는 않겠지만, 몇가지 중요한 포인트에 대해서 강조할 예정이다.

1. Making the Policy Network

# make core of policy network
obs_ph = tf.placeholder(shape=(None, obs_dim), dtype=tf.float32)
logits = mlp(obs_ph, sizes=hidden_sizes+[n_acts])

# make action selection op (outputs int actions, sampled from policy)
actions = tf.squeeze(tf.multinomial(logits=logits,num_samples=1), axis=1)

 해당 codeblock은 feedforward neural network을 통해 categorical policy를 만들어내는 부분이다. (이전 내용 중 Stochastic Policies 부분을 다시 살펴보면 좋다.) 여기서 logits tensor 변수는 action에 대한 probability나 log-probability를 얻는데 사용할 수 있고, actions tensor 변수는 logits 에 의해서 정의된 probability를 기반으로 action을 sampling하게 된다. 

2. Making the Loss Function

# make loss function whose gradient, for the right data, is policy gradient
weights_ph = tf.placeholder(shape=(None,), dtype=tf.float32)
act_ph = tf.placeholder(shape=(None,), dtype=tf.int32)
action_masks = tf.one_hot(act_ph, n_acts)
log_probs = tf.reduce_sum(action_masks * tf.nn.log_softmax(logits), axis=1)
loss = -tf.reduce_mean(weights_ph * log_probs)

  해당 block에서는 policy gradient algorithm에 대한 loss function을 만드는 부분이다. 만약 올바른 데이터가 들어온다면, loss function에 대한 gradient는 policy gradient와 동일할 것이다. 여기서 올바른 데이터란 현재의 policy를 통해 action을 취하면서 모은 (state, action, weight) tuple을 말한다. 여기서 state-action pair에 대한 weight은 episode가 지속되면서 얻는 reward들을 말한다. (뒤에서 이야기 할 부분이긴 하지만, weight 부분에 들어갈 적당한 값들은 또 있다.)

비록 위에서 Loss Function을 언급하긴 했지만, 이건 supervised learning에서 나오는 일반적인 loss function이 아니다. 그 loss function가 다른 두가지 차이가 여기에 있다.
 1. data의 distribution이 parameter에 따라서 달라진다.
 : loss function은 일반적으로 우리가 최적화하고자 하는 parameter와는 독립적인, 고정된 data distribution 상에서 정의되어야 한다. 여기서는 가장 최근에 수행한 policy로부터 data가 sampling되어야 한다.

 2. 이 값이 performance를 나타내지는 않는다.
 : 일반적인 loss function은 우리가 측정하고자 하는 성능 지표로 많이 활용된다. 여기서는 expected return \(J(\theta)\) 를 성능 지표로 삼는데, "loss" function은 expectation을 구하긴 해도 expected return을 전혀 approximate하지 않는다. 지금 다루는 "loss" function은 우리한테만 중요한 요소인데, 현재 적용되고 있는 parameter를 평가할 때, 이 상태에서 뽑은 data를 적용하면 performance에 대한 negative gradient가 나오게 된다는 점이다.
 하지만 이 gradient descent 단계를 넘어서게 되면, 더이상 performance와는 관련이 없게 된다. 이 말은, 주어진 batch size만큼의 데이터 내에서 "loss" function을 minimize한다고 해도, 이게 꼭 improved된 expected return을 가져온다는 아무런 보장이 없다는 것이다. 이 loss를 \(-\infty\)로 보내고 policy performance가 폭발할 수도 있다: 실제로도 그럴 거다. 때때로 어떤 Deep RL 연구자는 이런 현상을 보고 batch data에 대해서 policy가 "overfitting"되었다고 표현할 수 있다. 기술적으로 표현한 것이긴 하지만, 이게 generalization error를 나타내는 것은 아니기 때문에 있는 그대로 받아들여서는 안된다.
 여기서 이점을 부각시키는 이유는 ML을 현업에 적용하는 사람들이 loss function을 학습에 유용한 정보라고 이해하는 오해하는 경우가 많이 때문이다. 바로 "loss가 떨어지면, 모든 것이 좋아진다" 라고 말이다. Policy Gradient에서는 이런 관점은 잘못된 것이며, 반드시 average return에만 신경써야 한다. loss function은 아무 의미가 없는 것이다.
 위의 코드에서는 log_probs tensor를 만들었는데, 이걸 이용해서 action mask를 만들고, 이를 이용해서 특정 log probability를 선택하는데 사용하게 된다. 이런 방식은 categorical policy에서만 효과가 있고, 일반적으로 사용할 수 있는 방법은 아니다.

 3. Running One Epoch of Training

    # for training policy
    def train_one_epoch():
        # make some empty lists for logging.
        batch_obs = []          # for observations
        batch_acts = []         # for actions
        batch_weights = []      # for R(tau) weighting in policy gradient
        batch_rets = []         # for measuring episode returns
        batch_lens = []         # for measuring episode lengths

        # reset episode-specific variables
        obs = env.reset()       # first obs comes from starting distribution
        done = False            # signal from environment that episode is over
        ep_rews = []            # list for rewards accrued throughout ep

        # render first episode of each epoch
        finished_rendering_this_epoch = False

        # collect experience by acting in the environment with current policy
        while True:

            # rendering
            if not(finished_rendering_this_epoch):
                env.render()

            # save obs
            batch_obs.append(obs.copy())

            # act in the environment
            act = sess.run(actions, {obs_ph: obs.reshape(1,-1)})[0]
            obs, rew, done, _ = env.step(act)

            # save action, reward
            batch_acts.append(act)
            ep_rews.append(rew)

            if done:
                # if episode is over, record info about episode
                ep_ret, ep_len = sum(ep_rews), len(ep_rews)
                batch_rets.append(ep_ret)
                batch_lens.append(ep_len)

                # the weight for each logprob(a|s) is R(tau)
                batch_weights += [ep_ret] * ep_len

                # reset episode-specific variables
                obs, done, ep_rews = env.reset(), False, []

                # won't render again this epoch
                finished_rendering_this_epoch = True

                # end experience loop if we have enough of it
                if len(batch_obs) > batch_size:
                    break

        # take a single policy gradient update step
        batch_loss, _ = sess.run([loss, train_op],
                                 feed_dict={
                                    obs_ph: np.array(batch_obs),
                                    act_ph: np.array(batch_acts),
                                    weights_ph: np.array(batch_weights)
                                 })
        return batch_loss, batch_rets, batch_lens

 여기서 train_one_epoch() 함수는 policy gradient를 한 epoch만큼 수행하는데, 여기서 정의할 수 있는 것은

 1. agent가 가장 최근의 policy를 가지고 environment상에서 특정 episode만 action을 취했을 때 얻을 수 있는 experience collection step (while True 구문)
 2. 뒤이어 나오는 policy gradient의 update 과정 (batch_loss 구하는 부분)

 알고리즘은 단순히 train_one_epoch() 만 반복해서 호출하면 된다.

Expected Grad-Log-Prob Lemma

 이 파트에서는 policy gradient 이론을 통해서 광범위하게 활용되고 있는 중간 결과를 도출해보고자 한다. 보통 이걸 Expected Grad-Log-Prob (EGLP) lemma 라고 부르기로 한다.

사실 이 글의 저자는 EGLP라는 단어가 공식적인 용어인지에 대해서는 신경쓰지 않았다. 다만 이렇게 명칭으로 표현하면, 조금더 의미가 있는 거처럼 보일거 같아서 사용했다.

EGLP Lemma

 \(P_{\theta}\)를 random variable \(x\) 상에서의 parameterized probability distribution이라고 가정해보자. 그러면 다음과 같이 관계를 가지게 된다.

$$ E_{x \sim P_{\theta}} [\nabla_{\theta} \log P_{\theta}(x)] = 0 $$

위 식의 증명은 다음과 같다.

우선 모든 probability distribution이 normalized되어 있다는 것을 상기해보자.
$$ \int_{x} P_{\theta}(x) = 1$$
normalization된 것의 양변에 gradient를 취해보자.
$$ \nabla_{\theta} \int_{x} P_{\theta}(x) = \nabla_{\theta} 1 = 0 $$
여기서 앞에서 언급한 log derivative trick을 적용하면
$$ \begin{align} 0 & = \nabla_{\theta} \int_{x} P_{\theta}(x) \\ & = \int_{x} \nabla_{\theta} P_{\theta}(x) \\  & = \int_{x} P_{\theta}(x) \nabla_{\theta} \log P_{\theta} (x) \\ \therefore 0 & = E_{x \sim P_{\theta}}[\nabla_{\theta} \log P_{\theta}(x)] \end{align} $$

Don`t Let the Past Disctract You

 Policy Gradient에 대해서 최근에 다룬 표현식을 살펴보자

$$ \nabla_{\theta} J(\pi_{\theta}) = E_{\tau \sim \pi_{\theta}} \left[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t}) \sum_{t'=t}^{T} R(s_{t'}, a_{t'}, s_{t'+1}) \right]$$

 해당 gradient에 대해서 step을 진행하게 되면, 그동안 얻은 모든 reward의 합인 \(R(\tau))\)에 비례해서 각 action에 대한 log-probability를 높이게 된다. 하지만 이게 그렇게 의미가 있지는 않다.

 Agent는 자신이 얻은 결과에 기반해서만 action을 reinforce해야 한다. action을 취하기 전에 얻은 reward는 그 action이 얼마나 좋은 action이었는지와는 전혀 관계가 없다. 단지 이후에 얻은 reward만 상관이 있을 뿐이다.

 이런 개념을 수학적으로 표현할 수 있는데, 그렇게 되면 policy gradient를 다음과 같이도 표현할 수 있게 된다.

$$ \nabla_{\theta} J(\pi_{\theta}) = E_{\tau \sim \pi_{\theta}} \left[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t}) \sum_{t'=t}^{T} R(s_{t'}, a_{t'}, s_{t'+1}) \right] $$

 이 식에서는 actions이 취한 이후에 얻은 reward에 대해서만 reinforce 되는 것을 확인할 수 있다.

위의 식을 "reward-to-go policy gradient"라고 부르는데, 그 이유는 trajectory상에서 현재 state이후에 얻은 reward의 총합은

$$ \hat{R_{t}} \doteq \sum_{t'=t}^{T} R(s_{t'}, a_{t'}, s_{t'+1}) $$

이 되는데 이를 해당 state에서의 reward-to-go라고 부르고, 위의 policy gradient expression은 state-action pair에서 나온 reward-to-go의 영향을 받는다.

But how is this better? 
policy gradient의 문제는 trajectory에 대한 low-variance sample estimate를 하기 위해서 sample trajectory가 얼마나 필요하냐는 것이다. 처음 전개했던 action을 reinforce하는 항이 들어간 식은 past reward에 비례하는데, 이 past reward는 mean이 0이지만, variance가 0이 아니다. 결론적으로 policy gradient에 대해서 sample estimate를 하는데 단순히 noise만 더해지는 꼴이 되는 것이다. 결국 이를 제거함으로써, 필요한 sample trajectory의 갯수를 줄일 수 있게 된다.

 위에 대한 증명은 다른 부분에서 하게 될텐데, 결국 EGLP lemma에 따라 변하게 되는 것임을 알 수 있다.

Implementing Reward-to-Go Policy Gradient

 spinup/examples/pg_math/2_rtg_pg.py (github) 에 reward-to-go policy gradient를 tensorflow로 구현한 코드를 공유했다.

 1_simple_pg.py 와 다른 부분은 loss function에서 다른 weight을 사용했다는 점이다. 수정된 부분도 많지 않다. 단순히 함수를 추가하고, 두 줄을 변경했다. 새로운 함수는 다음과 같다.

def reward_to_go(rews):
    n = len(rews)
    rtgs = np.zeros_like(rews)
    for i in reversed(range(n)):
        rtgs[i] = rews[i] + (rtgs[i+1] if i+1 < n else 0)
    return rtgs

 그리고 기존의 코드인 이 부분은

                # the weight for each logprob(a|s) is R(tau)
                batch_weights += [ep_ret] * ep_len

다음과 같이 변경했다.

                # the weight for each logprob(a_t|s_t) is reward-to-go from t
                batch_weights += list(reward_to_go(ep_rews))

Baselines in Policy Gradient

 EGLP lemma의 중간 결론은 state \(s_{t}\)에만 영향을 받는 function \(b\)가 있다고 했을 때,

$$ E_{a_{t} \sim \pi_{\theta}} [\nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t}) b(s_{t})] = 0 $$

가 성립한다는 것이다. 이를 통해 expectation 측면에서 공식을 변경할 필요없이, 아래 항과 같이 기존의 policy gradient 식에서 어떤 항을 더하거나 뺄 수 있게 된다.

$$ \nabla_{\theta} J(\pi_{\theta}) = E_{\tau \sim \pi_{\theta}} \left[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t}) \left( \sum_{t'=t}^{T} R(s_{t'}, a_{t'}, s_{t'+1}) - b(s_{t'}) \right) \right] $$

(\( \because \) 양변에 0을 빼도 결론은 똑같기 때문이다. )

 이렇게 사용된 function \(b\)를 baseline이라고 부른다.

 Baseline으로 가장 많이 고려되는 것이 바로 on-policy value function \(V^{\pi}(s_{t})\) 이다. 아마 알겠지만 이 함수는 agent가 state \(s_{t}\)에서 시작하고, policy \(\pi\)에 따라서 action을 취할때 얻는 average return이다. 

 경험적으로 말하자면, \(b(s_{t}) = V^{\pi}(s_{t})\) 라고 정의하는 것은 policy gradient에서 sample estimate를 할때 variance를 줄이는데 있어 눈에 띌만한 효과를 보여준다. 이를 통해 policy learning을 빠르고 안정적으로 할 수 있게 된다. 또한 개념적인 부분도 만족시키게 되는데, agent가 기대한대로 얻게 된다면, 기대한 것에 대해서 중립적으로 "feel"이 되어야 한다는 개념을 반영한다는 것을 나타낸다.

실제 세상에서는 \(V^{\pi}(s_{t})\)를 정확하게 계산할 수 없기 때문에, 이를 approximate해야 한다. 보통 이 작업을 neural network인 \(V_{\phi}(s_{t})\) 로 할 수 있는데, 이 함수는 policy를 함께 update 해주는 역할을 한다. (그래서 value network은 가장 최근에 취한 policy의 value function을 항상 approximate 하는 것이다.)

 \(V_{\phi}\) 를 학습시키는 가장 간단한 방법은, (VPG, TRPO, PPO, A2C에도 포함되어 있는 것과 같이) policy optimization의 구현에도 사용되는 내용인데, 바로 objective에 대한 mean-squared-error를 최소화하는 것이다.
$$ \phi_{k} = arg \min_{\phi} E_{s_{t}, \hat{R_{t}} \sim \pi_{k}} \left[(V_{\phi}(s_{t}) - \hat{R_{t}})^2\right] $$
여기서 \(\pi_{k}\)는 k epoch에서의 policy를 말한다. 이 과정은 이전의 value parameter인 \(\phi_{k-1}\)에서 시작한 gradient descent를 여러번 수행함으로써 구할 수 있다.

 Other Forms of the Policy Gradient

 지금까지 우리가 알아봤던 것은 policy gradient가 다음과 같은 형태를 띠고 있다는 것이다.

$$ \nabla_{\theta} J(\pi_{\theta}) = E_{\tau \sim \pi_{\theta}} \left[ \sum_{t=0}^{T} \nabla_{\theta} \log \pi_{\theta} (a_{t} | s_{t}) \Phi_{t} \right] $$

 여기서 \( \Phi_{t} \)는 다음과 같은 형태를 띌 수 있다.

$$ \Phi_{t} = R(\tau) $$
$$ \Phi_{t} = \sum_{t'=t}^{T} R(s_{t'}, a_{t'}, s_{t'+1}) $$
$$ \Phi_{t} = \sum_{t'=t}^{T} R(s_{t'}, a_{t'}, s_{t'+1}) - b(s_{t}) $$

위의 표현식 모두 policy gradient에 대해서 서로 다른 variance를 갖긴 하지만,같은 expected value를 가진다. 여기에 weights \( \Phi_{t} \)를 표현할 수 있는 방법이 두가지 더 있는데, 알 필요가 있다.

 1. On-Policy Action-Value Function 

 $$ \Phi_{t} = Q^{\pi_{\theta}}(s_{t}, a_{t}) $$

도 사용 가능하다. 여기서 해당 식에 대한 증명을 확인할 수 있다.

 2.  The Advantage Function

 이전 글에서 \( A^{\pi}(s_{t}, a_{t}) = Q^{\pi}(s_{t}, a_{t}) - V^{\pi}(s_{t}) \) 라고 정의된 action에 대한 advantage 에 대해서 다뤘는데, 이 식이 의미하는 것은 현재의 policy가 전체의 평균에서 취한 action에 비해 얼마나 좋은지 나쁜지를 표현한 것이다. 그렇기 때문에 

$$ \Phi_{t} = A^{\pi_{\theta}}(s_{t}, a_{t}) $$

역시 적용할 수 있다. 이는 \( \Phi_{t} = Q^{\pi_{\theta}}(s_{t}, a_{t}) \) 를 사용하고, 추가로 value function baseline을 사용하는 것 자체가 동일한 건데, 이렇게 해도 상관이 없기 때문에 앞의 식을 적용할 수 있는 것이다.

advantage function을 사용한 policy gradient 공식은 매우 많이 사용되며, 다른 알고리즘을 사용해서 advantage function을 estimate하는 방법들도 많이 있다.
 이 주제에 대해서 조금더 다뤄보려면, Generalized Advantage Estimation (GAE) 과 관련된 paper를 읽어봐야 하는데, 여기에는 background section에서 \( \Phi_{t} \) 를 어떻게 선택하느냐를 상세하게 다루고 있다.
 
 그러고 난 후에 GAE에 대해서 소개하는데, 여기에서는 많이 사용되는 policy optimization 알고리즘 내에서 advantage function을 approximate하는 방법을 소개하고 있다. 예를 들어 Spinning Up에서 구현된 VPG나 TRPO,  PPO도 이 방법을 사용했다. 그래서 한번 이걸 공부해보는 것을 강력하게 추천한다.

Recap

 이번 글에서는 policy gradient 에 대한 기초적인 이론을 소개하고 몇몇 결과를 코드와 연결시켜보았다. 관심있는 사람들은 여기서 더 나아가 뒤에서 다룬 결과들(value function baselines와 policy gradient의 advantage formulation)들을 Spinning Up에서 구현한 Vanila Policy Gradient로 변환시켜보는 것을 더 알아보면 좋을거 같다.

댓글