当前位置: 首页 > article >正文

Double Q-learning实战:如何用Python解决过估计问题(附代码示例)

Double Q-learning实战如何用Python解决过估计问题附代码示例强化学习中的Q-learning算法因其简洁高效而广受欢迎但在某些场景下会出现严重的过估计问题。本文将深入探讨这一现象的本质并手把手教你用Python实现Double Q-learning算法通过代码对比展示其如何有效解决过估计问题。1. Q-learning的过估计问题理论与现象在标准Q-learning中我们通过以下公式更新Q值Q(s,a) Q(s,a) α * (r γ * max_a Q(s,a) - Q(s,a))这里的max操作正是过估计的根源。假设在某状态下有两个动作a1和a2其真实Q值分别为1.0和0.9。由于采样误差估计值可能变为1.2和0.8。此时max会选择1.2导致更新时使用了比真实最大值更大的值。这种现象在Atari游戏等复杂环境中尤为明显。van Hasselt在2010年的论文中通过数学证明指出Q-learning的过估计偏差可以表示为E[max Q(s,a)] ≥ max E[Q(s,a)]为了直观理解我们模拟一个简单的过估计场景import numpy as np # 真实Q值 true_q [1.0, 0.9, 0.8] # 估计Q值带有噪声 estimated_q true_q np.random.normal(0, 0.2, size3) print(f真实最大值: {max(true_q):.2f}) print(f估计最大值: {max(estimated_q):.2f}) print(f过估计量: {max(estimated_q) - max(true_q):.2f})多次运行会发现估计最大值经常高于真实最大值。这种偏差会通过贝尔曼方程传播导致整个学习过程不稳定。2. Double Q-learning算法原理Double Q-learning的核心思想是使用两个独立的Q函数(Q_A和Q_B)来解耦动作选择和价值评估用Q_A选择最优动作a* argmax_a Q_A(s,a)用Q_B评估该动作的价值Q_B(s,a*)更新Q_AQ_A(s,a) α * (r γ * Q_B(s,a*) - Q_A(s,a))交替更新Q_B使用相同的逻辑这种解耦使得即使一个Q函数过估计了某个动作只要另一个Q函数没有同样过估计最终的更新就不会受到严重影响。数学上可以证明Double Q-learning会产生轻微的欠估计但相比Q-learning的过估计这种偏差通常更可控E[Q_B(s,argmax_a Q_A(s,a))] ≤ max E[Q(s,a)]3. Python实现与对比实验我们以一个简单的GridWorld环境为例对比标准Q-learning和Double Q-learning的表现。3.1 环境设置import numpy as np class GridWorld: def __init__(self): self.size 5 self.goal (4,4) self.trap (2,2) self.reset() def reset(self): self.state (0,0) return self.state def step(self, action): x, y self.state if action 0: # 上 x max(0, x-1) elif action 1: # 右 y min(self.size-1, y1) elif action 2: # 下 x min(self.size-1, x1) elif action 3: # 左 y max(0, y-1) self.state (x,y) if self.state self.goal: return self.state, 10, True elif self.state self.trap: return self.state, -10, True else: return self.state, -1, False3.2 Q-learning实现class QLearningAgent: def __init__(self, n_states, n_actions, alpha0.1, gamma0.95, epsilon0.1): self.q_table np.zeros((n_states, n_actions)) self.alpha alpha self.gamma gamma self.epsilon epsilon def choose_action(self, state): if np.random.random() self.epsilon: return np.random.randint(0, len(self.q_table[state])) return np.argmax(self.q_table[state]) def learn(self, state, action, reward, next_state, done): predict self.q_table[state][action] target reward if done else reward self.gamma * np.max(self.q_table[next_state]) self.q_table[state][action] self.alpha * (target - predict)3.3 Double Q-learning实现class DoubleQLearningAgent: def __init__(self, n_states, n_actions, alpha0.1, gamma0.95, epsilon0.1): self.q_a np.zeros((n_states, n_actions)) self.q_b np.zeros((n_states, n_actions)) self.alpha alpha self.gamma gamma self.epsilon epsilon def choose_action(self, state): if np.random.random() self.epsilon: return np.random.randint(0, len(self.q_a[state])) return np.argmax(self.q_a[state] self.q_b[state]) def learn(self, state, action, reward, next_state, done): # 随机选择更新Q_A或Q_B if np.random.random() 0.5: q_predict self.q_a[state][action] if done: q_target reward else: best_action np.argmax(self.q_a[next_state]) q_target reward self.gamma * self.q_b[next_state][best_action] self.q_a[state][action] self.alpha * (q_target - q_predict) else: q_predict self.q_b[state][action] if done: q_target reward else: best_action np.argmax(self.q_b[next_state]) q_target reward self.gamma * self.q_a[next_state][best_action] self.q_b[state][action] self.alpha * (q_target - q_predict)3.4 训练与结果对比def train_agent(env, agent, episodes1000): rewards [] for episode in range(episodes): state env.reset() total_reward 0 done False while not done: action agent.choose_action(state[0]*env.size state[1]) next_state, reward, done env.step(action) agent.learn(state[0]*env.size state[1], action, reward, next_state[0]*env.size next_state[1], done) state next_state total_reward reward rewards.append(total_reward) return rewards # 训练并比较两种算法 env GridWorld() q_agent QLearningAgent(25, 4) double_q_agent DoubleQLearningAgent(25, 4) q_rewards train_agent(env, q_agent) double_q_rewards train_agent(env, double_q_agent)通过绘制两种算法的奖励曲线可以明显看到Double Q-learning更稳定且最终表现更好import matplotlib.pyplot as plt plt.plot(np.convolve(q_rewards, np.ones(100)/100, modevalid), labelQ-learning) plt.plot(np.convolve(double_q_rewards, np.ones(100)/100, modevalid), labelDouble Q-learning) plt.xlabel(Episode) plt.ylabel(Average Reward) plt.legend() plt.show()4. 高级技巧与超参数调优4.1 学习率调度动态调整学习率可以提升算法性能class DoubleQLearningAgent: def __init__(self, n_states, n_actions, alpha0.1, gamma0.95, epsilon0.1): # ... 其他初始化 ... self.alpha_decay 0.9995 self.min_alpha 0.01 def learn(self, state, action, reward, next_state, done): self.alpha max(self.min_alpha, self.alpha * self.alpha_decay) # ... 其余学习逻辑 ...4.2 探索策略优化ε-greedy策略可以随时间衰减class DoubleQLearningAgent: def __init__(self, n_states, n_actions, alpha0.1, gamma0.95, epsilon1.0): # ... 其他初始化 ... self.epsilon_decay 0.995 self.min_epsilon 0.01 def choose_action(self, state): self.epsilon max(self.min_epsilon, self.epsilon * self.epsilon_decay) # ... 其余动作选择逻辑 ...4.3 经验回放集成虽然传统Double Q-learning不使用经验回放但我们可以结合from collections import deque import random class ReplayBuffer: def __init__(self, capacity): self.buffer deque(maxlencapacity) def push(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): return random.sample(self.buffer, batch_size) def __len__(self): return len(self.buffer) class DoubleQLearningWithReplay(DoubleQLearningAgent): def __init__(self, n_states, n_actions, batch_size32, buffer_size10000, **kwargs): super().__init__(n_states, n_actions, **kwargs) self.buffer ReplayBuffer(buffer_size) self.batch_size batch_size def learn(self, state, action, reward, next_state, done): self.buffer.push(state, action, reward, next_state, done) if len(self.buffer) self.batch_size: batch self.buffer.sample(self.batch_size) for s, a, r, ns, d in batch: super().learn(s, a, r, ns, d)5. 实际应用中的注意事项网络架构设计当结合深度神经网络时可以共享部分网络参数来减少计算量目标网络定期更新目标网络可以进一步提高稳定性偏差-方差权衡Double Q-learning减少了方差但可能增加偏差需要平衡多步学习结合n步回报可以加速学习但可能引入额外偏差以下是一个结合了上述改进的完整实现示例class AdvancedDoubleQLearning: def __init__(self, n_states, n_actions, alpha0.1, gamma0.95, epsilon1.0): self.q_a np.random.uniform(-0.1, 0.1, (n_states, n_actions)) self.q_b np.random.uniform(-0.1, 0.1, (n_states, n_actions)) self.alpha alpha self.gamma gamma self.epsilon epsilon self.alpha_decay 0.9995 self.min_alpha 0.01 self.epsilon_decay 0.995 self.min_epsilon 0.01 self.target_update_freq 100 self.steps 0 self.q_a_target self.q_a.copy() self.q_b_target self.q_b.copy() def choose_action(self, state): self.epsilon max(self.min_epsilon, self.epsilon * self.epsilon_decay) if np.random.random() self.epsilon: return np.random.randint(0, len(self.q_a[state])) return np.argmax(self.q_a[state] self.q_b[state]) def learn(self, state, action, reward, next_state, done): self.steps 1 self.alpha max(self.min_alpha, self.alpha * self.alpha_decay) # 随机选择更新Q_A或Q_B if np.random.random() 0.5: q_predict self.q_a[state][action] if done: q_target reward else: best_action np.argmax(self.q_a_target[next_state]) q_target reward self.gamma * self.q_b_target[next_state][best_action] self.q_a[state][action] self.alpha * (q_target - q_predict) else: q_predict self.q_b[state][action] if done: q_target reward else: best_action np.argmax(self.q_b_target[next_state]) q_target reward self.gamma * self.q_a_target[next_state][best_action] self.q_b[state][action] self.alpha * (q_target - q_predict) # 定期更新目标网络 if self.steps % self.target_update_freq 0: self.q_a_target self.q_a.copy() self.q_b_target self.q_b.copy()在实际项目中我发现将ε的初始值设为1.0并缓慢衰减配合动态调整的学习率能够取得比固定参数更好的效果。同时目标网络的更新频率需要根据具体问题调整——对于更稳定的环境可以降低更新频率而对于快速变化的环境则需要更频繁地更新。

