我正在以GAE为优势实施PPO。以下代码是我计算GAE并根据OpenAI的基线实现返回的方式。
advantages = np.zeros_like(rewards)
last_adv = 0
for i in reversed(range(len(rewards))):
delta = rewards[i] + nonterminals[i] * self._gamma * values[i+1] - values[i]
advantages[i] = last_adv = delta + nonterminals[i] * gamma * lam * last_adv
returns = advantages + values[:-1]
advantages = normalize(advantages) # normalize advantages
值得注意的是,values
比其他数组(如rewards
)多一个元素,因此values[-1]
可以用作额外的下一个状态。但是,这种实现方式的效果远不如下面简单给出的归一化回报和优势
returns = rewards
next_return = 0
for i in reversed(range(len(rewards))):
returns[i] = rewards[i] + nonterminals[i] * gamma * next_return
next_return = returns[i]
# normalize returns and advantages
values = normalize(values[:-1], np.mean(returns), np.std(returns))
advantages = normalize(returns - values)
returns = normalize(returns)
在不进行任何其他更改的情况下,以上实现在270+
环境gym
中不断获得关于LunarLanderContinuous-v2
的平均得分。另一方面,GAE实施从未获得超过100
的分数。参见下图的示例,其中更好的示例通过规范化的实现运行
我的实现有什么问题?
此外,这是normalize
def normalize(x, mean=0., std=1., epsilon=1e-8):
x = (x - np.mean(x)) / (np.std(x) + epsilon)
x = x * std + mean
return x
答案 0 :(得分:0)
您的计算优势代码似乎是正确的。 normalize
是做什么的?通常,您标准化数据,这意味着您要减去其均值并除以其标准差。我问是因为在代码的第二部分中,您将平均值和返回值的标准偏差传递给了函数normalize
,而在第一部分中,您没有这样做。
此外,为什么还要在代码的第二部分中使用values
来规范returns
?对我来说似乎很奇怪。
最后,您如何训练V功能? (我假设values
包含V值)。我发现学习它的方法如下
for epoch in range(epochs_v):
v_values = ... # compute your values using V
a_values = ... # compute A as in your code
target_values = v_values + a_values # generalized Bellman operator
# optimize V on your dataset with minibatches and ADAM
比“一次性适合”效果更好
v_values = ... # compute your values using V
a_values = ... # compute A as in your code
target_values = v_values + a_values # generalized Bellman operator
# fit V to target_values
答案 1 :(得分:0)
我没有发现您的退货折扣或GAE代码有任何问题。但是,我不知道您是否有必要在折现收益或GAE计算后使用规范化函数。我将提出以下建议:
1)您可以在计算折现收益或GAE之前直接尝试对rewards
进行标准化。
2)您可以使用OpenAI基线中的类running_mean_std进行方差计算,在该方差中可以标准化奖励,然后在将处理后的奖励传递给折现收益或回报之前,通过在一定间隔内对它们进行裁剪来对它们进行归一化。 GAE功能。
示例:
假设running_stats_r
是类RunningMeanStd
的对象。
running_stats_r.update(np.array(buffer_r))
buffer_r = np.clip( (np.array(buffer_r) - running_stats_r.mean) / running_stats_r.std, -stats_CLIP, stats_CLIP )
[-stats_CLIP, stats_CLIP]
可以是一个间隔,例如介于-10到10之间。