您的位置:首页 > 理论基础 > 计算机网络

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

2018-01-17 10:57 459 查看
所谓的策略网络,即建立一个神经网络模型,它可以通过观察环境状态,直接预测出目前最应该执行的策略(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()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: