组合使用基于价值和基于策略 -- Actor Critic 学习模式

  |  

摘要: Actor Critic 学习模式

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的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 代码模板。

在文章 Q学习与SARSA 中,我们研究了第三点,主要涉及到 SARSA,以及与 Q 学习的对比。并实现了 SARSA 的 Agent 代码模板。最后给出了 On-policy 和 Off-policy 对 Agent 的影响的对比。

本文我们在上面的第三点的基础上,研究一下组合使用基于价值和基于策略的学习模式 Actor Critic。并实现 Actor Critic 代码模板。


组合使用基于价值和基于策略 — Actor Critic

SARSA 与策略迭代的区别

在文章 MDP的动态规划解法-策略迭代和价值迭代 中,我们学习了策略迭代,并实现了代码模板。

这里简要总结一下策略迭代和 SARSA 的区别。

  • SARSA 将策略和价值近似通过一个 Q 表格(self.Q) 来实施。
  • 策略迭代将策略和价值近似分开实施。

这一区别通过两者的代码对比会更清晰

  • SARSA

将行动的选择(策略)和价值近似通过同一个 self.Q 进行。

1
2
n_action = self.policy(n_state, actions) # On-policy
gain = reward + gamma * self.Q[n_state][n_action]
  • 策略迭代

基于策略计算出的状态价值 V 来进行价值近似。

1
2
policy_action = take_max_action(self.policy[s])
r += prob * (reward + gamma * V[next_state])

Actor Critic 学习模式

Actor Critic 学习模式是基于类似于策略迭代的那种将策略和价值近似分开考虑的思路。

Actor (决策者)负责策略,Critic (评价者)负责价值近似,这种 Actor Critic 交替更新来进行学习的方法称为 Actor Critic 方法。

Actor Critic 代码模板

(1) Actor

Actor 继承自 ELAgent,但是并不使用 Epsilon 贪心。其行动由 self.Q 值决定。

self.Q 用 np.ranodm.uniform 初始化,也就是均等地采取行动(与策略迭代相同)。

softmax 用于将多个值变为概率值的函数。

self.Q[s] 保存了状态 s 下各种行动所对应的价值,softmax 将各个价值转变成行动概率,然后基于行动概率来选择行动。基于概率的选择用 np.random.choice 实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import gym


class Actor(ELAgent):

def __init__(self, env):
super().__init__(epsilon=-1)
nrow = env.observation_space.n
ncol = env.action_space.n
self.actions = list(range(env.action_space.n))
self.Q = np.random.uniform(0, 1, nrow * ncol).reshape((nrow, ncol))

def softmax(self, x):
return np.exp(x) / np.sum(np.exp(x), axis=0)

def policy(self, s):
a = np.random.choice(self.actions, 1,
p=self.softmax(self.Q[s]))
return a[0]

(2) Critic

Critic 仅用 self.V 储存状态价值,并用 0 初始化。

1
2
3
4
5
class Critic():

def __init__(self, env):
states = env.observation_space.n
self.V = np.zeros(states)

(3) Actor Critic 的学习过程

代码中的重点在于: 计算 gain 时使用了 Critic 的评价值。

1
gain = reward + gamma * critic.V[n_state]

所得的 TD 误差(gain - estimated)分别用于 Actor 和 Critic 的更新。其中 Actor 更新各种状态下的行动评价 Q,Critic 更新状态价值 V

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
class ActorCritic():

def __init__(self, actor_class, critic_class):
self.actor_class = actor_class
self.critic_class = critic_class

def train(self, env, episode_count=1000, gamma=0.9,
learning_rate=0.1, render=False, report_interval=50):
actor = self.actor_class(env)
critic = self.critic_class(env)

actor.init_log()
for e in range(episode_count):
s = env.reset()
done = False
while not done:
if render:
env.render()
a = actor.policy(s)
n_state, reward, done, info = env.step(a)

gain = reward + gamma * critic.V[n_state]
estimated = critic.V[s]
td = gain - estimated
actor.Q[s][a] += learning_rate * td
critic.V[s] += learning_rate * td
s = n_state

else:
actor.log(reward)

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

return actor, critic

Actor Critic 的训练和可视化

1
2
3
4
5
6
7
8
9
10
11
def train():
trainer = ActorCritic(Actor, Critic)
env = gym.make("FrozenLakeEasy-v0")
env.render()
actor, critic = trainer.train(env, episode_count=3000)
show_q_value(actor.Q)
actor.show_reward_log()


if __name__ == "__main__":
train()

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

1
2
3
4
SFFF
FHFH
FFFH
HFFG

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

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

Actor Critic 对各状态和行动的评价如下:

reward 曲线如下:


Share