【强化学习】11 —— Double DQN算法与Dueling DQN算法

2023-10-30 10:44

本文主要是介绍【强化学习】11 —— Double DQN算法与Dueling DQN算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • Q-learning中的过高估计
  • Double DQN
    • Double DQN代码实践
      • Pendulum环境
      • 代码
      • 结果
  • Dueling DQN
    • Dueling DQN网络结构
    • Dueling DQN优点
    • Dueling DQN 代码实践
      • 结果
  • 参考

Q-learning中的过高估计

普通的 DQN 算法通常会导致对值的过高估计(overestimation)。传统 DQN 优化的 TD 误差目标为 r + γ max ⁡ a ′ Q ω − ( s ′ , a ′ ) r+\gamma\max_{a^{\prime}}Q_{\omega^{-}}\left(s^{\prime},a^{\prime}\right) r+γamaxQω(s,a)
其中 max ⁡ a ′ Q ω − ( s ′ , a ′ ) \max_{a^{\prime}}Q_{\omega^{-}}\left(s^{\prime},a^{\prime}\right) maxaQω(s,a)可以变为这种形式 Q ω − ( s ′ , arg ⁡ max ⁡ a ′ Q ω − ( s ′ , a ′ ) ) Q_{\omega^-}\left(s',\arg\max_{a'}Q_{\omega^-}\left(s',a'\right)\right) Qω(s,argamaxQω(s,a)) m a x max max操作实际可以被拆解为两部分:首先选取状态 s ′ s' s下的最优动作 a ∗ = arg ⁡ max ⁡ a ′ Q ω − ( s ′ , a ′ ) a^*=\arg\max_{a^{\prime}}Q_{\omega^-}\left(s^{\prime},a^{\prime}\right) a=argmaxaQω(s,a),接着计算该动作对应的价值 Q ω − ( s ′ , a ∗ ) Q_{\omega^{-}}\left(s^{\prime},a^{*}\right) Qω(s,a)。但 m a x max max 操作使得 𝑄 函数的值越来越大,甚至高于真实值。原因如下:假设有随机变量 X 1 X_1 X1, X 2 X_2 X2,取 m a x max max之后的期望大于等于先取期望再取 m a x max max操作, E [ max ⁡ ( X 1 , X 2 ) ] ≥ max ⁡ ( E [ X 1 ] , E [ X 2 ] ) \mathbb{E}[\max(X_1,X_2)]\geq\max(\mathbb{E}[X_1],\mathbb{E}[X_2]) E[max(X1,X2)]max(E[X1],E[X2])𝑄 函数的值被视作在状态 𝑠′,动作 𝑎′ 下的回报期望值。于是有 max ⁡ a ′ ∈ A Q θ ′ ( s t + 1 , a ′ ) = Q θ ′ ( s t + 1 , arg ⁡ max ⁡ a ′ Q θ ′ ( s t + 1 , a ′ ) ) = E [ R ∣ s t + 1 , arg ⁡ max ⁡ a ′ Q θ ′ ( s t + 1 , a ′ ) , θ ′ ] ≥ max ⁡ ( E [ R ∣ s t + 1 , a 1 , θ ′ ] , E [ R ∣ s t + 1 , a 2 , θ ′ ] , ⋯ ) , a i ∈ A \begin{aligned} \max_{a'\in A}Q_{\theta'}(s_{t+1},a')& =Q_{\theta^\prime}(s_{t+1},\arg\max_{a^\prime}Q_{\theta^\prime}(s_{t+1},a^\prime)) \\ &=\mathbb{E}[R|s_{t+1},\arg\max_{a^{\prime}}Q_{\theta^{\prime}}(s_{t+1},a^{\prime}),\theta^{\prime}] \\ &\geq\max(\mathbb{E}[R|s_{t+1},a_1,\theta^{\prime}],\mathbb{E}[R|s_{t+1},a_2,\theta^{\prime}],\cdots),a_i\in A \end{aligned} aAmaxQθ(st+1,a)=Qθ(st+1,argamaxQθ(st+1,a))=E[Rst+1,argamaxQθ(st+1,a),θ]max(E[Rst+1,a1,θ],E[Rst+1,a2,θ],),aiA
举个例子:在这里插入图片描述
如图所示,x轴为状态,有10个候选行动;左列紫线是真实价值函数,绿点是训练数据点,绿线是拟合的价值函数。中间黑色虚线是取最大值后的估计。可以看到由于数据点的分布,使得拟合出的价值函数无法与真实的价值函数吻合,甚至在部分区域产生了明显的偏差。’

