强化学习 12 —— Actor-Critic 算法介绍与 Tensorflow 2.0 实现

一、Actor-Critic 介绍

1、引入 Actor-Critic

还是从上篇强化学习——REINFORCE Algorithm推导出的目标函数的梯度说起:
∇ θ J ( θ ) = E π θ [ ∑ t = 0 T − 1 G t ⋅ ∇ θ    l o g    π θ ( a t ∣ s t ) ] \nabla_\theta J(\theta) = E_{\pi_\theta} \left[\sum_{t=0}^{T-1}G_t\cdot \nabla_\theta\;log\;\pi_\theta(a_t|s_t) \right] θJ(θ)=Eπθ[t=0T1Gtθlogπθ(atst)]
其中 G t G_t Gt 就表示当前采取的行为,到episode结束一共能获得的奖励。对于 G t G_t Gt 是使用 MC 采样得到的 sample,只有到达最终状态才能逆序计算 G t G_t Gt ,这也是 REINFORCE 算法效率不高的原因,那么能不能不用等到游戏结束就可以更新参数呢?当然是可以的,那就是不再使用 MC 采样的方式来更新,而是采用的 TD 方式更新。

使用 TD 就可以每隔一步更新或者每隔两步更新,但是存在一个问题:如何计算每一个动作的 Q 值呢?因为没有等到episode结束就要更新参数,所以只能来估计 Q 值,很自然的就想到了使用神经网络来估计。然后就可以用参数化 的 Q Q Q 来代替 G t G_t Gt 。所以就需要两个神经网络,此时的目标梯度如下:
∇ θ J ( θ ) = E π θ [ ∑ t = 0 T − 1 Q w ( s t , a t ) ⋅ ∇ θ    l o g    π θ ( a t ∣ s t ) ] \nabla_\theta J(\theta) = E_{\pi_\theta} \left[\sum_{t=0}^{T-1}{Q_{\color{red}w}(s_t, a_t)}\cdot \nabla_{\color{red}\theta}\;log\;\pi_\theta(a_t|s_t) \right] θJ(θ)=Eπθ[t=0T1Qw(st,at)θlogπθ(atst)]
这样就得到了 Actor-Critic Policy Gradient。把 Value FunctionPolicy Function 两者结合起来的一中算法。其包含两个成分:

  • Actor:Actor 就是指的 Policy Function,是用来和环境交互,做出动作,可以理解为一个”表演者“。
  • Critic:Critic 就是式子中的 Q,是一个”评论者“的角色,用来评论 actor 所做出的动作实际能得到多少价值。

可以把 Actor-Critic 算法比喻为:Actor在台上跳舞,一开始舞姿并不好看,Critic根据Actor的舞姿打分。Actor通过Critic给出的分数,去学习:如果Critic给的分数高,那么Actor会调整这个动作的输出概率;相反,如果Critic给的分数低,那么就减少这个动作输出的概率。

下面介绍一个最简单的 Actor-Critic 算法:Sample QAC。

2、Sample QAC 算法

Sample QAC 算法使用线性特征组合来逼近 : Q w ( s , a ) = ψ ( s , a ) T w Q_w(s,a) = \psi(s,a)^Tw Qw(s,a)=ψ(s,a)Tw 。通过 TD(0) 的方式来更新 参数 w w w ,Actor使用 policy gradient来优化:

在这里插入图片描述

首先根据 策略 π θ \pi_\theta πθ 生成一系列样本数据,然后得到TD Target 进一步计算 TD Error ,来更新 价值函数的参数 w w w ,这里因为是线性特征组合,所以经过求导后直接取 feature(特征)来更新: w ← w + β δ ψ ( s , a ) w \leftarrow w + \beta\delta\psi(s,a) ww+βδψ(s,a) 。然后第二部分得到 Q w ( s , a ) Q_w(s,a) Qw(s,a) 后直接乘以 score function 通过 policy gradient 来进行更新。然后一直重复这个步骤,就得到了 Simple QAC 算法。

3、通过 Baseline 减少 Actor-Critic 的方差

