主题
Q-learning 与 SARSA
SARSA 和 Q-learning 是两种最经典的 TD 控制算法,都直接学习动作价值函数
从 TD 预测到 TD 控制
TD(0) 解决的是预测问题。要做控制,沿用 GPI 框架:
- 估计动作价值
(没有模型时,只有 才能直接用来选动作); - 用相对于
的 ε-贪心策略选动作(兼顾探索)。
区别在于“TD 目标中下一步的价值用什么动作来估计”。
SARSA
更新规则
SARSA 的名字来自更新所用的五元组
其中
算法流程
text
初始化 Q(s, a) 任意,Q(终止状态, ·) = 0
对每个回合:
初始化 S
用 ε-贪心(Q) 选择 A
重复直到 S 为终止状态:
执行 A,观察 R, S'
用 ε-贪心(Q) 选择 A'
Q(S, A) ← Q(S, A) + α [R + γ Q(S', A') − Q(S, A)]
S ← S';A ← A'1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
在 GLIE 条件下(所有状态—动作对被无限次访问,且 ε 逐渐衰减到 0),配合合适的步长,表格 SARSA 以概率 1 收敛到最优策略。
Q-learning
更新规则
Q-learning(Watkins, 1989)的 TD 目标用下一状态所有动作中的最大值:
这正是对 贝尔曼最优方程
为什么是异策略
Q-learning 的目标值中,下一步动作取的是
与 异策略 MC 不同,单步 Q-learning 不需要重要性采样:更新的是
这一性质使 Q-learning 可以使用任意来源的数据(包括旧经验),这也是 DQN 能够使用经验回放的根本原因。
扩展阅读:Q-learning 收敛性的证明思路
Watkins & Dayan(1992)以及之后基于随机逼近的证明(Jaakkola、Jordan & Singh, 1994;Tsitsiklis, 1994)的核心步骤如下(表格情形,奖励有界,
1. 贝尔曼最优算子是压缩映射。 定义
所以
2. 把 Q-learning 写成“带噪声的不动点迭代”。 记
其中
3. 套用随机逼近定理。 若每个
则上式这类“期望部分是压缩、噪声零均值且方差受控”的迭代以概率 1 收敛到 0,即
要点: 证明不要求行为策略是什么,只要求它无限次访问所有状态—动作对,这正是 Q-learning 异策略性质的理论依据。实践中常用的固定步长(如 0.1)不满足第二个条件,此时 Q 值会在
Expected SARSA
Expected SARSA 把 SARSA 中的采样项换成对下一步动作的期望:
它消除了
悬崖行走(Cliff Walking)对比实验
Gymnasium 的 CliffWalking-v1 是一个 4×12 的网格(Sutton & Barto 例 6.6):起点在左下角,终点在右下角,两者之间的底边是悬崖。每走一步奖励 −1,掉下悬崖奖励 −100 并被送回起点。最短路径是贴着悬崖走(13 步,回报 −13)。
py
"""SARSA 与 Q-learning 在 CliffWalking-v1(悬崖行走)上的对比。"""
import gymnasium as gym
import numpy as np
env = gym.make("CliffWalking-v1")
nS, nA = env.observation_space.n, env.action_space.n
def eps_greedy(Q, s, eps, rng):
if rng.random() < eps:
return int(rng.integers(nA))
q = Q[s]
return int(rng.choice(np.flatnonzero(q == q.max()))) # 随机打破平局
def train(method, episodes=500, alpha=0.1, gamma=1.0, eps=0.1, seed=0):
rng = np.random.default_rng(seed)
Q = np.zeros((nS, nA))
returns = []
for ep in range(episodes):
s, _ = env.reset(seed=seed * 10_000 + ep)
a = eps_greedy(Q, s, eps, rng)
G, done = 0.0, False
while not done:
s2, r, term, trunc, _ = env.step(a)
done = term or trunc
G += r
a2 = eps_greedy(Q, s2, eps, rng)
if method == "sarsa":
target = r + gamma * Q[s2, a2] * (not term)
else: # Q-learning
target = r + gamma * np.max(Q[s2]) * (not term)
Q[s, a] += alpha * (target - Q[s, a])
s, a = s2, a2
returns.append(G)
return Q, np.array(returns)
def greedy_return(Q):
s, _ = env.reset()
G = 0
for _ in range(100):
s, r, term, trunc, _ = env.step(int(np.argmax(Q[s])))
G += r
if term or trunc:
break
return G
for method in ["sarsa", "q_learning"]:
online, greedy = [], []
for seed in range(10):
Q, rets = train(method, seed=seed)
online.append(rets[-100:].mean())
greedy.append(greedy_return(Q))
print(f"{method:10s} 训练中(ε=0.1)最后100回合平均回报: {np.mean(online):7.1f} | "
f"贪心策略回报: {np.mean(greedy):6.1f}")1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
参数:
text
sarsa 训练中(ε=0.1)最后100回合平均回报: -21.5 | 贪心策略回报: -15.4
q_learning 训练中(ε=0.1)最后100回合平均回报: -49.6 | 贪心策略回报: -13.01
2
2
结果解读:
- Q-learning 学到了最优路径:贪心策略回报恰好是 −13(10 个种子全部如此),即贴着悬崖走的最短路线。
- 但训练时 Q-learning 的表现更差(−49.6 对 −21.5):因为执行的是 ε-贪心策略,贴着悬崖走时偶尔的随机动作会让它掉下去。
- SARSA 学到的是更安全的路线:它评估的是“带探索的”策略本身,知道靠近悬崖时随机动作的风险,因此选择离悬崖远一些的路线,训练时回报更高;它的贪心路线平均 −15.4(10 个种子中多数为 −15 或 −17)。
这个例子说明:同策略方法学习的是它实际执行的策略的价值,异策略方法学习的是目标策略的价值。如果训练过程中的表现也很重要(例如真实机器人不能经常“掉下悬崖”),SARSA 这类同策略方法更合适。
我们也观察到:当
代码:表格型 Q-learning
上面的 train 函数就是完整的表格型实现,核心只有几行:
python
if method == "sarsa":
target = r + gamma * Q[s2, a2] * (not term) # 用实际将执行的 A'
else: # Q-learning
target = r + gamma * np.max(Q[s2]) * (not term) # 用 max_a Q(S', a)
Q[s, a] += alpha * (target - Q[s, a])1
2
3
4
5
2
3
4
5
在 FrozenLake 上的 Q-learning 示例见 Gymnasium 上手。
n 步 SARSA
单步 SARSA 的目标只用一步真实奖励,其余全靠
(若回合在
实现时要等到时刻
越大,奖励信息向前传播得越快(一次更新就能把 步之外的奖励传回来),目标的偏差越小;- 但目标中包含更多随机奖励和随机动作,方差越大,通常需要更小的步长。
注意 n 步 SARSA 仍是同策略的:
实验:悬崖行走上的 n = 1、3、5
设置与上面相同(
py
"""n 步 SARSA(on-policy,ε-贪心)在 CliffWalking-v1 上的对比:n = 1, 3, 5。
n 步目标:G_{t:t+n} = r_{t+1} + γ r_{t+2} + ... + γ^{n-1} r_{t+n} + γ^n Q(s_{t+n}, a_{t+n})。"""
import gymnasium as gym
import numpy as np
env = gym.make("CliffWalking-v1")
nS, nA = env.observation_space.n, env.action_space.n
EPISODES, SEEDS, GAMMA, EPS = 500, 10, 1.0, 0.1
def eps_greedy(Q, s, eps, rng):
if rng.random() < eps:
return int(rng.integers(nA))
q = Q[s]
return int(rng.choice(np.flatnonzero(q == q.max())))
def nstep_sarsa(n, alpha, seed):
rng = np.random.default_rng(seed)
Q = np.zeros((nS, nA)); returns = []
for ep in range(EPISODES):
s, _ = env.reset(seed=seed * 10_000 + ep)
S, A, R = [s], [eps_greedy(Q, s, EPS, rng)], [0.0] # R[0] 占位,使 R[t+1] 对应第 t 步的奖励
T, t = np.inf, 0
while True:
if t < T:
s2, r, term, trunc, _ = env.step(A[t])
S.append(s2); R.append(r)
if term or trunc:
T = t + 1
else:
A.append(eps_greedy(Q, s2, EPS, rng))
tau = t - n + 1 # 本步要更新的时刻
if tau >= 0:
G = sum(GAMMA ** (i - tau - 1) * R[i] for i in range(tau + 1, int(min(tau + n, T)) + 1))
if tau + n < T:
G += GAMMA ** n * Q[S[tau + n], A[tau + n]]
Q[S[tau], A[tau]] += alpha * (G - Q[S[tau], A[tau]])
if tau == T - 1:
break
t += 1
returns.append(sum(R))
return np.array(returns)
if __name__ == "__main__":
print(f"CliffWalking,ε={EPS},{EPISODES} 回合 × {SEEDS} 个种子;回报越接近 0 越好(最优路径为 -13)")
for n in (1, 3, 5):
for alpha in (0.1, 0.5):
R = np.stack([nstep_sarsa(n, alpha, sd) for sd in range(SEEDS)])
print(f"n={n} α={alpha:.1f} 前50回合平均回报 {R[:, :50].mean():7.1f} "
f"最后100回合平均回报 {R[:, -100:].mean():6.1f}")1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
实际运行输出:
text
CliffWalking,ε=0.1,500 回合 × 10 个种子;回报越接近 0 越好(最优路径为 -13)
n=1 α=0.1 前50回合平均回报 -209.2 最后100回合平均回报 -22.3
n=1 α=0.5 前50回合平均回报 -105.0 最后100回合平均回报 -26.2
n=3 α=0.1 前50回合平均回报 -139.3 最后100回合平均回报 -23.9
n=3 α=0.5 前50回合平均回报 -107.1 最后100回合平均回报 -37.7
n=5 α=0.1 前50回合平均回报 -144.9 最后100回合平均回报 -26.7
n=5 α=0.5 前50回合平均回报 -136.6 最后100回合平均回报 -46.01
2
3
4
5
6
7
2
3
4
5
6
7
结果解读:
- 小步长时,多步目标让前期学得更快:
时,前 50 回合的平均回报从 的 −209.2 提高到 的 −139.3、 的 −144.9。奖励(尤其是掉下悬崖的 −100)能更快地传回较早的状态。 - 后期
反而略好(−22.3 对 −23.9、−26.7):多步目标包含了更多 ε-贪心的随机动作,学到的价值更“悲观”、方差也更大。 - 大步长时,
越大越不稳定: 时最后 100 回合回报随 从 −26.2 降到 −37.7、−46.0。多步目标的方差与大步长叠加,Q 值来回震荡。 - 结论:
与步长 需要一起调;这也是 DQN 改进中常用 3 步回报(Rainbow)、并配合较小学习率的原因之一。
从表格到函数近似:通往 DQN
表格方法要为每个
但“函数近似 + 自举 + 异策略”三者同时出现会导致训练不稳定,Sutton & Barto 称之为致命三要素(deadly triad)。DQN 用经验回放和目标网络缓解了这个问题。
常见问题与技巧
- Q 值初始化:乐观初始化(例如全部初始化为较大值)可以鼓励早期探索。
- 平局处理:初期 Q 值全为 0,
np.argmax总是返回第一个动作,可能导致系统性偏差。示例代码用随机打破平局。 - ε 的衰减:训练初期大量探索,后期逐渐减小 ε。
- 最大化偏差:Q-learning 的
会系统性地高估 Q 值(因为对噪声取最大值)。Double Q-learning(van Hasselt, 2010)用两套 Q 表分别选择和评估动作来消除这一偏差,它是 Double DQN 的前身。
小结
| SARSA | Q-learning | Expected SARSA | |
|---|---|---|---|
| TD 目标 | |||
| 类型 | 同策略 | 异策略 | 均可 |
| 学到的是 | 当前 ε-贪心策略的价值 | 最优价值 | 目标策略 |
| 悬崖行走表现 | 安全路线,训练回报高 | 最短路线,训练回报低 | 介于两者之间 |
n 步 SARSA 把 SARSA 的单步目标换成
练习题
- 在例题中加入 Expected SARSA,并比较三者在训练中的回报和贪心策略回报。
- 把 ε 逐渐衰减到 0(例如
),SARSA 最终学到的路线会变成什么?为什么? - 实现 Double Q-learning,在悬崖行走上与 Q-learning 比较。
- 把
改回 0.5,打印 SARSA 学到的贪心路线,复现“来回打转”的现象,并解释原因。 - 为什么单步 Q-learning 不需要重要性采样,而 n 步 Q-learning 需要?
- 在
nstep_sarsa_cliff.py中把 扩展到 10 和 20,并把 降到 0.05,观察前期学习速度与最终回报的变化。
参考文献
- Watkins, C. J. C. H. (1989). Learning from Delayed Rewards. PhD thesis, University of Cambridge.
- Watkins, C. J. C. H., & Dayan, P. (1992). Q-learning. Machine Learning, 8, 279–292.
- Rummery, G. A., & Niranjan, M. (1994). On-line Q-learning using connectionist systems. Technical Report CUED/F-INFENG/TR 166, Cambridge University.(SARSA 的提出)
- van Seijen, H., van Hasselt, H., Whiteson, S., & Wiering, M. (2009). A theoretical and empirical analysis of Expected Sarsa. IEEE ADPRL.
- van Hasselt, H. (2010). Double Q-learning. NeurIPS.
- Jaakkola, T., Jordan, M. I., & Singh, S. P. (1994). On the convergence of stochastic iterative dynamic programming algorithms. Neural Computation, 6(6), 1185–1201.
- Tsitsiklis, J. N. (1994). Asynchronous stochastic approximation and Q-learning. Machine Learning, 16, 185–202.
- Sutton, R. S., & Barto, A. G. (2018). Reinforcement Learning: An Introduction (2nd ed.), 第 7 章(n 步自举).
下一步
继续阅读:Gymnasium 上手——熟悉标准环境接口,亲手训练 FrozenLake 智能体。