学习笔记:强化学习之Actor-Critic

写在前面:我是根据莫烦的视频学习的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

你可能感兴趣的:(学习笔记:强化学习之Actor-Critic)