[PARL强化学习]连续动作空间上求解RL——DDPG
一、连续动作
生过中有很多动作不可数的情况,即是连续动作

为了解决连续动作的问题,我们需要使用Policy网络,输出一个确定性的策略,例如一个具体的浮点数等。常见做法就是在网络的最后增加一个tanh层。


二、DDPG简介
DDPG全称是Deep Deterministic Policy Gradient,即深度确定性策略梯度。
从DDPG这个名字看,它是由D(Deep)+D(Deterministic )+ PG(Policy Gradient)组成。那么它的特点也可以这么进行拆解。
DDPG的提出动机其实是为了让DQN可以扩展到连续的动作空间。DDPG借鉴了DQN的两个技巧:经验回放 和 固定Q网络。DDPG使用策略网络直接输出确定性动作。DDPG使用了Actor-Critic的架构。

DDPG是DQN的扩展版本,可以扩展到连续动作的控制空间。因此它在DQN的基础上增加了一层策略网络,用于输出动作值。DDPG需要一边学习Q网络,一边学习策略网路。

DDPG有4个网络,分别是 Actor当前网络、Actor目标网络、Critic当前网络、Critic目标网络。
-
Actor当前网络:负责策略网络参数θθ的迭代更新,负责根据当前状态SS选择当前动作AA,用于和环境交互生成S′,RS′,R。
-
Actor目标网络:负责根据经验回放池中采样的下一状态S′S′选择最优下一动作A′A′。网络参数θ′θ′定期从θθ复制。
-
Critic当前网络:负责价值网络参数ww的迭代更新,负责计算负责计算当前Q值Q(S,A,w)Q(S,A,w)。目标Q值yi=R+γQ′(S′,A′,w′)yi=R+γQ′(S′,A′,w′)
-
Critic目标网络:负责计算目标Q值中的Q′(S′,A′,w′)Q′(S′,A′,w′)部分。网络参数w′w′定期从ww复制。

DDPG从当前网络到目标网络的复制和我们之前讲到了DQN不一样。回想DQN,我们是直接把将当前Q网络的参数复制到目标Q网络,即w′=ww′=w, DDPG这里没有使用这种硬更新,而是使用了软更新,即每次参数只更新一点点,即:
$$
w′←τw+(1−τ)w′\
θ′←τθ+(1−τ)θ′
$$
三、算法流程

使用PARL框架实现整个分为了三个部分:

四、代码实践
使用DDPG解决连续控制版本的CartPole问题,给小车一个力(连续量)使得车上的摆杆倒立起来。
导入依赖
import gym
import numpy as np
from copy import deepcopy
import paddle.fluid as fluid
import parl
from parl import layers
from parl.utils import logger
设置超参数
ACTOR_LR = 1e-3 # Actor网络的 learning rate
CRITIC_LR = 1e-3 # Critic网络的 learning rate
GAMMA = 0.99 # reward 的衰减因子
TAU = 0.001 # 软更新的系数
MEMORY_SIZE = int(1e6) # 经验池大小
MEMORY_WARMUP_SIZE = MEMORY_SIZE // 20 # 预存一部分经验之后再开始训练
BATCH_SIZE = 128
REWARD_SCALE = 0.1 # reward 缩放系数
NOISE = 0.05 # 动作噪声方差
TRAIN_EPISODE = 6000 # 训练的总episode数
搭建Model、Algorithm、Agent架构
Agent把产生的数据传给algorithm,algorithm根据model的模型结构计算出Loss,使用SGD或者其他优化器不断的优化,PARL这种架构可以很方便的应用在各类深度强化学习问题中。
(1)Model
Model用来定义前向(Forward)网络,用户可以自由的定制自己的网络结构
class Model(parl.Model):
def __init__(self, act_dim):
self.actor_model = ActorModel(act_dim)
self.critic_model = CriticModel()
def policy(self, obs):
return self.actor_model.policy(obs)
def value(self, obs, act):
return self.critic_model.value(obs, act)
def get_actor_params(self):
return self.actor_model.parameters()
class ActorModel(parl.Model):
def __init__(self, act_dim):
hid_size = 100
self.fc1 = layers.fc(size=hid_size, act='relu')
self.fc2 = layers.fc(size=act_dim, act='tanh')
def policy(self, obs):
hid = self.fc1(obs)
means = self.fc2(hid)
return means
class CriticModel(parl.Model):
def __init__(self):
hid_size = 100
self.fc1 = layers.fc(size=hid_size, act='relu')
self.fc2 = layers.fc(size=1, act=None)
def value(self, obs, act):
concat = layers.concat([obs, act], axis=1)
hid = self.fc1(concat)
Q = self.fc2(hid)
Q = layers.squeeze(Q, axes=[1])
return Q
(2)Algorithm
Algorithm定义了具体的算法来更新前向网络(Model),也就是通过定义损失函数来更新Model,和算法相关的计算都放在algorithm中。
# from parl.algorithms import DDPG # 也可以直接从parl库中快速引入DDPG算法,无需自己重新写算法
class DDPG(parl.Algorithm):
def __init__(self,
model,
gamma=None,
tau=None,
actor_lr=None,
critic_lr=None):
""" DDPG algorithm
Args:
model (parl.Model): actor and critic 的前向网络.
model 必须实现 get_actor_params() 方法.
gamma (float): reward的衰减因子.
tau (float): self.target_model 跟 self.model 同步参数 的 软更新参数
actor_lr (float): actor 的学习率
critic_lr (float): critic 的学习率
"""
assert isinstance(gamma, float)
assert isinstance(tau, float)
assert isinstance(actor_lr, float)
assert isinstance(critic_lr, float)
self.gamma = gamma
self.tau = tau
self.actor_lr = actor_lr
self.critic_lr = critic_lr
self.model = model
self.target_model = deepcopy(model)
def predict(self, obs):
""" 使用 self.model 的 actor model 来预测动作
"""
return self.model.policy(obs)
def learn(self, obs, action, reward, next_obs, terminal):
""" 用DDPG算法更新 actor 和 critic
"""
actor_cost = self._actor_learn(obs)
critic_cost = self._critic_learn(obs, action, reward, next_obs,
terminal)
return actor_cost, critic_cost
def _actor_learn(self, obs):
action = self.model.policy(obs)
Q = self.model.value(obs, action)
cost = layers.reduce_mean(-1.0 * Q)
optimizer = fluid.optimizer.AdamOptimizer(self.actor_lr)
optimizer.minimize(cost, parameter_list=self.model.get_actor_params())
return cost
def _critic_learn(self, obs, action, reward, next_obs, terminal):
next_action = self.target_model.policy(next_obs)
next_Q = self.target_model.value(next_obs, next_action)
terminal = layers.cast(terminal, dtype='float32')
target_Q = reward + (1.0 - terminal) * self.gamma * next_Q
target_Q.stop_gradient = True
Q = self.model.value(obs, action)
cost = layers.square_error_cost(Q, target_Q)
cost = layers.reduce_mean(cost)
optimizer = fluid.optimizer.AdamOptimizer(self.critic_lr)
optimizer.minimize(cost)
return cost
def sync_target(self, decay=None, share_vars_parallel_executor=None):
""" self.target_model从self.model复制参数过来,可设置软更新参数
"""
if decay is None:
decay = 1.0 - self.tau
self.model.sync_weights_to(
self.target_model,
decay=decay,
share_vars_parallel_executor=share_vars_parallel_executor)
(3)Agent
Agent负责算法与环境的交互,在交互过程中把生成的数据提供给Algorithm来更新模型(Model),数据的预处理流程也一般定义在这里。
class Agent(parl.Agent):
def __init__(self, algorithm, obs_dim, act_dim):
assert isinstance(obs_dim, int)
assert isinstance(act_dim, int)
self.obs_dim = obs_dim
self.act_dim = act_dim
super(Agent, self).__init__(algorithm)
# 注意:最开始先同步self.model和self.target_model的参数.
self.alg.sync_target(decay=0)
def build_program(self):
self.pred_program = fluid.Program()
self.learn_program = fluid.Program()
with fluid.program_guard(self.pred_program):
obs = layers.data(
name='obs', shape=[self.obs_dim], dtype='float32')
self.pred_act = self.alg.predict(obs)
with fluid.program_guard(self.learn_program):
obs = layers.data(
name='obs', shape=[self.obs_dim], dtype='float32')
act = layers.data(
name='act', shape=[self.act_dim], dtype='float32')
reward = layers.data(name=

最低0.47元/天 解锁文章
536

被折叠的 条评论
为什么被折叠?



