1.前言
这次的内容主要是针对上一下讲解的DDPG理论部分进行实战,实战效果如下:
image
2.代码
2.1 主结构
我们先看一下tensorboard输出的图:
乍一看有点复杂,但是我们拆开看就容易了。我们来看看
Actor
和Critic
中各有什么结构把。image
搭建的代码如下:
class Actor(object):
def __init__(self):
...
with tf.variable_scope('Actor'):
# 这个网络用于及时更新参数
self.a = self._build_net(S, scope='eval_net', trainable=True)
# 这个网络不及时更新参数, 用于预测 Critic 的 Q_target 中的 action
self.a_ = self._build_net(S_, scope='target_net', trainable=False)
...
class Critic(object):
def __init__(self):
with tf.variable_scope('Critic'):
# 这个网络是用于及时更新参数
self.a = a # 这个 a 是来自 Actor 的, 但是 self.a 在更新 Critic 的时候是之前选择的 a 而不是来自 Actor 的 a.
self.q = self._build_net(S, self.a, 'eval_net', trainable=True)
# 这个网络不及时更新参数, 用于给出 Actor 更新参数时的 Gradient ascent 强度
self.q_ = self._build_net(S_, a_, 'target_net', trainable=False)
2.2 Actor Critic
有了对Actor Critic
每个里面各两个神经网络结构的了解,我们再来具体看看他们是如何进行交流,传递信息的。我们从Actor
的学习更新方式开始说起。
这张图我们就能一眼看穿
Actor
的更新到底基于什么东西。可以看出,它使用了两个eval_net
,所以Actor
class中用于train的代码我们这样写:
with tf.variable_scope('policy_grads'):
# 这是在计算 (dQ/da) * (da/dparams)
self.policy_grads = tf.gradients(
ys=self.a, xs=self.e_params, # 计算 ys 对于 xs 的梯度
grad_ys=a_grads # 这是从 Critic 来的 dQ/da
)
with tf.variable_scope('A_train'):
opt = tf.train.AdamOptimizer(-self.lr) # 负的学习率为了使我们计算的梯度往上升, 和 Policy Gradient 中的方式一个性质
self.train_op = opt.apply_gradients(zip(self.policy_grads, self.e_params)) # 对 eval_net 的参数更新
同时下面也提到的传送给Actor
的a_grad
应该用Tensorflow怎么计算。这个a_grad
是Critic
class里面的。这个a
是来自Actor
根据s
计算而来的:
with tf.variable_scope('a_grad'):
self.a_grads = tf.gradients(self.q, a)[0] # dQ/da
而在Critic
中,我们用的东西简单一点
下面就是
Critic
更新时的代码
# 计算 target Q
with tf.variable_scope('target_q'):
self.target_q = R + self.gamma * self.q_ # self.q_ 根据 Actor 的 target_net 来的
# 计算误差并反向传递误差
with tf.variable_scope('TD_error'):
self.loss = tf.reduce_mean(tf.squared_difference(self.target_q, self.q)) # self.q 又基于 Actor 的 target_net
with tf.variable_scope('C_train'):
self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss)
最后我们建立并把Actor
和Critic
融合在一起的时候是这样写的。
actor = Actor(...)
critic = Critic(..., actor.a, actor.a_) # 将 actor 同它的 eval_net/target_net 产生的 a/a_ 传给 Critic
actor.add_grad_to_graph(critic.a_grads) # 将 critic 产出的 dQ/da 加入到 Actor 的 Graph 中去
2.3 经验池
以下是类似于DQN
中的记忆库代码,我们用一个class来建立。
class Memory(object):
def __init__(self, capacity, dims):
#用numpy初始化记忆库
self.capacity = capacity
self.data = np.zeros((capacity, dims))
self.pointer = 0
def store_transition(self, s, a, r, s_):
#保存每次记忆在numpy array里
transition = np.hstack((s, a, [r], s_))
index = self.pointer % self.capacity # replace the old memory with new memory
self.data[index, :] = transition
self.pointer += 1
def sample(self, n):
#随即从记忆库中抽取n个记忆进行学习
assert self.pointer >= self.capacity, 'Memory has not been fulfilled'
indices = np.random.choice(self.capacity, size=n)
return self.data[indices, :]
2.4 每回合算法
这里的回合算法只提到最重要的部分,省掉了一些没必要的,完整代码我在最后会附上我的github链接
var = 3 # 这里初始化一个方差用于增强 actor 的探索性
for i in range(MAX_EPISODES):
...
for j in range(MAX_EP_STEPS):
...
a = actor.choose_action(s)
a = np.clip(np.random.normal(a, var), -2, 2) # 增强探索性
s_, r, done, info = env.step(a)
M.store_transition(s, a, r / 10, s_) # 记忆库
if M.pointer > MEMORY_CAPACITY: # 记忆库头一次满了以后
var *= .9998 # 逐渐降低探索性
b_M = M.sample(BATCH_SIZE)
... # 将 b_M 拆分成下面的输入信息
critic.learn(b_s, b_a, b_r, b_s_)
actor.learn(b_s)
s = s_
if j == MAX_EP_STEPS-1:
break
完整代码:https://github.com/cristianoc20/RL_learning/tree/master/DDPG
参考:https://github.com/MorvanZhou/Reinforcement-learning-with-tensorflow
网友评论