相关文章:

Double Q-learning实战:如何用Python解决过估计问题(附代码示例)

Double Q-learning实战:如何用Python解决过估计问题(附代码示例) 强化学习中的Q-learning算法因其简洁高效而广受欢迎,但在某些场景下会出现严重的过估计问题。本文将深入探讨这一现象的本质,并手把手教你用Python实现…...

手把手教你实现glitch free的时钟切换电路(附Verilog代码)

手把手教你实现glitch free的时钟切换电路(附Verilog代码) 时钟切换电路是数字系统设计中的关键模块,尤其在多时钟域系统中,可靠的时钟切换能确保系统稳定运行。本文将深入探讨如何实现无毛刺(glitch free)…...

RStudio Server部署与运维实战:从零搭建到高效管理

1. 环境准备:搭建RStudio Server的基石 在开始部署RStudio Server之前,我们需要确保服务器环境已经准备就绪。就像盖房子需要打地基一样,这一步决定了后续所有工作的稳定性。我遇到过不少因为环境问题导致的安装失败案例,大多数都…...

GORM实战避坑指南:从‘小白’到‘老鸟’必须知道的10个细节(含MySQL连接配置)

GORM实战避坑指南:从‘小白’到‘老鸟’必须知道的10个细节(含MySQL连接配置) 1. MySQL连接配置的隐藏陷阱 charsetutf8mb4的必要性 MySQL默认的utf8编码只支持最多3字节的字符,而emoji表情等特殊字符需要4字节存储。若不指定utf8…...

Altium Designer16禁止区域设置避坑指南:为什么你的剪切块总是不生效?

Altium Designer 16禁止区域设置避坑指南:为什么你的剪切块总是不生效? 在PCB设计过程中,禁止区域(Keep-Out Region)的设置是确保电路板可靠性的重要环节。然而,许多Altium Designer 16用户在实际操作中经常遇到剪切块转换失败的问…...

告别玄学调参:在ADS里用Yield Analysis给你的射频滤波器设计上个‘保险’

射频滤波器设计的工程化验证:用ADS Yield Analysis实现稳健性设计 在Wi-Fi 6E和5G毫米波频段快速普及的今天,射频前端模块的性能直接决定了通信质量的上限。作为信号链路上的"守门人",滤波器设计不仅要满足理想仿真环境下的指标要求…...

C#实战:5分钟搞定Modbus RTU通讯(基于NModbus4库)

C#实战:5分钟搞定Modbus RTU通讯(基于NModbus4库) 工业自动化领域的数据采集离不开设备通讯协议的支持,而Modbus RTU作为最广泛应用的串行通信协议之一,几乎成为工控开发者的必修课。今天我们就用C#和NModbus4库&#…...

告别第三方工具:用Cloudflare官方测速文件快速检测你的网络性能

告别第三方工具:用Cloudflare官方测速文件快速检测你的网络性能 你是否遇到过这样的场景:视频缓冲转圈、文件下载龟速、在线会议卡顿,却不知道是网络问题还是服务商的问题?传统的测速工具要么需要安装软件,要么广告满天…...

