我正在用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
您计算优势的代码似乎是正确的。 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
我认为您的折扣退货或GAE代码没有任何问题。但是,我不明白在打折返回或GAE计算之后是否有必要使用normalize函数。我会建议如下:
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之间。