往期博客:
基于值函数逼近的强化学习方法
Policy gradient 算法思想
基于策略:
Policy gradient 是 RL 中另外一个大家族, 他不像 Value-based 方法 (Q learning, Sarsa), 但他也要接受环境信息 (observation), 不同的是他要输出不是 action 的 value, 而是具体的那一个 action, 这样 policy gradient 就跳过了 value 这个阶段. 而且 Policy gradient 最大的一个优势是: 输出的这个 action 可以是一个连续的值, 之前我们说到的 value-based 方法输出的都是不连续的值, 然后再选择值最大的 action. 而 policy gradient 可以在一个连续分布上选取 action.
基于值与基于策略:
在值函数的方法中,我们迭代计算的是值函数,然后根据值函数对策略进行改进;而在策略搜索方法中,我们直接对策略进行迭代计算,也就是迭代更新参数值,直到累积回报的期望最大,此时的参数所对应的策略为最优策略。
比较一下值函数方法和直接策略搜索方法的优缺点:
- 直接策略搜索方法是对策略进行参数化表示,与值函数方中对值函数进行参数化表示相比,策略参数化更简单,有更好的收敛性。
- 利用值函数方法求解最优策略时,策略改进需要求解。
a r g arg arg max a Q θ ( s , a ) \max _{a} Q_{\theta}(s, a) maxaQθ(s,a),当要解决的问题动作空间很大或者动作为连续集时,该式无法有效求解。 - 直接策略搜索方法经常采用的随机策略,能够学习随机策略。可以将探索直接集成到策略之中。
与值函数方法相比,策略搜索方法也普遍存在缺点,比如:
- 策略搜索的方法容易收敛到局部最小值。
- 评估单个策略时并不充分,方差较大。
推导策略梯度:
用
τ
\tau
τ来表示一组状态-行为序列,
s
0
,
u
0
,
⋯
,
s
H
,
u
H
i
s_{0}, u_{0}, \cdots, s_{H}, u_{H_{i}}
s0,u0,⋯,sH,uHi,
R
(
τ
)
=
∑
t
=
0
H
R
(
s
t
,
u
t
)
\mathcal{R}(\tau)=\sum_{t=0}^{H} R\left(s_{t}, u_{t}\right)
R(τ)=∑t=0HR(st,ut)表示轨迹
τ
\tau
τ的回报,
P
(
τ
;
θ
)
P(\tau ; \theta)
P(τ;θ)表示轨迹
τ
\tau
τ 出现的概率;则强化学习的目标函数可表示为:
U
(
θ
)
=
E
(
∑
t
=
0
H
R
(
s
t
,
u
t
)
;
π
θ
)
=
∑
τ
P
(
τ
;
θ
)
R
(
τ
)
U(\theta)=E\left(\sum_{t=0}^{H} R\left(s_{t}, u_{t}\right) ; \pi_{\theta}\right)=\sum_{\tau} P(\tau ; \theta) R(\tau)
U(θ)=E(∑t=0HR(st,ut);πθ)=∑τP(τ;θ)R(τ)
强化学习的目标是找到最优参数
θ
\theta
θ使得:
推导得到了策略梯度公式为:
其中第一项 ∇ θ log P ( τ ; θ ) \nabla_{\theta} \log P(\tau ; \theta) ∇θlogP(τ;θ)是轨迹 τ \tau τ的概率随参数 θ \theta θ变化最陡的方向,参数在该方向进行更新时,若沿着正方向,则该轨迹 τ \tau τ的概率会变大,而沿着负方向进行更新时,该轨迹 τ \tau τ的概率会变小。再看第二项 R ( τ ) R(\tau) R(τ),该项控制了参数更新的方向和步长。 R ( τ ) R(\tau) R(τ)为正且越大则参数更新后该轨迹的概率越大; R ( τ ) R(\tau) R(τ)为负,则降低该轨迹的概率,抑制该轨迹的发生。
因此,对策略梯度从直观上进行理解时,我们发现策略梯度会增加高回报路径的概率,减小低回报路径的概率。
减小方差:
策略梯度的计算式:
策略梯度是无偏的,但是 方差很大 。我们引入常数基线b来减小方差。
- 首先,证明当回报中引入常数b时,策略梯度不变
- 求使得策略梯度的方差最小时的基线 b
进一步减小方差的方法: 修改回报函数
强化学习方法:
- 第一种称为(G(PO)MDP):
- 第二种称为策略梯度理论
softmax策略及其损失函数:
输入数据有三项:
第一项,小车倒立摆的状态 s;
第二项,作用在小车上的动作 a;
第三项,每个动作对应的累计回报 v;
第一项,小车倒立摆的状态是与环境交互得到的;第二项,作用在小车上的动作a是由采样网络得到的,在训练过程中充当标签作用;第三项,每个动作对应的累计回报是由该动作后的回报进行累计并经归一化处理得到的。
因此,代码可以分几个关键的函数: 策略神经网络的构建,动作选择函数,损失函数的构建,累计回报函数v的处理。
import numpy as np
import tensorflow as tf
np.random.seed(1)
tf.set_random_seed(1)
class PolicyGradient:
def __init__(
self,
n_actions,
n_features,
learning_rate=0.01,
reward_decay=0.95,
output_graph=False,
):
#动作空间的维数
self.n_actions = n_actions
#状态特征的维数
self.n_features = n_features
#学习速率
self.lr = learning_rate
#回报衰减率
self.gamma = reward_decay
#一条轨迹的观测值,动作值,和回报值
self.ep_obs, self.ep_as, self.ep_rs = [],[],[]
#创建策略网络
self._build_net()
#启动一个默认的会话
self.sess = tf.Session()
if output_graph:
tf.summary.FileWriter("logs/", self.sess.graph)
# 初始化会话中的变量
self.sess.run(tf.global_variables_initializer())
#创建策略网络的实现
def _build_net(self):
with tf.name_scope('input'):
#创建占位符作为输入
self.tf_obs = tf.placeholder(tf.float32, [None, self.n_features], name="observations")
self.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions_num")
self.tf_vt = tf.placeholder(tf.float32, [None, ], name="actions_value")
#第一层
layer = tf.layers.dense(
inputs=self.tf_obs,
units=10,
activation=tf.nn.tanh,
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
bias_initializer=tf.constant_initializer(0.1),
name='fc1',
)
#第二层
all_act = tf.layers.dense(
inputs=layer,
units=self.n_actions,
activation=None,
kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3), # 生成具有正态分布的张量的初始化器
bias_initializer=tf.constant_initializer(0.1),
name='fc2'
)
#利用softmax函数得到每个动作的概率
self.all_act_prob = tf.nn.softmax(all_act, name='act_prob')
#定义损失函数
with tf.name_scope('loss'):
neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act,labels=self.tf_acts) # or in this way: neg_log_prob = tf.reduce_sum(-tf.log(self.all_act_prob)*tf.one_hot(self.tf_acts, self.n_actions), axis=1)
loss = tf.reduce_mean(neg_log_prob*self.tf_vt)
#定义训练,更新参数
with tf.name_scope('train'):
self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)
#定义如何选择行为,即状态s处的行为采样.根据当前的行为概率分布进行采样
def choose_action(self, observation):
prob_weights = self.sess.run(self.all_act_prob, feed_dict={self.tf_obs:observation[np.newaxis,:]})
#按照给定的概率采样
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())
return action
def greedy(self, observation):
prob_weights = self.sess.run(self.all_act_prob, feed_dict={self.tf_obs: observation[np.newaxis, :]})
action = np.argmax(prob_weights.ravel())
return action
#定义存储,将一个回合的状态,动作和回报都保存在一起
def store_transition(self, s, a, r):
self.ep_obs.append(s)
self.ep_as.append(a)
self.ep_rs.append(r)
#学习,以便更新策略网络参数,一个episode之后学一回
def learn(self):
#计算一个episode的折扣回报
discounted_ep_rs_norm = self._discount_and_norm_rewards()
#调用训练函数更新参数
self.sess.run(self.train_op, feed_dict={
self.tf_obs: np.vstack(self.ep_obs),
self.tf_acts: np.array(self.ep_as),
self.tf_vt: discounted_ep_rs_norm,
})
#清空episode数据
self.ep_obs, self.ep_as, self.ep_rs = [], [],[]
return discounted_ep_rs_norm
def _discount_and_norm_rewards(self):
#折扣回报和
discounted_ep_rs =np.zeros_like(self.ep_rs)
running_add = 0
for t in reversed(range(0, len(self.ep_rs))):
running_add = running_add * self.gamma + self.ep_rs[t]
discounted_ep_rs[t] = running_add
#归一化
discounted_ep_rs-= np.mean(discounted_ep_rs)
discounted_ep_rs /= np.std(discounted_ep_rs)
return discounted_ep_rs
代码解读:
with tf.name_scope('loss'):
# to maximize total reward (log_p * R) is to minimize -(log_p * R), and the tf only have minimize(loss)
# neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act, labels=self.tf_acts) # this is negative log of chosen action
# or in this way:
neg_log_prob = tf.reduce_sum(-tf.log(self.all_act_prob)*tf.one_hot(self.tf_acts, self.n_actions), axis=1)
loss = tf.reduce_mean(neg_log_prob * self.tf_vt) # reward guided loss
在损失函数处出现负号的原因是:TensorFlow中只有minimize,而我们的目的是让损失,也就是在概率较小时反而得到更大奖励
这一种行为更加有可能发生
精彩博客: