• Home
  • About
    • Dr.Zee Blog photo

      Dr.Zee Blog

      Xiangyu Zhang;PHD student;Reinforcement learning, intelligent wireless communication

    • Learn More
    • Email
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

OPENAI-Baeslines-详解(三)-DDPG中文

19 Mar 2019

Reading time ~4 minutes

DDPG 深度确定性策略梯度下降算法。论文链接。采用了Actor-Critic 架构,可以有效的处理连续域的问题。

同时,其actor的确定性动作输出,提高了采样的有效性。

Actor-Critic and DPG

强化学习算法的主要目标是去学习一个策略,来指导agent与环境交互从而得到更好的收益。策略$\pi_{\theta}(a s)$是以$\theta$为参数的概率分布,代表不同状态下所采用的动作的概率分布。在学习的过程中不断的改变该函数的参数 $\theta$,从而改变应对环境的策略,以得到更好的奖励。当策略固定时,其所遍历的状态动作概率可以表示为

对单个状态而言,其到达概率为:

那么在策略$\pi_{\theta}(a s)$下得到的期望收益可以表示为:
实际上 DDPG是DPG算法利用深度神经网络去逼进 策略$\pi_{\theta}(a s)$和期望$Q$。$Q$函数的更新 需要与DQN类似:

所以$Q$函数更新的loss可以表示为: 这样我们需要2组神经网络,其中一组 用来生成现在的状态S和动作A 另一组 用于生成 未来$Q$函数估值 $Q\left(s^{\prime}, a^{\prime} ; \theta\right)$ 一组用于更新当前$Q(s, a ; \theta)$网络。

1564451525193

调用DDPG

根据OPENAI-Baeslines-详解(一)中,需要在learning中传入的DDPG的参数。

在DDPG进行学习的时候,分为多个epoch。

每个epoch 中 有进行 多个cycles ,每个cycles ,进行rollout次采样 、train_steps次训练和eval_steps次评估。

total_step = epochs * epoch_cycles* rollout

总步数= 总回合数 * 每个回合的循环运行次数 * 每个回合与环境交互的次数。

一个回合 不等于 一个episode 。

由于可以使用多个环境并行采样,所以 在一个cycle中 多个环境同时采样,每个环境都采样rollout次,无论这个环境是否done。

有可能这个环境已经提前done了 ,他也要继续采样,到rollout次结束。

训练步数和 评估步数是不算在其中的

  • 必备参数
network, env,
seed=None,
 
 # 总步数 和总回合数 只能存在一个
 # 若两个都不存在,那么epoch为500 
total_timesteps=None,             # 总步数
nb_epochs=None,                   # 总回合数 
nb_epoch_cycles=20,               # 每个回合的循环运行次数
nb_rollout_steps=100,             # 每个回合与环境交互的次数
nb_train_steps=50,                # 每个回合训练次数
nb_eval_steps=100,                # 每个回合的评估次数
eval_env=None, 			    # 在每个回合训练完成之后,开始测试环境的步数。

render=False,                     # 是否显示交互
render_eval=False,
noise_type='adaptive-param_0.2',

  • 超参数
    gamma=0.99,
    critic_l2_reg=1e-2,                 # critic正则化约束
    actor_lr=1e-4,                      # actor 学习率
    critic_lr=1e-3,                     # critic 学习率
    tau=0.01,                           # 软切换 的参数
    **network_kwarg                     # 网络参数
    
  • 技巧参数参数
    reward_scale=1.0,                   # 奖励的剪裁
    normalize_returns=False,            # 
    normalize_observations=True,        # 是否对噪声归一化
    popart=False,                       # 自适应Q值剪裁
    clip_norm=None,			    # 将输出的模裁剪到一定范围内
    #如果输出的为t 那么操作为t * clip_norm / l2norm(t)
    batch_size=64, # per MPI worker
    param_noise_adaption_interval=50,3
    

观察baseline中的输出

