ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2.4 FrozenLake使用cross-entropy方法

2022-01-12 10:29:59  阅读:251  来源: 互联网

标签:episode observation cross batch 智能 entropy FrozenLake reward


FrozenLake是gym的另一个grid world环境。其环境简单的栅格地图,有四种栅格状态,分别用字母SFHG表示,下面是一个地图的例子:

SFFF (S: starting point, safe)
FHFH (F: frozen surface, safe)
FFFH (H: hole, fall to your doom)
HFFG (G: goal, where the frisbee is located)

智能体的环境是一个大小为4×4的栅格地图,可以向四个方向移动:上、下、左、右。智能体总是从左上位置开始,它的目标是到达网格的右下单元格。地图中有些特定位置是洞(Hole),如果智能体进入这些洞,则当前episode就结束了,且奖励为零。如果智能体到达目标栅格(Goal),那么它将获得的奖励为1.0,此时episode结束。

除此之外,FrozenLake环境还有很多不确定性因素,正如其名,智能体要在冰面上移动,移动时会打滑,所以智能体执行action后,其结果并不一定如action一样:它有33%的几率将滑到action的右边或左边。例如,如果智能体向左移动,则会有33%的概率实际是向上或向下移动,当然也会有33%的可能性就是向左移动。这种不确定性给训练带来了麻烦。

我们先看看FrozenLake的基本属性:

In [1]: import gym

In [2]: e = gym.make('FrozenLake-v1')

In [3]: e.observation_space
Out[3]: Discrete(16)

In [4]: e.action_space
Out[4]: Discrete(4)

In [5]: e.reset()
Out[5]: 0

In [6]: e.render()

SFFF
FHFH
FFFH
HFFG

可以发现,观测空间是离散的从0到15的数字,显然,这个数字代表智能体在网格中的位置。动作空间也是离散的从0到3的数字。根据之前控制CartPole的经验,我们也可以使用cross-entropy方法控制FrozenLake环境,只需稍微修改一下代码。最主要的改动是神经网络的输入,CartPole中神经网络的输入是一个状态向量,而FrozenLake的状态是一个从0到16的整数,但是在这个问题中最好不要直接将状态输入神经网络,而应该使用one-hot编码的状态(为什么要用one-hot编码,互联网上已有很多相关讨论)。为了尽可能减少代码的改动,我们使用gym中的ObservationWrapper类实现one-hot编码类。

class DiscreteOneHotWrapper(gym.ObservationWrapper):
    def __init__(self, env: gym.Env) -> None:
        super(DiscreteOneHotWrapper, self).__init__(env)
        assert isinstance(env.observation_space, gym.spaces.Discrete)
        shape = (env.observation_space.n, )
        self.observation_space = spaces.Box(0.0, 1.0, shape=shape, dtype=np.float32)

    def observation(self, observation):
        obs = np.copy(self.observation_space.low)
        obs[observation] = 1.0
        return obs

除此之外还要在main函数中用DiscreteOneHotWrapper类对环境进行处理:

env = DiscreteOneHotWrapper(gym.make('FrozenLake-v1'))

程序运行后在tensorboard中监控训练过程
在这里插入图片描述
我们发现不论训练了多少batch,虽然loss下降了,但是智能体的reward_bound总是0,也就是说智能体啥都没学到。为什么cross-entropy算法在CartPole环境中表现良好,但在FrozenLake环境中表现很差呢。

我们需要更深入地研究这两种环境的奖励机制。

