文章目录
在本节中,我们将讨论政策优化算法的数学基础, 我们将介绍政策梯度理论中的三个关键结果:
- 关于策略参数的最简单的等式描述策略表现的梯度,
- 一条规则,允许我们从该表达式中删除无用的术语,
- 以及允许我们在该表达式中添加有用术语的规则。
最后,我们将这些结果结合在一起,并描述策略梯度的基于优势的表达式,这是我们在Vanilla Policy Gradient实现中使用的版本。
Deriving the Simplest Policy Gradient
在这里,我们考虑一种随机的,参数化的策略,. 我们的目标是最大化期望收益 。 出于此推导的目的,我们将使用来给出有限步长的无折扣收益,此外无限步长的折现收益设置的推导几乎是相同的。
我们想通过梯度上升来优化策略,例如:
策略的梯度称为策略梯度,以这种方式优化策略的算法称为策略梯度算法。 (示例包括Vanilla Policy Gradient和TRPO。PPO通常被称为策略梯度算法,尽管这有点不准确。)
要实际使用此算法,我们需要一个可以通过数值计算的策略梯度表达式。 这涉及两个步骤:1)得出策略表现的分析梯度,结果证明是期望值的形式,然后2)构建该期望值的样本估计值,可以使用代理与环境相互作用有限数量的数据来计算 。
在本小节中,我们将找到该表达式的最简单形式。 在后面的小节中,我们将展示如何以最简单的形式进行改进,以获取我们在标准策略梯度实现中实际使用的版本。
我们将从列出一些对得出分析梯度有用的事实开始。
1.轨迹的概率 (Probability of a Trajectory)
给定动作来自的轨迹的概率为:
2.对数导数技巧
对数导数技巧是基于微积分的简单规则:相对于x的导数为1 ⁄ x。 重新排列并与链式规则结合后,我们得到:
3.轨迹的对数概率
轨迹的对数概率就是:
4.环境函数的梯度
环境不依赖于,因此和的梯度为零。
5.轨迹的梯度对数概率
因此,轨迹的对数概率的梯度为
综上所述,我们得出以下结论:
这是一个期望,这意味着我们可以使用样本均值对其进行估计。 如果我们收集一组轨迹:`,其中通过使代理在环境中作用获得每个轨迹 ,策略梯度可以使用以下作为估计:
其中 是 中轨迹的个数(here, ).
最后一个表达式是我们想要的可计算表达式的最简单版本。 假设我们以可计算出 的方式表示我们的策略,并且如果是在能够收集轨迹数据集的环境中,我们可以计算策略梯度并采取更新步骤。
Implementing the Simplest Policy Gradient
我们在spinup / examples / pg_math / 1_simple_pg.py中给出了这个简单版本的策略梯度算法的简短Tensorflow实现。 (也可以在github https://github.com/openai/spinningup/blob/master/spinup/examples/pg_math/1_simple_pg.py __上查看)。它只有122行,因此我们高度重视 建议您深入阅读。 尽管我们不会在此处介绍全部代码,但我们将重点介绍一些重要的部分。
1.建立策略网络
# 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)
此块构建了前馈神经网络分类策略。 (有关更新,请参阅第1部分中的随机策略部分。)logits张量可用于构造对数概率和操作的概率,而action张量基于logit隐含的概率对操作进行采样。
2.构建损失函数
# 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)
在此块中,我们为策略梯度算法构建“损失”函数。 当插入合适的数据时,策略梯度等于这个损失的梯度。 合适的数据表示根据当前策略执行操作时收集的一组(状态,动作,权重)元组,其中状态-动作对的权重是从其所属回合返回的。 (尽管我们将在后面的小节中显示,但是您可以插入其他值来也可以正常工作的重量。)
即使我们将其描述为损失函数,但从监督学习的角度来看,它并不是典型的损失函数。与标准损失函数有两个主要区别。
1.数据分布取决于参数。损失函数通常在固定的数据分布上定义,该分布与我们要优化的参数无关。这里不是,必须在最新策略上对数据进行采样。
2.它无法衡量效果。损失函数通常会评估我们关注的性能指标。在这里,我们关心期望收益,但即使在期望中,我们的“损失”函数也根本不近似。此“损失”功能仅对我们有用,因为当在当前参数下进行评估时,使用当前参数生成的数据时,其性能会呈现负梯度。
但是,在梯度下降的第一步之后,就不再与性能相关。这意味着,对于给定的一批数据,最小化此“损失”功能无法保证提高预期收益。可以将这一损失放到,而策略表现能可能会下降;实际上,通常会这样。有时,资深RL研究人员可能会将此结果描述为对大量数据“过度拟合”的策略。这是描述性的,但不应从字面上理解,因为它没有涉及泛化错误。
我们提出这一点是因为,ML练习者通常会在训练过程中将损失函数解释为有用的信号-“如果损失减少了,一切都会好起来的。”在政策梯度中,这种直觉是错误的,您应该只在乎平均回报率。损失函数没有任何意义。
此处用于制作log_probs张量的方法(创建操作掩码,并使用它来选择特定的对数概率)仅适用于分类策略。 通常它不起作用。
3.运行训练的一个Epoch
# 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()函数运行策略梯度的一个“epoch”,我们定义为
- 1.经验收集步骤(L62-97),其中代理使用最新策略在环境中互动一定数量的回合,其后是
- 2.一个策略梯度的更新 (L99-105).
Expected Grad-Log-Prob Lemma
在本小节中,我们将得出一个中间结果,该结果在整个政策梯度理论中得到了广泛使用。 我们将其称为“梯度对数概率期望(EGLP)”引理 [1]。
EGLP引理。 假设是随机变量x上的参数化概率分布。 然后:
回想一下,所有概率分布都是“归一化”的:
取两侧的梯度:
使用对数派生技巧可以获取:
[1] 本文的作者没有意识到在文献中的任何地方都给该引理指定了标准名称。 但是考虑到它出现的频率,似乎很值得给它起一个名字以便于参考。
Don’t Let the Past Distract You
到现在的策略梯度的表达变成了:
在这个梯度上迈出一步,将每个动作的对数概率与(曾经获得的所有奖励之和)成比例。 但这没有多大意义。
代理实际上仅应根据其“后果”加强行动。 采取行动之前获得的奖励与行动的好坏没有关系:仅在“之后”获得奖励。
事实证明,这种直觉体现在数学上,我们可以证明策略梯度也可以表示为
以这种形式,仅基于采取行动后获得的奖励来加强行动。
我们称这种形式为“未来奖励策略梯度”,因为轨迹上某点之后的奖励总和,
这就是从 t 时刻起的“未来奖励”,而这种策略梯度表述取决于状态动作对的“未来奖励”。
但是,这会更好吗? 策略梯度的关键问题是需要多少个样本轨迹才能获得它们的低方差样本估计。 我们从公式开始就包括了与过去的报酬成比例的加强行动的条件,所有这些均值均值为零,但方差不为零:结果,它们只会给策略梯度的样本估计值增加噪音。 通过删除它们,我们减少了所需的样本轨迹数量。
An (optional) proof of this claim can be found here, and it ultimately depends on the EGLP lemma.
Implementing Reward-to-Go Policy Gradient
We give a short Tensorflow implementation of the reward-to-go policy gradient in spinup/examples/pg_math/2_rtg_pg.py
. (It can also be viewed on github <https://github.com/openai/spinningup/blob/master/spinup/examples/pg_math/2_rtg_pg.py>
_.)
The only thing that has changed from 1_simple_pg.py
is that we now use different weights in the loss function. The code modification is very slight: we add a new function, and change two other lines. The new function is:
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
然后我们从以下方法调整旧的L86-87,从:
# 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 Gradients
EGLP引理的直接后果是,对于仅依赖状态的任何函数:
这使我们能够从我们的策略梯度表达式中添加或减去任何数量的这样的术语,而在期望中无需更改它:
Any function b used in this way is called a baseline.
基准的最常见选择是策略价值函数. 这是从状态开始,然后遵循策略的代理所得到的平均回报。
根据经验,选择具有减少策略梯度样本估计中方差的理想效果。这样可以更快,更稳定地学习策略。 从概念的角度来看,它也很吸引人:它编码了一种直觉,即如果一个代理达到了预期,它将“感觉”到中立。
实际上,不能够准确计算,所以它需要被近似。这通常用一个神经网络来近似,,它可以与策略同时被更新(这样值网络总是近似最近策略的值函数)。
学习的最简单的方法, 被用在在较多数策略优化算法中(包括VPG,TRPO,PPO,A2C),是去最小化均方误差项:
其中是在第k个epoch的策略。这是从前一个值函数的参数,通过梯度下降的一个或多个步骤完成的.
Other Forms of the Policy Gradient
到目前为止,我们看到的是策略梯度具有一般形式:
其中可以任意的这些形式:
or:
or:
尽管有不同的差异,所有这些选择都会导致相同的策略梯度期望值。 事实证明,有两个权重 很重要:
-
1.On-Policy Action-Value Function.
-
2. The Advantage Function.
回忆动作的优势函数, 定义为 , (对于当前的策略)描述平均地相对于其他动作而言好还是坏。 这个选择:也是有效的, 证据是它相当于使用然后再使用一个值函数的基准。
具有优势函数的策略梯度的制定极为普遍,并且有许多不同的方法来估算不同算法所使用的优势函数。
要对此主题进行更详细的处理,您应该阅读有关广义优势估计(GAE)的文章,该文章深入探讨了背景部分中的不同选择。
然后,该论文继续描述GAE,GAE是一种在策略优化算法中具有广泛用途的近似优势函数的方法。 例如,Spinning Up的VPG,TRPO和PPO的实现都利用了它。 因此,我们强烈建议您进行研究。