You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Q-Learning Table收敛至-inf:自定义实现Mountain-Car任务遇困境

问题分析与修复方案

1. 初始代码Q值发散至-inf的根本原因

你的第一个训练循环的更新公式完全偏离了Q-Learning的核心时序差分(TD)逻辑:

# 错误的更新公式
Q[entry[0],entry[1]] = Q[entry[0],entry[1]] + lr * (entry[2] + latest_best * gamma)

标准Q-Learning的更新是基于TD误差,即目标值与当前Q值的差值:
Q(s,a) = Q(s,a) + α * (r + γ * max_a' Q(s',a') - Q(s,a))
这个公式的作用是让Q值逐步向「当前奖励+未来最大预期奖励」的目标靠拢。

而你的代码缺少了减去当前Q(s,a)的关键步骤,相当于每次都在给Q(s,a)叠加一个新的数值。MountainCar环境的每步奖励固定为-1(直到到达终点),所以每次叠加的都是负数的累积值,随着训练轮数增加,Q值会不断向负方向累积,最终数值溢出变为-inf。

为什么初始效果好?因为初始时Q表全为0,第一次更新时Q(s,a) = 0 + lr*(r + γ*maxQ),这和标准更新的结果一致(因为0 - 0 = 0),所以前几轮看起来有效,但当Q值开始变化后,后续更新就会不断错误叠加,最终数值崩溃。

2. 修改后代码效果差的核心问题

你修改后的公式形式上接近标准Q-Learning,但存在两个关键问题:

(1)仿真阶段缺少探索机制

你的仿真代码是纯贪婪选择:

max_indexes = np.argwhere(Q[state,] == np.amax(Q[state,])).flatten()
action = np.random.choice(max_indexes)

MountainCar是典型的需要探索的环境——初始时Q值全为0,纯贪婪选择会让智能体一直重复同一个动作,无法探索到能爬上山顶的动作组合。标准Q-Learning必须搭配ε-贪婪策略:以ε的概率随机选择动作(探索),以1-ε的概率选择当前最优动作(利用),并且ε可以随着训练逐步降低(减少探索,增加利用)。

(2)状态转移记录缺失(可选但重要)

你的history只记录了(state,action,reward),没有记录执行动作后进入的下一个状态。虽然反向遍历可以通过latest_best传递后续状态的Q值,但直接记录下一个状态会让训练逻辑更清晰,避免依赖反向遍历的顺序出错。

(3)参数设置可能不合理

  • 学习率(lr):如果lr设置太大,Q值会波动剧烈,难以收敛。MountainCar适合较小的学习率(比如0.1~0.01)。
  • 折扣因子(gamma):需要设置接近1的值(比如0.99),因为这是一个长期奖励问题,智能体需要考虑后续多步的奖励。

3. 修复后的完整代码示例

仿真阶段(加入ε-贪婪策略)

epsilon = 0.1  # 初始探索概率,可随训练逐步降低
state = env.reset()
history = []

for t in range(SAMPLE_PER_EPISODE):
    # ε-贪婪选择动作
    if np.random.uniform(0, 1) < epsilon:
        action = env.action_space.sample()  # 随机探索
    else:
        max_indexes = np.argwhere(Q[state,] == np.amax(Q[state,])).flatten()
        action = np.random.choice(max_indexes)  # 利用最优动作
    
    observation, reward, done, info = env.step(action)
    history.append((state, action, reward, observation))  # 记录下一个状态
    state = observation
    total_reward += reward
    
    if done:
        break  # 到达终点提前结束

# 可选:逐步降低探索概率
epsilon = max(0.01, epsilon * 0.99)

训练阶段(标准Q-Learning更新)

lr = 0.1
gamma = 0.99
latest_best = 0  # 终端状态的下一个状态无奖励,maxQ为0

# 反向遍历history,基于下一个状态的Q值更新当前状态
for entry in reversed(history):
    s, a, r, s_next = entry
    # 标准TD更新:目标值 = 当前奖励 + 未来最大预期奖励
    target = r + gamma * latest_best
    Q[s, a] += lr * (target - Q[s, a])
    # 更新latest_best为当前状态的maxQ,供前序状态使用
    latest_best = np.max(Q[s, :])

内容的提问来源于stack exchange,提问作者greece57

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:51:41