此外,Q函数的过高估计程度随着候选行动数量增大变得更严重.下图中𝑄′函数是另一组独立训练的价值函数。可以看到红色部分的误差明显大于蓝色。
在这里插入图片描述

Double DQN

为了解决这一问题,Double DQN 算法提出利用两个独立训练的神经网络估算 max ⁡ a ′ Q ∗ ( s ′ , a ′ ) \max_{a^{\prime}}Q_{*}\left(s^{\prime},a^{\prime}\right) maxaQ(s,a)。具体做法是将原有的 Q ω − ( s ′ , arg ⁡ max ⁡ a ′ Q ω − ( s ′ , a ′ ) ) Q_{\omega^-}\left(s',\arg\max_{a'}Q_{\omega^-}\left(s',a'\right)\right) Qω(s,argmaxaQω(s,a))更改为 Q ω − ( s ′ , arg ⁡ max ⁡ a ′ Q ω ( s ′ , a ′ ) ) Q_{\omega^-}\left(s',\arg\max_{a'}Q_{\omega}\left(s',a'\right)\right) Qω(s,argmaxaQω(s,a)),即利用一套神经网络 Q ω Q_{\omega} Qω的输出选取价值最大的动作,但在使用该动作的价值时,用另一套神经网络 Q ω − Q_{\omega^-} Qω计算该动作的价值。这样,即使其中一套神经网络的某个动作存在比较严重的过高估计问题,由于另一套神经网络的存在,这个动作最终使用的值不会存在很大的过高估计问题。

在传统的 DQN 算法中,本来就存在两套 Q Q Q函数的神经网络——目标网络和训练网络,只不过 Q ω − ( s ′ , arg ⁡ max ⁡ a ′ Q ω − ( s ′ , a ′ ) ) Q_{\omega^-}\left(s',\arg\max_{a'}Q_{\omega^-}\left(s',a'\right)\right) Qω(s,argmaxaQω(s,a))的计算只用到了其中的目标网络,那么我们恰好可以直接将训练网络作为 Double DQN 算法中的第一套神经网络来选取动作,将目标网络作为第二套神经网络计算值,这便是 Double DQN 的主要思想。由于在 DQN 算法中将训练网络的参数记为 ω \omega ω,将目标网络的参数记为 ω − \omega^- ω,这与本节中 Double DQN 的两套神经网络的参数是统一的,因此,我们可以直接写出如下 Double DQN 的优化目标: r + γ Q ω − ( s ′ , arg ⁡ max ⁡ a ′ Q ω ( s ′ , a ′ ) ) r+\gamma Q_{\omega^-}\left(s^{\prime},\arg\max_{a^{\prime}}Q_{\omega}\left(s^{\prime},a^{\prime}\right)\right) r+γQω(s,argamaxQω(s,a))

Double DQN代码实践

Pendulum环境

Pendulum环境文档
本节采用的环境是倒立摆(Inverted Pendulum),该环境下有一个处于随机位置的倒立摆。环境的状态包括倒立摆角度的正弦值 s i n θ sin\theta sinθ,余弦值 c o s θ cos\theta cosθ,角速度 θ ˙ \dot\theta θ˙;动作为对倒立摆施加的力矩 τ \tau τ.每一步都会根据当前倒立摆的状态的好坏给予智能体不同的奖励,该环境的奖励函数为 r = − ( t h e t a 2 + 0.1 ∗ t h e t a d t 2 + 0.001 ∗ t o r q u e 2 ) r = -(theta^2 + 0.1 * theta_dt^2 + 0.001 * torque^2) r=(theta2+0.1thetadt2+0.001torque2).倒立摆向上保持直立不动时奖励为 0,倒立摆在其他位置时奖励为负数(最大回报为零,倒立摆是垂直的,速度为零,没有施加扭矩)。环境本身没有终止状态,运行 200 步后游戏自动结束。
在这里插入图片描述
Pendulum环境的状态空间

