算法介绍
除了DQN这种action value方法,还有policy gradients这种直接学习【参数化policy】的方法。 这样就不用将所有Q值都记录在table里,面对有无数action连续任务时,直接用【参数化policy】从action的分布中来选择action,效率更高。
算法原理
《Reinforcement Learning : An introduction》第十三章:Policy gradients
主要实现了书里的【REINFORCE 算法】
由于 REINFORCE 算法使用了完整的返回值 ,因此属于蒙特卡罗算法,只适用于 episodic 形式。
然后下面时莫烦大大给的伪代码,和书中的只有细节上的差异 :
下面重点列举一下Policy gradient(REINFORCE)和DQN的差异:
- 没有使用Q值,而使用action的分布,直接从分布中选择要进行的action
- 一个episod结束后才进行更新,上面的两个伪代码中的
和
就是就是一个total return
编程实现
build_net
以下为神经网络的结构图
与DQN不同之处:
- 全连接层FC
- softmax层输出概率
tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act,labels=self.tf_acts)
这个函数对all_act操作了两个步骤:
- Softmax
- 计算Cross-Entropy
tf.one_hot(self.tf_acts, self.n_actions)
tf.one_hot()函数是将input转化为one-hot类型数据输出,相当于将多个数值联合放在一起作为多个相同类型的向量,可用于表示各自的概率分布,通常用于分类任务中作为最后的FC层的输出,有时翻译成“独热”编码。
♥关于loss的解释:
neg_log_prob=tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act,labels=self.tf_acts)#同时进行了softmax和交叉熵操作,
# 输出的是交叉熵loss:=-Σylogy
#如果放在监督学习的分类问题上,logits就是预测值,labels就是标签真实值;
# 交叉熵loss就是将神经网络的参数按照这个真实标签改进;neg_log_prob就是error
# 下面是上面的展开形式,拆开了更好理解:
# 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) #vt就是total return G_t (vt = 本reward + 衰减的未来reward)
#为了确保这个动作真的是 “正确标签”, 我们的 loss 在原本的 cross-entropy 形式上乘以 vt, 用 vt 来告诉这个 cross-entropy 算出来的梯度是不是一个值得信任的梯度.
# 如果 vt 小, 或者是负的, 就说明这个梯度下降是一个错误的方向, 我们应该向着另一个方向更新参数,
# 如果这个 vt 是正的, 或很大, vt 就会称赞 cross-entropy 出来的梯度, 并朝着这个方向梯度下降.
choose_action
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())
p:The probabilities associated with each entry in a. If not given the sample assumes a uniform distribution over all entries in a.
示例解释如下:
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
x = tf.constant([[100., 88., 98.]])
tf.global_variables_initializer()
with tf.Session() as sess:
prob_weights=sess.run(tf.nn.softmax(x))
print(prob_weights.shape[1]) #3 3列
print(prob_weights.shape[0]) #1 1行
print(prob_weights)#[[8.8079226e-01 5.4117750e-06 1.1920227e-01]]
print(prob_weights.ravel())#[8.8079226e-01 5.4117750e-06 1.1920227e-01] 概率值
#.ravel()将向量拉回一维list
action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())
print(action)#随机输出0 1 2 表示第几个action