前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Policy Gradient实战

Policy Gradient实战

作者头像
CristianoC
发布2020-05-31 15:34:49
6400
发布2020-05-31 15:34:49
举报

目录

1.前言2.算法2.1算法主循环2.2 Policy Gradient部分3. 结果分析

1.前言

今天利用上篇文章讲解的Policy Gradient理论进行实战,背景仍然是杆子不倒游戏和小车登顶游戏。

2.算法

理论部分上篇文章已经介绍过了,这里不多赘述,感兴趣的读者可以去看我上一篇文章。

2.1算法主循环

我们先定义一下算法的主循环,这里要注意我们采取的是回合更新,而不是Q-Learning等的单步更新

 1import gym
 2from RL_brain import PolicyGradient
 3import matplotlib.pyplot as plt
 4
 5DISPLAY_REWARD_THRESHOLD = 400  # 当回合总reward大于400时显示模拟窗口
 6RENDER = False  # 在屏幕上显示模拟窗口会拖慢运行速度,我们等计算机学的差不多了再进行模拟
 7
 8env = gym.make('CartPole-v0')
 9env.seed(1)     # 普通的Policy gradient方法,使得回合的方差比较大,所以我们选了一个好点的随机种子
10env = env.unwrapped
11
12print(env.action_space)
13print(env.observation_space)
14print(env.observation_space.high)
15print(env.observation_space.low)
16
17RL = PolicyGradient(
18    n_actions=env.action_space.n,
19    n_features=env.observation_space.shape[0],
20    learning_rate=0.02,
21    reward_decay=0.99,
22    # output_graph=True,
23)
24
25for i_episode in range(3000):
26
27    observation = env.reset()
28
29    while True:
30        if RENDER: env.render()
31
32        action = RL.choose_action(observation)
33
34        observation_, reward, done, info = env.step(action)
35
36        RL.store_transition(observation, action, reward)#存储这一回合的transition
37
38        if done:
39            ep_rs_sum = sum(RL.ep_rs)
40
41            if 'running_reward' not in globals():
42                running_reward = ep_rs_sum
43            else:
44                running_reward = running_reward * 0.99 + ep_rs_sum * 0.01
45            if running_reward > DISPLAY_REWARD_THRESHOLD: RENDER = True   # 判断是否显示模拟
46            print("episode:", i_episode, "  reward:", int(running_reward))
47
48            vt = RL.learn() #学习,输出vt
49
50            if i_episode == 0:
51                plt.plot(vt)    # plot the episode vt
52                plt.xlabel('episode steps')
53                plt.ylabel('normalized state-action value')
54                plt.show()
55            break
56
57        observation = observation_

2.2 Policy Gradient部分

我们先初始化我们的神经网络

 1import numpy as np
 2import tensorflow as tf
 3
 4# reproducible
 5np.random.seed(1)
 6tf.set_random_seed(1)
 7
 8
 9class PolicyGradient:
10    def __init__(
11            self,
12            n_actions,
13            n_features,
14            learning_rate=0.01,
15            reward_decay=0.95,
16            output_graph=False,
17    ):
18        self.n_actions = n_actions
19        self.n_features = n_features
20        self.lr = learning_rate
21        self.gamma = reward_decay # reward递减率
22
23        self.ep_obs, self.ep_as, self.ep_rs = [], [], [] #存储回合信息的list
24
25        self._build_net()   #建立policy神经网络
26
27        self.sess = tf.Session()
28
29        if output_graph:
30            # $ tensorboard --logdir=logs
31            # http://0.0.0.0:6006/
32            # tf.train.SummaryWriter soon be deprecated, use following
33            tf.summary.FileWriter("logs/", self.sess.graph)
34
35        self.sess.run(tf.global_variables_initializer())

然后我们来建立我们的神经网络,我们要建立的神经网络是这样的:

 1    def _build_net(self):
 2        with tf.name_scope('inputs'):
 3            #接受observation
 4            self.tf_obs = tf.placeholder(tf.float32, [None, self.n_features], name="observations")
 5            #接收我们在这个回合中选过的actions
 6            self.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions_num")
 7            #接收每个state-action所对应的value(通过reward计算)
 8            self.tf_vt = tf.placeholder(tf.float32, [None, ], name="actions_value")
 9        # fc1