标号名称最小值最大值
0x = cos(theta)-1.01.0
1y = sin(theta)-1.01.0
2Angular Velocity-8.08.0

Pendulum环境的动作空间

标号名称最小值最大值
0力矩-2.02.0

力矩大小是在 [ − 2 , 2 ] [-2,2] [2,2]范围内的连续值。为了能够应用 DQN,我们需要采用离散化动作的技巧。例如,下面的代码将连续的动作空间离散为 11 个动作。动作 [ 0 , 1 , 2 , … , 9 , 10 ] [0,1,2,\dots,9,10] [0,1,2,,9,10]分别代表力矩为 [ − 2 , − 1.6 , − 1.2 , … , 1.2 , 1.6 , 2.0 ] [-2,-1.6,-1.2,\dots,1.2,1.6,2.0] [2,1.6,1.2,,1.2,1.6,2.0]

代码

PS:
torch.max()[0], 只返回最大值的每个数
troch.max()[1], 只返回最大值的每个索引

class DQN:''' DQN算法 '''def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma,epsilon, target_update_rate, device, numOfEpisodes, env,buffer_size, minimal_size, batch_size, DQNtype="DQN"):self.action_dim = action_dim# Q网络self.q_net = Qnet(state_dim, hidden_dim, self.action_dim).to(device)# 目标网络self.target_q_net = Qnet(state_dim, hidden_dim, self.action_dim).to(device)# 使用Adam优化器self.optimizer = torch.optim.Adam(self.q_net.parameters(),lr=learning_rate)self.gamma = gammaself.epsilon = epsilon# 目标网络更新频率self.target_update_rate = target_update_rate# 计数器,记录更新次数self.count = 0self.device = deviceself.numOfEpisodes = numOfEpisodesself.env = envself.buffer_size = buffer_sizeself.minimal_size = minimal_sizeself.batch_size = batch_sizeself.DQNtype = DQNtype# Choose A from S using policy derived from Q (e.g., epsilon-greedy)def ChooseAction(self, state):if np.random.random() < self.epsilon:action = np.random.randint(self.action_dim)else:state = torch.tensor(np.array([state]), dtype=torch.float).to(self.device)action = self.q_net(state).argmax().item()return actiondef Update(self, transition_dict):states = torch.tensor(transition_dict['states'],dtype=torch.float).to(self.device)actions = torch.tensor(transition_dict['actions']).view(-1, 1).to(self.device)rewards = torch.tensor(transition_dict['rewards'],dtype=torch.float).view(-1, 1).to(self.device)next_states = torch.tensor(transition_dict['next_states'],dtype=torch.float).to(self.device)terminateds = torch.tensor(transition_dict['terminateds'],dtype=torch.float).view(-1, 1).to(self.device)truncateds = torch.tensor(transition_dict['truncateds'],dtype=torch.float).view(-1, 1).to(self.device)#Q值q_values = self.q_net(states).gather(1, actions)# 下个状态的最大Q值if self.DQNtype == "DoubleDQN":max_action = self.q_net(next_states).max(1)[1].view(-1, 1)max_next_q_values = self.target_q_net(next_states).gather(1, max_action)else: # DQN的情况max_next_q_values = self.target_q_net(next_states).max(1)[0].view(-1, 1)# TD误差目标q_targets = rewards + self.gamma * max_next_q_values * (1 - terminateds + truncateds)# 均方误差损失函数dqn_loss = torch.mean(F.mse_loss(q_values, q_targets))# PyTorch中默认梯度会累积,这里需要显式将梯度置为0self.optimizer.zero_grad()# 反向传播更新参数dqn_loss.backward()self.optimizer.step()if self.count % self.target_update_rate == 0:self.target_q_net.load_state_dict(self.q_net.state_dict())  # 更新目标网络self.count += 1def max_q_value(self, state):state = torch.tensor(np.array([state]), dtype=torch.float).to(self.device)return self.q_net(state).max().item()# 离散动作转回连续的函数def dis_to_con(self, discrete_action, env, action_dim):action_lowbound = env.action_space.low[0]actoin_upbound = env.action_space.high[0]return action_lowbound + (discrete_action / (action_dim - 1)) \* (actoin_upbound - action_lowbound)def DQNtrain(self):replay_buffer = util.ReplayBuffer(self.buffer_size)returnList = []max_q_valueList = []max_q_value = 0for i in range(10):with tqdm(total=int(self.numOfEpisodes / 10), desc='Iteration %d' % i) as pbar:for episode in range(int(self.numOfEpisodes / 10)):# initialize statestate, info = self.env.reset()terminated = Falsetruncated = FalseepisodeReward = 0# Loop for each step of episode:while (not terminated) or (not truncated):action = self.ChooseAction(state)# 平滑处理max_q_value = self.max_q_value(state) * 0.005 + max_q_value * 0.995max_q_valueList.append(max_q_value)action_continuous = self.dis_to_con(action, self.env, self.action_dim)next_state, reward, terminated, truncated, info = self.env.step([action_continuous])replay_buffer.add(state, action, reward, next_state, terminated, truncated)if terminated or truncated:breakstate = next_stateepisodeReward += reward# 当buffer数据的数量超过一定值后,才进行Q网络训练if replay_buffer.size() > self.minimal_size:b_s, b_a, b_r, b_ns, b_te, b_tr = replay_buffer.sample(self.batch_size)transition_dict = {'states': b_s,'actions': b_a,'next_states': b_ns,'rewards': b_r,'terminateds': b_te,'truncateds': b_tr}self.Update(transition_dict)returnList.append(episodeReward)if (episode + 1) % 10 == 0:  # 每10条序列打印一下这10条序列的平均回报pbar.set_postfix({'episode':'%d' % (self.numOfEpisodes / 10 * i + episode + 1),'return':'%.3f' % np.mean(returnList[-10:])})pbar.update(1)return returnList, max_q_valueList

