一、价值迭代的问题
在FrozenLake环境中,交叉熵方法改为价值迭代后,模型收敛速度加快。价值迭代时对所有状态进行循环,并对每个状态用Bellman方程更新价值。该过程中,对于同一方法中Q值(动作价值)变化似乎相同,但要估算并存储每个状态和动作的价值,所以这个过程的问题如下:
1、环境状态的数量及迭代能力
(1)状态和动作迭代加耗CPU;
(2)真正优质的状态转移动态的估计所需要的样本数量。假设一个环境,有十亿个状态(约为31600*31600的FrozenLake),每个状态计算近似价值,需要在状态之间均匀分布数千亿转移。
(3)枚举所有可能状态,但其中99.9%的状态为无用状态。例如Atari平台的状态空间,屏幕分辨率210*160像素,每个像素128种颜色,每一帧210*160=33600个像素,总状态数128^33600,
比10^70802略多。如果枚举Atari的所有可能状态,那么即使是超级计算机也要数十亿亿年。99.9%的时间在做无用功,因为大多数组合即使在很长的游戏过程中都不会出现,因此永远不会有这些状态的样本。
2、限制为离散动作空间
Q(s,a)和V(s)的近似指都假设动作是互斥的离散集,对于动作可以是连续变量(例如方向盘的角度、执行器上的力或者加热器的温度)的连续控制问题并不一定正确。
二、表格Q-learning
不需要遍历状态空间中的所有状态,只关心从环境中获得的状态更新状态价值。对于有明确状态价值映射的情况,具体步骤如下:
(1)从空表开始,将状态映射到动作价值。
(2)通过与环境交互,获得(s,a,r,)(状态、动作、奖励和新状态)。在此步骤中,要确定所需采取的动作,并且没有单一的正确方法来做出此决定,需要应用探索与利用的方法。
(3)使用Bellman近似更新Q(s,a)值:
(4)从步骤2开始重复。
终止条件是更新的某个阈值,或可以执行测试片段以估计策略的预期奖励。
更新Q值,采用学习率平衡新、旧Q值:
三、代码与分析
1、代码
Agent类包含四个方法:
- sample_env在动作空间中随机选取动作
- best_value_and_action接收环境中的状态,并通过表格查找在当前状态下可以获得的最大价值和最大价值对应的动作
- value_update函数进行价值更新
- play_episode使用提供的测试表运行一个片段,每个动作由Q值决定
#!/usr/bin/env python3 import gym import collections from tensorboardX import SummaryWriter ENV_NAME = "FrozenLake-v0" GAMMA = 0.9 ALPHA = 0.2 TEST_EPISODES = 20 class Agent: def __init__(self): """ 初始化环境、状态、价值表 """ self.env = gym.make(ENV_NAME) self.state = self.env.reset() self.values = collections.defaultdict(float) def sample_env(self): """ 在动作空间中随机选取动作 :return:旧状态,所采取动作、所获得奖励、新状态组成的元组 """ action = self.env.action_space.sample() old_state = self.state new_state, reward, is_done, _ = self.env.step(action) self.state = self.env.reset() if is_done else new_state return old_state, action, reward, new_state def best_value_and_action(self, state): """ 接收环境中的状态,并通过表格查找在当前状态下可以获得 最大价值和最大价值对应的动作 :param state: 环境中状态 :return: 表格查找,获取当前状态下可以获得的 最大价值和最大价值对应的动作 """ best_value, best_action = None, None for action in range(self.env.action_space.n): action_value = self.values[(state, action)] if best_value is None or best_value < action_value: best_value = action_value best_action = action return best_value, best_action def value_update(self, s, a, r, next_s): """ 计算状态s和动作a的价值的新近似值 :param s: 当前状态 :param a: 当前动作 :param r: 当前奖励 :param next_s: 下一个状态 :return: 前进一步,更新价值表 """ best_v, _ = self.best_value_and_action(next_s) new_v = r + GAMMA * best_v # Bellman近似 old_v = self.values[(s, a)] self.values[(s, a)] = old_v * (1-ALPHA) + new_v * ALPHA #使用学习率的值混合平均 def play_episode(self, env): """ 使用提供的测试表运行一个片段,每个动作由Q值决定 该方法用于评估当前策略,以检查学习进度 注意此方法不会改变价值表,只是用它查找要采取的最佳动作 :param env:测试环境 :return:总奖励 """ total_reward = 0.0 state = env.reset() while True: _, action = self.best_value_and_action(state) new_state, reward, is_done, _ = env.step(action) total_reward += reward if is_done: break state = new_state return total_reward if __name__ == "__main__": test_env = gym.make(ENV_NAME) agent = Agent() writer = SummaryWriter(comment="-q-learning") iter_no = 0 best_reward = 0.0 while True: iter_no += 1 s, a, r, next_s = agent.sample_env() agent.value_update(s, a, r, next_s) reward = 0.0 for _ in range(TEST_EPISODES): reward += agent.play_episode(test_env) reward /= TEST_EPISODES writer.add_scalar("reward", reward, iter_no) if reward > best_reward: print("Best reward updated %.3f -> %.3f" % ( best_reward, 9)) best_reward = reward if reward > 0.80: print("Solved in %d iterations!" % iter_no) break writer.close()2、结果显示
![]()
四、总结
与价值迭代方法相比,此版本迭代次数更多,原因是不再使用测试中获得的经验数据,在测试过程中不触及Q值表更新。