在CartPole中,每过一个timestep,智能体都会得到1.0的奖励,直到倒立摆落下的那一刻。所以,智能体平衡倒立摆的时间越长,得到的奖励就越多。由于智能体行为的随机性,不同的episode长度也不同,而不同的奖励服从正态分布。在选择奖励边界后,我们“淘汰”了表现不好的episode,并通过对“优秀”的episode进行训练获得更好的表现。就像下图一样
在这里插入图片描述
而在FrozenLake的奖励机制是不同的。只有当我们达到目标(Goal)时,我们才会获得1.0的奖励,而这种奖励并不能说明episode有多棒。它是快速有效的还是瞎猫碰上死耗子一样走到Goal的?我们不知道。我们只知道他成功了。除此之外,episode的奖励分配也存在问题。只有两种奖励:0(失败)或1(成功)。显然,刚开始的时候“失败”占主导地位,所以,我们只考虑“优秀”的episode是不对的(注意在这个环境中“优秀”==“成功到达Goal”,显然智能体很难在随机的policy下到达终点)。这就是训练失败的原因。
在这里插入图片描述
这个例子说明了交叉熵方法的局限性

  1. 每个episode必须是有限的,越短越好
  2. 每个episode的奖励应该有足够的区分度,以区分“优秀”和“差劲”的episode
  3. 智能体要么到达终点,要么正在前往终点,而不会在半路上嗝屁(比如半路上掉到洞里)

其实有很多种算法可以用来解决这些限制,但是现在我们专注于解决cross-entropy方法遇到的问题。我们可以做如下调整:

  1. 更大的batchsize: 在CartPole中,每个batch有16个episode就足够了,但FrozenLake至少需要100个以上episode才能获得一些成功的episode。
  2. 对reward施加discount factor:为了让episode的总奖励和它的长度呈正相关,并使reward多样化(而不是只有0和1),我们可以使 γ = 0.99 \gamma=0.99 γ=0.99或 γ = 0.9 \gamma=0.9 γ=0.9。增加了奖励分配的可变性,这有助于避免两极分化严重。
  3. 让“优秀”的episode多训练几次:在“CartPole”中,我们选取较好的episode进行训练,然后把它们扔掉,继续之后的随机采样。但是在FrozenLake中,优秀的episode是稀客,所以我们需要将它们保留下来,多训练几次。
  4. 降低学习率:这个没啥可说的,深度学习基本调参技巧。
  5. 延长训练时间:对于深度学习来说这也是基本技巧,但是针对这个问题本身是有实际意义的,由于好的episode太罕见了,为了获得更多的训练样本就需要采样更多的episode(由此可见训练效率之低下)。

对于代码的调整,我们需要添加一个函数计算折扣奖励并保存历史记录中优秀的episode:

def batch_filter(batch, percentile):
    reward_list = list(map(lambda episode: episode.rewards, batch))
    reward_bound = np.percentile(reward_list, percentile)
    reward_mean = np.mean(reward_list)

    train_observation = []
    train_action = []

    for rewards, steps in batch:
        if rewards < reward_bound:
            continue
        train_observation.extend(map(lambda step: step.observation, steps))
        train_action.extend(map(lambda step: step.action, steps))

    return torch.tensor(train_observation), torch.tensor(train_action), reward_bound, reward_mean

在训练时,保存之前优秀的episode,把它传递给下一次迭代:

    for iter_no, batch in enumerate(batch_iterator(env, net, BATCH_SIZE)):
        reward_mean = np.mean(list(map(lambda step: step.rewards, batch)))
        full_batch, observation, action, reward_bound = batch_filter_with_gamma(full_batch + batch, PERCENTILE, GAMMA)
        if not full_batch:
            continue
        full_batch = full_batch[-500:]

再就是之前提到的降低学习率和增大batch_size。在tensorboard中监控训练过程:

经过一段时间的耐心等待,可以看到,模型的训练在成功率在55%左右停止了改进。有一些深度学习的技巧可以解决这个问题(例如,正则化),我们后面会讨论这些技术。

FrozenLake还有一个没有随机滑动的版本,也就是说智能体执行什么action就会产生相应的结果。只需要更改相应参数即可:

env = gym.make('FrozenLake-v1', is_slippery=True)

这时候训练速度会大大提高,没有了环境随机性的影响,智能体的表现也变得很好了。
在这里插入图片描述

标签:episode,observation,cross,batch,智能,entropy,FrozenLake,reward
来源: https://blog.csdn.net/cxwwyc/article/details/122425763

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有