在强化学习(十三) 策略梯度(Policy Gradient)中,咱们讲到了基于策略(Policy Based)的强化学习方法的基本思路,并讨论了蒙特卡罗策略梯度reinforce算法。可是因为该算法须要完整的状态序列,同时单独对策略函数进行迭代更新,不太容易收敛。html
在本篇咱们讨论策略(Policy Based)和价值(Value Based)相结合的方法:Actor-Critic算法。git
本文主要参考了Sutton的强化学习书第13章和UCL强化学习讲义的第7讲。github
Actor-Critic从名字上看包括两部分,演员(Actor)和评价者(Critic)。其中Actor使用咱们上一节讲到的策略函数,负责生成动做(Action)并和环境交互。而Critic使用咱们以前讲到了的价值函数,负责评估Actor的表现,并指导Actor下一阶段的动做。算法
回想咱们上一篇的策略梯度,策略函数就是咱们的Actor,可是那里是没有Critic的,咱们当时使用了蒙特卡罗法来计算每一步的价值部分替代了Critic的功能,可是场景比较受限。所以如今咱们使用相似DQN中用的价值函数来替代蒙特卡罗法,做为一个比较通用的Critic。网络
也就是说在Actor-Critic算法中,咱们须要作两组近似,第一组是策略函数的近似:$$\pi_{\theta}(s,a) = P(a|s,\theta)\approx \pi(a|s)$$多线程
第二组是价值函数的近似,对于状态价值和动做价值函数分别是:$$\hat{v}(s, w) \approx v_{\pi}(s)$$$$\hat{q}(s,a,w) \approx q_{\pi}(s,a)$$app
对于咱们上一节讲到的蒙特卡罗策略梯度reinforce算法,咱们须要进行改造才能变成Actor-Critic算法。框架
首先,在蒙特卡罗策略梯度reinforce算法中,咱们的策略的参数更新公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t) v_t$$ide
梯度更新部分中,$\nabla_{\theta}log \pi_{\theta}(s_t,a_t) $是咱们的分值函数,不用动,要变成Actor的话改动的是$v_t$,这块不能再使用蒙特卡罗法来获得,而应该从Critic获得。函数
而对于Critic来讲,这块是新的,不过咱们彻底能够参考以前DQN的作法,即用一个Q网络来作为Critic, 这个Q网络的输入能够是状态,而输出是每一个动做的价值或者最优动做的价值。
如今咱们汇总来讲,就是Critic经过Q网络计算状态的最优价值$v_t$, 而Actor利用$v_t$这个最优价值迭代更新策略函数的参数$\theta$,进而选择动做,并获得反馈和新的状态,Critic使用反馈和新的状态更新Q网络参数$w$, 在后面Critic会使用新的网络参数$w$来帮Actor计算状态的最优价值$v_t$。
在上一节咱们已经对Actor-Critic算法的流程作了一个初步的总结,不过有一个能够注意的点就是,咱们对于Critic评估的点选择是和上一篇策略梯度同样的状态价值$v_t$,实际上,咱们还能够选择不少其余的指标来作为Critic的评估点。而目前可使用的Actor-Critic评估点主要有:
a) 基于状态价值:这是咱们上一节使用的评估点,这样Actor的策略函数参数更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t) V(s,w)$$
b) 基于动做价值:在DQN中,咱们通常使用的都是动做价值函数Q来作价值评估,这样Actor的策略函数参数更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t) Q(s,a,w)$$
c) 基于TD偏差:在强化学习(五)用时序差分法(TD)求解中,咱们讲到了TD偏差,它的表达式是$\delta(t) = R_{t+1} + \gamma V(S_{t+1}) -V(S_t)$或者$\delta(t) = R_{t+1} + \gamma Q(S_{t+1},A_{t+1} ) -Q(S_t,A_t)$, 这样Actor的策略函数参数更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)\delta(t)$$
d) 基于优点函数:在强化学习(十二) Dueling DQN中,咱们讲到过优点函数A的定义:$A(S,A,w,\beta) = Q(S,A, w, \alpha, \beta) - V(S,w,\alpha) $,即动做价值函数和状态价值函数的差值。这样Actor的策略函数参数更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)A(S,A,w,\beta)$$
e) 基于TD($\lambda$)偏差:通常都是基于后向TD($\lambda$)偏差, 在强化学习(五)用时序差分法(TD)求解中也有讲到,是TD偏差和效用迹E的乘积。这样Actor的策略函数参数更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)\delta(t)E_(t)$$
对于Critic自己的模型参数$w$,通常都是使用均方偏差损失函数来作作迭代更新,相似以前DQN系列中所讲的迭代方法. 若是咱们使用的是最简单的线性Q函数,好比$Q(s,a ,w) = \phi(s,a)^Tw$,则Critic自己的模型参数$w$的更新公式能够表示为:$$\delta = R_{t+1} + \gamma Q(S_{t+1},A_{t+1} ) -Q(S_t,A_t)$$$$w = w+ \beta\delta\phi(s,a)$$
经过对均方偏差损失函数求导能够很容易的获得上式。固然实际应用中,咱们通常不使用线性Q函数,而使用神经网络表示状态和Q值的关系。
这里给一个Actor-Critic算法的流程总结,评估点基于TD偏差,Critic使用神经网络来计算TD偏差并更新网络参数,Actor也使用神经网络来更新网络参数
算法输入:迭代轮数$T$,状态特征维度$n$, 动做集$A$, 步长$\alpha,\beta$,衰减因子$\gamma$, 探索率$\epsilon$, Critic网络结构和Actor网络结构。
输出:Actor 网络参数$\theta$, Critic网络参数$w$
1. 随机初始化全部的状态和动做对应的价值$Q$. 随机初始化Critic网络的全部参数$w$。随机初始化Actor网络的全部参数$\theta$。
2. for i from 1 to T,进行迭代。
a) 初始化S为当前状态序列的第一个状态, 拿到其特征向量$\phi(S)$
b) 在Actor网络中使用$\phi(S)$做为输入,输出动做$A$,基于动做$A$获得新的状态$S'$,反馈$R$。
c) 在Critic网络中分别使用$\phi(S), \phi(S‘’)$做为输入,获得Q值输出$V(S), V(S’)$
d) 计算TD偏差$\delta = R +\gamma V(S’) -V(S) $
e) 使用均方差损失函数$\sum\limits(R +\gamma V(S’) -V(S,w))^2$做Critic网络参数$w$的梯度更新
f) 更新Actor网络参数$\theta$:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(S_t,A)\delta$$
对于Actor的分值函数$\nabla_{\theta}log \pi_{\theta}(S_t,A)$,能够选择softmax或者高斯分值函数。
上述Actor-Critic算法已是一个很好的算法框架,可是离实际应用还比较远。主要缘由是这里有两个神经网络,都须要梯度更新,并且互相依赖。可是了解这个算法过程后,其余基于Actor-Critic的算法就好理解了。
下面咱们用一个具体的例子来演示上面的Actor-Critic算法。仍然使用了OpenAI Gym中的CartPole-v0游戏来做为咱们算法应用。CartPole-v0游戏的介绍参见这里。它比较简单,基本要求就是控制下面的cart移动使链接在上面的pole保持垂直不倒。这个任务只有两个离散动做,要么向左用力,要么向右用力。而state状态就是这个cart的位置和速度, pole的角度和角速度,4维的特征。坚持到200分的奖励则为过关。
算法流程能够参考上面的第三节,这里的分值函数咱们使用的是softmax函数,和上一片的相似。完整的代码参见个人Github:https://github.com/ljpzzz/machinelearning/blob/master/reinforcement-learning/actor_critic.py
代码主要分为两部分,第一部分是Actor,第二部分是Critic。对于Actor部分,你们能够和上一篇策略梯度的代码对比,改动并不大,主要区别在于梯度更新部分,策略梯度使用是蒙特卡罗法计算出的价值$v(t)$,则咱们的actor使用的是TD偏差。
在策略梯度部分,对应的位置以下:
self.loss = tf.reduce_mean(self.neg_log_prob * self.tf_vt) # reward guided loss
而咱们的Actor对应的位置的代码是:
self.exp = tf.reduce_mean(self.neg_log_prob * self.td_error)
此处要注意的是,因为使用的是TD偏差,而不是价值$v(t)$,此处须要最大化self.exp,而不是最小化它,这点和策略梯度不一样。对应的Actor代码为:
#这里须要最大化当前策略的价值,所以须要最大化self.exp,即最小化-self.exp self.train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(-self.exp)
除此以外,Actor部分的代码和策略梯度的代码区别并不大。
对于Critic部分,咱们使用了相似于DQN的三层神经网络。不过咱们简化了这个网络的输出,只有一维输出值,而不是以前DQN使用的有多少个可选动做,就有多少维输出值。网络结构以下:
def create_Q_network(self): # network weights W1q = self.weight_variable([self.state_dim, 20]) b1q = self.bias_variable([20]) W2q = self.weight_variable([20, 1]) b2q = self.bias_variable([1]) self.state_input = tf.placeholder(tf.float32, [1, self.state_dim], "state") # hidden layers h_layerq = tf.nn.relu(tf.matmul(self.state_input, W1q) + b1q) # Q Value layer self.Q_value = tf.matmul(h_layerq, W2q) + b2q
和以前的DQN相比,这里还有一个区别就是咱们的critic没有使用DQN的经验回放,只是使用了反馈和当前网络在下一个状态的输出来拟合当前状态。
对于算法中Actor和Critic交互的逻辑,在main函数中:
for step in range(STEP): action = actor.choose_action(state) # e-greedy action for train next_state,reward,done,_ = env.step(action) td_error = critic.train_Q_network(state, reward, next_state) # gradient = grad[r + gamma * V(s_) - V(s)] actor.learn(state, action, td_error) # true_gradient = grad[logPi(s,a) * td_error] state = next_state if done: break
你们对照第三节的算法流程和代码应该能够比较容易理清这个过程。可是这个程序很难收敛。所以你们跑了后发现分数老是很低的话是能够理解的。咱们须要优化这个问题。
基本版的Actor-Critic算法虽然思路很好,可是因为难收敛的缘由,还须要作改进。
目前改进的比较好的有两个经典算法,一个是DDPG算法,使用了双Actor神经网络和双Critic神经网络的方法来改善收敛性。这个方法咱们在从DQN到Nature DQN的过程当中已经用过一次了。另外一个是A3C算法,使用了多线程的方式,一个主线程负责更新Actor和Critic的参数,多个辅线程负责分别和环境交互,获得梯度更新值,汇总更新主线程的参数。而全部的辅线程会按期从主线程更新网络参数。这些辅线程起到了相似DQN中经验回放的做用,可是效果更好。
在后面的文章中,咱们会继续讨论DDPG和A3C。
(欢迎转载,转载请注明出处。欢迎沟通交流: liujianping-ok@163.com)