再运行程序的最后会得到progress.csv 输出的结果分为三个方面:

  • 样本的输出

    • ‘ret_rms_mean’,’ret_rms_std’
    • ‘obs_rms_mean’,’obs_rms_std’ # 固定样本的观察的 均值 和 方差
    • ‘reference_Q_mean’,’reference_Q_std’
      固定样本的Q值的 均值 和 方差 由样本的动作和状态 经由 critic 直接进行计算。
    • ‘reference_actor_Q_mean’,’reference_actor_Q_std’ 固定样本的Q值 的 均值 和 方差 由样本状态 经由actor 输出动作 然后 给critic 进行计算。
    • ‘reference_action_mean’ ‘reference_action_std’ 动作均值和方差
    • ‘reference_perturbed_action_mean’ ‘reference_perturbed_action_std’ 加入噪声之后的动作均值和方差
  • 本次epoch 的样本的输出

    • ‘rollout/return’ # 从训练开始到现在的奖励均值
    • ‘rollout/return_std’ # 从训练开始到现在的奖励方差
    • ‘rollout/return_history’ # 100步 奖励均值
    • ‘rollout/return_history_std’ # 100步 奖励方差
    • ‘rollout/episode_steps’ # 从训练开始到现在的 每个episode 的步数。
    • ‘rollout/actions_mean’ # 从训练开始到现在的动作平均
    • ‘rollout/Q_mean’ # 从训练开始到现在的Q值平均
  • 总共的

    • ‘train/loss_actor’ # 本epoch 的actor的loss
    • ‘train/loss_critic’ # 本epoch 的critic的loss
    • ‘train/param_noise_distance’ # 本epoch 的actor的loss
    • ‘total/duration’ # 总共持续的时间
    • ‘total/steps_per_second’ # 每一步所花的时间
    • ‘total/episodes’ # 总共完成的回合数

    • ‘rollout/episodes’

      ##### 一个epoch完成的回合数 episodes(这边有一个小bug,弟124行的 epoch_episodes = 0 应该在for循环下面)

    • ‘rollout/actions_std’ # 动作平均

Baseline 中的DDPG

DDPG文件夹下包含以下5个文件:

  • ddpg 主要程序 主要是 runner
  • ddpg—learner DDPG算法核心 主要是生成agent
  • memory 记忆库
  • models 神经网络
  • noise 增加噪声

DDPG 主程序

初始化

建立网络 63~65行

memory = Memory(limit=int(1e6), action_shape=env.action_space.shape, observation_shape=env.observation_space.shape) # 创建记忆库
critic = Critic(network=network, **network_kwargs) # critic 网络
actor = Actor(nb_actions, network=network, **network_kwargs) # actor 网络

67~84行 创建noise模型 ,noise 主要作用是用于增大探索

89行 调用ddpg—learner 创建agent 并开始循环与环境交互。

这里可以同时对多个环境 进行探索。

每个循环 有 epoch 、cycle、

每个epoch 需要有多个cycle 每个 cycle 中 rollout_step 次与环境交互 train_step 次进行训练。

for epoch in range(nb_epochs):   
    for cycle in range(nb_epoch_cycles):

与环境交换阶段

        # reset环境
        if nenvs > 1:     
            agent.reset()
        for t_rollout in range(nb_rollout_steps):   
            # 输出动作
            action, q, _, _ = agent.step(obs, apply_noise=True, compute_Q=True)
         	  # 动作都是归一化在-1到1之间
            new_obs, r, done, info = env.step(max_action * action)  

            t += 1
            if rank == 0 and render:
                env.render()
            episode_reward += r
            episode_step += 1

            # 存进memory
            epoch_actions.append(action)
            epoch_qs.append(q)
            agent.store_transition(obs, action, r, new_obs, done) 
            # 新旧 状态更新
            obs = new_obs
            for d in range(len(done)):  # 对每一个agent进行reset
                if done[d]:
                    if nenvs == 1:
                        agent.reset()

训练阶段

for t_train in range(nb_train_steps):
   	# 噪声更新
	if memory.nb_entries >= batch_size and t_train % param_noise_adaption_interval == 0:
      distance = agent.adapt_param_noise()
      epoch_adaptive_distances.append(distance)
     # agent 训练
   	cl, al = agent.train()

##### ddpg—learner

该类下,主要包含了各种DDPG中所需要包含的操作,包括利用状态值的actor 和critic 的 前向传播

、保存数据到经验池、从经验池提取数据 进行 后向传播训练、噪声的增加以及初始化等工作。

1、创建target—net、及其更新函数

创建target—network 120-126行

target_actor = copy(actor)          
target_actor.name = 'target_actor'
self.target_actor = target_actor

target_critic = copy(critic)
target_critic.name = 'target_critic'
self.target_critic = target_critic

创建 target—net的更新

# 先创建单个网络函数   36行定义的函数
def get_target_updates(vars, target_vars, tau)
# 返回的是两组操作op,一组是硬更新 一组是软更新。
# 每组更新都是一个对每一个参数 进行 更新。
	return tf.group(*init_updates), tf.group(*soft_updates)

# 2个网络的更新函数   149行 class 中定义的函数	
def setup_target_network_updates(self)
	可以得到self.target_init_updates  self.target_soft_updates 
2、actor 和critic 的 前向传播

128行 首先需要创建loss 以及 创建actor 与 critic之间的链接

# actor
self.actor_tf = actor(normalized_obs0)
# critic 输入中的动作位置 为placeholder  
self.normalized_critic_tf = critic(normalized_obs0, self.actions)
self.critic_tf = denormalize(tf.clip_by_value(self.normalized_critic_tf,self.return_range[0], self.return_range[1]), self.ret_rms)

# critic 输入中的动作位置 为actor的输出
self.normalized_critic_with_actor_tf = critic(normalized_obs0, self.actor_tf, reuse=True)
self.critic_with_actor_tf =denormalize(tf.clip_by_value(self.normalized_critic_with_actor_tf, self.return_range[0], self.return_range[1]), self.ret_rms)

