返回

Q-learning vs SARSA: On-Policy与Off-Policy核心区别详解

Ai

Q-learning 和 SARSA:不只是 Max 的区别

刚接触强化学习,看到 Q-learning 和 SARSA 的更新公式,是不是有点懵?它们长得太像了:

SARSA:
Q(s_t, a_t) = Q(s_t, a_t) + α * (r_t + γ * Q(s_{t+1}, a_{t+1}) - Q(s_t, a_t))

Q-learning:
Q(s_t, a_t) = Q(s_t, a_t) + α * (r_t + γ * max_a Q(s_{t+1}, a) - Q(s_t, a_t))

这俩公式就差一个 ‘max’,但背后代表的意义和学习方式却差了不少。咱们这就来彻底捋捋清楚。

关键分歧:跟着规则走 vs. 看着最优走 (On-policy vs. Off-policy)

要理解 Q-learning 和 SARSA 的不同,得先搞明白「On-policy」(策略学习)和「Off-policy」(离策略学习)是啥意思。这俩词听着挺玄乎,其实说白了就是智能体(Agent)学习更新的方式不同。

  • On-policy (比如 SARSA) :智能体学习的策略,就是它当前正在执行的策略。它像个循规蹈矩的学生,自己怎么做,就学怎么做的结果好坏。它更新自己的 Q 表(动作价值函数)时,会用到自己下一步 实际 要走的动作 a_{t+1}。它基于当前的策略(比如 ε-greedy 策略,有一定概率随机探索,一定概率选择当前认为最好的动作)来产生行为,也基于这个行为产生的数据来学习。
  • Off-policy (比如 Q-learning) :智能体学习的策略,可以和它当前执行的策略不一样。它像个会“旁观”的学霸,就算自己因为探索偶尔走了“臭棋”,它学习的时候还是会看着“最优”的路来更新自己的认知。它更新 Q 表时,并不会看自己下一步 实际 会走哪个动作 a_{t+1},而是直接去看在下一个状态 s_{t+1} 时,所有可能 的动作里,哪个动作能带来最大的 Q 值(也就是 max_a Q(s_{t+1}, a))。

这个核心区别直接导致了它们更新公式的不同。

更新规则拆解:一个‘瞻前顾后’,一个‘大胆假设’

让我们再仔细看看这两个公式,把它们和 On/Off-policy 联系起来。两个公式都在尝试估计 Q(s_t, a_t) 的值,也就是在状态 s_t 执行动作 a_t 能获得的长期回报。这个估计基于当前的估计值 Q(s_t, a_t) 和一个“目标值”(Target Value)。不同之处就在于这个目标值的计算方式。

目标值通常由两部分组成:

  1. 立即奖励 r_t:执行动作 a_t 后立刻得到的奖励。
  2. 未来回报的估计:对后续状态价值的折现估计 γ * (未来价值)

差别就在这个「未来价值」怎么算。

SARSA:步步为营,用实际行动更新

Q(st, at) = Q(st, at) + α * (rt + **γ * Q(st+1, at+1) ** - Q(st, at))

看加粗的部分 γ * Q(s_{t+1}, a_{t+1})。这里的 a_{t+1} 是关键。它是智能体在状态 s_{t+1} 时,根据当前遵循的策略(例如 ε-greedy)实际选择的下一个动作

原理和作用:
SARSA 的更新,是基于一个完整的交互样本 (s_t, a_t, r_t, s_{t+1}, a_{t+1})。这也是它名字(State-Action-Reward-State-Action)的由来。它用智能体实际要走的下一步 a_{t+1} 的 Q 值来估算未来的回报。这意味着 SARSA 的学习过程完全依赖于当前策略产生的行为。如果当前策略是探索性的(比如经常随机选择动作),那么它的学习目标也会包含这些探索性动作带来的(可能非最优的)价值。它评估的是“在当前策略下,执行这个动作有多好”。