多人对话录音整理神器:ClearerVoice-Studio语音分离功能详细教程

多人对话录音整理神器:ClearerVoice-Studio语音分离功能详细教程 1. 引言:告别混乱的多人录音 你是否经常需要整理会议录音、访谈记录或多人讨论内容?传统的录音文件往往混杂着多个人的声音,背景噪音干扰严重,整理起…...

提示工程架构师用Agentic AI,为智能城市提升品质生活

提示工程架构师:借助Agentic AI提升智慧城市品质生活 一、引言 (Introduction) 钩子 (The Hook) 想象一下,你生活在这样一个城市:每天清晨,你的智能设备会根据当天的天气、你的日程安排,精准推荐最适宜的衣物和出行方式…...

国产AI 调用量反超美国,22个免费大模型API集结,DMXAPI 成开发者首选

据 OpenRouter 最新数据,2026 年 3 月中国 AI 大模型周调用量达 4.69 万亿 Token,连续两周超越美国,全球调用量前三席位被小米 MiMo-V2-Pro、阶跃星辰 Step 3.5 Flash、MiniMax M2.5 包揽,国产模型凭性能与性价比获全球开发者认可…...

掌握BepInEx:Unity游戏扩展全家桶的零门槛实践指南

掌握BepInEx:Unity游戏扩展全家桶的零门槛实践指南 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 🔍 游戏模组管理的行业痛点与解决方案 在Unity游戏生态…...

淘宝母婴购物数据可视化分析:从数据清洗到商业洞察

1. 淘宝母婴数据清洗实战:从原始数据到分析就绪 做数据分析最头疼的就是拿到一堆乱七八糟的原始数据,淘宝母婴数据也不例外。我最近处理过一批天池比赛的脱敏数据,光是清洗环节就踩了不少坑。先说说最基础的CSV导入,用pandas的rea…...

pkNX:定制宝可梦游戏体验的全能编辑工具指南

pkNX:定制宝可梦游戏体验的全能编辑工具指南 【免费下载链接】pkNX Pokmon (Nintendo Switch) ROM Editor & Randomizer 项目地址: https://gitcode.com/gh_mirrors/pk/pkNX 你是否曾想过在宝可梦游戏中拥有独一无二的精灵阵容?是否希望调整训…...

Scratch3.0离线编辑器安装指南:一步步教你轻松搞定

1. 为什么你需要Scratch3.0离线编辑器 Scratch作为全球最受欢迎的少儿编程工具,它的在线版本虽然方便,但经常会遇到网络不稳定、加载缓慢的问题。我去年给小学生上课时就遇到过这种情况——全班40个孩子同时登录在线编辑器,结果服务器直接卡死…...

高效解决Magpie插件更新难题:完全掌握图像增强功能升级指南

高效解决Magpie插件更新难题:完全掌握图像增强功能升级指南 【免费下载链接】Magpie An all-purpose window upscaler for Windows 10/11. 项目地址: https://gitcode.com/gh_mirrors/mag/Magpie 识别插件更新需求:为何及时升级至关重要 在使用M…...

【HFP】规范精讲[15]: HFP蓝牙特有AT命令:免提场景专属功能的控制语言

在蓝牙HFP的命令体系中,除了复用自传统移动通信标准的AT命令,还有一类专门为蓝牙免提场景设计的专属AT命令。这些命令就像为蓝牙免提设备量身定制的方言,针对无线音频传输、设备间状态同步、蓝牙特有功能等场景进行了精准优化,是实…...

别再只会用滑动平均了!用Python从零实现数字陷波器,精准滤除50Hz工频干扰

从零构建Python数字陷波器:精准滤除50Hz工频干扰的工程实践 当你在深夜调试一个心爱的传感器项目时,突然发现采集到的数据波形上叠加了一个顽固的50Hz正弦波——这种经历想必不少硬件开发者都深有体会。工频干扰就像电子世界中的背景噪音,无…...

别再死记硬背!用拖拽和右键菜单玩转汇川CodeSys网络与硬件组态

汇川CodeSys图形化组态实战:拖拽与右键菜单的高效玩法 第一次打开汇川CodeSys的组态界面时,那些密密麻麻的菜单和复杂的参数设置确实让人望而生畏。但当我发现可以用鼠标拖拽完成90%的配置工作时,整个PLC编程体验彻底改变了——就像从DOS命令…...