结果

在这里插入图片描述
在这里插入图片描述
如图所示,DQN相比DoubleDQN能够获得更多的回报。
在这里插入图片描述
如图所示,DQN算法会产生过高估计(超越黄色虚线0),甚至部分Q值超过了10。而DoubleDQN算法则能有效缓解过高估计这一问题。

Dueling DQN

Dueling DQN 是 DQN 另一种的改进算法,它在传统 DQN 的基础上只进行了微小的改动,但却能大幅提升 DQN 的表现。在强化学习中,我们将状态动作价值函数 Q Q Q减去状态价值函数 V V V的结果定义为优势函数 A A A,即 A ( s , a ) = Q ( s , a ) − V ( s ) A(s,a)=Q(s,a)-V(s) A(s,a)=Q(s,a)V(s).在同一个状态下,所有动作的优势值之和为 0,因为所有动作的动作价值的期望就是这个状态的状态价值。据此,在 Dueling DQN 中,Q 网络被建模为: Q η , α , β ( s , a ) = V η , α ( s ) + A η , β ( s , a ) Q_{\eta,\alpha,\beta}(s,a)=V_{\eta,\alpha}(s)+A_{\eta,\beta}(s,a) Qη,α,β(s,a)=Vη,α(s)+Aη,β(s,a)
其中, V η , α ( s ) V_{\eta,\alpha}(s) Vη,α(s)为状态价值函数,而 A η , β ( s , a ) A_{\eta,\beta}(s,a) Aη,β(s,a)则为该状态下采取不同动作的优势函数,表示采取不同动作的差异性; η \eta η是状态价值函数和优势函数共享的网络参数,一般用在神经网络中,用来提取特征的前几层;而 α , β \alpha,\beta α,β分别为状态价值函数和优势函数的参数。

