写在前面:我是根据莫烦的视频学习的Reinforce learning,具体代码实现包括Q-learning,SARSA,DQN,Policy-Gradient,Actor-Critic以及A3C。(莫凡老师的网站:https://morvanzhou.github.io/tutorials/machine-learning/reinforcement-learning/)
今天发表其中的Actor-Criitic算法,这是一个非常重要的算法,包括在强化学习领域最受欢迎的A3C、DDPG等算法都是基于AC算法框架,其主要原因是它集成了以值为基础 (比如 Q learning) 和以动作概率为基础 (比如 Policy Gradients) 两类强化学习算法,因此体现出了较好的效果。这篇博客以代码为基础谈一谈本算法。
算法理解:首先我们知道Q-learning是一种以值为基础的算法,他维持了一个状态-行动的Q表,基于Q表的值进行选择并更新Q表,这在状态和行为个数较少时较为适用。而Policy-Gradient算法是一种基于动作概率的算法,它不通过分析奖励值, 而是在某一个状态下利用已知动作概率直接输出行为,而在下一次增大这次选择动作再次发生的概率。(利用reward奖惩信息确定增大概率正确与否,从而实现好的动作更多被选中,坏的动作更少被选中)。
而在Actor-Critic中,同时维持以上两种神经网络:Actor(演员)以及Critic(评判家)。
其中Actor 的前生是 Policy Gradients,利用它能快速选出合适的动作。而Critic的前生是Q-learning,利用它能更好的训练Actor。类似于演员表演而评判家告诉演员演得好不好,从而促使演员表演地更好获得更高的分数。
具体算法中,actor—critic维持两个神经网络,Actor网络和Critic神经网络。首先向Actor网络中传入当前状态,依据actor网络输出的动作概率选择action,从环境中得到reward和下一个状态等信息,将这些信息传入Critic网络中得到td—error(td-error=v现实-v估计,所谓的v现实,可以理解为某个动作对应的值函数,v现实 = r+γV(s ′ ) ,而v估计是直接由神经网络得到,是该状态下所有值函数关于动作概率的期望) ,td-error也就是所谓的优势函数,可以表征当前动作值函数相对于平均值的大小。利用td-error告知Actor网络这一步做得好不好(有没有比平常取得更好的结果)。因此Actor网络利用td-error作为新的奖惩信息确定这个动作概率更新的方向,而Critic网络利用td—error来最小化v现实和v估计之间的差值(使评判更加精准)。
具体代码:
环境:开发环境python3.7。 游戏环境:gym中的 ‘CartPole-v0’ 一级倒立摆
import numpy as np
import tensorflow as tf
import gym
np.random.seed(2)
tf.set_random_seed(2)
Output_graph=False
Max_episode=3000
Dispaly_reward_threshold=200 #显示图像的门限值
Max_ep_steps=1000
Render=False
gamma=0.9
LR_A=0.001 #actor的learning—rate
LR_C=0.01 #critic的learning—rate
env=gym.make('CartPole-v0') #确定gym环境
env.seed(1)
env=env.unwrapped #取消环境限制
N_F=env.observation_space.shape[0] #观测值个数
N_A=env.action_space.n #动作个数
class Actor(object): #Actor网络,用于选择行动
def __init__(self,sess,n_features,n_actions,lr=0.001): #建立actor神经网络
self.sess=sess
self.s=tf.placeholder(tf.float32,[1,n_features],"state")
self.a=tf.placeholder(tf.int32,None,"act")
self.td_error=tf.placeholder(tf.float32,None,"td_error")
with tf.variable_scope('Actor'):
l1=tf.layers.dense(
inputs=self.s, #输入是当前的状态
units=20, #中间层神经元个数
activation=tf.nn.relu,
kernel_initializer=tf.random_normal_initializer(0.,1.), #初始化权重
bias_initializer=tf.constant_initializer(0.1), #初始化偏移
name='l1'
)
self.acts_prob=tf.layers.dense(
inputs=l1, #输入是第一层的输入
units=n_actions, #输出神经元个数是actions的个数
activation=tf.nn.softmax,
kernel_initializer=tf.random_normal_initializer(0.,1.),
bias_initializer=tf.constant_initializer(0.1),
name='acts_prob'
)
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) #等同于policy中的总体reward
with tf.variable_scope('train'):
self.train_op=tf.train.AdamOptimizer(lr).minimize(-self.exp_v)
#训练过程就是最大化总体reward:exp-v,即最小化负的exp-v
def learn(self,s,a,td): #td来自于critic,由他来告诉actor下降方向对不对,s,a产生梯度下降的方向
s=s[np.newaxis,:]
_,exp_v=self.sess.run([self.train_op,self.exp_v],feed_dict={self.s:s,self.a:a,self.td_error:td})
return exp_v
def choose_action(self,s): #根据s选择行为a
s=s[np.newaxis,:] #扩展一个维度
probs=self.sess.run(self.acts_prob,feed_dict={self.s:s}) #acts_prob由actor网络获得,取得所有动作的选择概率
return np.random.choice(np.arange(probs.shape[1]),p=probs.ravel()) #依据概率选择一个动作,返回动作序号
class Critic(object): #Critic网络,用于评价
def __init__(self,sess,n_features,lr=0.01): #建立critic神经网络
self.sess=sess
self.s=tf.placeholder(tf.float32,[1,n_features],'state')
self.v_=tf.placeholder(tf.float32,[1,1],'v_next') #??????
self.r=tf.placeholder(tf.float32,None,'r')
with tf.variable_scope('Critic'):
l1=tf.layers.dense(
inputs=self.s,
units=20,
activation=tf.nn.relu,
kernel_initializer=tf.random_normal_initializer(0.,1.),
bias_initializer=tf.constant_initializer(0.1),
name='l1'
)
self.v=tf.layers.dense(
inputs=l1,
units=1, #输出神经元个数为1,就是一个评价值
activation=None,
kernel_initializer=tf.random_normal_initializer(0.,1.), #weights
bias_initializer=tf.constant_initializer(0.1),
name='V'
)
with tf.variable_scope('squared_TD_error'):
self.td_error=(self.r+gamma*self.v_) - self.v #TD_error = (r+gamma*V_next) - V_eval。即v现实减去v估计
#r+v_-v
self.loss=tf.square(self.td_error) #平方来避免误差为负数
with tf.variable_scope('train'):
self.train_op=tf.train.AdamOptimizer(lr).minimize(self.loss) #训练过程:最小化误差TD—error
def learn(self,s,r,s_): #Critic学习过程
s,s_=s[np.newaxis,:],s_[np.newaxis,:]
v_=self.sess.run(self.v,feed_dict={self.s:s_}) #同样用Critic网络计算下一个state的评价值v-
td_error,_=self.sess.run([self.td_error,self.train_op],feed_dict={self.s:s,self.v_:v_,self.r:r})
#计算td—error,并且执行训练过程,利用v现实和v估计,以及下一个状态的reward来最小化v现实和v估计间的误差
return td_error #返回误差
#主程序部分
sess=tf.Session()
actor=Actor(sess,n_features=N_F,n_actions=N_A,lr=LR_A) #产生actor网络
critic=Critic(sess,n_features=N_F,lr=LR_C) #产生critic网络
sess.run(tf.global_variables_initializer()) #初始化参数
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得到应该选择的动作a
s_,r,done,info=env.step(a) #进行动作a,获得下一个状态及reward和结束标志
if done: #如果结束,reward为-20
r=-20
track_r.append(r) #记录本步的奖励
td_error=critic.learn(s,r,s_) #Critic网络学习,得到一个评价td—error,传入参数为当前状态,reward以及下一个状态
actor.learn(s,a,td_error) #Actor网络通过Critic网络反馈的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>Dispaly_reward_threshold:
Render=True
print('episode:',i_episode,'reward:',running_reward)
break