转自 我的博客
DQN (Nature)
一、 算法流程:
-
定义可配置参数
- episode 数量 M
- 最大仿真时间 T,
ϵ
−
g
r
e
e
d
y
\epsilon-greedy
ϵ−greedy参数
ϵ
l
o
w
\epsilon_{low}
ϵlow,
ϵ
h
i
g
h
\epsilon_{high}
ϵhigh
- batch size
N
N
N
- 折扣率
γ
\gamma
γ,学习率
α
\alpha
α等优化器参数
- Soft update 频率
C
C
C
-
初始化
- 初始化 replay buffer 大小N
- 初始化 Q 网络
Q
Q
Q ,使用随机权值
θ
\theta
θ
- 初始化 TargetQ 网络
Q
^
\hat{Q}
Q^ 权值
θ
−
\theta^-
θ−,使用 Q 网络的权值
θ
\theta
θ
-
DQN 一个Episode的流程
- 使用
ϵ
−
g
r
e
e
d
y
\epsilon-greedy
ϵ−greedy 策略 选择一个 action
a
t
a_t
at
- 执行当前 action
a
t
a_t
at, 获取下一个状态
s
t
+
1
s_{t+1}
st+1 和 reward
r
t
r_{t}
rt
- 将当前状态
s
t
s_t
st赋值为下一个状态
s
t
+
1
s_{t+1}
st+1
- 将五元组$\langle s_t,a_t,r_t,s_{t+1},done \rangle $存入 replay buffer
D
D
D
- 训练Q网络
Q
Q
Q:
- **[Pre-condition]**训练网络的前提是 **replay buffer 的已有五元组数量大于 batch size **
N
N
N
- 从 replay buffer
D
D
D中随机选取 batch size
N
N
N条数据
⟨
s
j
,
a
j
,
r
j
,
s
j
+
1
,
d
o
n
e
⟩
\langle s_j,a_j,r_j,s_{j+1},done\rangle
⟨sj,aj,rj,sj+1,done⟩
D
s
e
l
e
c
t
e
d
D_{selected}
Dselected
- 计算目标Q值
y
y
y,
y
y
y是一个向量,
{
y
j
∈
y
∣
j
∈
[
0
,
N
]
}
\{y_j \in y |j\in[0,N]\}
{yj∈y∣j∈[0,N]},大小为 batch size
N
N
N
- 当
D
s
e
l
e
c
t
e
d
D_{selected}
Dselected[j] 中
d
o
n
e
=
T
r
u
e
done=True
done=True 时,即终局状态,此时
y
j
=
r
j
y_j=r_j
yj=rj
- 当
D
s
e
l
e
c
t
e
d
D_{selected}
Dselected[j] 中
d
o
n
e
=
F
a
l
s
e
done=False
done=False 时,即非终局状态,此时
y
i
=
r
j
+
γ
m
a
x
a
′
Q
^
(
s
j
+
1
,
a
′
;
θ
−
)
y_i=r_j+\gamma max_{a'}\hat{Q}(s_{j+1},a';\theta^-)
yi=rj+γmaxa′Q^(sj+1,a′;θ−), 注意这里是用的 TargetQ 网络进行的
- 使用优化器进行梯度下降,损失函数是(一个batch里面)
(
y
−
Q
(
s
,
a
;
θ
)
)
2
(y-Q(s,a;\theta))^2
(y−Q(s,a;θ))2,注意这里使用的是Q网络进行,来让计算出来的目标Q值与当前Q网络输出的Q值进行MSE
- 每
C
C
C 次 episode,soft update 一次 target net 参数,
θ
−
=
θ
\theta^- = \theta
θ−=θ
-
不断迭代Episode流程
M
M
M次
二、对应代码
完整代码地址: Nature DQN
-
初始化
- 初始化 replay buffer 大小N
- 初始化 Q 网络
Q
Q
Q ,使用随机权值
θ
\theta
θ
- 初始化 TargetQ 网络
Q
^
\hat{Q}
Q^ 权值
θ
−
\theta^-
θ−,使用 Q 网络的权值
θ
\theta
θ
def create_Q_network(self):
"""
Q net 网络定义
:return:
"""
self.state_input = tf.placeholder("float", [None, self.state_dim])
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)
self.Q_value = tf.matmul(h_layer, W2) + b2
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)
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')
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)
-
ϵ
−
g
r
e
e
d
y
\epsilon-greedy
ϵ−greedy 策略 定义,这里对
ϵ
\epsilon
ϵ进行一个随时间步的迁移而减小的策略,使其动作选择的不确定性逐渐减小。
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)
-
Replay buffer的定义
def perceive(self, state, action, reward, next_state, done):
"""
Replay buffer
:param state:
:param action:
:param reward:
:param next_state:
:param done:
:return:
"""
one_hot_action = np.zeros(self.action_dim)
one_hot_action[action] = 1
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()
-
Q网络训练
def train_Q_network(self):
"""
Q网络训练
:return:
"""
self.time_step += 1
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]
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
})
-
Soft update
def update_target_q_network(self, episode):
if episode % REPLACE_TARGET_FREQ == 0:
三、实验结果
环境 cart-pole-v0 (期望回报是200)
四、DQN参考论文流程:
五、Double DQN
DQN存在的问题是Q function容易过拟合,根据状态
s
t
+
1
s_{t+1}
st+1 选择动作
a
t
+
1
a_{t+1}
at+1 的过程,以及估计
Q
(
s
t
+
1
,
a
t
+
1
)
Q(s_{t+1},a_{t+1})
Q(st+1,at+1) 使用的同一个Q net网络参数,这可能导致选择过高的估计值,从而导致过于乐观的值估计。为了避免这种情况的出现,可以对选择和衡量进行解耦,从而就有了使用 Double DQN 来解决这一问题。
Double DQN与DQN的区别仅在于
y
y
y的求解方式不同,Double DQN根据Q网络参数来选择动作
a
t
+
1
a_{t+1}
at+1,再用Target Q网络参数来衡量
Q
(
s
t
+
1
,
a
t
+
1
)
Q(s_{t+1},a_{t+1})
Q(st+1,at+1)的值。
Y
t
D
Q
N
=
R
t
+
1
+
γ
Q
^
(
S
t
+
1
,
a
r
g
m
a
x
a
Q
^
(
S
t
+
1
,
a
;
θ
t
−
)
;
θ
t
−
)
Y
t
D
D
Q
N
=
R
t
+
1
+
γ
Q
^
(
S
t
+
1
,
a
r
g
m
a
x
a
Q
(
S
t
+
1
,
a
;
θ
t
)
;
θ
t
−
)
Y_t^{DQN}=R_{t+1}+\gamma \hat{Q}(S_{t+1},argmax_a\hat{Q}(S_{t+1},a;\theta_t^-);\theta_t^-)\\ Y_t^{DDQN}=R_{t+1}+\gamma \hat{Q}(S_{t+1},argmax_aQ(S_{t+1},a;\theta_t);\theta_t^-)
YtDQN=Rt+1+γQ^(St+1,argmaxaQ^(St+1,a;θt−);θt−)YtDDQN=Rt+1+γQ^(St+1,argmaxaQ(St+1,a;θt);θt−)
反映在代码上,就是训练的时候选择Q的时候有点变动:
def train_Q_network(self):
"""
Q网络训练
:return:
"""
self.time_step += 1
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]
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:
if DOUBLE_DQN:
selected_q_next = QTarget_value_batch[i][np.argmax(Q_value_batch[i])]
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对比
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)