基于值的深度强化学习算法

news2024/12/24 8:27:54

目录

  • DQN2013 —— Playing Atari with Deep Reinforcement Learning
  • DQN2015 —— Human-level control through deep reinforcement learning
  • 引用文献

DQN2013 —— Playing Atari with Deep Reinforcement Learning

  • 论文下载地址

  • 论文介绍

    该论文提出了一个基于卷积神经网络的深度强化学习模型,该模型是Q-learning算法的变体,该模型的输入数据是Atari游戏的原始原素,输出是预测未来的动作价值奖励(Q-value),其神经网络结构可以简单表示如下:
    在这里插入图片描述
    输入数据为 [batch_size, width, height, channel](不同深度学习框架中的channel的位置可能不一样,在tf中输入的数据是将channel设置 在最后一个维度,我们暂以此描述)形式的多维数据,输入数据经过卷积神经网络得到特征向量 [batch_size, hidden_dim],将特征向量作为全连接神经网络的输入,得到输出的动作价值函数 Q ( s , a ) Q(s,a) Q(s,a) [batch_size, action_dim],使用完全贪婪算法或者epsilon贪婪算法基于 Q ( s , a ) Q(s,a) Q(s,a)的值选择对应的动作。

  • 前人相关工作:

    • TD-gammon:TD-gammon是一种双陆棋游戏程序,通过强化学习和自我对弈来学习并达到了超人类的游戏水平。TD-gammon使用了类似的无模型强化学习算法Q-learning,并使用具有一个隐藏层的多层感知器来近似价值函数1 (事实上,TD-gammon近似的是状态价值函数 V ( s ) V(s) V(s)而不是动作价值函数 Q ( s , a ) Q(s,a) Q(s,a))。然而,通过改进TD-gammon或者直接使用相同的方法应用于国际象棋、围棋和跳棋都不太成功,这导致人们普遍认为TD-gammon是一种特殊情况,仅适用于双陆棋,也许是因为掷骰子的随机性有助于探索状态空间,并使价值函数特别平滑2
    • 研究表明,将无模型强化学习算法(如Q-learning)与非线性函数逼近器(近似器)3或者与离线策略学习4相结合可能会导致Q-network发散。随后,强化学习的大部分工作都集中在具有更好收敛保证的线性函数逼近器上3
    • 最近,人们对将深度学习与强化学习相结合重新产生了兴趣。深度神经网络已经被用来评估环境 ϵ \epsilon ϵ,受限玻尔兹曼机已经被用来预测价值函数5或者策略6。此外,Q-learning的发散问题已经通过梯度时间差分得到部分解决。这些方法被证明在使用非线性函数逼近器7预测固定策略或使用Q-learning的受限变体8通过线性函数逼近器学习控制策略时是收敛的。然而,这些方法尚未扩展到非线性控制。
    • 也许与该论文的方法最相似的前人工作是神经拟合Q-learning(neural fitted Q-learning,NFQ)9,NFQ优化了公式(1) L i ( θ i ) = E s , a ∼ ρ ( ⋅ ) [ ( y i − Q ( s , a ; θ i ) ) 2 ] (1) L_{i}(\theta_{i})=\mathbb{E}_{s,a\sim \rho(·)}[(y_i-Q(s,a;\theta_i))^{2}] \tag{1} Li(θi)=Es,aρ()[(yiQ(s,a;θi))2](1)的损失函数序列,使用RPROP算法更新Q-learning的参数。然后,它使用批量更新参数的方式,每次迭代的计算成本和数据集的大小成正比,而该论文DQN使用随机梯度更新,每次迭代的成本比较低,并且可以扩展到大型数据集,NFQ还成功应用于使用纯视觉输入的简单现实世界控制任务,首先使用深度自动编码器学习任务的低维表示,然后将低维表示作为NFQ的输入10。相比之下,DQN直接使用端到端的强化学习方法将视觉数据作为神经网络的输入,因此,DQN可以学习到与区分动作价值直接相关的特征。Q-learning之前也曾将经验回放和简单的神经网络11相结合,但同样是以低维状态而不是原始视觉作为神经网络的输入。
  • DQN2013伪代码
    在这里插入图片描述

    • 初始化经验回放池 D D D,回放池可存放数据数量为 N N N
    • 随机初始化动作值函数网络 Q Q Q的模型参数;
    • 在每个回合(episode)获取环境初始状态 s 1 s_1 s1,同时对状态进行预处理 ϕ 1 = ϕ ( s 1 ) \phi_1=\phi(s_1) ϕ1=ϕ(s1);
      • 根据网络 Q ( ϕ s t , a ; θ ) Q(\phi_{s_t},a;\theta) Q(ϕst,a;θ),以 ϵ \epsilon ϵ贪婪策略选择动作 a t a_t at
      • 执行动作 a t a_t at与模拟器进行交互,获取奖励 r t r_t rt和下一个环境状态 s t + 1 s_{t+1} st+1,对 s t + 1 s_{t+1} st+1进行预处理 ϕ ( s t + 1 ) \phi(s_{t+1}) ϕ(st+1)
      • ( ϕ t , a t , r t , ϕ t + 1 ) (\phi_{t},a_t,r_t,\phi_{t+1}) (ϕt,at,rt,ϕt+1)存储到回放池 D D D中,如果回放池数据量达到最大值 N N N,抛弃最先存储的数据;
      • 当回放池中的数据量足够,从回放池中随机采样 m i n i b a t c h minibatch minibatch个数据;
      • 对每个数据,根据是否为终止状态,通过公式 y j = { r j r j + γ   m a x a ′ Q ( ϕ j + 1 , a ′ ; θ ) y_j=\begin{cases} r_j \\ r_j + \gamma \space max_{a'}Q(\phi_{j+1,a';\theta}) \end{cases} yj={rjrj+γ maxaQ(ϕj+1,a;θ)计算 y j y_j yj
      • 最小化目标损失 ( y j − Q ( ϕ j , a j ; θ ) ) 2 (y_j-Q(\phi_{j,a_j;\theta}))^2 (yjQ(ϕj,aj;θ))2,以此更新网络 Q Q Q
      • 伪代码中的公式3是指下面的公式(2); ∇ θ i L i ( θ i ) = E s , a ∼ ρ ( ⋅ ) ; s ′ ∼ E [ ( r + γ max ⁡ a ′ Q ( s ′ , a ′ ; θ i − 1 ) − Q ( s , a ; θ i ) ) ∇ θ i Q ( s , a ; θ i ) ] (2) \nabla_{\theta_i} L_i\left(\theta_i\right)=\mathbb{E}_{s, a \sim \rho(\cdot) ; s^{\prime} \sim \mathcal{E}}\left[\left(r+\gamma \max _{a^{\prime}} Q\left(s^{\prime}, a^{\prime} ; \theta_{i-1}\right)-Q\left(s, a ; \theta_i\right)\right) \nabla_{\theta_i} Q\left(s, a ; \theta_i\right)\right] \tag{2} θiLi(θi)=Es,aρ();sE[(r+γamaxQ(s,a;θi1)Q(s,a;θi))θiQ(s,a;θi)](2) L i L_i Li如公式(3)所示: L i ( θ i ) = E s , a ∼ ρ ( ⋅ ) [ ( y i − Q ( s , a ; θ i ) ) 2 ] (3) L_i\left(\theta_i\right)=\mathbb{E}_{s, a \sim \rho(\cdot)}\left[\left(y_i-Q\left(s, a ; \theta_i\right)\right)^2\right] \tag{3} Li(θi)=Es,aρ()[(yiQ(s,a;θi))2](3)
  • 训练流程

    • 收集训练数据:我们可以用任何策略函数 π \pi π控制智能体与环境交互,这个 π \pi π可以叫做行为策略,dqn中使用的是 ϵ − g r e e d y \epsilon-greedy ϵgreedy策略,如公式(4)所示: a t = { a r g m a x a   Q ( s t , a ; ω ) ,                            以概率 ( 1 − ϵ ) 均匀抽取动作空间 A 中的一个动作     以概率 ϵ (4) a_t=\begin{cases} argmax_a \space Q(s_t,a;\omega),\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space以概率(1-\epsilon) \\ 均匀抽取动作空间\mathcal{A}中的一个动作 \space\space\space\space\space以概率\epsilon \end{cases} \tag{4} at={argmaxa Q(st,a;ω),                           以概率(1ϵ)均匀抽取动作空间A中的一个动作     以概率ϵ(4)把一个回合获取到的轨迹划分成 n n n个四元组 ( s t , a t , r t , s t + 1 ) (s_t,a_t,r_t,s_{t+1}) (st,at,rt,st+1),存入经验回放池。
    • 更新DQN参数 w \boldsymbol{w} w:随机从经验回放池中取出一个四元组,记作 ( s j , a j , r j , s j + 1 ) (s_j,a_j,r_j,s_{j+1}) (sj,aj,rj,sj+1),设DQN的当前参数为 w n o w \boldsymbol{w}_{now} wnow,执行下面的步骤对参数做一次更新,得到新的参数 w n e w \boldsymbol{w}_{new} wnew
      (1)对DQN做正向传播,得到 s t s_t st s t + 1 s_{t+1} st+1的Q-value值,如公式(5)和公式(6)所示: q ^ j = Q ( s j , a j , w n o w ) (5) \hat{q}_j=Q(s_j,a_j,\boldsymbol{w}_{now}) \tag{5} q^j=Q(sj,aj,wnow)(5) q ^ j + 1 = max ⁡ a ∈ A Q ( s j + 1 , a ; w now  ) (6) \widehat{q}_{j+1}=\max _{a \in \mathcal{A}} Q\left(s_{j+1}, a ; \boldsymbol{w}_{\text {now }}\right) \tag{6} q j+1=aAmaxQ(sj+1,a;wnow )(6)
      (2)计算目标Q-value值和误差,如公式(7)和公式(8)所示: y ^ j = r j + γ ⋅ q ^ j + 1 (7) \widehat{y}_j=r_j+\gamma \cdot \widehat{q}_{j+1} \tag{7} y j=rj+γq j+1(7) δ j = q ^ j − y ^ j (8) \delta_j=\widehat{q}_j-\widehat{y}_j \tag{8} δj=q jy j(8)
      (3)对DQN做反向传播,得到梯度,如公式(9)所示: g j = ∇ w Q ( s j , a j ; w now  ) (9) \boldsymbol{g}_j=\nabla_{\boldsymbol{w}} Q\left(s_j, a_j ; \boldsymbol{w}_{\text {now }}\right) \tag{9} gj=wQ(sj,aj;wnow )(9)
      (4)做梯度下降更新DQN的参数,如公式(10)所示: w new  ← w now  − α ⋅ δ j ⋅ g j (10) \boldsymbol{w}_{\text {new }} \leftarrow \boldsymbol{w}_{\text {now }}-\alpha \cdot \delta_j \cdot \boldsymbol{g}_j \tag{10} wnew wnow αδjgj(10)
  • 基于gym的DQN2013代码实现:

    • requirement:

      • python: 3.8
      • gym: 0.17.3
      • tensorflow: 2.9
    • code

      import gym
      import random
      import collections
      import numpy as np
      import tensorflow as tf
      import matplotlib.pyplot as plt
      from tqdm import tqdm
      
      
      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 Q_net(tf.keras.Model):
          """只有一层隐藏层的Q网络"""
          def __init__(self, hidden_dim, action_dim):
              super(Q_net, self).__init__()
              self.fc1 = tf.keras.layers.Dense(hidden_dim, activation=tf.keras.activations.relu)
              self.fc2 = tf.keras.layers.Dense(action_dim)
      
          def call(self, x):
              return self.fc2(self.fc1(x))
      
      
      class DQN:
          def __init__(self, hidden_dim, action_dim, learning_rate, gamma, epsilon):
              self.action_dim = action_dim
              self.q_net = Q_net(hidden_dim, self.action_dim)  # Q网络
              # 使用Adam优化器
              self.optimizer = tf.optimizers.Adam(learning_rate=learning_rate)
              # 折扣因子
              self.gamma = gamma
              # epsilon-贪婪策略,这里没有对epsilon进行退火处理
              self.epsilon = epsilon
      
          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 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值
                  q_values = tf.gather(self.q_net.call(states), actions, batch_dims=1)
                  # 下个状态的最大Q值,下个状态不进行梯度优化,使用tf.stop_gradient
                  max_next_q_values = tf.stop_gradient(tf.reduce_max(self.q_net.call(next_states), axis=1, keepdims=True))
                  # TD误差目标
                  q_targets = rewards + self.gamma * max_next_q_values * (1 - dones)
                  # 均方差损失函数
                  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))
      
      
      lr = 2e-3
      num_episodes = 500
      hidden_dim = 128
      gamma = 0.98
      epsilon = 0.01
      buffer_size = 10000
      minimal_size = 500
      batch_size = 64
      
      
      env_name = 'CartPole-v0'
      env = gym.make(env_name)
      random.seed(0)
      np.random.seed(0)
      env.seed(0)
      tf.random.set_seed(0)
      replay_buffer = ReplayBuffer(buffer_size)
      state_dim = env.observation_space.shape[0]  # 输入状态维数
      action_dim = env.action_space.n  # 动作维数
      agent = DQN(hidden_dim, action_dim, lr, gamma, epsilon)
      
      
      return_list = []
      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)
                      next_state, reward, done, _ = env.step(action)
                      replay_buffer.add(state, action, reward, next_state, done)
                      state = next_state
                      episode_return += reward
                      # 当buffer数据的数量超过一定值后,才进行Q网络训练
                      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)
      
      episodes_list = list(range(len(return_list)))
      plt.plot(episodes_list, return_list)
      plt.xlabel('Episodes')
      plt.ylabel('Returns')
      plt.title('DQN on {}'.format(env_name))
      plt.show()
      
    • result
      在这里插入图片描述

  • DQN2013的高估问题

    Q-learning算法有一个缺陷,用Q-learning训练的DQN会高估真实的价值,而且高估通常是非均匀的,这个缺陷导致DQN的表现很差。高估问题不是DQN模型的问题,是Q-learning的问题。

    Q-learning产生高估的原因有两个:

    (1)自举导致偏差的传播;
    (2)动作价值函数最大化导致目标动作价值高估真实价值;

    下面简单分析一下导致高估的两个原因:

    • 自举导致偏差的传播

      在强化学习中,自举意思是“用一个估算去更新同类的估算”,类似于“自己把自己给举起来” 。我们回顾一下训练DQN使用的Q-learning算法,研究其中存在的自举。

      算法每次从经验回放池中抽取四元组 ( s j , a j , r j , s j + 1 ) (s_j, a_j, r_j, s_{j+1}) (sj,aj,rj,sj+1),然后执行下面步骤更新DQN的参数:

      (1)计算目标动作值函数: y ^ j = r j + γ ⋅ max ⁡ a j + 1 ∈ A Q ( s j + 1 , a j + 1 ; w now  ) ⏟ D Q N  自己做出的估计  \widehat{y}_j=r_j+\gamma \cdot \underbrace{\max _{a_{j+1} \in \mathcal{A}} Q\left(s_{j+1}, a_{j+1} ; \boldsymbol{w}_{\text {now }}\right)}_{\mathrm{DQN} \text { 自己做出的估计 }} y j=rj+γDQN 自己做出的估计  aj+1AmaxQ(sj+1,aj+1;wnow )(2)计算损失函数: L ( w ) = 1 2 [ Q ( s j , a j ; w ) − y ^ j ⏟ 让 DQN 拟合  y ^ j ] 2 L(\boldsymbol{w})=\frac{1}{2}[\underbrace{Q\left(s_j, a_j ; \boldsymbol{w}\right)-\widehat{y}_j}_{\text {让 DQN 拟合 } \widehat{y}_j}]^2 L(w)=21[ DQN 拟合 y j Q(sj,aj;w)y j]2(3)进行梯度下降更新DQN参数: w new  ← w now  − α ⋅ ∇ w L ( w now  ) \boldsymbol{w}_{\text {new }} \leftarrow \boldsymbol{w}_{\text {now }}-\alpha \cdot \nabla_{\boldsymbol{w}} L\left(\boldsymbol{w}_{\text {now }}\right) wnew wnow αwL(wnow )第一步计算目标动作值 y ^ j \hat{y}_j y^j是基于DQN自己做出的估计,第二步让DQN去拟合 y ^ j \hat{y}_j y^j,这意味着用DQN自己做出的估计去更新DQN自身,这属于自举。

      DQN中 Q ( s , a , w ) Q(s,a,\boldsymbol{w}) Q(s,a,w)是对于最优动作价值函数 Q ⋆ ( s , a ) Q_{\star}(s, a) Q(s,a)的近似,最理想的情况下 Q ( s , a ; w ) = Q ⋆ ( s , a ) , ∀ s , a Q(s, a ; \boldsymbol{w})=Q_{\star}(s, a), \forall s, a Q(s,a;w)=Q(s,a),s,a,假如碰巧 Q ( s j + 1 , a j + 1 ; w ) Q\left(s_{j+1}, a_{j+1} ; \boldsymbol{w}\right) Q(sj+1,aj+1;w)低估(或高估)真实价值 Q ⋆ ( s j + 1 , a j + 1 ) Q_{\star}\left(s_{j+1}, a_{j+1}\right) Q(sj+1,aj+1),则会发生下面的情况:           Q ( s j + 1 , a j + 1 ; w )  低估(或高估)      Q ⋆ ( s j + 1 , a j + 1 ) ⟹           y ^ j            低估(或高估)      Q ⋆ ( s j , a j ) ⟹      Q ( s j , a j ; w )  低估(或高估)  Q ⋆ ( s j , a j ) .  \begin{aligned} & \space \space \space \space \space \space \space \space \space Q\left(s_{j+1}, a_{j+1} ; \boldsymbol{w}\right) \text { 低估(或高估) } \space \space \space \space Q_{\star}\left(s_{j+1}, a_{j+1}\right) \\ & \Longrightarrow \quad \space \space \space \space \space \space \space \space \space \widehat{y}_j \space \space \space \space \space \space \space \space \space \quad \text { 低估(或高估) } \space \space \space \space Q_{\star}\left(s_j, a_j\right) \\ & \Longrightarrow \space \space \space \space Q\left(s_j, a_j ; \boldsymbol{w}\right) \quad \text { 低估(或高估) } \quad Q_{\star}\left(s_j, a_j\right) \text {. } \\ & \end{aligned}          Q(sj+1,aj+1;w) 低估(或高估)     Q(sj+1,aj+1)         y j          低估(或高估)     Q(sj,aj)    Q(sj,aj;w) 低估(或高估) Q(sj,aj)

      如果 Q ( s j + 1 , a j + 1 ; w ) Q\left(s_{j+1}, a_{j+1} ; \boldsymbol{w}\right) Q(sj+1,aj+1;w)是对真实价值 Q ⋆ ( s j + 1 , a j + 1 ) Q_{\star}\left(s_{j+1}, a_{j+1}\right) Q(sj+1,aj+1)的低估(或高估),就会导致 Q ( s j , a j ; w ) Q\left(s_{j}, a_{j} ; \boldsymbol{w}\right) Q(sj,aj;w)低估或高估价值 Q ⋆ ( s j , a j ) Q_{\star}\left(s_{j}, a_{j}\right) Q(sj,aj)。也就是说低估(或高估)从 ( s j + 1 , a j + 1 ) (s_{j+1},a_{j+1}) (sj+1,aj+1)传播到 ( s j , a j ) (s_j,a_j) (sj,aj),让更多的价值被低估(或高估)。