别再死记硬背了!用Halcon的vector_angle_to_rigid算子搞定视觉定位,附完整代码

视觉定位实战:用Halcon的vector_angle_to_rigid算子避开几何变换的三大误区 在工业视觉项目中,刚体变换是坐标转换的核心技术,但许多工程师在使用Halcon的vector_angle_to_rigid算子时,常陷入三个致命误区:误认为旋转…...

Tomcat服务没启动?手把手解决127.0.0.1拒绝连接问题(附端口排查技巧)

Tomcat服务没启动?手把手解决127.0.0.1拒绝连接问题(附端口排查技巧) 当你满怀期待地在浏览器输入http://127.0.0.1:8080准备测试刚部署的Java Web应用时,屏幕上冰冷的"拒绝连接"提示就像一盆冷水浇下来。这种情况我见过…...

5分钟搞定Qwen2-7B本地部署:从GGUF下载到API调用的保姆级教程

5分钟极速部署Qwen2-7B:从模型下载到API调用的实战手册 在人工智能技术快速迭代的今天,能够在本地高效运行大语言模型已成为开发者的一项核心竞争力。Qwen2-7B作为当前最受关注的中等规模开源模型之一,以其出色的中文理解能力和适中的硬件需求…...

联想X3650M5服务器双模式切换实战:UEFI与Legacy BIOS自由转换技巧

联想X3650M5服务器双模式切换实战:UEFI与Legacy BIOS自由转换技巧 在企业级IT基础设施中,服务器启动模式的灵活配置往往是系统部署的关键第一步。联想X3650M5作为主流机架式服务器,其双模式切换功能直接影响着操作系统兼容性、磁盘性能表现乃…...

OpenClaw+GLM-4.7-Flash:科研数据收集与处理自动化方案

OpenClawGLM-4.7-Flash:科研数据收集与处理自动化方案 1. 为什么科研需要自动化助手 去年冬天,我在整理一篇跨学科综述论文时,经历了连续三周每天14小时的手动文献筛选和数据提取。当我在凌晨三点对着第237篇PDF文件发呆时,突然…...

基于遗忘因子递推最小二乘法的电池模型参数在线辨识与优化

1. 电池模型参数辨识为什么需要FFRLS算法 我第一次接触电池参数辨识是在开发一款智能硬件时,当时发现传统最小二乘法有个致命问题——它会把所有历史数据同等对待。这就像用算盘计算平均数时,不管数据是昨天还是去年的,都按相同权重处理。但在…...

从YOLO到DeepLab:盘点CV任务中那些‘神级’特征融合技巧与避坑指南

从YOLO到DeepLab:盘点CV任务中那些‘神级’特征融合技巧与避坑指南 在计算机视觉领域,特征融合技术就像一位隐形的调音师,默默协调着神经网络中不同层次、不同来源的信息流。当你在目标检测任务中遇到小目标识别率低的问题,或在图…...

Python量化交易入门:利用Baostock API高效获取股票历史数据

1. 为什么选择Baostock获取股票数据? 第一次接触量化交易时,最头疼的就是数据来源问题。市面上的数据接口要么收费昂贵,要么数据质量参差不齐。直到发现了Baostock这个宝藏工具,我的量化研究才真正走上正轨。 Baostock最大的优势在…...

手把手调试Linux DRM:如何用ftrace和debugfs深入connector的生命周期

深入Linux DRM调试:用ftrace与debugfs剖析connector全生命周期 当一块崭新的显示板卡接入系统时,DRM驱动中的connector如同一位尽职的接线员,负责建立显示设备与内核之间的通信桥梁。但在实际开发中,我们常会遇到热插拔检测失灵、…...

MAD与标准差:鲁棒统计中的抗噪利器

1. 为什么我们需要抗噪统计量? 在日常数据分析中,我们经常会遇到一些"不听话"的数据点。比如分析员工薪资时突然冒出几个高管的天价年薪,或者测量温度时混入几个明显错误的极端值。这时候如果直接用传统的标准差来计算离散程度&…...

OpenClaw+GLM-4.7-Flash数据助手:Excel报表自动生成与分析

OpenClawGLM-4.7-Flash数据助手:Excel报表自动生成与分析 1. 为什么需要自动化数据助手 作为一位经常与Excel报表打交道的分析师,我每天要花大量时间重复执行数据清洗、格式转换和基础分析。最痛苦的是每月底需要手动合并十几个分公司的销售数据&#…...