论文下载地址
论文介绍
Dueling Network与传统DQN的区别在于神经网络结构的不同,Dueling Netowrk在传统DQN的基础上只进行了微小的改动,但能大幅提升DQN的表现。从图9中可以看出,在传统DQN中,DQN的神经网络直接输出Q函数的值,即某个状态下不同动作对应的动作价值,输出层的前一层是全连接层。
Dueling Network对DQN的结构改进主要在全连接层上,它在全连接层和输出层之间进行修改,将全连接层改为两条流,其中一条输出关于状态的价值,另外一条输出关于动作的优势函数的值,最终将两条流合并得到Q动作价值。
在某些情况下,Dueling Network可以了解哪些状态有价值或没有价值,而不必了解每个动作对状态的影响(在一些状态中,不管选择哪个动作的影响都不大),这在agent的行为不会以任何方式影响到环境的情况下特别有用。
理论分析
假设在Atari游戏中,智能体在时间步 t t t根据由 M M M个视频帧组成的 s t = ( x t − M + 1 , . . . , x t ) ∈ S s_t=(x_{t-M+1},...,x_t)\in S st=(xt−M+1,...,xt)∈S,从动作空间 A = { 1 , . . . , ∣ A ∣ } \mathcal{A}=\{1,...,|\mathcal{A}|\} A={1,...,∣A∣}选择动作 a t a_t at ,并观测由模拟器产生的奖励信号 r t r_t rt 。
在一局游戏中,把从起始到结束的所有奖励记作 r 1 , . . . , r t , . . . , r n r_1,...,r_t,...,r_n r1,...,rt,...,rn,定义折扣回报率为 γ ∈ [ 0 , 1 ] \gamma \in [0,1] γ∈[0,1]。折扣回报的定义如公式(1)所示: R t = r t + γ ⋅ r t + 1 + γ 2 ⋅ r t + 2 + ⋯ + γ n − t ⋅ r n (1) R_t=r_t+\gamma \cdot r_{t+1}+\gamma^2 \cdot r_{t+2}+\cdots+\gamma^{n-t} \cdot r_n \tag{1} Rt=rt+γ⋅rt+1+γ2⋅rt+2+⋯+γn−t⋅rn(1)在游戏尚未结束的 t t t时刻, R t R_t Rt是一个随机变量,其随机性来自于 t t t时刻之后的所有状态和动作。动作价值函数的定义如公式(2)所示: Q π ( s t , a t ) = E [ R t ∣ S t = s t , A t = a t ] (2) Q_\pi\left(s_t, a_t\right)=\mathbb{E}\left[R_t \mid S_t=s_t, A_t=a_t\right] \tag{2} Qπ(st,at)=E[Rt∣St=st,At=at](2)公式(2)中的期望消除了 t t t时刻之后的所有状态 S t + 1 , . . . , S n S_{t+1},...,{S_n} St+1,...,Sn 与所有动作 A t + 1 , . . . , A n A_{t+1},...,A_n At+1,...,An,期望只与当前的状态 s t s_t st和动作 a t a_t at相关。上面介绍的状态动作价值函数可以用动态规划递归计算,如公式(3)所示: Q π ( s t , a t ) = E S t + 1 [ r + γ E A t + 1 [ Q π ( s t + 1 , a t + 1 ) ] ∣ S t = s t , A t = a t ] (3) Q_\pi(s_t, a_t)=\mathbb{E}_{S_{t+1}}\left[r+\gamma \mathbb{E}_{A_{t+1} }\left[Q_\pi\left(s_{t+1}, a_{t+1}\right)\right] \mid S_t=s_t, A_t=a_t\right] \tag{3} Qπ(st,at)=ESt+1[r+γEAt+1[Qπ(st+1,at+1)]∣St=st,At=at](3) 动作价值函数 Q π ( s t , a t ) Q_\pi(s_t,a_t) Qπ(st,at)依赖于 s t s_t st、 a t a_t at和 π \pi π,不依赖于 t + 1 t+1 t+1时刻及之后的状态和动作,因为随机变量 S t + 1 , A t + 1 , . . . , S n , A n S_{t+1},A_{t+1},...,S_n,A_n St+1,At+1,...,Sn,An 都被期望消除了。
为了消除策略 π \pi π的影响,只评价当前状态 s t s_t st和动作 a t a_t at的好坏,引出了最优动作价值函数,如公式(4)所示: Q ⋆ ( s t , a t ) = max π Q π ( s t , a t ) , ∀ s t ∈ S , a t ∈ A (4) Q_{\star}\left(s_t, a_t\right)=\max _\pi Q_\pi\left(s_t, a_t\right), \quad \forall s_t \in \mathcal{S}, \quad a_t \in \mathcal{A} \tag{4} Q⋆(st,at)=πmaxQπ(st,at),∀st∈S,at∈A(4)最优动作价值函数可以理解为有很多种策略函数 π \pi π可以选择,我们选择最好的策略函数,如公式(5)所示: π ⋆ = argmax π Q π ( s t , a t ) , ∀ s t ∈ S , a t ∈ A (5) \pi^{\star}=\underset{\pi}{\operatorname{argmax}} Q_\pi\left(s_t, a_t\right), \quad \forall s_t \in \mathcal{S}, \quad a_t \in \mathcal{A} \tag{5} π⋆=πargmaxQπ(st,at),∀st∈S,at∈A(5)
状态价值函数的定义如公式(6)所示: V π ( s t ) = E A t ∼ π ( ⋅ ∣ s t ) [ Q π ( s t , A t ) ] = ∑ a ∈ A π ( a ∣ s t ) ⋅ Q π ( s t , a ) . (6) \begin{aligned} V_\pi\left(s_t\right) & =\mathbb{E}_{A_t \sim \pi\left(\cdot \mid s_t\right)}\left[Q_\pi\left(s_t, A_t\right)\right] \\ & =\sum_{a \in \mathcal{A}} \pi\left(a \mid s_t\right) \cdot Q_\pi\left(s_t, a\right) . \end{aligned} \tag{6} Vπ(st)=EAt∼π(⋅∣st)[Qπ(st,At)]=a∈A∑π(a∣st)⋅Qπ(st,a).(6)公式(6)中把动作 A t A_t At作为随机变量,然后关于 A t A_t At求期望,把 A t A_t At消掉。状态价值函数 V π ( s t ) V_{\pi}(s_t) Vπ(st)只依赖于策略 π \pi π与当前状态 s t s_t st,不依赖于动作。状态价值函数 V π ( s t ) V_{\pi}(s_t) Vπ(st)也是折扣回报 R t R_t Rt的期望,如公式(7)所示: V π ( s t ) = E A t , S t + 1 , A t + 1 , ⋯ , S n , A n [ R t ∣ S t = s t ] (7) V_\pi\left(s_t\right)=\mathbb{E}_{A_t, S_{t+1}, A_{t+1}, \cdots, S_n, A_n}\left[R_t \mid S_t=s_t\right] \tag{7} Vπ(st)=EAt,St+1,At+1,⋯,Sn,An[Rt∣St=st](7)公式(7)中消除了 R t R_t Rt依赖的随机变量 A t , S t + 1 , A t + 1 , . . . , S n , A n A_t,S_{t+1},A_{t+1},...,S_n,A_n At,St+1,At+1,...,Sn,An,状态价值函数值越大,表示当前状态 S t S_t St回报的期望越大,用状态价值可以衡量策略 π \pi π与状态 s t s_t st的好坏。
为了消除策略 π \pi π的影响,只评价当前状态 s t s_t st的好坏,引出了最优状态价值函数,如公式(8)所示: V ⋆ ( s ) = max π V π ( s ) , ∀ s ∈ S (8) V_{\star}(s)=\max _\pi V_\pi(s), \quad \forall s \in \mathcal{S} \tag{8} V⋆(s)=πmaxVπ(s),∀s∈S(8)
我们定义优势函数,将状态值函数和状态动作值函数联系起来,如公式(9)所示: A π ( s , a ) = Q π ( s , a ) − V π ( s ) (9) A_\pi(s, a)=Q_\pi(s, a)-V_\pi(s) \tag{9} Aπ(s,a)=Qπ(s,a)−Vπ(s)(9) 公式(9)中, V ( s ) V(s) V(s)可以理解为在该状态 s s s下所有可能动作所对应的动作值函数乘以采取该动作的概率之和;通俗来说,值函数 V ( s ) V(s) V(s)是该状态下所有动作值函数关于动作概率的期望。而动作价值函数 Q ( s , a ) Q(s,a) Q(s,a)是单个动作所对应的值函数, Q ( s , a ) − V ( s ) Q(s,a)-V(s) Q(s,a)−V(s)表示当前动作值函数相对于平均值的大小。
所以优势函数表示的是当前动作值函数相比于当前状态值函数的优势,如果优势值大于零,表示当前动作比平均动作好,如果优势函数值小于0,表示当前动作比平均动作差。优势函数表示在当前状态下各个动作的相对好坏程度。
根据优势函数的定义,我们可以知道优势函数有以下性质,如公式(10)所示: E a ∼ π ( s ) [ A π ( s , a ) ] = E a ∼ π ( s ) [ Q π ( s , a ) − V π ( s ) ] = E a ∼ π ( s ) [ Q π ( s , a ) ] − V π ( s ) = V π ( s ) − V π ( s ) = 0 (10) \mathbb{E}_{a \sim \pi(s)}\left[A^\pi(s, a)\right]= \mathbb{E}_{a \sim \pi(s)}\left[Q_\pi(s, a)-V_\pi(s)\right] = \mathbb{E}_{a \sim \pi(s)}\left[Q_\pi(s, a)\right]-V_\pi(s)= V_\pi(s) - V_\pi(s) = 0\tag{10} Ea∼π(s)[Aπ(s,a)]=Ea∼π(s)[Qπ(s,a)−Vπ(s)]=Ea∼π(s)[Qπ(s,a)]−Vπ(s)=Vπ(s)−Vπ(s)=0(10)
优势函数刻画的是某个动作相对于平均值函数的“优势”。例如,在状态 s s s下有3个动作 a 1 a_1 a1, a 2 a_2 a2, a 3 a_3 a3,对应的 Q Q Q函数分别为 Q ( s , a 1 ) = 0.9 Q(s,a_1)=0.9 Q(s,a1)=0.9 Q ( s , a 2 ) = 1 Q(s,a_2)=1 Q(s,a2)=1 Q ( s , a 3 ) = 1.1 Q(s,a_3)=1.1 Q(s,a3)=1.1状态 s s s的状态值函数为: V ( s ) = E a [ Q ( s , a ) ] = ( 0.9 + 1 + 1.1 ) / 3 = 1.0 V(s)=\mathbb{E}_a[Q(s,a)]=(0.9+1+1.1)/3=1.0 V(s)=Ea[Q(s,a)]=(0.9+1+1.1)/3=1.0根据优势函数的定义,三个动作的优势函数值分别为: A ( s , a 1 ) = − 0.1 A(s,a_1)=-0.1 A(s,a1)=−0.1 A ( s , a 2 ) = 0 A(s,a_2)=0 A(s,a2)=0 A ( s , a 3 ) = 0.1 A(s,a_3)=0.1 A(s,a3)=0.1因此 a 1 a_1 a1对于平均水平(由 V V V定义)的优势为-0.1,表示该动作低于平均的值函数水平, a 2 a_2 a2等于平均水平, a 3 a_3 a3的优势高于平均水平,因此如果按照贪心策略来选择动作,应该选择动作 a 3 a_3 a3,最优动作的选择可以仅通过优势函数 A ( s , a ) A(s,a) A(s,a)得到,即如公式(11)所示: a ∗ = a r g max a A ( s , a ) = a r g max a [ A ( s , a ) + V ( s ) ] = a r g max a Q ( s , a ) (11) a^*=arg\space \max_{a}A(s,a)=arg \space \max_{a} [A(s,a) + V(s)] = arg \space \max_{a}Q(s,a) \tag{11} a∗=arg amaxA(s,a)=arg amax[A(s,a)+V(s)]=arg amaxQ(s,a)(11)
Dueling Network的核心思想是将动作值 Q ( s , a ) Q(s,a) Q(s,a)的计算分解成两部分:状态值和优势函数,即如公式(12)所示: Q ( s , a ) = V ( s ) + A ( s , a ) (12) Q(s,a)=V(s)+A(s,a) \tag{12} Q(s,a)=V(s)+A(s,a)(12)但是Dueling Network并不是直接基于公式(12)实现的,因为这会导致不唯一性。假如基于公式(12)实现Dueling Network,那么 V ( s ) V(s) V(s)和 A ( s , a ) A(s,a) A(s,a)可以随意上下波动,比如一个增大100,另一个减小100,这样的上下波动不影响最终的输出,如公式(13)所示: V ( s ; w ~ V ) ≜ V ( s ; w V ) + 100 , A ( s , a ; w ~ A ) ≜ A ( s , a ; w A ) − 100 V ( s ; w V ) + A ( s , a ; w A ) = V ( s ; w ~ V ) + A ( s , a ; w ~ A ) (13) \begin{aligned} V\left(s ; \tilde{\boldsymbol{w}}^V\right) & \triangleq V\left(s ; \boldsymbol{w}^V\right)+100, \\ A\left(s, a ; \tilde{\boldsymbol{w}}^A\right) & \triangleq A\left(s, a ; \boldsymbol{w}^A\right)-100 \tag{13}\\ V\left(s ; \boldsymbol{w}^V\right)+A\left(s, a ; \boldsymbol{w}^A\right) & =V\left(s ; \tilde{\boldsymbol{w}}^V\right)+A\left(s, a ; \tilde{\boldsymbol{w}}^A\right) \end{aligned} V(s;w~V)A(s,a;w~A)V(s;wV)+A(s,a;wA)≜V(s;wV)+100,≜A(s,a;wA)−100=V(s;w~V)+A(s,a;w~A)(13)这意味着 V V V和 D D D的参数可以很随意的变化却不会影响输出的 Q Q Q,这会导致训练过程中参数的不稳定,这不是我们希望看到的。为了解决这个问题,论文中强制令动作的优势函数的最大值最大只能为0,即对每个动作的优势函数减去最优动作的优势函数值,如公式(14)所示: Q ( s , a ; θ , α , β ) = V ( s ; θ , β ) + ( A ( s , a ; θ , α ) − max a ′ ∈ ∣ A ∣ A ( s , a ′ ; θ , α ) ) (14) Q(s, a ; \theta, \alpha, \beta)=V(s ; \theta, \beta)+ \left(A(s, a ; \theta, \alpha)-\max _{a^{\prime} \in|\mathcal{A}|} A\left(s, a^{\prime} ; \theta, \alpha\right)\right) \tag{14} Q(s,a;θ,α,β)=V(s;θ,β)+(A(s,a;θ,α)−a′∈∣A∣maxA(s,a′;θ,α))(14)在公式(12)的基础上加上 max a ′ ∈ ∣ A ∣ A ( s , a ′ ; θ , α ) \max _{a^{\prime} \in|\mathcal{A}|} A\left(s, a^{\prime} ; \theta, \alpha\right) maxa′∈∣A∣A(s,a′;θ,α)之后,它使得 V V V和 A A A不能随意上下波动。假如让 V V V变大00,让 A A A变小100,则Dueling Network的输出会增大100,不会再是公式(13)展示的结果,其影响如公式(15)所示: V ( s ; w V ) + A ( s , a ; w A ) − m a x a A ( s , a ; w A ) + 100 = V ( s ; w ~ V ) + A ( s , a ; w ~ A ) − m a x a A ( s , a ; w ~ A ) (15) V\left(s ; \boldsymbol{w}^V\right)+A\left(s, a ; \boldsymbol{w}^A\right) - max_aA(s,a;{\boldsymbol{w}}^{A}) + 100=V\left(s ; \tilde{\boldsymbol{w}}^V\right)+A\left(s, a ; \tilde{\boldsymbol{w}}^A\right) - max_aA(s,a;\tilde{\boldsymbol{w}}^A) \tag{15} V(s;wV)+A(s,a;wA)−maxaA(s,a;wA)+100=V(s;w~V)+A(s,a;w~A)−maxaA(s,a;w~A)(15)基于当前状态 s s s的最优动作 a ⋆ a^{\star} a⋆的获取如公式(16)所示: a ⋆ = a r g m a x a ′ ∈ A Q ( s , a ′ ; θ , α , β ) = a r g m a x a ′ ∈ A A ( s , a ′ ; θ , α , β ) (16) a^{\star}=arg\space max_{a' \in \mathcal{A}}Q(s,a';\theta,\alpha,\beta)=arg\space max_{a' \in \mathcal{A}}A(s,a';\theta,\alpha,\beta) \tag{16} a⋆=arg maxa′∈AQ(s,a′;θ,α,β)=arg maxa′∈AA(s,a′;θ,α,β)(16)根据公式(14)和公式(16),可以得到 Q ( s , a ∗ ; θ , α , β ) = V ( s ; θ , β ) Q\left(s, a^* ; \theta, \alpha, \beta\right)=V(s;\theta,\beta) Q(s,a∗;θ,α,β)=V(s;θ,β)除了使用 m a x max max方法外,论文中提到可以使用期望的方式来求解 Q Q Q动作价值函数,如公式(17)所示: Q ( s , a ; θ , α , β ) = V ( s ; θ , β ) + ( A ( s , a ; θ , α ) − 1 ∣ A ∣ ∑ a ′ A ( s , a ′ ; θ , α ) ) (17) \begin{aligned} & Q(s, a ; \theta, \alpha, \beta)=V(s ; \theta, \beta)+ \left(A(s, a ; \theta, \alpha)-\frac{1}{|\mathcal{A}|} \sum_{a^{\prime}} A\left(s, a^{\prime} ; \theta, \alpha\right)\right) \end{aligned} \tag{17} Q(s,a;θ,α,β)=V(s;θ,β)+(A(s,a;θ,α)−∣A∣1a′∑A(s,a′;θ,α))(17)公式(14)和公式(17)使用了两种不同的方式来展示Dueling Network的工作原理,两个公式分别通过求解最大值或者期望的形式使得Dueling Network训练过程更加稳定(这也使得 V V V和 A A A失去了其原始语义,因为它们现在偏离目标一个常量)。
Dueling Network的优势在于其能够有效学习状态值函数 V V V,随着Dueling Network更新 Q Q Q动作价值函数, V V V状态价值函数也会更新,这与传统的DQN或者Double DQN是不相同的,传统的DQN或者Double DQN只更新对应动作的 Q Q Q动作价值函数,其它动作的 Q Q Q并不会更新。在Dueling Network中,更新 V V V可以影响其它动作的 Q Q Q动作价值函数,使得Dueling Network能够有更好的训练效果。
基于gym的Dueling Network代码实现:
requirement:
code(代码主要参考书籍【动手学强化学习】(张伟楠,沈键,俞勇),京东购买链接 | 当当购买链接)
import gym
import random
import tf_rl_utils
import collections
import numpy as np
from tqdm import tqdm
import tensorflow as tf
import matplotlib.pyplot as plt
class ReplayBuffer:
"""
经验回放池
"""
def __init__(self, capacity):
self.buffer = collections.deque(maxlen=capacity) # 队列,先进先出
def add(self, state, action, reward, next_state, done): # 将数据加入buffer
self.buffer.append((state, action, reward, next_state, done))
def sample(self, batch_size): # 从buffer中采样数据,数量为batch_size
transitions = random.sample(self.buffer, batch_size)
state, action, reward, next_state, done = zip(*transitions)
return np.array(state), action, reward, np.array(next_state), done
def size(self): # 目前buffer中数据的数量
return len(self.buffer)
class VAnet(tf.keras.Model):
""" 只有一层隐藏层的A网络和V网络 """
def __init__(self, hidden_dim, action_dim):
super(VAnet, self).__init__()
self.fc1 = tf.keras.layers.Dense(hidden_dim, activation=tf.keras.activations.relu) # 共享网络部分
self.fc_A = tf.keras.layers.Dense(action_dim)
self.fc_V = tf.keras.layers.Dense(1)
def call(self, x):
A = self.fc_A(self.fc1(x))
V = self.fc_V(self.fc1(x))
Q = V + A - tf.reduce_mean(A, axis=1, keepdims=True) # Q值由V值和A值计算得到
return Q
class DQN:
def __init__(self, hidden_dim, action_dim, learning_rate, gamma,
epsilon, target_update, dqn_type='VanillaDQN'):
self.action_dim = action_dim
self.q_net = VAnet(hidden_dim, self.action_dim) # Q网络
# 目标网络
self.target_q_net = VAnet(hidden_dim, self.action_dim)
# 使用Adam优化器
self.optimizer = tf.optimizers.Adam(learning_rate=learning_rate)
self.gamma = gamma # 折扣因子
self.epsilon = epsilon # epsilon-贪婪策略
self.target_update = target_update # 目标网络更新频率
self.count = 0 # 计数器,记录更新次数
self.dqn_type = dqn_type
def take_action(self, state): # epsilon-贪婪策略采取动作
if np.random.random() < self.epsilon:
action = np.random.randint(self.action_dim)
else:
state = tf.cast(tf.expand_dims(state, axis=0), dtype=tf.float32)
action = tf.argmax(self.q_net.call(state), axis=1).numpy()[0]
return action
def max_q_value(self, state):
state = tf.cast(tf.expand_dims(state, axis=0), dtype=tf.float32)
return tf.reduce_max(self.q_net.call(state)).numpy()
def update(self, transition_dict):
states = tf.cast(transition_dict['states'], dtype=tf.float32)
actions = tf.reshape(transition_dict['actions'], shape=(-1))
rewards = tf.reshape(tf.cast(transition_dict['rewards'], dtype=tf.float32), shape=(-1, 1))
next_states = tf.cast(transition_dict['next_states'], dtype=tf.float32)
dones = tf.reshape(tf.cast(transition_dict['dones'], dtype=tf.float32), shape=(-1, 1))
with tf.GradientTape() as tape:
q_values = tf.gather(self.q_net.call(states), actions, batch_dims=1) # Q值
# 下个状态的最大Q值
if self.dqn_type == 'DoubleDQN': # DQN与Double DQN的区别
max_action = tf.argmax(self.q_net.call(next_states), axis=1, output_type=tf.int32)
max_next_q_values = tf.reshape(tf.gather(self.target_q_net.call(next_states), max_action, batch_dims=1), shape=(-1,1)) # Q值
else:
max_next_q_values = tf.reduce_max(self.target_q_net.call(next_states), axis=1, keepdims=True)
q_targets = rewards + self.gamma * max_next_q_values * (1 - dones) # TD误差目标
loss_func = tf.keras.losses.MeanSquaredError()
dqn_loss = tf.reduce_mean(loss_func(q_values, q_targets)) # 均方误差损失函数
grads = tape.gradient(dqn_loss, self.q_net.trainable_variables)
self.optimizer.apply_gradients(zip(grads, self.q_net.trainable_variables))
if self.count % self.target_update == 0:
self.target_q_net.set_weights(self.q_net.get_weights()) # 更新目标网络
self.count += 1
lr = 1e-2
num_episodes = 200
hidden_dim = 128
gamma = 0.98
epsilon = 0.01
target_update = 50
buffer_size = 5000
minimal_size = 1000
batch_size = 64
env_name = 'Pendulum-v0'
env = gym.make(env_name)
state_dim = env.observation_space.shape[0]
action_dim = 11 # 将连续动作分成11个离散动作
def dis_to_con(discrete_action, env, action_dim): # 离散动作转回连续的函数
action_lowbound = env.action_space.low[0] # 连续动作的最小值
action_upbound = env.action_space.high[0] # 连续动作的最大值
return action_lowbound + (discrete_action / (action_dim - 1)) * (action_upbound - action_lowbound)
def train_DQN(agent, env, num_episodes, replay_buffer, minimal_size,
batch_size):
return_list = []
max_q_value_list = []
max_q_value = 0
for i in range(10):
with tqdm(total=int(num_episodes / 10),
desc='Iteration %d' % i) as pbar:
for i_episode in range(int(num_episodes / 10)):
episode_return = 0
state = env.reset()
done = False
while not done:
action = agent.take_action(state)
max_q_value = agent.max_q_value(
state) * 0.005 + max_q_value * 0.995 # 平滑处理
max_q_value_list.append(max_q_value) # 保存每个状态的最大Q值
action_continuous = dis_to_con(action, env,
agent.action_dim)
next_state, reward, done, _ = env.step([action_continuous])
replay_buffer.add(state, action, reward, next_state, done)
state = next_state
episode_return += reward
if replay_buffer.size() > minimal_size:
b_s, b_a, b_r, b_ns, b_d = replay_buffer.sample(
batch_size)
transition_dict = {
'states': b_s,
'actions': b_a,
'next_states': b_ns,
'rewards': b_r,
'dones': b_d
}
agent.update(transition_dict)
return_list.append(episode_return)
if (i_episode + 1) % 10 == 0:
pbar.set_postfix({
'episode':
'%d' % (num_episodes / 10 * i + i_episode + 1),
'return':
'%.3f' % np.mean(return_list[-10:])
})
pbar.update(1)
return return_list, max_q_value_list
random.seed(0)
np.random.seed(0)
env.seed(0)
tf.random.set_seed(0)
replay_buffer = ReplayBuffer(buffer_size)
agent = DQN(hidden_dim, action_dim, lr, gamma, epsilon, target_update, "DoubleDQN")
return_list, max_q_value_list = train_DQN(agent, env, num_episodes,
replay_buffer, minimal_size,
batch_size)
episodes_list = list(range(len(return_list)))
mv_return = tf_rl_utils.moving_average(return_list, 5)
plt.plot(episodes_list, mv_return)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Double DQN on {}'.format(env_name))
plt.show()