同样的,在 AC 算法中也可以引入 Baseline 来减少方差。一般都会把平均值作为 Baseline 。回忆动作价值函数 Q: Q π , γ ( s , a ) = E π [ r 1 + γ r 2 + … ∣ s 1 = s , a 1 = a ] Q_{\pi,\gamma}(s,a) = E_\pi[r_1+\gamma r_2+\ldots|s_1=s,a_1=a] Qπ,γ(s,a)=Eπ[r1+γr2+s1=s,a1=a] ,而状态价值 V π , γ ( s ) V_{\pi,\gamma}(s) Vπ,γ(s) 就是动作状态价值 Q 的期望:
V π , γ ( s ) = E π [ r 1 + γ 2 + … ∣ s 1 = s ] = E a ~ π [ Q π , γ ( s , a ) ] \begin{aligned} V_{\pi, \gamma}(s) & = E_\pi[r_1+\gamma_2 + \ldots|s_1=s] \\ &= E_{a~\pi}[Q_{\pi,\gamma}(s,a)] \end{aligned} Vπ,γ(s)=Eπ[r1+γ2+s1=s]=Eaπ[Qπ,γ(s,a)]
也就是 说 状态价值函数 V 可以天然的做 Baseline,所以就得出了更新的权重:定义 为 Advantage function
A π , γ ( s , a ) = Q π , γ ( s , a ) − V π , γ ( s ) A_{\pi, \gamma}(s,a) = Q_{\pi, \gamma}(s,a) - V_{\pi, \gamma}(s) Aπ,γ(s,a)=Qπ,γ(s,a)Vπ,γ(s)
这样又需要计算 V,但是贝尔曼公式告诉我们 QV 是可以互换的:
T D − E r r o r = Q ( s , a ) − V ( s ) = r + g a m m a ∗ V ( s ′ ) − V ( s ) \begin{aligned} TD-Error & =Q(s,a) - V(s) \\ & = r + gamma * V(s') - V(s) \end{aligned} TDError=Q(s,a)V(s)=r+gammaV(s)V(s)
TD-Error 就是 Actor 更新时的权重值。所以 Critic 网络不需要估计 Q,而是去估计 V。然后就可以计算出 TD-Error,也就是Advantage function ,然后最小化 TD-Error

此时的 policy gradient:
∇ θ J ( θ ) = E π θ [ ∑ t = 0 T − 1 ∇ θ    l o g    π θ ( a t ∣ s t ) A π , γ ( s t , a t ) ] \nabla_\theta J(\theta) = E_{\pi_\theta} \left[\sum_{t=0}^{T-1} \nabla_{\theta}\;log\;\pi_\theta(a_t|s_t) {\color{red}A_{\pi, \gamma}(s_t,a_t)} \right] θJ(θ)=Eπθ[t=0T1θlogπθ(atst)Aπ,γ(st,at)]

二、代码分析

1、更新流程

本次代码还是采用 CartPole-v1 环境,在 REINFORCE算法中,agent 需要从头一直跑到尾,直到最终状态才开始进行学习,所以采用的回合更新制。 在AC中agent 采用是每步更新的方式。如下图所示

在这里插入图片描述

对于每一个 episode 流程如下,智能体每走一步都要分别更新 Critic 和 Actor 网络。注意:需要先更新Critic,并计算出TD-error。再用TD-error更新Actor

while True:
    if RENDER: env.render()
        action = self.actor.get_action(state)
        state_, reward, done, _ = env.step(action)
        
        td_error = self.critic.learn(state, reward, state_, done)
        self.actor.learn(state, action, td_error)

        state = state_
        if done or step >= MAX_STEPS:
            break

2、Critic 的更新

def learn(self, state, reward, state_, done):
    d = 0 if done else 1
    with tf.GradientTape() as tape:
        v = self.model(np.array([state]))
        v_ = self.model(np.array([state_]))
        td_error = reward + d * LAM * v_ - v
        loss = tf.square(td_error)
    grads = tape.gradient(loss, self.model.trainable_weights)
    self.optimizer.apply_gradients(zip(grads, self.model.trainable_weights))
    return td_error

Critic 网络通过估计当前状态和下一个状态 的 V 值,来计算 TD-Error,然后更新参数。

3、Actor 的更新

Actor的学习本质上就是 PG 的更新,只不过在 AC 中更新的权重变成了 TD-Error。在上一篇 介绍 REINFORCE算法文章中已经详细讲过,传送门强化学习——REINFORCE 算法推导与代码实现 。在此不再赘述。

def learn(self, state, action, td_error):
    with tf.GradientTape() as tape:
        _logits = self.model(np.array([state]))
        _exp_v = tl.rein.cross_entropy_reward_loss(
            logits=_logits, actions=[action], rewards=td_error)
    grad = tape.gradient(_exp_v, self.model.trainable_weights)
    self.optimizer.apply_gradients(zip(grad, self.model.trainable_weights))

注意的是在这里直接使用了 tensorlayer 提供的函数 cross_entropy_reward_loss 。其实这个函数就是把交叉熵函数包装了一下而已:

def cross_entropy_reward_loss(logits, actions, rewards, name=None):
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=actions, logits=logits, name=name)
    return tf.reduce_sum(tf.multiply(cross_entropy, rewards))

完整代码请查看Actor-Critic算法 。还望随手一个 star ,不胜感激

三、AC小结

要理解 AC,就要理解 TD-Error,而TD-Error 的本质就是 Q ( s , a ) − V ( s ) Q(s,a) - V(s) Q(s,a)V(s) 得来的。其中的 Q ( s , a ) Q(s,a) Q(s,a) 就是采用了TD的方法 r + γ V ( s ′ ) r + \gamma V(s') r+γV(s) 得来的。Critic 网络的作用就是最小化 TD-Error。Actor 网络的功能和 REINFORCE 算法中的 策略网络功能一样,就是交叉熵带权重更新。

基本版的Actor-Critic算法虽然思路很好,但是由于难收敛的原因,仍然还可以做改进。基于AC架构的改进算法还有DDPG、TD3等,都会在以后介绍。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值