Efficient exploration for largescale reinforcement lear
作者:禅与计算机程序设计艺术
1.简介
一、背景介绍
近年来,在人工智能领域取得了长足的发展的同时
然而,在现有方法中仍存在一些局限性。首先,在现有的应用范围内仅能处理已知的状态空间。对于那些无法准确预测的状态或具有不确定状态转移概率的复杂问题,则难以有效应对。其次,在实际应用中还需要学会如何选择合适的行为以最大化奖励。第三点是通常采用采样与探索相结合的方式进行行为优化。不过这种策略虽然有助于探索更多可能性却可能导致效率低下甚至效果不佳的情况出现
为了解决这些问题
本文将阐述分布随机性策略梯度(DRAG)的原理、模型、方法及其关键点。
二、基本概念术语说明
行为策略:定义为通过学习获得奖励的行为;行为策略并非环境真实动作;它基于环境状态作出决策。
政策分布函数(Policy Distribution Function, PDF)针对给定的状态s进行描述。这种描述涵盖了不同可能的动作及其对应的可能性大小。在实际应用中,默认情况下主要采用两种类型:一种是能直截了当地给出每个可选动作对应的具体概率值的方法;另一种则是通过求解贝尔曼期望方程来间接估计这些可能性大小的方法。
-
混合精度策略(Mixed Precision Policy):该策略指的是在不同位置上采用不同的数据类型精度,在混合精度训练的过程中可以在一定程度上提高准确性。DRAG算法中采用了并行训练两个浮点模型的方法,在同一个时间段内同时训练一个半浮点模型和一个全浮点模型,并让它们共同学习策略。
-
噰音(Noise):指agent behavior与true environmental behavior之间的差异(difference),主要包含两种类型:微小扰动(micro-perturbation)和系统噪声(system noise)。微小扰动是指那些变化幅度极小的干扰(interference),例如随机干扰(random interference)。系统噪声则涉及固定且不可预测的因素,并由其他变量来调节其影响(influence)。
三、核心算法原理和具体操作步骤以及数学公式讲解
(1)模型
分布随机性策略梯度(Distribution Randomization Gradient, DRAG)的基本原理是通过引入噪声干扰神经网络参数来生成潜在的行为方案,并以此增强智能体的探索能力。该方法包含两个关键神经网络:一个是专注于真实环境的数据建模(记为θ_d),另一个是专门处理代理行为评估(记为θ_φ)。其中后者的主要目标是精确建模辅助决策过程的关键指标Q_φ(s,a;θ_φ),以便能够基于此估计特定状态下行为的价值并实现奖励最大化目标;前者则无需独立训练,在实际应用中仅负责接收真实的环境反馈并与其预期值进行对比以计算训练损失
具体来说,DRAG训练时,按照如下方式进行:
更新规则:
在第t步时进行更新:
- \Delta \hat{\boldsymbol{\Theta}} = \alpha [r + \gamma Q(s', \text{argmax}_{a'} \pi(\cdot | s', \hat{\boldsymbol{\Theta}}, \epsilon)) - Q(s, a | \hat{\boldsymbol{\Theta}})] \nabla_{\\boldsymbol{\Theta}_d} \ln \pi(\cdot | s, \hat{\boldsymbol{\Theta}}) , 当\epsilon = 0$时采用随机策略
- 对于深度目标网络(DQN),其参数更新公式定义如下:
\Delta d = -β[\hat{π}(a | s, θ_ϕ) - π^*(a | s, θ_d)]∇θ_d ln π_θd(a | s)
其中s,a表示当前的状态和动作;θd代表真实环境参数
- 对两套参数\theta_{\phi} 和\theta_d进行更新,使得两者之间的差距减小
训练目标函数:
V^{\pi_{\theta_{\phi}}}表示估计值函数,V^{\pi_{\theta_{\phi}}}(\pi_{\theta_{\phi}})可以分为两部分:
- 第一部分:真实环境的预测损失:
$$L_d = - V^{\pi { \phi }( \Theta { \psi }) } { Θ { d }}( s, a ) + r + γ V^{\pi {φ( Θψ) }} {Θd}( s', argmax_ {a'} q(s',a')) + β D( π_ {φθψ}|π_ {θd} )
$D_{KL}$表示Kullback-Leibler散度。 * 第二部分:代理环境的预测损失: $$L_{\phi}=V^{\pi_{\theta_{\phi}}}_{\theta_{\phi}}(s,a)-Q_{\phi}(s,a)
其中q_{\phi}表示代理动作评估函数,可以是基于深度学习的神经网络。
总体而言,在训练过程中,我们的目标是让代理的动作评估函数Q_{\phi}尽可能接近真实环境中的真实动作评估函数,并且能够满足DRAG约束条件。
(2)策略梯度法更新
采用策略梯度法(PG method),我们首先基于给定的政策参数\theta进行初始化;随后,在价值函数空间中求取价值函数关于\theta的变化率\nabla_\theta J(\theta);最后通过沿线性搜索方向更新相应的政策参数\theta以实现目标优化
假设选取动作的概率分布为\pi_{\theta}(a|s),则导数\nabla_{\theta}J(\theta)为:
该梯度等于在所有动作a下的期望值:即奖励加上折扣因子乘以状态转移后的价值函数与策略参数梯度的乘积。
在其中情况下,在策略梯度法更新过程中可迭代地用于该动作价值函数(action value function, Q-function)的计算中;然而,在DRAG更新方法中,在策略梯度法更新过程中可迭代地用于该动作价值函数(action value function, Q-function)的计算中;但DRAG更新方法中将该动作价值函数重新定义为对动作概率分布的一个近似:即通过估计动作概率分布来替代传统的Q-function计算方式。
即首先在代理动作评估网络中使用模型预测出最优的动作随后在真实环境中进行验证以估算真实动作的价值
(3)推断误差修正
该方法旨在通过分布随机性策略梯度(Distribution Randomization Gradient, DRAG)来训练代理动作评估网络。具体而言,在每一轮训练中,该方法首先生成一组服从特定分布的噪声向量ξ,并基于当前策略参数θ和所选的噪声向量ξ来计算相应的代理动作a~ = πθ(a|s, ξ)。
在其中,在这一场景中涉及到了多个关键要素:噪声向量ξ、由代理动作评估网络所决定的参数θ_φ以及输入状态s. 通过分析可以看出,在这种情况下生成的动作可能会与实际所需的执行行为存在较大差异. 因此有必要对这一误差进行修正以减少其影响,并使生成的动作能够尽量贴近真实的执行行为.
为实现此目的, DRAG基于推断误差校正方法 (Inference Error Correction, IEC) 进行了研究。这是一种基于分布统计的技术, 通过对分布随机性参数 (noise parameter) \delta=(\sigma,\mu) 及其对应的分散性 (dispersion measure) 的分析, 来校正噪声参数的具体数值。具体而言, 该方法旨在通过寻找一个合适的噪声向量 \xi, 使其在不同情况下都能较为准确地模拟真实动作:
- 在代理动作评估阶段 中 :即当真实环境中的实际操作与当前代理操作不一致时 **:此时数学表达式表示为a^\ast\neq a。
基于DRAG的定义可知,在实际应用中代理动作评估网络可能会对真实环境中的动作估计产生偏差。具体而言,在状态s下有Q_{\phi}(s,\xi;\hat{a};\theta_{\phi}) \neq Q_{\phi}(s,\hat{\xi};a; \theta_{\phi})这一误差项存在的情况下,则可以通过最小化该误差项来进一步优化并校正代理动作评估网络的表现
这里,\hat{a}代表代理行为;而\hat{\xi}则用于表示扰动型代理行为中的噪声量.基于这一优化机制,在实际操作中我们可以通过这一方法使生成的行为更加贴近真实的执行方式.
- 在策略更新阶段 :即当代理动作不等于真实动作时,即a^\ast\neq a。
由于在PG更新过程中,Q_{ϕ}(s,ξ;\hat{a};θ_ϕ)和Q_{ϕ}(s,\hat{ξ};a; θ_ϕ)之间的残留误差难以消除,因此需要相应进行修正。
这里,\hat{a}'是代理动作,即对DRAG算法的两个错误之一。
综上所述,DRAG算法主要依据以下思路:
- 基于分布随机性参数生成噪声向量
- 通过引入噪声向量对代理动作评估函数进行误差修正
- 根据改进后的代理动作评估函数更新策略模型参数
- 对代理模型及其相关参数进行更新优化
(4)分布随机性策略梯度(DRAG)约束
DRAG施加了特定的限制。通过以下方式固定了噪声参数\delta=(\sigma,\mu)。DRAG系统地限制了权重更新过程。
其中,
\Delta\theta_{\phi}^{\rho}被定义为其更新向量受权重参数ρ的影响,
而\Delta\theta_{\phi}^{l}则代表与权重无关的更新向量。
值得注意的是,
DRAG参数ρ调节了权重更新的比例。
在运算符符号中,
\circ运算符定义为向量对应元素相乘。
因此,
DRAG算法中的约束条件旨在确保不同更新方向相互抵消,
从而有效降低噪声参数扰动所带来的影响。
最后,DRAG算法的训练目标是:
具体而言,在这里我们用符号KL来代表Kullback-Leibler散度。特别是,在公式中使用的表达式 - \beta KL(\pi_{\theta_{\phi}}||\pi_{\theta_d}) 代表了真实环境与代理环境之间的差异性。
四、具体代码实例和解释说明
本节将详细阐述DRAG算法的具体实现。DRAG算法的源代码参考了OpenAI Baselines库,并且其具体实现路径位于baselines/ppo2/model.py文件中。
(1)初始化
第一步, 引入原始神经网络架构及其相关的参数设置, 并将其中的参数赋值给\theta_{\phi}变量. 接着, 分别初始化两个独立的人工智能模型作为后续优化的基础步骤. 在整个过程中, 不涉及相关的优化过程或更新操作.
import tensorflow as tf
from baselines import logger
import numpy as np
class Model:
def __init__(self, ob_space, ac_space):
self._sess = tf.Session()
# Create policy and target networks
with tf.variable_scope('model'):
pi_logits = self.build_policy(ob_space, ac_space)
oldpi_logits = self.build_policy(ob_space, ac_space)
# set up placeholders
atarg = tf.placeholder(dtype=tf.float32, shape=[None]) # Target advantage function (if applicable)
ret = tf.placeholder(dtype=tf.float32, shape=[None]) # Empirical return
lrmult = tf.placeholder(name='lrmult', dtype=tf.float32, shape=[]) # learning rate multiplier, updated with schedule
# Calculate the loss
ratio = tf.exp(pi_logits - oldpi_logits) # pnew / pold
surrgain = tf.reduce_mean(atarg * ratio)
# Calculate entropy
ent = tf.reduce_mean(-tf.nn.softmax(pi_logits)*tf.nn.log_softmax(pi_logits))
# Total loss
loss = -surrgain + ent*0.01
# Add DRAG constraint to the loss
with tf.variable_scope("random_network"):
theta_ph = []
for var in tf.trainable_variables():
if "model" not in var.name[:6]:
continue
param = tf.get_default_session().run(var)
if len(param.shape)==1 or param.shape[-1] == 1:#fc layer
new_param = tf.Variable(np.zeros((param.shape)), dtype=tf.float32)
else:
new_param = tf.Variable(np.zeros((param.shape[:-1])), dtype=tf.float32)
theta_ph.append(new_param)
grads = tf.gradients(loss, tf.trainable_variables())
drag_grads = [tf.where(tf.equal(grad, None), tf.ones_like(grad)*lr_ph,
tf.clip_by_value((-drag_coeff)*(grad/(tf.norm(grad)+1e-10))+grad, -grad_limit, grad_limit))*lr_ph
for grad in grads]
optimizer = tf.train.AdamOptimizer(learning_rate=adam_epsilon)
optimize_expr = optimizer.apply_gradients(zip(drag_grads, tf.trainable_variables()))
update_ops = tf.group(*[var.assign(tf.where(mask==1., var_, mask_*new_param_))
for var, new_param_, mask in zip(tf.trainable_variables(), theta_ph,
masks)])
# Define ops
self.loss_names = ['policy_loss', 'entropy']
self.train_model = tf.function(lambda adv, returns, lr_mult :
self._train_model(adv, returns, lr_mult))
self.act_model = tf.function(lambda obs : self._act_model(obs))
self.update_target = tf.function(lambda tau : self._update_target(tau))
self.initial_state = None
self._saver = tf.train.Saver()
def build_policy(self, ob_space, ac_space):
"""Build actor critic model."""
pass
代码解读
(2)网络构建
DRAG算法采用了两个独立的神经网络进行功能划分:其中一个是用于代理动作评估的模块,另一个是专门负责真实环境预测的任务模型。两者在架构设计上具有完全相同的配置细节,请参见图1的具体结构描述。
@staticmethod
def build_policy(ob_space, ac_space):
ob = U.get_placeholder(name="ob", dtype=tf.float32, shape=[None]+list(ob_space))
with tf.variable_scope("model", reuse=False):
activ = tf.tanh
h1 = activ(U.dense(ob, 32, "h1", weight_init=U.normc_initializer(1.0)))
h2 = activ(U.dense(h1, 32, "h2", weight_init=U.normc_initializer(1.0)))
logits = U.dense(h2, ac_space.n, "out", weight_init=U.normc_initializer(0.01))
vf = tf.reshape(activ(U.dense(h2, 1, "vffc", weight_init=U.normc_initializer(1.0))), [-1])
return logits, vf
代码解读
(3)训练过程
训练过程与Policy Gradient算法具有相似性,在估计动作价值函数时采用了重要性采样方法(Importance Sampling)。该方法基于采用新的变体,并改进了现有的Policy Gradient框架,在复杂任务中表现出更为卓越的效果。
其中一项参数\beta被定义为KL散度的惩罚因子。此外,在DRAG算法中引入了新的约束条件以增强噪声参数的稳定性,并使噪声参数保持恒定。
def _train_model(self, advs, returns, lr_mult):
"""
Takes a mini batch of experiences and computes the loss for the network.
Returns:
A dictionary mapping from loss name to scalar loss values.
"""
td_map = {self.actor.td_map[key]:advs[key] for key in self.actor.td_map}
td_map.update({self.ret_rms.std:np.sqrt(returns).mean(),
self.actor.lr_ph: lr_mult * self.actor.learning_rate,
})
if hasattr(self.actor,'masks'):
td_map[self.actor.masks]=masks
if hasattr(self.actor,'mask_ops'):
self.actor.mask_ops.eval()
return self.sess.run([self.actor.loss, self.actor.vf_loss, self.actor.update_op],
td_map)[0:3]
代码解读
