经验用于更新价值还是更新策略 -- Q学习与SARSA

  |  

摘要: Q 学习与 SARSA

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


在文章 强化学习环境框架-从一个迷宫环境看MDP的要点 中,我们总结了强化学习的基本概念,以及作为强化学习机制的 MDP,并且以 grid 为例实现了 MDP 环境的代码框架。

有了 MDP 环境之后,我们就要解决如何在其中选择最优行动的问题,也就是 MDP 环境的代码框架中的 agent.policy 方法的实现,具体来说就是价值的计算方法(价值近似)基于价值近似的行动选择方法(策略)

当 MDP 环境的迁移函数 transit_func 和奖励函数 reward_func 已知的时候,就可以使用动态规划法来求解。这种基于迁移函数和奖励函数来学习行动的方法称为基于模型的学习方法。在文章 MDP的动态规划解法-策略迭代和价值迭代 中,我们就是从策略迭代和价值迭代两种思路实践的这种动态规划方法。

当不知道环境信息(迁移函数和奖励函数)的情况下,也可以使用这种基于模型的学习方法对这两种函数进行推算,后面我们有机会再探究这种用法。

此外对于环境信息(迁移函数和奖励函数)未知的情况,还有另一种学习方法:无模型的方法。无模型方法通过自身采取行动来积累经验,并根据经验进行学习的方法。

无模型的学习方法中,通过行动积累经验的过程中有以下 3 个要点

  1. 平衡经验的积累与应用
  2. 根据实际奖励来修正计划,还是根据预测来修正计划
  3. 用经验来更新价值近似还是更新策略

在文章 经验的积累与利用-Epsilon贪心 中,我们研究过第 1 个问题,主要内容是 Epsilon 贪心算法,并以多臂老虎机为例进行实践。

在文章 时序差分与蒙塔卡洛方法 中,我们研究了第 2 点,主要涉及到蒙特卡洛方法(根据实际奖励修正)和时序差分学习(根据预测来修正)。并通过 Multi-step learning 方法将蒙特卡洛方法和时序差分统一到一起。最后我们分别实现了蒙特卡洛方法的 Agent 以及基于 TD 方法的 Q 学习的 Agent 代码模板。

本文我们研究第三点,也就是使用经验更新价值和更新策略


基于价值与基于策略

不管是基于价值还是基于策略,都是根据经验(TD误差)进行学习。

基于价值和基于策略的不同在于选择行动的基准

基于价值的方法: 选择使价值最大化的动作进行状态迁移。不使用策略,称为 Off-policy

基于策略的方法: 根据策略来选择行动。以使用策略为前提,称为 On-policy

Q 学习与 SARSA 学习

Q 学习: 更新对象是价值近似,选择行动的基准是 Off-policy,细节和 QLearningAgent 参考文章 时序差分与蒙塔卡洛方法

对应地还有 SARSA 学习(State-Action-Reward-State-Action):以策略作为更新对象,基准为 On-policy。下面我们

SARSA Agent 代码模板

QLearningAgent 代码模板可以看文章 时序差分与蒙塔卡洛方法

这里我们给出 SARSAAgent 的代码模板,也是从 ELAgent 继承来,环境还是用 gym 的 FrozenLake-v0 环境类。代码和运行结果可以对比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from collections import defaultdict
import gym


class SARSAAgent(ELAgent):

def __init__(self, epsilon=0.1):
super().__init__(epsilon)

def learn(self, env, episode_count=1000, gamma=0.9,
learning_rate=0.1, render=False, report_interval=50):
self.init_log()
actions = list(range(env.action_space.n))
self.Q = defaultdict(lambda: [0] * len(actions))
for e in range(episode_count):
s = env.reset()
done = False
a = self.policy(s, actions)
while not done:
if render:
env.render()
n_state, reward, done, info = env.step(a)

n_action = self.policy(n_state, actions) # On-policy
gain = reward + gamma * self.Q[n_state][n_action]
estimated = self.Q[s][a]
self.Q[s][a] += learning_rate * (gain - estimated)
s = n_state
a = n_action
else:
self.log(reward)

if e != 0 and e % report_interval == 0:
self.show_reward_log(episode=e)

在代码中,SARSA 学习与 Q 学习的区别主要体现在 gain

  • Q 学习
1
gain = reward + gamma * max(self.Q[n_state])
  • SARSA
1
gain = reward + gamma * self.Q[n_state][n_action]

Q 学习采取的是迁移到使价值最大化的状态的行动: max(self.Q[n_state]),也就是 Off-policy。

SARSA 的行动基于策略决定 n_action = self.policy(n_state, action),也就是 On-policy。self.policy 使用 self.Q 来选择行动,所以 self.Q 的更新与 self.policy 的更新有关。

SARSA Agent 的训练和可视化

1
2
3
4
5
6
7
8
9
10
11
def train():
agent = SARSAAgent()
env = gym.make("FrozenLakeEasy-v0")
env.render()
agent.learn(env, episode_count=500)
show_q_value(agent.Q)
agent.show_reward_log()


if __name__ == "__main__":
train()

初始化的迷宫如下,由 env.render() 显示:

1
2
3
4
SFFF
FHFH
FFFH
HFFG

S 是起点、G 是终点、H 是陷阱。

  • 训练过程打印的日志如下:

  • SARSA 方法对各状态和行动的评价如下:

  • reward 曲线如下:

Off-policy 和 On-policy 对智能体影响的对比和可视化

Off-policy 以最好的行动为前提,因此比较乐观;On-policy 基于当权策略,因此会比较现实。

下面我们将乐观与现实的差异可视化,为了看的更清晰一点,这里以风险较高的设定进行试验,也就是提高 epsilon

由于提高了 epsilon,随机行动更容易发生,因此掉入陷阱的代价会比较大(在掉入陷阱的情况下给予负值作为惩罚),参考后面的 show_q_value 画出的热力图中的红色部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from multiprocessing import Pool
from collections import defaultdict
import gym


class CompareAgent(ELAgent):

def __init__(self, q_learning=True, epsilon=0.33):
self.q_learning = q_learning
super().__init__(epsilon)

def learn(self, env, episode_count=1000, gamma=0.9,
learning_rate=0.1, render=False, report_interval=50):
self.init_log()
self.Q = defaultdict(lambda: [0] * len(actions))
actions = list(range(env.action_space.n))
for e in range(episode_count):
s = env.reset()
done = False
a = self.policy(s, actions)
while not done:
if render:
env.render()

n_state, reward, done, info = env.step(a)

if done and reward == 0:
reward = -0.5 # 惩罚形式的奖励

n_action = self.policy(n_state, actions)

if self.q_learning:
gain = reward + gamma * max(self.Q[n_state])
else:
gain = reward + gamma * self.Q[n_state][n_action]

estimated = self.Q[s][a]
self.Q[s][a] += learning_rate * (gain - estimated)
s = n_state

if self.q_learning:
a = self.policy(s, actions)
else:
a = n_action
else:
self.log(reward)

if e != 0 and e % report_interval == 0:
self.show_reward_log(episode=e)

下面分别对 Q 学习和 SARSA 对比,也就是 q_learning=Trueq_learning=False

1
2
3
4
5
6
7
8
9
10
11
12
13
def train(q_learning):
env = gym.make("FrozenLakeEasy-v0")
env.render()
agent = CompareAgent(q_learning=q_learning)
agent.learn(env, episode_count=3000)
return dict(agent.Q)


if __name__ == "__main__":
with Pool() as pool:
results = pool.map(train, ([True, False]))
for r in results:
show_q_value(r)

迷宫初始化如下

1
2
3
4
SFFF
FHFH
FFFH
HFFG

Q 学习与 SARSA 的风险评价如下

  • Q 学习

  • SARSA

可以看出与 SARSA 相比,Q 学习的评价更高。因为 Q 学习以最佳行动为前提,不去设想容易掉入陷阱的行动;SARSA 基于策略决定行动,将由于随机行走掉入陷阱的可能性(较高的 epsilon 值)也考虑在内。


Share