不同的Advantage聚合形式:

  • Q η , α , β ( s , a ) = V η , α ( s ) + A η , β ( s , a ) − max ⁡ a ′ A η , β ( s , a ′ ) Q_{\eta,\alpha,\beta}(s,a)=V_{\eta,\alpha}(s)+A_{\eta,\beta}(s,a)-\max_{a'}A_{\eta,\beta}\left(s,a'\right) Qη,α,β(s,a)=Vη,α(s)+Aη,β(s,a)amaxAη,β(s,a)
  • Q η , α , β ( s , a ) = V η , α ( s ) + A η , β ( s , a ) − 1 ∣ A ∣ ∑ a ′ A η , β ( s , a ′ ) Q_{\eta,\alpha,\beta}(s,a)=V_{\eta,\alpha}(s)+A_{\eta,\beta}(s,a)-\frac{1}{|\mathcal{A}|}\sum_{a'}A_{\eta,\beta}\left(s,a'\right) Qη,α,β(s,a)=Vη,α(s)+Aη,β(s,a)A1aAη,β(s,a)

Dueling DQN网络结构

在这里插入图片描述

Dueling DQN优点

  • 能够处理与动作关联较小的状态在这里插入图片描述
  • Dueling DQN 能更高效学习状态价值函数。每一次更新时, V V V函数都会被更新,这也会影响到其他动作的 Q Q Q值。而传统的 DQN 只会更新某个动作的 Q Q Q值,其他动作的 Q Q Q值就不会更新。因此,Dueling DQN 能够更加频繁、准确地学习状态价值函数。

Dueling DQN 代码实践

class VAnet(torch.nn.Module):''' 只有一层隐藏层的A网络和V网络 '''def __init__(self, state_dim, hidden_dim, action_dim):super(VAnet, self).__init__()# 共享网络部分self.fc1 = torch.nn.Linear(state_dim, hidden_dim)self.fc_A = torch.nn.Linear(hidden_dim, action_dim)self.fc_V = torch.nn.Linear(hidden_dim, 1)def forward(self, x):A = self.fc_A(F.relu(self.fc1(x)))V = self.fc_V(F.relu((self.fc1(x))))Q = A + V - A.mean(1).view(-1, 1)return Qclass DQN:''' DQN算法 '''def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma,epsilon, target_update_rate, device, numOfEpisodes, env,buffer_size, minimal_size, batch_size, DQNtype="DQN"):self.action_dim = action_dimif DQNtype == "DuelingDQN":self.q_net = VAnet(state_dim, hidden_dim, self.action_dim).to(device)self.target_q_net = VAnet(state_dim, hidden_dim, self.action_dim).to(device)else:# Q网络self.q_net = Qnet(state_dim, hidden_dim, self.action_dim).to(device)# 目标网络self.target_q_net = Qnet(state_dim, hidden_dim, self.action_dim).to(device)# 使用Adam优化器self.optimizer = torch.optim.Adam(self.q_net.parameters(),lr=learning_rate)self.gamma = gammaself.epsilon = epsilon# 目标网络更新频率self.target_update_rate = target_update_rate# 计数器,记录更新次数self.count = 0self.device = deviceself.numOfEpisodes = numOfEpisodesself.env = envself.buffer_size = buffer_sizeself.minimal_size = minimal_sizeself.batch_size = batch_sizeself.DQNtype = DQNtype

结果

在这里插入图片描述
同样,DQN相比Dueling DQN能够获得更多的回报。
在这里插入图片描述
Dueling DQN可以缓解过高估计这一问题。

参考

[1] 伯禹AI
[2] https://www.davidsilver.uk/teaching/
[3] 动手学强化学习
[4] Reinforcement Learning

这篇关于【强化学习】11 —— Double DQN算法与Dueling DQN算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/307368

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用