[强化学习笔记专题(二)]Nature DQN

DQN (Nature)

1

一、 算法流程:

  1. 定义可配置参数
    • episode 数量 M
    • 最大仿真时间 T,$\epsilon-greedy$参数$\epsilon_{low}$,$\epsilon_{high}$
    • batch size $N​$
    • 折扣率 $\gamma$,学习率 $\alpha$等优化器参数
    • Soft update 频率 $C​$
  2. 初始化
    • 初始化 replay buffer 大小N
    • 初始化 Q 网络 $Q​$ ,使用随机权值 $\theta​$
    • 初始化 TargetQ 网络 $\hat{Q}$ 权值 $\theta^-$,使用 Q 网络的权值 $\theta$
  3. DQN 一个Episode的流程
    • 使用 $\epsilon-greedy$ 策略 选择一个 action $a_t$
    • 执行当前 action $a_t$, 获取下一个状态 $s_{t+1}$ 和 reward $r_{t}$
    • 将当前状态$s_t$赋值为下一个状态 $s_{t+1}$
    • 将五元组$\langle s_t,a_t,r_t,s_{t+1},done \rangle $存入 replay buffer $D$
    • 训练Q网络$Q​$:
      • [Pre-condition]训练网络的前提是 replay buffer 的已有五元组数量大于 batch size $N$
      • 从 replay buffer $D​$中随机选取 batch size $N​$条数据$\langle s_j,a_j,r_j,s_{j+1},done\rangle​$ $D_{selected}​$
      • 计算目标Q值$y​$, $y​$是一个向量,$\{y_j \in y |j\in[0,N]\} ​$,大小为 batch size $N​$
        • 当 $D_{selected}​$[j] 中 $done=True​$ 时,即终局状态,此时 $y_j=r_j​$
        • 当 $D_{selected}$[j] 中 $done=False$ 时,即非终局状态,此时$y_i=r_j+\gamma max_{a’}\hat{Q}(s_{j+1},a’;\theta^-)$, 注意这里是用的 TargetQ 网络进行的
      • 使用优化器进行梯度下降,损失函数是(一个batch里面) $(y-Q(s,a;\theta))^2​$,注意这里使用的是Q网络进行,来让计算出来的目标Q值当前Q网络输出的Q值进行MSE
      • 每 $C$ 次 episode,soft update 一次 target net 参数,$\theta^- = \theta$
  4. 不断迭代Episode流程$M$次

img

二、对应代码

完整代码地址: Nature DQN

  1. 初始化
    • 初始化 replay buffer 大小N
    • 初始化 Q 网络 $Q$ ,使用随机权值$\theta$
    • 初始化 TargetQ 网络 $\hat{Q}$ 权值 $\theta^-$,使用 Q 网络的权值 $\theta$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    def create_Q_network(self):
"""
Q net 网络定义
:return:
"""
# 输入状态 placeholder
self.state_input = tf.placeholder("float", [None, self.state_dim])

# Q 网络结构 两层全连接
with tf.variable_scope('current_net'):
W1 = self.weight_variable([self.state_dim, 100])
b1 = self.bias_variable([100])
W2 = self.weight_variable([100, self.action_dim])
b2 = self.bias_variable([self.action_dim])
h_layer = tf.nn.relu(tf.matmul(self.state_input, W1) + b1)
# Q Value
self.Q_value = tf.matmul(h_layer, W2) + b2

# Target Net 结构与 Q相同,可以用tf的reuse实现
with tf.variable_scope('target_net'):
W1t = self.weight_variable([self.state_dim, 100])
b1t = self.bias_variable([100])
W2t = self.weight_variable([100, self.action_dim])
b2t = self.bias_variable([self.action_dim])
h_layer_t = tf.nn.relu(tf.matmul(self.state_input, W1t) + b1t)
# target Q Value
self.target_Q_value = tf.matmul(h_layer_t, W2t) + b2t

t_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='target_net')
e_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='current_net')

# soft update 更新 target net
with tf.variable_scope('soft_replacement'):
self.target_replace_op = [tf.assign(t, e) for t, e in zip(t_params, e_params)]

#===============================================================#
def weight_variable(self, shape):
"""
初始化网络权值(随机, truncated_normal)
:param shape:
:return:
"""
initial = tf.truncated_normal(shape)
return tf.Variable(initial)