DQN2015 —— Human-level control through deep reinforcement learning

  • 论文下载地址

  • 论文介绍
    在这里插入图片描述
    众所周知,当强化学习使用神经网络等非线性函数来表示动作值函数时是不稳定的,甚至是发散的。这种不稳定性有几个原因:(1)观测序列数据存在相关性,当前状态和下一状态具有高度关联性,动作价值函数的微小更新都有可能显著改变策略参数并因此改变数据分布;(2)动作值函数Q和目标动作值函数 r + γ   m a x a ′ Q ( s ′ , a ′ ) r+\gamma \space max_{a'}Q(s',a') r+γ maxaQ(s,a)的相关性,具体可以看DQN,计算当前动作值函数和target动作值函数都是使用一个Q神经网络。

    该论文提出一种基于Q-learning的变体模型来解决这些不稳定性,它使用了两种关键方法,第一,使用经验回放机制,对训练数据进行随机化,消除观测序列中数据的相关性并平滑数据分布的变化;第二,使用目标网络预测下一状态的动作值函数用于更新当前网络的参数,并周期性地更新目标网络的参数(目标网络复制当前网络的参数),以此减少动作值函数Q和目标动作值函数 r + γ   m a x a ′ Q ( s ′ , a ′ ) r+\gamma \space max_{a'}Q(s',a') r+γ maxaQ(s,a)的相关性。(可以简单理解为比DQN2013多了一个目标网络)

  • 伪代码
    在这里插入图片描述

    • 初始化经验回放池 D D D,容量大小为 N N N;
    • 初始化动作值函数 Q Q Q的参数 θ \theta θ
    • 初始化目标动作值函数 θ ^ \hat{\theta} θ^的参数 θ − \theta^{-} θ,令 θ − = θ \theta^{-}=\theta θ=θ
    • 在每个回合(episode)获取环境初始状态 s 1 s_1 s1,同时对状态进行预处理 ϕ 1 = ϕ ( s 1 ) \phi_1=\phi(s_1) ϕ1=ϕ(s1);
      • 根据网络 Q ( ϕ s t , a ; θ ) Q(\phi_{s_t},a;\theta) Q(ϕst,a;θ),以 ϵ \epsilon ϵ贪婪策略选择动作 a t a_t at
      • 执行动作 a t a_t at与模拟器进行交互,获取奖励 r t r_t rt和下一个环境状态 s t + 1 s_{t+1} st+1,对 s t + 1 s_{t+1} st+1进行预处理 ϕ ( s t + 1 ) \phi(s_{t+1}) ϕ(st+1)
      • ( ϕ t , a t , r t , ϕ t + 1 ) (\phi_{t},a_t,r_t,\phi_{t+1}) (ϕt,at,rt,ϕt+1)存储到回放池 D D D中,如果回放池数据量达到最大值 N N N,清理最先存储的数据;
      • 当回放池中的数据量足够,从回放池中随机采样 m i n i b a t c h minibatch minibatch个数据;
      • 对每个数据,根据是否为终止状态,通过公式 y j = { r j r j + γ   m a x a ′ Q ^ ( ϕ j + 1 , a ′ ; θ ) y_j=\begin{cases} r_j \\ r_j + \gamma \space max_{a'}\hat{Q}(\phi_{j+1,a';\theta}) \end{cases} yj={rjrj+γ maxaQ^(ϕj+1,a;θ)计算 y j y_j yj,注意这里计算 y j y_j yj的时候使用的是目标网络 Q ^ \hat{Q} Q^,DQN2013使用的是 Q Q Q
      • 最小化目标损失 ( y j − Q ( ϕ j , a j ; θ ) ) 2 (y_j-Q(\phi_{j,a_j;\theta}))^2 (yjQ(ϕj,aj;θ))2,以此更新网络 Q Q Q的模型参数;
      • 每间 C C C步更新目标网络 Q ^ \hat{Q} Q^的模型参数,即将 Q Q Q的模型参数复制给目标网络 Q ^ \hat{Q} Q^
  • 训练流程

    • 收集训练数据:我们可以用任何策略函数 π \pi π控制智能体与环境交互,这个 π \pi π可以叫做行为策略,dqn中使用的是 ϵ − g r e e d y \epsilon-greedy ϵgreedy策略,如公式(11)所示: a t = { a r g m a x a   Q ( s t , a ; ω ) ,                            以概率 ( 1 − ϵ ) 均匀抽取动作空间 A 中的一个动作     以概率 ϵ (11) a_t=\begin{cases} argmax_a \space Q(s_t,a;\omega),\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space\space以概率(1-\epsilon) \\ 均匀抽取动作空间\mathcal{A}中的一个动作 \space\space\space\space\space以概率\epsilon \end{cases} \tag{11} at={argmaxa Q(st,a;ω),                           以概率(1ϵ)均匀抽取动作空间A中的一个动作     以概率ϵ(11)把一个回合获取到的轨迹划分成 n n n个四元组 ( s t , a t , r t , s t + 1 ) (s_t,a_t,r_t,s_{t+1}) (st,at,rt,st+1),存入经验回放池。
    • 更新DQN参数 w \boldsymbol{w} w:随机从经验回放池中取出一个四元组,记作 ( s j , a j , r j , s j + 1 ) (s_j,a_j,r_j,s_{j+1}) (sj,aj,rj,sj+1),设DQN的当前参数为 w n o w \boldsymbol{w}_{now} wnow,执行下面的步骤对参数做一次更新,得到新的参数 w n e w \boldsymbol{w}_{new} wnew
      (1)对DQN做正向传播,得到 s t s_t st s t + 1 s_{t+1} st+1的Q-value值,如公式(12)和公式(13)所示: q ^ j = Q ( s j , a j , w n o w ) (12) \hat{q}_j=Q(s_j,a_j,\boldsymbol{w}_{now}) \tag{12} q^j=Q(sj,aj,wnow)(12) q ^ j + 1 = max ⁡ a ∈ A Q ^ ( s j + 1 , a ; w now  − ) (13) \widehat{q}_{j+1}=\max _{a \in \mathcal{A}} \hat{Q}\left(s_{j+1}, a ; \boldsymbol{w}^{-}_{\text {now }}\right) \tag{13} q j+1=aAmaxQ^(sj+1,a;wnow )(13)
      (2)计算目标Q-value值和误差,如公式(14)和公式(15)所示: y ^ j = r j + γ ⋅ q ^ j + 1 (14) \widehat{y}_j=r_j+\gamma \cdot \widehat{q}_{j+1} \tag{14} y j=rj+γq j+1(14) δ j = q ^ j − y ^ j (15) \delta_j=\widehat{q}_j-\widehat{y}_j \tag{15} δj=q jy j(15)
      (3)对DQN做反向传播,得到梯度,如公式(16)所示: g j = ∇ w Q ( s j , a j ; w now  ) (16) \boldsymbol{g}_j=\nabla_{\boldsymbol{w}} Q\left(s_j, a_j ; \boldsymbol{w}_{\text {now }}\right) \tag{16} gj=wQ(sj,aj;wnow )(16)
      (4)做梯度下降更新DQN的参数,如公式(17)所示: w new  ← w now  − α ⋅ δ j ⋅ g j (17) \boldsymbol{w}_{\text {new }} \leftarrow \boldsymbol{w}_{\text {now }}-\alpha \cdot \delta_j \cdot \boldsymbol{g}_j \tag{17} wnew wnow αδjgj(17)
      (5)间隔固定次数更新目标网络的参数: w n e w − = w n e w \boldsymbol{w}^{-}_{new}=\boldsymbol{w}_{new} wnew=wnew
  • 基于gym的DQN2015代码实现:

    • requirement:

      • python: 3.8
      • gym: 0.17.3
      • tensorflow: 2.9
    • code

      import gym
      import random
      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 Q_net(tf.keras.Model):
          """只有一层隐藏层的Q网络"""
          def __init__(self, state_dim, hidden_dim, action_dim):
              super(Q_net, self).__init__()
              self.fc1 = tf.keras.layers.Dense(hidden_dim, activation=tf.keras.activations.relu)
              self.fc2 = tf.keras.layers.Dense(action_dim)
      
          def call(self, x):
              return self.fc2(self.fc1(x))
      
      
      class DQN:
          def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma,
                       epsilon, target_update):
              self.action_dim = action_dim
              self.q_net = Q_net(state_dim, hidden_dim, self.action_dim)  # Q网络
              # 目标网络
              self.target_q_net = Q_net(state_dim, 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  # 计数器,记录更新次数
      
          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 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值
                  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 = 2e-3
      num_episodes = 500
      hidden_dim = 128
      gamma = 0.98
      epsilon = 0.01
      target_update = 10
      buffer_size = 10000
      minimal_size = 500
      batch_size = 64
      
      
      env_name = 'CartPole-v0'
      env = gym.make(env_name)
      random.seed(0)
      np.random.seed(0)
      env.seed(0)
      tf.random.set_seed(0)
      replay_buffer = ReplayBuffer(buffer_size)
      state_dim = env.observation_space.shape[0]
      action_dim = env.action_space.n
      agent = DQN(state_dim, hidden_dim, action_dim, lr, gamma, epsilon, target_update)
      
      
      return_list = []
      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)
                      next_state, reward, done, _ = env.step(action)
                      replay_buffer.add(state, action, reward, next_state, done)
                      state = next_state
                      episode_return += reward
                      # 当buffer数据的数量超过一定值后,才进行Q网络训练
                      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)
      
      episodes_list = list(range(len(return_list)))
      plt.plot(episodes_list, return_list)
      plt.xlabel('Episodes')
      plt.ylabel('Returns')
      plt.title('DQN on {}'.format(env_name))
      plt.show()
      
    • result
      在这里插入图片描述

引用文献


  1. Gerald Tesauro. Temporal difference learning and td-gammon. Communications of the ACM,38(3):58–68, 1995. ↩︎

  2. Jordan B. Pollack and Alan D. Blair. Why did td-gammon work. In Advances in Neural Information Processing Systems 9, pages 10–16, 1996. ↩︎

  3. John N Tsitsiklis and Benjamin Van Roy. An analysis of temporal-difference learning with function approximation. Automatic Control, IEEE Transactions on, 42(5):674–690, 1997. ↩︎ ↩︎

  4. Leemon Baird. Residual algorithms: Reinforcement learning with function approximation. In Proceedings of the 12th International Conference on Machine Learning (ICML 1995), pages 30–37. Morgan Kaufmann, 1995. ↩︎

  5. Brian Sallans and Geoffrey E. Hinton. Reinforcement learning with factored states and actions. Journal of Machine Learning Research, 5:1063–1088, 2004. ↩︎

  6. Nicolas Heess, David Silver, and Yee Whye Teh. Actor-critic reinforcement learning with energy-based policies. In European Workshop on Reinforcement Learning, page 43, 2012. ↩︎

  7. Hamid Maei, Csaba Szepesvari, Shalabh Bhatnagar, Doina Precup, David Silver, and Rich Sutton. Convergent Temporal-Difference Learning with Arbitrary Smooth Function Approximation. In Advances in Neural Information Processing Systems 22, pages 1204–1212, 2009. ↩︎

  8. Hamid Maei, Csaba Szepesv´ari, Shalabh Bhatnagar, and Richard S. Sutton. Toward off-policy learning control with function approximation. In Proceedings of the 27th International Conference on Machine Learning (ICML 2010), pages 719–726, 2010. ↩︎

  9. Martin Riedmiller. Neural fitted q iteration–first experiences with a data efficient neural reinforcement learning method. In Machine Learning: ECML 2005, pages 317–328. Springer, 2005. ↩︎

  10. Sascha Lange and Martin Riedmiller. Deep auto-encoder neural networks in reinforcement learning. In Neural Networks (IJCNN), The 2010 International Joint Conference on, pages 1–8. IEEE, 2010. ↩︎

  11. Long-Ji Lin. Reinforcement learning for robots using neural networks. Technical report, DTIC Document, 1993. ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/708675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字IC验证环境的创建

本文介绍了从一组可重用的验证组件中构建测试平台所需的步骤。UVM促进了重用&#xff0c;加速了测试平台构建的过程。 首先对测试平台集成者&#xff08;testbench integrator&#xff09;和测试编写者&#xff08;test writer &#xff09;进行区分&#xff0c;前者负责测试平…

【Java EE】-博客系统二(前后端分离)

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【JavaEE】 分享: 徘徊着的 在路上的 你要走吗 易碎的 骄傲着 那也曾是我的模样 ——《平凡之路》 主要内容&#xff1a;显示用户信息、上传头像、新增博客、删除博客、修改博客…

Android:设计模式

文章参考来源1 文章参考来源2 文章参考来源3 MVC Model 数据来源&#xff0c;管理业务数据逻辑&#xff0c;读取数据等 View 视图 Controller 单例模式&#xff0c;处理业务逻辑&#xff0c;负责改变Model和View 经典的MVC架构是 用户点击View&#xff0c;View将用户输入转…

springboot详细整合mybatisplus

SpringBoot详细整合mybatisPlus 文章目录 SpringBoot详细整合mybatisPlus一、引入mybatis_plus依赖二、修改mybatis_plus的yml配置三、添加mybatis_plus的其他配置以及包扫描四&#xff0c;修改mybatis的配置&#xff08;这一步根据实际情况修改&#xff09; 无奈&#xff0c;一…

三个关键数字变化,剖析中国智能手机市场的趋势及其影响

近期&#xff0c;全球行业分析机构CounterpointResearch公布了《中国智能手机高端市场白皮书》&#xff0c;对中国智能手机市场数据进行详细分析。该报告揭示了几个关键数据&#xff0c;值得深入剖析。 上面的图表展示了中国智能手机市场在2012年至2022年的11年间销量、平均价格…

UE5《Electric Dreams》项目PCG技术解析 之 PCGDemo_Ditch关卡详解

文章目录 前导文章关卡概要PCGGraphPoints From Actor Tag作为PCG的分割工具分层装饰 一些知识点和技巧使用Attribute Operation将属性暂存到临时属性中是否生成碰撞 小结 前导文章 《UE5《Electric Dreams》项目PCG技术解析 之 基于关卡PCGSettings的工作流》《UE5《Electric…

mac系统通过终端连接远程服务器

mac系统通过终端连接远程服务器 1、通过自带终端连接1.1 命令直接连接1.2 方式2——创建连接 2、通过iTerm2连接2.1 方式一&#xff1a;命令直接连接 3. Mac上使用scp命令 1、通过自带终端连接 1.1 命令直接连接 如下&#xff1a;ssh -p 22 root远程IP1.2 方式2——创建连接 …

类Twitter风格的RSS阅读器

本文完成于 2 月中旬&#xff0c;其中的反代还是 frp npm 方案&#xff1b; 什么是 RSS ? RSS 是用 PHP、Laravel、Inertia.js、Tailwind 和 Vue.js 编写的简单的类Twitter 风格的 RSS阅读器&#xff0c;支持 RSS和ATOM 格式。 命令行安装 在群晖上以 Docker 方式安装。 官…

形式化验证,QED: Quick Error Detection Tests for Effective Post-Silicon Validation(二)

目录 一、Article:文献出处&#xff08;方便再次搜索&#xff09; &#xff08;1&#xff09;作者 &#xff08;2&#xff09;文献题目 &#xff08;3&#xff09;文献时间 &#xff08;4&#xff09;引用 二、Data:文献数据&#xff08;总结归纳&#xff0c;方便理解&am…

chatgpt赋能python:如何用Python打造一个简单的抽奖程序

如何用Python打造一个简单的抽奖程序 随着互联网的不断发展&#xff0c;抽奖活动已经成为了各种营销活动的必备环节&#xff0c;因此如何快速便捷地实现一个抽奖程序也变得尤为重要。本文将介绍如何使用Python打造一个简单的抽奖程序。 一、抽奖程序的工作原理 抽奖程序的核…

Vue使用vue-3d-model组件预览3D三维文件、立体文件,支持旋转、自动播放

实现效果 Tips:先泼个冷水&#xff0c;这个预览3D组件有个致命的缺陷——不能设置材质、皮肤文件的目录路径&#xff0c;必须要和3d文件放在同一个目录&#xff0c;如果项目是用hash模式(url后面会有/#/这种井号)&#xff0c;就会导致无法读取根目录的材质文件。所以推荐了解下…

LabVIEW利用相机开发零件处理和检查系统

LabVIEW利用相机开发零件处理和检查系统 为了将自动化运用于飞机发动机轮机机翼的去毛刺和检查流程&#xff0c;设计了一个系统&#xff0c;该系统使用六轴机器人操作抖动&#xff0c;并结合两个关键操作。首先&#xff0c;使用专门选定的工具对机翼进行去毛刺&#xff0c;以去…

ssh 端口转发

本地转发 ssh -L -CTfN 9527:remote_server_ip:23 ssh_server_ipL 本地转发模式C 压缩数据T 禁用模拟终端f 后台运行N 不执行远程指令&#xff0c; 常用于仅做端口转发 在local_server上开启本地转发模式之后 。ssh_server就会出现2端的TCP链接。然后所有发向9527端口TCP数据…

ASCII、Unicode、UTF-8、GBK、全角/半角

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 目录 一、定义 1、ASCII 2、Unicode 3、UTF-8 4、GB2312 5、GBK 6、\u和\x 7、全角和半角 二、相互转化 1、str 与 ASCI…

[acwing周赛复盘] 第 110 场周赛20230701

[acwing周赛复盘] 第 110 场周赛20230701 总结5044. 求和1. 题目描述2. 思路分析3. 代码实现 5045. 三角形数1. 题目描述2. 思路分析3. 代码实现 5046. 智商药1. 题目描述2. 思路分析3. 代码实现 六、参考链接 总结 状态不对&#xff0c;把自己写懵了。T1 模拟币T2 对向双指针…

关于Linux同步机制知识点整理

在Linux系统中&#xff0c;同步机制是操作系统中非常重要的一部分&#xff0c;以下是一些基本要点&#xff1a; 互斥锁 互斥锁是一种「独占锁」&#xff0c;比如当线程 A 加锁成功后&#xff0c;此时互斥锁已经被线程 A 独占了&#xff0c;只要线程 A 没有释放手中的锁&#…

梁宁:VisionPro、GPT、Web3三件套齐备,元宇宙开启

本文内容整理自图灵社区对谈栏目直播&#xff0c;主题为 ChatGPT 真需求&#xff0c;从产品的第一性原理解析。 上篇内容回顾&#xff1a;梁宁&#xff1a;为什么中国没有像 ChatGPT 和 Vision Pro 这样的创新产品&#xff1f; 梁宁&#xff0c;产品战略专家&#xff0c;曾任湖…

chatgpt赋能python:如何在Python中安装PIL

如何在Python中安装PIL Python Imaging Library&#xff08;PIL&#xff09;是一款用于处理图像的Python库&#xff0c;它提供了各种图像处理功能&#xff0c;包括大小调整、旋转、裁剪等等。如果你需要在你的Python项目中处理图像&#xff0c;那么PIL是一个不错的选择。 步骤…

DBeaver连接GaussDB

DBeaver 官网&#xff1a;https://dbeaver.io/打开DBeaver&#xff0c;点击菜单栏 “数据库”>“驱动管理” 点击“新建” 填入下面内容&#xff1a; 驱动名称&#xff1a;GS 驱动类型&#xff1a;Generic 类名&#xff1a;org.postgresql.Driver URL模板&#xff1a;jdbc…

Linux:LNMP上搭建discuz论坛(源码安装)

LNMP环境 Linux &#xff1a;LNMP&#xff08;源码包安装&#xff09;_鲍海超-GNUBHCkalitarro的博客-CSDN博客 discuz论坛 准备好源码包 LNMP环境正常 yum -y install unzip unzip Discuz_X3.3_SC_UTF8.zip # unzip 源码包名称 mv upload/ /usr/local/nginx/html/tarro…