简化版 SARSA 流程(单步更新):

  1. 初始化 Q 表(比如全零)。
  2. 对于每一轮(episode):
    a. 获取初始状态 s
    b. 根据当前策略 (比如 ε-greedy),基于 Q(s, :) 选择动作 a
    c. 循环直到 s 是终止状态:
    i. 执行动作 a,得到奖励 r 和下一个状态 s'
    ii. 根据当前策略 (比如 ε-greedy),基于 Q(s', :) 选择下一个动作 a'
    iii. 更新 Q 值:
    Q(s, a) = Q(s, a) + α * (r + γ * Q(s', a') - Q(s, a))
    iv. 将状态和动作更新到下一步:s = s', a = a'

伪代码示例(Python 风格):

def sarsa_update(q_table, state, action, reward, next_state, next_action, alpha, gamma):
  """ 使用 SARSA 更新 Q 表 """
  current_q = q_table.get((state, action), 0.0) # 获取当前 Q 值,没有则为 0
  next_q = q_table.get((next_state, next_action), 0.0) # 获取下一个状态-实际动作的 Q 值
  target_value = reward + gamma * next_q
  new_q = current_q + alpha * (target_value - current_q)
  q_table[(state, action)] = new_q
  return q_table

# 在主循环中:
# ...
# state = current_state
# action = choose_action(q_table, state, epsilon) # 根据 ε-greedy 等策略选择当前动作
# next_state, reward = environment.step(action)
# next_action = choose_action(q_table, next_state, epsilon) # 根据 ε-greedy 等策略选择下一个动作
# q_table = sarsa_update(q_table, state, action, reward, next_state, next_action, alpha, gamma)
# state = next_state
# action = next_action # 注意:SARSA 需要将选择的 next_action 用于下一轮的 action
# ...

安全建议/适用场景:
由于 SARSA 评估的是实际执行策略的表现,它通常更“保守”。如果智能体因为探索策略而选择了一个危险(低回报)的动作 a_{t+1},这个低价值会直接反映在 Q(s_t, a_t) 的更新中。这使得 SARSA 在需要规避风险或评估当前策略实际效果的场景下很有用,比如需要确保机器人不会在学习过程中频繁撞墙。

Q-learning:着眼未来,选最优路径更新

Q(st, at) = Q(st, at) + α * (rt + γ * maxa Q(st+1, a) - Q(st, at))

再看 Q-learning 加粗的部分 γ * max_a Q(s_{t+1}, a)。这里的 max_a Q(s_{t+1}, a) 表示在下一个状态 s_{t+1} 时,选取 所有可能的动作 a 中,能使 Q(s_{t+1}, a) 达到最大的那个 Q 值。

原理和作用:
Q-learning 的更新,只需要 (s_t, a_t, r_t, s_{t+1}) 这部分信息就够了,它不需要知道下一步实际执行了什么动作 a_{t+1}。它在计算目标值时,总是假设在未来会采取最优的行动(即使当前策略因为探索可能不会这么做)。这使得 Q-learning 直接学习最优策略(Greedy Policy),不管实际执行的是什么探索策略(Behavior Policy)。它评估的是“如果未来都按最优方式走,当前这个动作有多好”。

简化版 Q-learning 流程(单步更新):

  1. 初始化 Q 表(比如全零)。
  2. 对于每一轮(episode):
    a. 获取初始状态 s
    b. 循环直到 s 是终止状态:
    i. 根据当前策略 (比如 ε-greedy),基于 Q(s, :) 选择动作 a
    ii. 执行动作 a,得到奖励 r 和下一个状态 s'
    iii. 计算下一个状态的最大 Q 值:
    max_q_prime = max(Q(s', a') for a' in all_possible_actions(s')) (如果 s' 是终止状态,则为 0)。
    iv. 更新 Q 值:
    Q(s, a) = Q(s, a) + α * (r + γ * max_q_prime - Q(s, a))
    v. 更新状态:s = s'

伪代码示例(Python 风格):

import numpy as np

def q_learning_update(q_table, state, action, reward, next_state, all_possible_actions_func, alpha, gamma):
  """ 使用 Q-learning 更新 Q 表 """
  current_q = q_table.get((state, action), 0.0) # 获取当前 Q 值

  # 找到下一个状态所有可能动作的最大 Q 值
  next_possible_actions = all_possible_actions_func(next_state)
  max_next_q = 0.0
  if next_possible_actions: # 如果不是终止状态
      max_next_q = max(q_table.get((next_state, next_a), 0.0) for next_a in next_possible_actions)

  target_value = reward + gamma * max_next_q
  new_q = current_q + alpha * (target_value - current_q)
  q_table[(state, action)] = new_q
  return q_table

# 假设有一个函数能返回某个状态下所有合法的动作
def get_possible_actions(state):
    # ... 实现细节依赖具体环境 ...
    # 示例: return [0, 1, 2, 3] # 上下左右
    pass

# 在主循环中:
# ...
# state = current_state
# action = choose_action(q_table, state, epsilon) # 根据 ε-greedy 等策略选择当前动作
# next_state, reward = environment.step(action)
# q_table = q_learning_update(q_table, state, action, reward, next_state, get_possible_actions, alpha, gamma)
# state = next_state
# ...

安全建议/适用场景:
Q-learning 目标是学习最优策略,即使当前的探索策略比较“浪”,它也能朝着最优的方向去更新 Q 值。这让它在很多任务中学习速度可能更快,因为它不受次优探索行为的直接“拖累”。但是,这种“乐观”的态度也意味着它可能低估执行探索动作的负面影响。如果智能体探索时选择的动作有严重后果,Q-learning 不会直接把这个后果计入 Q 值更新(除非这个差动作恰好是当前认为“最优”的)。它更适合目标是找到绝对最优策略,且允许智能体在环境中进行充分探索的场景。

走迷宫的例子:SARSA 小心翼翼,Q-learning 眼观六路

想象一个机器人在走迷宫,目标是找到出口拿到奖励,同时路上有一些坑(负奖励)。

  • 使用 SARSA 的机器人: 它会按照某种探索策略(比如大部分时间走当前认为最好的路,小部分时间随机走)来移动。当它更新路径价值时,会看自己 实际 准备走的下一步。如果因为随机探索,它下一步准备踏入一个已知的坑附近(假设坑附近的格子 Q 值较低),那当前这一步的价值评估就会受到影响,变得更低。它学到的是“按照我现在的走路方式,走这条路大概会怎么样”。如果它一直很小心翼翼(探索率低),可能会找到一条安全的、但不一定最短的路。
  • 使用 Q-learning 的机器人: 它也可能因为探索策略而实际走向坑的方向。但是,在更新路径价值时,它会抬头看看下一步所有可能的方向,找到那个通往“光明”(最高 Q 值)的方向,并用那个方向的价值来更新当前这一步。它不关心自己下一步实际上会不会踩坑,只关心理论上的最优走法。所以,即使它偶尔因为探索走了弯路或靠近了坑,它内心对“最优路径”的估计仍然会朝着全局最优(最短路径)快速更新。

选哪个?看你的目标和环境

没有绝对的哪个更好,选择取决于具体任务:

  • 什么时候可能用 SARSA?

    • 安全性要求高: 当智能体的动作有潜在危险,不希望它在学习过程中过于冒险时。SARSA 的 On-policy 特性使得 Q 值的评估更贴近实际执行策略的表现,能更快地学到规避危险动作。
    • 需要评估当前策略: 如果你想知道当前采用的这个探索策略本身的效果怎么样,SARSA 更合适,因为它直接基于这个策略产生的数据进行学习。
    • 环境模型未知且在线学习: SARSA 非常适合在线学习场景,步步为营地根据实际反馈调整。
  • 什么时候可能用 Q-learning?

    • 目标是找到最优策略: 如果最终目标就是找到理论上的最优解决方案,不管智能体在学习过程中走了多少弯路,Q-learning 通常更直接。
    • 可以进行充分探索: 当环境允许智能体安全地进行大量探索尝试时。
    • 经验回放(Experience Replay): Q-learning 的 Off-policy 特性让它能很好地结合经验回放(把过去的 (s, a, r, s') 存起来,反复学习)。SARSA 直接用经验回放会有点问题,因为存储的样本可能来自旧的策略,直接用 SARSA 的更新规则(需要 a')就不对了(虽然也有改进版的 SARSA(λ) 可以利用历史数据)。

一些容易搞混的点 (以及记号的小坑)

  1. 行动选择 vs. 价值更新: 关键在于区分“智能体实际如何选择动作去与环境交互”(通常用 ε-greedy 等探索策略)和“用哪个值来更新 Q 表的目标值”。

    • 两个算法在选择 实际执行 的动作 a_t 时,通常都用同一个行为策略(比如 ε-greedy)。
    • 区别在于计算 更新目标 时:SARSA 用的是基于 同一个行为策略s_{t+1} 选择的 实际 下一个动作 a_{t+1} 的 Q 值;Q-learning 则是直接取 s_{t+1} 所有可能动作中 理论上 Q 值最大的那个,与行为策略无关。
  2. r_t vs r_{t+1} 记号问题:
    就像问题中提到的,一些资料(包括 Sutton & Barto 的书或 Wikipedia 的早期版本)在公式记号上可能有点模糊。公式里的 r 指的是执行动作 a_t 后、到达状态 s_{t+1} 之前 收到的那个即时奖励 (immediate reward) 。虽然下标有时会写成 r_{t+1},逻辑上它是紧跟在 a_t 之后的结果,与 s_{t+1} 和 (如果是 SARSA 的话)a_{t+1} 处于同一“时间片”的开端。更清晰的理解是:在时间步 t,处于状态 s_t,执行动作 a_t,获得奖励 r_t,然后转移到状态 s_{t+1}。所以更新 Q(s_t, a_t) 时用的奖励就是 r_t。理解这一点很重要,别被下标弄晕了。

进阶:Q-learning 的‘过于乐观’与 Double Q-learning

Q-learning 因为总是选取 max_a Q(s_{t+1}, a),在 Q 值存在估计误差时(这在学习初期很常见),可能会系统性地高估 Q 值。这就是所谓的「最大化偏差」(Maximization Bias)。想象一下,如果 s_{t+1} 有多个动作,它们的真实 Q 值都差不多,但因为噪声或估计不足,某个动作的 Q 值被偶然高估了,max 操作就会选中它,导致整体的目标值偏高。

如何缓解?Double Q-learning
一个常用的改进方法是 Double Q-learning。它维护两套独立的 Q 值估计表(比如叫 QA 和 QB)。

  • 在选择下一步最优动作时,用其中一个表,比如 QA:a* = argmax_a QA(s_{t+1}, a)
  • 在计算目标值时,用另一个表,比如 QB,来评估这个选定动作的价值:Target = r_t + γ * QB(s_{t+1}, a*)
    然后用这个 Target 去更新 QA。反之亦然(用 QB 选动作,用 QA 评估值来更新 QB)。这样就把“选择最优动作”和“评估该动作价值”这两步分开了,减少了因为同一个表中的偶然高估而被选中的概率,从而缓解最大化偏差。

希望这次能把 Q-learning 和 SARSA 的区别讲明白了。记住核心:一个看实际下一步怎么走,一个看理论上最优的下一步在哪儿。