#===============================================================#
def bias_variable(self, shape):
"""
初始化bias(const)
:param shape:
:return:
"""
initial = tf.constant(0.01, shape=shape)
return tf.Variable(initial)
  1. $\epsilon-greedy$ 策略 定义,这里对$\epsilon$进行一个随时间步的迁移而减小的策略,使其动作选择的不确定性逐渐减小。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def egreedy_action(self, state):
    """
    epsilon-greedy策略
    :param state:
    :return:
    """
    Q_value = self.Q_value.eval(feed_dict={
    self.state_input: [state]
    })[0]
    if random.random() <= self.epsilon:
    self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
    return random.randint(0, self.action_dim - 1)
    else:
    self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
    return np.argmax(Q_value)
  1. Replay buffer的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def perceive(self, state, action, reward, next_state, done):
    """
    Replay buffer
    :param state:
    :param action:
    :param reward:
    :param next_state:
    :param done:
    :return:
    """
    # 对action 进行one-hot存储,方便网络进行处理
    # [0,0,0,0,1,0,0,0,0] action=5
    one_hot_action = np.zeros(self.action_dim)
    one_hot_action[action] = 1

    # 存入replay_buffer
    # self.replay_buffer = deque()
    self.replay_buffer.append((state, one_hot_action, reward, next_state, done))

    # 溢出出队
    if len(self.replay_buffer) > REPLAY_SIZE:
    self.replay_buffer.popleft()

    # 可进行训练条件
    if len(self.replay_buffer) > BATCH_SIZE:
    self.train_Q_network()
  1. Q网络训练

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    def train_Q_network(self):
    """
    Q网络训练
    :return:
    """
    self.time_step += 1
    # 从 replay buffer D中随机选取 batch size N条数据<s_j,a_j,r_j,s_j+1,done> D_selected
    minibatch = random.sample(self.replay_buffer, BATCH_SIZE)
    state_batch = [data[0] for data in minibatch]
    action_batch = [data[1] for data in minibatch]
    reward_batch = [data[2] for data in minibatch]
    next_state_batch = [data[3] for data in minibatch]

    # 计算目标Q值y
    y_batch = []
    Q_value_batch = self.target_Q_value.eval(feed_dict={self.state_input: next_state_batch})
    for i in range(0, BATCH_SIZE):
    done = minibatch[i][4]
    if done:
    y_batch.append(reward_batch[i])
    else:
    y_batch.append(reward_batch[i] + GAMMA * np.max(Q_value_batch[i]))

    self.optimizer.run(feed_dict={
    self.y_input: y_batch,
    self.action_input: action_batch,
    self.state_input: state_batch
    })
  1. Soft update

    1
    2
    3
    def update_target_q_network(self, episode):
    # 更新 target Q netowrk
    if episode % REPLACE_TARGET_FREQ == 0:

三、实验结果

环境 cart-pole-v0 (期望回报是200)


四、DQN参考论文流程:

img

五、Double DQN

DQN存在的问题是Q function容易过拟合,根据状态 $s_{t+1}$ 选择动作 $a_{t+1}$ 的过程,以及估计 $Q(s_{t+1},a_{t+1})​$ 使用的同一个Q net网络参数,这可能导致选择过高的估计值,从而导致过于乐观的值估计。为了避免这种情况的出现,可以对选择和衡量进行解耦,从而就有了使用 Double DQN 来解决这一问题。

Double DQN与DQN的区别仅在于$y$的求解方式不同,Double DQN根据Q网络参数来选择动作$a_{t+1}$,再用Target Q网络参数来衡量$Q(s_{t+1},a_{t+1})$的值。

反映在代码上,就是训练的时候选择Q的时候有点变动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def train_Q_network(self):
"""
Q网络训练
:return:
"""
self.time_step += 1
# 从 replay buffer D中随机选取 batch size N条数据<s_j,a_j,r_j,s_j+1,done>$ D_selected
minibatch = random.sample(self.replay_buffer, BATCH_SIZE)
state_batch = [data[0] for data in minibatch]
action_batch = [data[1] for data in minibatch]
reward_batch = [data[2] for data in minibatch]
next_state_batch = [data[3] for data in minibatch]

# 计算目标Q值y
y_batch = []
QTarget_value_batch = self.target_Q_value.eval(feed_dict={self.state_input: next_state_batch})
Q_value_batch = self.Q_value.eval(feed_dict={self.state_input: next_state_batch})
for i in range(0, BATCH_SIZE):
done = minibatch[i][4]
if done:
y_batch.append(reward_batch[i])
else:
#################用target Q(Q)#######################
if DOUBLE_DQN:
selected_q_next = QTarget_value_batch[i][np.argmax(Q_value_batch[i])]
#################用target Q(target Q)################
else:
selected_q_next = np.max(QTarget_value_batch[i])

y_batch.append(reward_batch[i] + GAMMA * selected_q_next)

self.optimizer.run(feed_dict={
self.y_input: y_batch,
self.action_input: action_batch,
self.state_input: state_batch
})

六、DQN,DDQN实验结果对比

可以看到DoubleDQN的表现比 DQN稳定

七、 Dueling DDQN

八、 Dueling DQN, DDQN, DQN对比

本文标题:[强化学习笔记专题(二)]Nature DQN

文章作者:zhkmxx930

发布时间:2019年01月24日 - 16:01

最后更新:2019年02月17日 - 21:02

原始链接:https://zhkmxx9302013.github.io/post/22518.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

一分钱也是爱,mua~