Actor Critic

本文转载自莫烦-强化学习-Actor Critic
今天我们会来说说强化学习中的一种结合体 Actor Critic (演员评判家), 它合并以值为基础 (比如 Q learning) 和 以动作概率为基础 (比如 Policy Gradients) 两类强化学习算法.

为什么要有Actor和Critic?

我们有了像 Q-learning 这么伟大的算法, 为什么还要瞎折腾出一个 Actor-Critic? 原来 Actor-Critic 的 Actor 的前生是 Policy Gradients, 这能让它毫不费力地在连续动作中选取合适的动作, 而 Q-learning 做这件事会瘫痪. 那为什么不直接用 Policy Gradients 呢? 原来 Actor Critic 中的 Critic 的前生是 Q-learning 或者其他的 以值为基础的学习法 , 能进行单步更新, 而传统的 Policy Gradients 则是回合更新, 这降低了学习效率.
在这里插入图片描述

Actor和Critic的分工

在这里插入图片描述
现在我们有两套不同的体系, Actor 和 Critic, 他们都能用不同的神经网络来代替 . 在 Policy Gradients 的影片中提到过, 现实中的奖惩会左右 Actor 的更新情况. Policy Gradients 也是靠着这个来获取适宜的更新. 那么何时会有奖惩这种信息能不能被学习呢? 这看起来不就是 以值为基础的强化学习方法做过的事吗. 那我们就拿一个 Critic 去学习这些奖惩机制, 学习完了以后. 由 Actor 来指手画脚, 由 Critic 来告诉 Actor 你的那些指手画脚哪些指得好, 哪些指得差, Critic 通过学习环境和奖励之间的关系, 能看到现在所处状态的潜在奖励, 所以用它来指点 Actor 便能使 Actor 每一步都在更新, 如果使用单纯的 Policy Gradients, Actor 只能等到回合结束才能开始更新.
在这里插入图片描述
但是事物终有它坏的一面, Actor-Critic 涉及到了两个神经网络, 而且每次都是在连续状态中更新参数, 每次参数更新前后都存在相关性, 导致神经网络只能片面的看待问题, 甚至导致神经网络学不到东西. Google DeepMind 为了解决这个问题, 修改了 Actor Critic 的算法.

改进版 Deep Deterministic Policy Gradient (DDPG)

将之前在电动游戏 Atari 上获得成功的 DQN 网络加入进 Actor Critic 系统中, 这种新算法叫做 Deep Deterministic Policy Gradient, 成功的解决的在连续动作预测上的学不到东西问题. 所以之后, 我们再来说说什么是这种高级版本的 Deep Deterministic Policy Gradient 吧.

一句话概括 Actor Critic 方法:
结合了 Policy Gradient (Actor) 和 Function Approximation (Critic) 的方法. Actor 基于概率选行为, Critic 基于 Actor 的行为评判行为的得分, Actor 根据 Critic 的评分修改选行为的概率.

Actor Critic 方法的优势:
可以进行单步更新, 比传统的 Policy Gradient 要快.

Actor Critic 方法的劣势:
取决于 Critic 的价值判断, 但是 Critic 难收敛, 再加上 Actor 的更新, 就更难收敛. 为了解决收敛问题, Google Deepmind 提出了 Actor Critic 升级版 Deep Deterministic Policy Gradient. 后者融合了 DQN 的优势, 解决了收敛难的问题. 我们之后也会要讲到 Deep Deterministic Policy Gradient. 不过那个是要以 Actor Critic 为基础, 懂了 Actor Critic, 后面那个就好懂了.

算法

这套算法是在普通的 Policy gradient 算法上面修改的, 对这套算法打个比方如下:
Actor 修改行为时就像蒙着眼睛一直向前开车, Critic 就是那个扶方向盘改变 Actor 开车方向的.
在这里插入图片描述
或者说详细点, 就是 Actor 在运用 Policy Gradient 的方法进行 Gradient ascent 的时候, 由 Critic 来告诉他, 这次的 Gradient ascent 是不是一次正确的 ascent, 如果这次的得分不好, 那么就不要 ascent 那么多.

代码

下面是Actor神经网络的结构
在这里插入图片描述
接下来是Actor的代码结构:

class Actor(object):
def __init__(self, sess, n_features, n_actions, lr=0.001):
# 用 tensorflow 建立 Actor 神经网络,
# 搭建好训练的 Graph.

def learn(self, s, a, td):
# s, a 用于产生 Gradient ascent 的方向,
# td 来自 Critic, 用于告诉 Actor 这方向对不对.

def choose_action(self, s):
# 根据 s 选 行为 a

下面是Critic神经网络的结构:
在这里插入图片描述
下面是Critic的代码结构:

class Critic(object):
def __init__(self, sess, n_features, lr=0.01):
# 用 tensorflow 建立 Critic 神经网络,
# 搭建好训练的 Graph.

def learn(self, s, r, s_):
# 学习 状态的价值 (state value), 不是行为的价值 (action value),
# 计算 TD_error = (r + v_) - v,
# 用 TD_error 评判这一步的行为有没有带来比平时更好的结果,
# 可以把它看做 Advantage
return # 学习时产生的 TD_error

Actor和Critic的学习方式

Actor 想要最大化期望的 reward, 在 Actor Critic 算法中, 我们用 “比平时好多少” (TD error) 来当做 reward, 所以就是:

with tf.variable_scope('exp_v'):
log_prob = tf.log(self.acts_prob[0, self.a]) # log 动作概率
self.exp_v = tf.reduce_mean(log_prob * self.td_error) # log 概率 * TD 方向
with tf.variable_scope('train'):
# 因为我们想不断增加这个 exp_v (动作带来的额外价值),
# 所以我们用过 minimize(-exp_v) 的方式达到
# maximize(exp_v) 的目的
self.train_op = tf.train.AdamOptimizer(lr).minimize(-self.exp_v)

Critic 的更新很简单, 就是像 Q learning 那样更新现实和估计的误差 (TD error) 就好了.

with tf.variable_scope('squared_TD_error'):
self.td_error = self.r + GAMMA * self.v_ - self.v
self.loss = tf.square(self.td_error) # TD_error = (r+gamma*V_next) - V_eval
with tf.variable_scope('train'):
self.train_op = tf.train.AdamOptimizer(lr).minimize(self.loss)

每回合算法

for i_episode in range(MAX_EPISODE):
s = env.reset()
t = 0
track_r = [] # 每回合的所有奖励
while True:
if RENDER: env.render()

a = actor.choose_action(s)

s_, r, done, info = env.step(a)

if done: r = -20 # 回合结束的惩罚

track_r.append(r)

td_error = critic.learn(s, r, s_) # Critic 学习 gradient=grad[r+gamma*V(s_)-V(s)]
actor.learn(s, a, td_error) # Actor 学习 true_gradient=grad[logP(s,a)*td_error]

s = s_
t += 1

if done or t >= MAX_EP_STEPS:
# 回合结束, 打印回合累积奖励
ep_rs_sum = sum(track_r)
if 'running_reward' not in globals():
running_reward = ep_rs_sum
else:
running_reward = running_reward * 0.95 + ep_rs_sum * 0.05
if running_reward > DISPLAY_REWARD_THRESHOLD: RENDER = True # rendering
print("episode:", i_episode, " reward:", int(running_reward))
break

Shiroha