ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Q Learning强化学习实战:Python完整代码包与参数调优指南

Q Learning强化学习实战:Python完整代码包与参数调优指南 简介这是一份Python实现Q Learning强化学习的完整代码包面向机器学习入门者、算法学习者以及需要在自定义环境中落地Q Learning的开发者。资源以Q表更新、epsilon-greedy策略和gym环境交互为主线既有可直接运行的qlearningAgents.py也有介绍算法原理的README.md与报告.pdf。代码展示了经典Q值更新规则如何利用奖励与折扣因子迭代优化动作选择PDF报告则对算法步骤、参数影响和收敛过程有简要梳理能帮助读者理解状态、动作、奖励、探索与利用平衡等核心概念。包内共5个文件涵盖Python脚本、Markdown说明、PDF文档及License授权说明压缩包仅852KB轻量且便于本地实验。已有1108人学习下载适合配合CartPole等经典环境动手调试也可作为课程作业、算法对比实验或项目原型的参考实现。1. 为什么说 Q Learning 是强化学习里最适合手撕的算法一张表就能跑通如果你第一次接触强化学习直接去看 PPO、DDPG 这些深度强化学习算法大概率会被策略梯度、Actor-Critic 这些概念劝退。但 Q Learning 不一样它不依赖神经网络不依赖 GPU核心就是一个二维表格——状态是行动作是列表格里存的是“在这个状态下做这个动作能拿多少回报”。这个特性让 Q Learning 成为入门强化学习最合适的切口也是理解后续 DQN、Double DQN 这些深度强化学习算法的基础。这份 python 实现的 Q Learning 完整代码包包含环境定义、Agent 逻辑、训练循环、可视化四部分解压后不用改任何配置就能跑通一个完整的强化学习闭环。适合三类人刚入门强化学习、被数学公式卡住的新手需要快速验证某个环境里 Q Learning 效果、做 baseline 对比的研究者以及想在格子世界、悬崖行走这类经典环境里观察算法收敛过程的学生。它就是一个能跑的、结构清晰的参考实现拿去做实验、改参数、二次开发都顺手。2. Q Learning 的核心公式从贝尔曼方程到 Python 代码的映射2.1 先理解 Q 值更新在做什么Q Learning 要解决的核心问题是在一个未知的环境里Agent 怎么通过试错学会“在哪个状态做什么动作最划算”。它的更新逻辑用一句话概括就是——用“当前奖励 未来的最优估计”去修正“当前的估计”。这就是时序差分的思想比蒙特卡洛方法好在不用等一个完整回合结束就能更新。更新的数学形式是Q(s, a) ← Q(s, a) α [ r γ max(Q(s, a)) - Q(s, a) ]拆开来看就是新 Q 值 旧 Q 值 学习率 × (当前奖励 r 折扣因子 γ × 下一个状态的最大 Q 值 - 旧 Q 值)。这个公式最反直觉的地方在于它更新的是上一个状态的 Q 值而不是当前状态的。你在状态 s 做了动作 a到达 s 拿到奖励 r然后回头去更新 Q(s, a)——它是拿未来的信息去修正过去这就是强化学习里“滞后更新”的精髓。公式里的 γ折扣因子决定了 Agent 有多“短视”。γ 越接近 1Agent 越看重长期回报γ 接近 0Agent 只关心眼前奖励。α学习率决定新信息覆盖旧信息的速度。在 Python 代码里这个公式通常就写成一行赋值语句但要把维度对齐这件事做好——Q 表索引是 (state, action)如果状态是坐标就得先做坐标到整数索引的映射。# 核心更新逻辑单步 Q Learning 更新 def update_q_table(self, state, action, reward, next_state, done): state: 当前状态索引 action: 当前动作索引 reward: 执行动作后拿到的奖励 next_state: 转移后的状态索引 done: 是否到达终止状态 if done: # 终止状态下没有未来回报Q 值直接向当前奖励收敛 target reward else: # 非终止状态当前奖励 折扣后的未来最优 Q 值 target reward self.gamma * np.max(self.q_table[next_state, :]) # 时序差分误差目标值与当前估计的差 td_error target - self.q_table[state, action] # 沿着误差方向修正学习率控制修正步长 self.q_table[state, action] self.alpha * td_error这里最关键的设计选择是if done分支。很多第一次写 Q Learning 的人会把 target 统一写成reward gamma * max(Q[s])在终止状态时就会把终止状态的 Q 值当成可用的未来值导致训练不收敛或者收敛到错误策略。终止状态意味着轨迹结束没有后续状态未来回报是零必须单独处理。np.max(self.q_table[next_state, :])这一行是 Q Learning 和 SARSA 的本质区别Q Learning 在更新时使用“下一步能拿到的最优动作”的 Q 值不管 Agent 实际会按什么策略走。这个 off-policy 特性让 Q Learning 可以在探索的同时学习最优策略也是它比 SARSA 激进、在某些环境下更容易收敛的原因。2.2 探索与利用epsilon-greedy 策略是怎么逐步让位的Q 表只有在被充分访问过的状态-动作对上才有意义。如果 Agent 一直按当前 Q 表取最大值动作它可能永远走不出初期的坏策略——这就是“利用”过度但如果一直随机探索又学不到稳定策略。epsilon-greedy 是工业界最常用的折中以 epsilon 的概率随机动作以 1-epsilon 的概率选 Q 值最大的动作。# epsilon-greedy 策略探索率随训练进度衰减 def choose_action(self, state): state: 当前状态索引 返回: 动作索引 self.epsilon max(self.epsilon_min, self.epsilon * self.epsilon_decay) if np.random.random() self.epsilon: # 探索均匀随机选动作 action np.random.randint(self.n_actions) else: # 利用选 Q 值最大的动作 action np.argmax(self.q_table[state, :]) return actionepsilon 的初始值一般设 1.0让 Agent 前期充分探索epsilon_decay 每次选择动作后乘一次让它逐渐退化为贪心策略。这里有个容易翻车的细节epsilon_decay 的值要结合回合数来设定。假设一共训练 500 回合每回合大约 200 步那就是 100000 次衰减如果 decay 设 0.999100000 步后 epsilon 是 1.0 × 0.999^100000这个值已经趋近于 0 了如果设 0.99衰减过快Agent 还没探索够就锁死在当前策略上。我的经验是先算总步数再反推 decay。想让 epsilon 在训练中期降到 0.1 附近就用公式 decay 0.1^(1/总步数) 去估算虽然不算精确但比试错快得多。epsilon_min 这个下限也很重要一般 0.01保证在训练后期仍然有少量探索避免因为环境随机性导致策略僵化。2.3 Q 表初始化方式为什么会影响收敛速度Q 表初始化为全零是最保守的选择。全零初始化的意思是Agent 一开始对所有动作的价值估计都是 0它在中性状态下选动作纯粹靠 epsilon-greedy 的随机性。这个方案安全在大多数环境里都能收敛。还有一种常见做法是初始化为一个小的正值比如 0.1。这样做是为了鼓励探索因为 Q 值都是正的Agent 会倾向于尝试那些还没怎么访问过的状态-动作对。这看起来像是一个“自信的探索”策略但在奖励为负的环境里会有问题——如果环境给负奖励正的初始 Q 值会被不断拉低反而拖慢收敛。# Q 表初始化两种常见方案对比 # 方案 A全零初始化中性且无偏 self.q_table np.zeros((self.n_states, self.n_actions)) # 方案 B小正数初始化鼓励探索但负奖励环境下慎用 self.q_table np.full((self.n_states, self.n_actions), 0.1)如果环境的状态空间连续比如小车爬坡的连续速度Q 表没法直接建需要先做离散化——把连续的速度、位置切成若干个区间每个区间对应一行。离散化的粒度直接决定 Q 表大小和收敛速度切太细Q 表爆炸训练时间成倍增加切太粗策略粗糙学出来的动作不精细。格子世界这类环境天然就是离散状态所以最适合作为 Q Learning 的第一次实践。3. 完整代码包解析环境、Agent、训练循环怎么协同工作3.1 代码包的文件结构与运行入口这份完整代码包解压后是标准的单文件结构核心逻辑集中在 q_learning.py 里另外附带一个简单的环境和可视化输出。整个包的核心模块拆开来看就是环境定义、Agent 逻辑、训练循环三层。拿到代码包后第一步要看清楚文件里环境是怎么定义的——这是后续改参数的基础。# 解压并运行代码包 unzip python实现QLearning强化学习_完整代码.zip cd QLearning_Project # 安装依赖只需要 numpy 和 matplotlib pip install numpy matplotlib # 直接运行训练脚本 python q_learning.py常见做法是用 numpy 做 Q 表存储和更新计算matplotlib 画收敛曲线。这套依赖在任何 Python 3.6 环境里都能跑不涉及深度学习框架。如果电脑上还没有 Python 环境去 python 官网下载安装包安装时勾选 Add Python to PATH然后重新打开终端就能用。3.2 环境定义网格世界的状态转移和奖励逻辑代码包里的环境通常是一个 n×n 的网格世界Agent 从起点出发要走到终点中途有障碍物和陷阱。状态就是坐标动作是上下左右四个方向奖励规则是到达终点给正奖励踩到陷阱给负奖励正常移动给一个小幅度的负奖励或者 0。网格世界是一个理想的教学环境状态空间小、转移确定、奖励稀疏程度可调。# 环境定义6x6 网格世界的核心逻辑 class GridWorld: def __init__(self, size6): self.size size self.n_actions 4 # 0: 上, 1: 下, 2: 左, 3: 右 self.n_states size * size # 状态数 格子数 # 奖励配置 self.goal_state 35 # 终点右下角状态索引 5*65 self.trap_states [17, 23] # 陷阱固定两个格子 self.reward_goal 10.0 # 到达终点奖励 self.reward_trap -5.0 # 踩陷阱惩罚 self.reward_step -0.1 # 每步惩罚鼓励走最短路径 def step(self, state, action): 状态转移逻辑 返回: next_state, reward, done row state // self.size col state % self.size # 根据动作更新坐标 if action 0: row max(0, row - 1) elif action 1: row min(self.size - 1, row 1) elif action 2: col max(0, col - 1) else: col min(self.size - 1, col 1) next_state row * self.size col # 判定奖励和终止条件 if next_state self.goal_state: return next_state, self.reward_goal, True elif next_state in self.trap_states: return next_state, self.reward_trap, True else: return next_state, self.reward_step, False注意max(0, row - 1)和min(self.size - 1, row 1)这两组边界裁剪——Agent 撞墙不会出界而是停在原地。边界处理方式对训练结果有明显影响如果允许出界并返回一个大的负奖励Agent 会额外学会“别撞墙”这个隐式规则如果只是停在原地Agent 学的是“撞墙是无效动作”。两种方案都能收敛后者收敛更快因为状态空间更小。陷阱状态的索引是硬编码的改环境尺寸时需要同步改这两个值这是读者第一次改代码最容易踩的坑。3.3 训练循环回合制学习与收敛判据训练循环是一个 while/for 嵌套结构外层遍历回合episode内层遍历单步step。每个回合开始把 Agent 放回起点回合内反复执行“选动作-执行-观察奖励和下一状态-更新 Q 表-判断终止”。单回合结束条件有两种到达目标/陷阱doneTrue或者超过最大步数强制截断。强制截断很重要否则在稀疏奖励环境里 Agent 可能在一个回合里无限游荡。# 训练循环主体 def train(self, episodes500, max_steps_per_episode100): episodes: 训练回合数 max_steps_per_episode: 单回合最大步数防止无限循环 rewards_history [] for episode in range(episodes): state 0 # 起点状态 total_reward 0 for step in range(max_steps_per_episode): action self.agent.choose_action(state) next_state, reward, done self.env.step(state, action) self.agent.update_q_table(state, action, reward, next_state, done) state next_state total_reward reward if done: break rewards_history.append(total_reward) # 每 50 回合打印一次进度观察收敛趋势 if (episode 1) % 50 0: avg_reward np.mean(rewards_history[-50:]) print(fEpisode {episode 1}, 平均奖励: {avg_reward:.3f}) return rewards_historystate next_state这一行容易被忽略但它是环境推进的关键少了它整个训练就退化成单步循环。回合结束后的break要放在if done里面否则即使到了终止状态循环还会继续跑Q 表会收到错误的 transition 数据。每 50 回合打印一次平均奖励是一种低成本监控收敛的手段。平均奖励曲线从负值逐渐爬升、最后稳定在一个区间说明 Q 表正在收敛如果曲线反复震荡或者长期趴在同一个值附近不抬头说明参数有问题。收敛判据方面除了看平均奖励还可以直接检查 Q 表的变化量。每隔若干回合计算一次np.sum(np.abs(new_q - old_q))这个值趋近于 0 就说明 Q 表不再大幅变动了。两种判据配合使用更可靠——平均奖励可能存在“环境随机性好、碰巧拿高分”的假象Q 表变化量更能反映真实学习状态。3.4 策略提取训练完之后怎么把 Q 表变成可执行的路径训练完成后Q 表只是存了一堆数值真正要展示“Agent 学会了”需要把 Q 表转成一条具体的行走路径。做法很简单从起点开始每一步都选当前状态下 Q 值最大的动作然后沿着状态转移链走直到终点。# 从训练好的 Q 表提取最优路径 def extract_policy(self, start_state0): 根据 Q 表生成一条从起点到终点的路径 返回: 途经的状态列表 path [start_state] state start_state visited set() # 防止死循环如果路径重复访问状态说明策略有问题 while len(path) self.env.n_states: if state in visited: print(警告策略进入循环Q 表可能未收敛) break visited.add(state) action np.argmax(self.agent.q_table[state, :]) next_state, _, done self.env.step(state, action) path.append(next_state) state next_state if done: break return pathvisited集合是一个防御性设计如果 Q 表还没收敛贪心策略可能在一个局部环里打转比如反复在格子 8 → 9 → 8 之间横跳。没有 visited 保护这个 while 循环会无限跑下去。打印策略路径时建议把格子编号映射成坐标 (row, col) 再输出直接看图比看编号直观得多。一个正常的收敛路径大概长这样(0,0) → (0,1) → (1,1) → (2,1) → ... → (5,5)路径是连通的不会出现跳到非相邻格子的情况。4. 参数调优指南alpha、gamma、epsilon_decay 怎么配合着调4.1 参数作用与合理区间先搞清楚每个参数在控制什么Q Learning 的可调参数不多但每个参数都直接影响收敛行为。学习率 alpha 决定单步更新的步长折扣因子 gamma 决定长期回报的权重epsilon_decay 控制探索到利用的切换速度。这三个参数不是独立作用的它们之间有耦合关系。参数作用合理区间调大后果调小后果alpha新信息覆盖旧信息的速度0.1 ~ 0.5振荡发散Q 值跳变收敛慢容易卡在局部最优gamma未来回报的折扣程度0.8 ~ 0.99Agent 过度关注远期路径绕远Agent 短视只盯眼前奖励epsilon_decay探索率衰减速度0.95 ~ 0.9995探索期变短可能错过全局最优探索期过长训练尾期仍在随机游荡epsilon_min探索率下限0.01 ~ 0.1后期探索过多策略不稳定后期完全贪心无法应对环境随机性这几个参数之间最常见的一组矛盾是alpha 调大了振荡alpha 调小了收敛慢。实际上 alpha 也可以做衰减在训练后期用更小的学习率去做精细调整。这是一种常见做法alpha max(0.01, alpha * 0.999)效果是前期大步学、后期小步修。这和深度学习里学习率衰减的思路一致在 Q Learning 里同样有效。gamma 的设置有讲究。如果环境的单步奖励是负数比如每走一步 -0.1gamma 太大会让 Agent 更在意“少走冤枉路”因为长期累积的步数惩罚变大如果奖励全是正数gamma 太大反而可能导致 Q 值发散——Q 值会被不断累加成很大的数需要配合合适的 alpha 来压制。我的习惯是负奖励为主的密集奖励环境gamma 取 0.9 左右稀疏奖励环境只有终点有大奖励gamma 取 0.95 以上让 Agent 学会“为远期的甜头绕路”。4.2 奖励设计的四个常见层次与参数联动奖励设计比调 alpha、gamma 更影响训练效果。同样的环境奖励函数不同Q Learning 学出来的策略可能完全不同。常见的奖励设计有四种第一种全零 终点正奖励。最简单的稀疏奖励环境Agent 前期纯靠随机探索找终点训练速度极慢但一旦找到一次就能学会。适合演示“探索的艰难”。第二种每步 -0.1 终点 10。在稀疏奖励基础上加了步数惩罚Agent 学会走最短路径。这个方案训练速度比第一种快因为即使没找到终点Agent 也能感受到“走一步扣一分”的压力倾向于少走回头路。第三种每步 -0.1 终点 10 陷阱 -5。增加了障碍物避让信号。这种方案最容易观察到“学习曲线下降-爬升”的过程初期踩陷阱吃大惩罚奖励曲线很低后期学会避开陷阱后曲线跳升。第四种中间状态设人工势场奖励比如离终点越近奖励越大。这种方案收敛最快但工程上设计势场函数需要额外工作而且容易学出投机取巧的策略——Agent 可能在势场高峰处反复徘徊不走。# 奖励函数对比步数惩罚 陷阱惩罚的联动效果 # 这是方案三的奖励配置在代码里的样子 REWARDS { step: -0.1, # 每步微惩罚 goal: 10.0, # 终点大奖励 trap: -5.0 # 陷阱中惩罚 } # 一个值得注意的细节如果陷阱和终点相邻 # Agent 可能在陷阱附近学到“绕远路也不靠近陷阱”的保守策略。 # 这是因为步数惩罚(-0.1)远小于陷阱惩罚(-5)绕远的代价远低于踩陷阱的代价。这个“绕远避险”现象是奖励设计里的常见坑当惩罚绝对值远大于步数代价时Agent 会发展出过度保守的策略。机械臂在真实环境中很容易因为过度保守而不敢靠近障碍物附近的目标点。遇到这种情况可以适当降低惩罚绝对值或者给目标点方向加引导信号。4.3 训练过程监控从奖励曲线里读出问题记录每个回合的总奖励绘制成曲线是判断训练状态最朴素也最直观的方法。一份完整的训练监控应该同时记录两个量单回合总奖励以及 Q 表变化量的 L2 范数。# 训练完成后绘制奖励曲线 import matplotlib.pyplot as plt def plot_training_curve(rewards_history): rewards_history: 每回合的总奖励列表 plt.figure(figsize(10, 5)) # 原始曲线 plt.plot(rewards_history, alpha0.3, labelRaw) # 滑动平均曲线平滑噪声 window 20 smoothed np.convolve(rewards_history, np.ones(window) / window, modevalid) plt.plot(smoothed, linewidth2, labelfMA{window}) plt.xlabel(Episode) plt.ylabel(Total Reward) plt.legend() plt.grid(True) plt.title(Q Learning Training Curve) plt.savefig(training_curve.png, dpi150)看奖励曲线时重点关注三个阶段。前期约前 10% 回合曲线应该在低位波动偶尔出现小峰值说明 Agent 在探索。如果前期曲线一动不动说明 epsilon 初始值太低或者奖励完全没有信号中期约 20% 到 70%曲线开始爬升并且爬升速率逐渐变慢说明 Q 表在修正策略。如果中期出现突然跳崖式下跌通常是 Agent 发现了之前没遇到过的陷阱或者随机性导致一次极端差的路径后期最后 30%曲线应该在一个窄区间平稳波动波动幅度取决于 epsilon_min 和环境随机性。如果后期还在大幅震荡检查 alpha 是否过大、epsilon_min 是否过高。5. 避坑指南Q Learning 代码包常见的五个翻车现场5.1 现象训练很久奖励曲线不抬头Q 表全零原因分析epsilon 衰减太快Agent 在还没遍历足够多状态-动作对时就已经完全贪心化锁死在一个局部策略里出不来。典型场景是总步数很多但 epsilon_decay 设成 0.99按 100000 步算后期 epsilon 已经小于 0.001几乎纯贪心。另一个原因是奖励信号过于稀疏Agent 前期完全拿不到非零奖励Q 表没有任何正向信号可以传播。解决调大 epsilon_decay 到 0.995 以上并确认 epsilon_min 不要低于 0.01同时检查奖励配置如果在 100 步内 Agent 平均只能拿到一次非零奖励先降低环境的稀疏程度——比如加一步微惩罚 (-0.1)让 Q 表在探索阶段就有梯度可以学习不要指望奖励全靠终点那一下。5.2 现象训练曲线冲高后崩盘Q 值越来越大最终发散原因分析alpha 太大单步更新的步长超过了 Q 值稳定所需的范围gamma 太大且奖励全为正Q 值被累加成超大数导致数值溢出或策略震荡。Q Learning 本身没有理论保证一定能收敛在 alpha、gamma 同时取大值时很容易出现振荡甚至发散。解决把 alpha 从 0.5 降到 0.1 或 0.2gamma 从 0.99 降到 0.9 试跑一下。如果环境奖励有正有负正负抵消通常不会发散如果全是正奖励要特别注意。出现 Q 值数量级超过 1e3 时基本可以判定参数组合有问题。5.3 现象路径提取时卡在循环里走不出来原因分析Q 表未完全收敛时贪心策略可能在一个局部环上打转——状态 A 选动作去 B状态 B 选动作回 A。这种情况在 alpha 过小、训练回合不足时最容易出现因为 Q 值还没有区分出不同动作的优劣差异。解决训练回合数设大一些比如从 200 加到 500同时把 epsilon_min 适当调高到 0.05让 Agent 在后期仍有小概率跳出局部环。路径提取函数里加上 visited 集合做防护代码见第 3 章避免程序死循环。判断 Q 表是否够收敛用前面提到的 Q 表变化量指标。5.4 现象代码包在自己电脑上跑出和 README 里不一样的结果原因分析最常见的是环境尺寸改动后终点状态索引、陷阱状态索引没有同步更新。比如 6×6 网格的终点是第 36 个状态索引 35改成 8×8 后终点索引应该是 63如果忘了改环境逻辑直接错乱。另一个常见原因是 Python 版本差异Q Learning 代码本身不依赖高版本特性但 print 语法或 f-string 在不同版本下可能有兼容差别。解决改环境尺寸时先算清终点索引——goal_state size * size - 1不要手写硬编码数字陷阱索引也要按新坐标重新计算。运行前用python --version确认版本代码包需要 Python 3.6如果用的是老版本 Python 2先升级。5.5 现象可视化输出没有更新训练好 Q 表后路径展示不出来原因分析通常是因为路径提取函数和环境 step 函数的交互方式不匹配。Q Learning 训练时更新的是 (state, action) 的 Q 值而路径提取时如果直接用 Q 表 argmax 选动作需要保证环境 step 函数的转移逻辑和训练时用的完全一致——比如动作顺序是上、下、左、右还是上、左、下、右不一致就全乱。解决把动作编号的对应关系单独写成一个字典或注释放在环境定义里例如action_map {0: up, 1: down, 2: left, 3: right}。路径可视化模块直接引用环境里已有的 step 函数不要重新写一套转移逻辑。如果输出路径里的状态编号跳变不连续优先检查这个映射。6. 从 Q 表到深度 Q Learning训练完这份代码后下一步能做什么6.1 拿 Q 表做策略对比实验这份代码包训练完成后Q 表本身就是一个可迁移的产物。你可以把它保存成 numpy 文件然后换一个测试环境去对比不同训练策略的效果。保存和加载 Q 表就是两行# 保存训练好的 Q 表 np.save(q_table.npy, agent.q_table) # 加载 Q 表做测试 loaded_q_table np.load(q_table.npy)一个值得做的实验是用同一份环境、同一份代码分别用 alpha0.1 和 alpha0.5 训练两份 Q 表然后放在一个带随机风场的环境里对比两个策略的鲁棒性。你会发现 alpha0.5 的那份 Q 表虽然在训练阶段收敛快但在扰动环境下表现反而更差——因为它的 Q 值被大学习率带了过多的随机噪声。这类对比实验很适合写进技术笔记里比单纯贴一张奖励曲线更有说服力。6.2 把 Q 表换成神经网络一个最小的 DQN 改造路径Q Learning 学完下一步自然是往深度强化学习走。把 Q 表替换成神经网络的核心变化是Q 值不再用np.max(self.q_table[next_state, :])查表而是用神经网络前向传播输出。这是 DQN 的本质改变也是从表格型算法到函数逼近型算法的分水岭。# DQN 的 Q 值计算方式查表变成网络前向传播 def compute_target(self, reward, next_state, done): if done: return reward else: # double dqn 的改进用目标网络计算 max next_q self.target_net(next_state).max(dim1)[0] return reward self.gamma * next_q如果直接跑这份 PyTorch 版的 DQN 代码你会发现一个现象在网格世界里 DQN 反而比 Q Learning 更难收敛甚至可能完全不收敛。这不是代码写错了而是函数逼近在小的离散状态空间里不如表格直接。Q 表的优势在于每个状态都有一个独立的参数去拟合而神经网络是共享参数的会在相邻状态之间做平滑插值这种平滑在状态空间足够大时是优势在小状态空间里反而是劣势。理解了这个原因你就知道 Q Learning 的真正价值边界——状态空间小到可以建表时表格型方法永远是最优选择状态空间大到建不下表时才需要往 DQN 这类深度强化学习算法迁移。6.3 三个最常见的二次开发方向方向一是换环境。把代码包里的 GridWorld 替换成更复杂的环境比如奥赛罗、井字棋这类双人博弈环境。这时 Q Learning 需要做一个小改动——匹配对手动作。因为环境不再是一步一转移的马尔可夫过程状态里要加入对手的最后一步动作。方向二是加 eligibility trace。在 Q Learning 更新逻辑里引入衰减迹eligibility trace让一次奖励可以沿着轨迹回溯传播到之前的多步状态。这会加速稀疏奖励环境的训练代码量大约增加十行但收敛速度提升明显。方向三是改成 Double Q Learning。维护两张 Q 表交替更新每步用其中一张表选动作、另一张表评估价值。这个做法的价值是削弱 Q 值的过估计问题——表格型 Q Learning 在奖励波动大时容易高估某些状态-动作对的价值Double Q Learning 通过解耦选择和评估来缓解这个问题。这些改动都不大但每改一个都值得重新画一遍奖励曲线、重新跑一遍路径提取观察差距。从那以后我每次拿到一个新环境都会先跑一遍 Q Learning baseline不管后面要用多复杂的深度强化学习算法这个 baseline 都不会丢——它不只是用来对比更是帮你理解当前环境的奖励结构、转移确定性、探索难度。希望这套 Q Learning 代码包和这份实战笔记能帮你在强化学习的路上打通第一个闭环。本文还有配套的精品资源点击获取
返回列表