用tensorflow 创建一个基于策略网络的Agent来解决CartPole问题

所谓的策略网络,即建立一个神经网络模型,它可以通过观察环境状态,直接预测出目前最应该执行的策略(policy),执行这个策略可以获得最大的期望收益(包括现在的和未来的reward)。和之前的任务不同,在强化学习中可能没有绝对正确的学习目标,样本的feature和label也不在一一对应。我们的学习目标是期望价值,即当前获得的reward和未来潜在的可获取的reward。所以在策略网络中不只是使用当前的reward作为label,而是使用Discounted Future Reward,即把所有未来奖励一次乘以衰减系数γ。这里的衰减系数是一个略小于但接近1的数,防止没有损耗地积累导致Reward目标发散,同时也代表了对未来奖励的不确定性的估计。

我们先安装OpenAI Gym。-----------------------> pip install gym

接着我们载入Numpy,Tensorflow和gym,这里用gym.make('CartPole-v0')创建CartPole问题的环境env。

import numpy as np

import tensorflow as tf

import gym

env=gym.make('CartPole-v0')

先测试在CartPole环境中使用随机ACTION的表现,作为接下来对比的Baseline。首先,我们使用env.reset()初始化环境,然后进行10次随机试验,这里调用env.render()将CartPole问题的图像渲染出来。使用np.random.randint(0,2)产生随机的Action,然后用env.step()执行随机的Action,并获取返回的observation,reward和done。如果done标记为True,则代表这次试验结束,即倾角超过15度或者偏离中心过远导致任务失败。在一次试验结束后,我们展示这次演示累计的奖励reward_sum并重启环境。

env.reset()

random_episodes=0

reward_sum=0

while random_episodes<10:

        env.render()

        observation,reward,done,_=env.step(np.random.randint(0,2))

        reward_sum+=reward

        if done:

                random_episodes+=1

                print 'Reward for this episode was:',reward_sum

                reward_sum=0

                env.reset()

我们的策略网络使用简单的带有一个隐含层的MLP。先设置网络的各个超参数,这里的隐含节点数H设为50,batch_size设为25,学习速率learning_rate为0.1,环境信息observation的维度Dwei 4,gamma即Reward的discount比例设为0.99.在估算Action的期望价值(即估算样本的学习目标)时会考虑Delayed Reward,会将某个Action之后获得的所有Reward 做Discount并累加起来,这样可让模型学习到未来可能出现的潜在Reward。注意,一般discount比例要小于1,防止Reward被无损耗的不断累加导致发散,这样也可以区分当前reward和未来reward的价值(当前Action直接带来的reward不需要discount,而未来的reward因存在不确定性所以需要discount)

H=50

batch_size=25

learning_rate=1e-1

D=4

gamma=0.99

下面定义策略网络的具体结构。这个网络将接受observation作为输入信息,最后输出一个概率值用以选择action(我们只有两个action,向左施加力或者向右施加力,因此可以通过一个概率值决定)。我们创建输入信息observation的placeholder,其维度为[D,H]。接着用tf.matmul将环境信息observation乘上W1再使用RELU激活函数处理得到隐含层输出layer1,这里注意我们并不需要加偏置。同样用xavier_initializer算法创建最后sigmoid输出层的权重W2,将隐含层输出layer1乘以W2后,使用sigmoid激活函数处理得到最后的输出概率。

observation=tf.placeholder(tf.float32,[None,D],name='input_x')

W1=tf.get_variable('W1',shape=[D,H],initializer=tf.contrib.layers.xavier_initializer())

layer1=tf.nn.relu(tf.matmul(observations,W1))

W2=tf.get_variable('W2',shape=[H,1],initializer=tf.contrib.layers.xavier_initializer())

score=tf.matmul(layer1,W2)

probability=tf.nn.sigmoid(score)

这里模型的优化器使用Adam算法。我们分别设置两层神经玩过参数的梯度的placeholder------W1Grad和W2Grad,并使用adam.apply_gradients定义我们更新模型参数的操作updateGrads。之后计算参数的梯度,当积累到一定样本量的梯度,就传入W1Grad和W2Grad,并执行updateGrads更新模型参数。这里注意,深度强化学习的训练和其他神经网络一样,也使用batch training的方式。累计到一个batch_size的样本的梯度再更新参数,防止单一样本随机扰动的噪声对模型带来不良影响。