# target Q值计算
Q_obs1 = denormalize(target_critic(normalized_obs1, target_actor(normalized_obs1)), self.ret_rms)
self.target_Q = self.rewards + (1. - self.terminals1) * gamma * Q_obs1

259行 step 函数 是在每次交互过程中 ,根据当前状态 前向传输。根据当前状态 求取动作和Q值

def step(self, obs, apply_noise=True, compute_Q=True):
	feed_dict = {self.obs0: U.adjust_shape(self.obs0, [obs])}# 送入数据
	# 利用网络计算动作和Q值 
      action, q = self.sess.run([actor_tf, self.critic_with_actor_tf],	feed_dict=feed_dict)  
      # 之后是为了增加噪声
      noise = self.action_noise()
	action += noise
      action = np.clip(action, self.action_range[0], self.action_range[1])
3、反向传播

172 行 创建actor 网络训练

$\nabla_{\theta} J\left(\pi_{\theta}\right)$是$Q$对actor的参数求导数。

采用的是 利用action 的输出作为输入的critic的输出

因为经验回放 更新actor的时候是对当前actor的参数求导,所以必须对当前actor输入state 然后求得action 再将此时的action和state 送入critic ,并最后得到Q值 来更新 actor 参数。

def setup_actor_optimizer(self):
	self.actor_loss = -tf.reduce_mean(self.critic_with_actor_tf)# Q值
	self.actor_grads = U.flatgrad(self.actor_loss, self.actor.trainable_vars, clip_norm=self.clip_norm) # 计算梯度
      self.actor_optimizer = MpiAdam(var_list=self.actor.trainable_vars,beta1=0.9, beta2=0.999, epsilon=1e-08)

183 行 创建critic 网络训练

更新critic的时候,从经验库中取得的数据,其reward 是当时state-action所得到的,而此时critic网络参数经由多次训练之后,发生了非常大的变化, 所以必须用当前的网络在计算一遍Q值然后,利用当前target 网络Q值和 当前 main 网络Q值 加上当时的reward 重新计算。

def setup_critic_optimizer(self):
 	normalized_critic_target_tf = tf.clip_by_value(normalize(self.critic_target, self.ret_rms), self.return_range[0], self.return_range[1])
	self.critic_loss = tf.reduce_mean(tf.square(self.normalized_critic_tf - normalized_critic_target_tf))
	# 187-196 在这里会对critic的loss 增加 l2 约束。
      self.critic_grads = U.flatgrad(self.critic_loss, self.critic.trainable_vars, clip_norm=self.clip_norm)  # 计算梯度 
      self.critic_optimizer = MpiAdam(var_list=self.critic.trainable_vars,
            beta1=0.9, beta2=0.999, epsilon=1e-08) # 反向训练      

289 行 train

def train(self):
	# 经验池随机采样
	batch = self.memory.sample(batch_size=self.batch_size)
	ops = [self.actor_grads, self.actor_loss, self.critic_grads, self.critic_loss]
      # 根据采样数据重新计算Q值等。
      actor_grads, actor_loss, critic_grads, critic_loss = self.sess.run(ops, feed_dict={
            self.obs0: batch['obs0'],
            self.actions: batch['actions'],
            self.critic_target: target_Q,
        })
      # 训练328 行
	self.actor_optimizer.update(actor_grads, stepsize=self.actor_lr)
      self.critic_optimizer.update(critic_grads, stepsize=self.critic_lr)
4、噪声

噪声主要是为了增加action的探索作用。噪声主要有两种 一种是 静态参数的 一种是 动态参数(未使用)

噪声的生成主要是通过首先对actor 进行 copy (155行函数)

def setup_param_noise(self, normalized_obs0):
	param_noise_actor = copy(self.actor)
     self.perturbed_actor_tf = param_noise_actor(normalized_obs0)
     

然后对copy后的actor的输出增加噪声

#50 行
def get_perturbed_actor_updates(actor, perturbed_actor, param_noise_stddev):
	# 增加均值为零 方差为param_noise_stddev的 高斯噪声
	updates.append(tf.assign(perturbed_var, var + tf.random_normal(tf.shape(var), mean=0., stddev=param_noise_stddev)))

执行增加噪声, 在step函数中直接选择

#259行 
if self.param_noise is not None and apply_noise:
    actor_tf = self.perturbed_actor_tf  # 注意 这里只选择了参数固定的噪声。
else:
    actor_tf = self.actor_tf

其他函数 def reset(self):# 初始化噪声

5、功能函数
# 初始化 将所有网络初始化、优化器初始化、硬更新一次target网络
def initialize(self, sess):
# 软更新target_net
def update_target_net(self):

# 通过从 数据库中采样数据并得到所有结果的函数
def setup_stats(self):
def get_stats(self):



GYMreinforcement learning Share Tweet +1