10        layer = tf.layers.dense(
11            inputs=self.tf_obs,
12            units=10,
13            activation=tf.nn.tanh,  # tanh activation
14            kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
15            bias_initializer=tf.constant_initializer(0.1),
16            name='fc1'
17        )
18        # fc2
19        all_act = tf.layers.dense(
20            inputs=layer,
21            units=self.n_actions,
22            activation=None, #后面用softmax
23            kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
24            bias_initializer=tf.constant_initializer(0.1),
25            name='fc2'
26        )
27
28        self.all_act_prob = tf.nn.softmax(all_act, name='act_prob')  # 用softmax出概率
29
30        with tf.name_scope('loss'):
31            # 最大化总体reward(log_p*R)就是在最小化-(log_p*R)。而tf的功能里只有最小化loss
32            neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act, labels=self.tf_acts)  # 所选action的概率-log值
33            # or in this way:
34            # neg_log_prob = tf.reduce_sum(-tf.log(self.all_act_prob)*tf.one_hot(self.tf_acts, self.n_actions), axis=1)
35            loss = tf.reduce_mean(neg_log_prob * self.tf_vt)  # (vt=本reward+衰减的未来reward)引导参数的梯度下降
36
37        with tf.name_scope('train'):
38            self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)

为了确保我们选择的动作真的是“正确的”,我们的loss在原本的cross-entropy形式上乘以vt。用vt来告诉这个cross-entropy算出来的梯度是不是一个值得信任的梯度。如果vt小或者是负的,就说明这个梯度下降是一个错误的方向,我们应该向着另一个方向更新参数。如果这个vt是正的,或很大,vt就会称赞cross-entropy出来的梯度,并朝着这个方向梯度下降。下面有一张从karpathy大神网页下扣下来的图,也正是阐述这个思想。

接着我们就可以通过概率而不是Q value来选择我们的行为了。这里即使不采用epsilon-greedy,也具有一定的随机性。

1    def choose_action(self, observation):
2        prob_weights = self.sess.run(self.all_act_prob, feed_dict={self.tf_obs: observation[np.newaxis, :]})
3        action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())  # 直接根据概率来选action
4        return action

选择完行为就可以存储回合了,就是把这一步的observationactionreward加到列表中去。因为本回合完毕之后要清空列表,然后存储下一回合的数据,我们会在learn()当中清空列表的动作。

1    def store_transition(self, s, a, r):
2        self.ep_obs.append(s)
3        self.ep_as.append(a)
4        self.ep_rs.append(r)

这次的learn()很简单。首先我们要对本回合所有的reward动下手脚,使他变得更适合被学习。第一就是随着时间推进,用gamma衰减未来的reward,然后为了一定程度下减少policy gradient回合方差,我们标准化回合的state-action value,依据在Andrej Karpathy的blog

 1    def learn(self):
 2        # discount and normalize episode reward
 3        discounted_ep_rs_norm = self._discount_and_norm_rewards()
 4
 5        # train on episode
 6        self.sess.run(self.train_op, feed_dict={
 7             self.tf_obs: np.vstack(self.ep_obs),  # shape=[None, n_obs]
 8             self.tf_acts: np.array(self.ep_as),  # shape=[None, ]
 9             self.tf_vt: discounted_ep_rs_norm,  # shape=[None, ]
10        })
11
12        self.ep_obs, self.ep_as, self.ep_rs = [], [], []    # 清空回合的数据
13        return discounted_ep_rs_norm
14
15    def _discount_and_norm_rewards(self):
16        # discount episode rewards
17        discounted_ep_rs = np.zeros_like(self.ep_rs)
18        running_add = 0
19        for t in reversed(range(0, len(self.ep_rs))):
20            running_add = running_add * self.gamma + self.ep_rs[t]
21            discounted_ep_rs[t] = running_add
22
23        # normalize episode rewards
24        discounted_ep_rs -= np.mean(discounted_ep_rs)
25        discounted_ep_rs /= np.std(discounted_ep_rs)
26        return discounted_ep_rs

3. 结果分析

我们来看看vt的输出,看看他是怎么诱导我们的gradient descent。

可以看出,左边一段的vt有较高的值,右边较低,这就是vt在说:

“对于回合开始的一些列动作,因为前面一段时间杆子还没有掉下来,所以要重视;对于后面的动作,因为他们让杆子掉下来了,所以要惩罚”或者是

“我每次都想让这个动作在下一次增加被做的可能性(gra(log(Policy))),但是增加可能性的这种做法是好还是坏呢?这就要由vt告诉我了,所以后段时间的增加可能性做法并没有被提倡,而前段时间的增加可能性做法是被提倡的”

这样vt就能在这里loss = tf.reduce_mean(log_prob * self.tf_vt)诱导gradient descent朝着正确的方向发展了。

MountainCar中的vt长这样:

这是在说:”请重视我这回合最后的一系列动作,因为这一系列动作让我爬上了山,而且请惩罚我开始的一系列动作,因为这些动作让我没能爬上山“

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 计算机视觉漫谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 1.前言
  • 2.算法
    • 2.1算法主循环
      • 2.2 Policy Gradient部分
      • 3. 结果分析
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档