adam=tf.train.AdamOptimizer(learning_rate=learning_rate)

W1Grad=tf.placeholder(tf.float32,name='batch_grad1')

W2Grad=tf.placeholder(tf.float32,name='batch_grad2')

batchGrad=[W1Grad,W2Grad]

updateGrads=adam.apply_gradients(zip(batchGrad,tvars))

下面定义函数discount_rewards,用来估算每一个Action对应的潜在价值discount_r。每次获得的reward都和前面的action有关,属于delayed_reward。我们定义每个action除直接获得的reward外的潜在价值为running_add,running_add是从后向前累计的,并且需要经过discount衰减。而每一个action的潜在价值,即为后一个action的潜在价值乘以衰减系数gamma再加上它直接获得的reward,即running_add*gamma+r[t]。这样从最后一个action开始不断向前累计计算,即可获得全部action的潜在价值。这种对潜在价值的估算方法符合我们的期望,越靠前的action潜在价值越大。

def discount_rewards(r):

        discounted_r=np.zeros_like(r)

        running_add=0

        for t in reversed(range(r.size)):

                running_add=running_add*gamma+r[t]

                discounted_r[t]=running_add

        return discounted_r

我们定义人工设置的虚拟label(取值为0或1)的placeholder---input_y,以及每个action的潜在价值的placeholder-advantages。这里loglik定义略显复杂,我们来看一下Loglik到底代表什么。action取值为1的概率为probability(即策略网络输出的概率),action取值为0的概率为1-probability,label的取值与action想法,即label=1-action。当action为1时,label为0,此时loglik=tf.log(probability),action取值为1的概率的对数;当action为0是,label为1,此时Loglik=tf.log(1-probability),即action取值为0的概率的对数。所以loglik其实就是当前action对应的概率的对数,我们将loglik与潜在机制advantages相乘,并取负数作为损失,即优化目标。我们使用优化器优化时,会让能获得较多advantages的action的概率变大,并让能获得较少advantages的action的概率变小,这样能让损失变小。通过不断的训练,我们便能持续加大能获得较多advantages的action的概率,即学习到一个能获得更多潜在价值的策略。最后,使用tf.trainable_variables()获取策略网络中全部可训练的参数tvars,并使用tf.gradients求解模型参数关于loss的梯度。

input_y=tf.placeholder(tf.float32,[None,1],name='input_y')

advantages=tf.placeholder(tf.float32,name='reward_signal')

loglik=tf.log(input_y*(input_y-probability)+(1-input_y)*(input_y+probability))

loss=-tf.reduce_mean(loglik*advantages)

tvars=tf.trainable_variables()

newGrads=tf.grandients(loss,tvars)

在正式进入训练过程前,我们先定义一些参数,xs为环境信息observation的列表,ys为我们定义的label的列表,drs为我们记录的每一个action的reward,我们定义累计的reward为reward_sum,总试验次数total_episodes为10000,直到达到获取200的reward才停止训练。

xs,ys,drs=[],[],[]

reward_sum=0

episode_number=1

total_episodes=10000

我们创建默认的Session,初始化全部参数,并在一开始将render的标志关闭。因为render会带来比较大的延迟,所以一开始不太成熟的模型还没必要去观察。县初始化CartPole的环境并获得初始状态。然后使用sess.run执行tvars获取所有模型参数,用来创建储存参数梯度的缓冲器gradBuffer,并把gradBuffer全部出池化为0.接下来的每次试验中,我们将手机参数的梯度存储到gradBuffer中,直到完成了一个batch_size的试验,再将汇总的梯度更新到模型参数。

with tf.Session() as sess:

        rendering=False

        init=tf.global_variables_initializer()

        sess.run(init)

        observation=env.reset()

        gradBuffer=sess.run(tvars)

        for ix,grad in enumerate(gradBuffer):

                gradBuffer[ix]=grad*0

下面进入实验的循环,最大循环次数即为total_episodes。当某个batch的平均reward达到100以上时,即agent表现良好时,调用env.render()对试验环境进行展示。先使用tf.reshape将observation变形为策略网络输入的格式,然后传入网络中,使用sess.run执行probability获得网络出书的概率tfprob,即action取值为1的概率。接下来我们在(0,1)间随机抽样,若随机值小于tfprob,则令action取值为1,否则令action取值为0,即代表action取值为1的概率为tfprob。

        while episode_number<=total_episodes:

                if reward_sum/batch_size > 100 or rendering==True:

                        env.render()

                        redering=True

                x=np.reshape(observation,[1,D])

                tfprob=sess.run(probability,feed_dict={observations:x})

                action= 1 if np.random.uniform() < tfprob else 0

然后将输入的环境信息observation添加到列表xs中。这里我们制造虚拟的Lable-y,它取值与action相反,即y=1-action,并将其添加到列表ys中,然后使用env.step执行一次action,获取observation,reward,done和info,并将reward累加到reward_sum,同时将reward添加到列表drs中。

                xs.append(x)

                y=1-action

                ys.append(y)

                observation,reward,done,info=env.step(action)

                reward_sum+=reward

                drs.append(reward)

将done为True,即一次试验结束时,将episode_number加1.同时使用np.vstack将几个列表xs,ys,drs中的元素纵向堆叠起来,得到epx,epy和epr,并将xs,ys,drs清空以备下次试验使用。这里注意,epx,epy,drs即为一次试验中获得的所有observation、label、reward的列表。我们使用前面定义好的discount_rewards函数计算每一步action的潜在价值,并进行标准化(减去均值再除以标准差),得到一个零均值标准差为1的分布。这么做是因为discount_reward会参与到模型损失的计算,而分布稳定的discount_reward有利于训练的稳定。

                if done:

                        episode_num+=1

                        epx=np.vstack(xs)

                        epy=np.vstack(ys)

                        epr=np.vstack(drs)

                        xs,ys,drs=[],[],[]

                        discounted_epr = discount_rewards(epr)

                        discounted_epr -= np.mean(discounted_epr)

                        discounted_epr /= np.std(discounted_epr)

我们将epx,epy和discounted_epr输入神经网络,并使用操作newGrads求解梯度。再将获得的梯度累加到gradBuffer中去。

                        tGrad=sess.run(newGrads,feed_dict={observations:epx,input_y:epy,advantages:discounted_epr})

                        for ix,grad in enumerate(tGrad):

                                gradBuffer[ix] += grad

当进行试验的次数达到batch_size的整倍数时,gradBuffer中就累计了足够多的梯度,因此使用updateGrads操作将gradBuffer中的梯度更新到策略网络的模型参数中,并清空gradBuffer,为计算下一个batch的梯度做准备。这里注意,我们是使用一个batch的梯度更新参数,但是每一个梯度是使用一次试验中全部样本(一个action对应一个样本)计算出来的,因此一个batch中的样本数实际上使25(batch_size)次试验的样本数之和。同时,我们展示当前的试验次数episode_number,和batch内每次试验平均获得的reward。当我们batch内的每次试验的平均reward大于200时,我们的策略网络就成功完成了任务,并将终止循环。如果没有达到目标,则清空reward_sum,中心累计下一个batch的总reward。同时,在每次试验结束后,将任务环境env重置,方便下一次试验。

                        if episode_number % batch_size ==0:

                                sess.run(updateGrads,feed_dict={W1Grad:gradBuffer[0],W2Grad:gradBuffer[1]})

                                for ix,grad in enumerate(gradBuffer):

                                        gradBuffer[ix]=grad*0

                                print 'Average reward for episode %d:%f.' % (episode_number,reward_sum/batch_size)

                                if reward_sum/batch_size > 200:

                                        print 'Task solved in ',episode_number,'episodes'

                                        break

                                reward_sum=0

                        observation=env.reset()

---------------------

作者:河南骏

来源:CSDN

原文:https://blog.csdn.net/Eason_oracle/article/details/79076184

版权声明:本文为博主原创文章,转载请附上博文链接!

你可能感兴趣的:(用tensorflow 创建一个基于策略网络的Agent来解决CartPole问题)