SARSA算法解析:从在策略学习到安全探索的强化学习实践
1. 从“试错”到“学习”理解SARSA算法的核心定位在强化学习的广阔天地里我们常常听到Q-Learning的大名它以其简洁高效的“离线学习”特性成为许多入门教程的首选。然而当你真正开始动手实现一个智能体尤其是在那些需要与环境持续交互、每一步决策都关乎即时安全与长期收益的场景下你可能会发现Q-Learning那种“大胆假设小心求证”的更新方式有时会带来意想不到的麻烦。比如你训练一个机器人走路Q-Learning可能会让它为了追求远处的一个高额奖励而先尝试一个可能导致摔倒的危险动作。这时另一种同样经典但理念迥异的算法——SARSA就走进了我们的视野。SARSA代表的是State-Action-Reward-State-Action这五个字母连起来恰恰勾勒出了它最核心的运作逻辑基于实际采取的行动序列进行学习。它不是旁观者而是参与者每一次价值更新都严格依赖于智能体“说到做到”的完整行动轨迹。这种“在策略”的学习方式使得SARSA天生带有一种稳健和保守的气质特别适合需要平衡探索与利用、对每一步风险都需谨慎评估的任务。简单来说如果你把智能体比作一个正在学习走迷宫的新手Q-Learning像是一个在旁边观察并记录“从A点到B点最优走法”的教练它可能告诉新手“理论上从这块砖跳到那块砖最快”但不管新手实际敢不敢跳。而SARSA则更像是手把手带着新手走的导师它说“好你现在站在这里你决定往左走一步拿到了这点奖励并且发现左边是堵墙那你下次在这个位置时往左走的期望价值就应该这样调整。” SARSA学习的是当前策略下的行动价值因此它的策略改进与策略评估是紧密耦合的。这使得它在诸如机器人控制、游戏AI特别是需要避免即时死亡的游戏、金融交易避免单次巨大亏损等领域展现出独特的优势。理解SARSA不仅是掌握一个算法更是理解强化学习中“安全探索”与“策略跟随”这一重要哲学。2. SARSA算法流程拆解五步循环的奥秘SARSA的名称已经揭示了其算法更新的核心要素。让我们把这个名字拆解开一步步看它是如何在一个时间步中运作的。假设在时间步t我们正处于状态S_t。第一步感知与决策 (State Action)智能体首先观察当前环境状态S_t。然后它需要依据某个策略例如ε-greedy策略来选择一个动作A_t。这个策略是基于当前的动作价值函数Q(S_t, a)做出的它可能以大概率选择当前认为最好的动作利用也可能以小概率随机探索其他动作。第二步执行与反馈 (Action Reward)智能体将选定的动作A_t发送给环境。环境接收到动作后发生状态转移进入下一个状态S_{t1}并给予智能体一个即时奖励R_{t1}。这个奖励是环境对动作A_t在状态S_t下效果的即时评价。第三步再次决策 (State Action)关键的一步来了。在新的状态S_{t1}下智能体再次根据当前策略注意是同一个策略例如同一个ε-greedy策略选择下一个动作A_{t1}。这里的选择是实实在在的是智能体在S_{t1}状态下“打算”执行的动作。第四步价值更新 (Update)现在我们集齐了SARSA的五个字母(S_t, A_t, R_{t1}, S_{t1}, A_{t1})。SARSA算法使用这组序列来更新旧状态-动作对(S_t, A_t)的价值Q(S_t, A_t)。其更新公式如下Q(S_t, A_t) ← Q(S_t, A_t) α * [ R_{t1} γ * Q(S_{t1}, A_{t1}) - Q(S_t, A_t) ]让我们拆解这个公式α学习率。控制本次更新对旧值的调整幅度。通常设置在0到1之间。[ ... ]时序差分误差。这是驱动学习的核心信号。R_{t1}执行A_t后获得的即时奖励。γ * Q(S_{t1}, A_{t1})对未来回报的估计。这里用的是在S_{t1}状态下根据当前策略实际会选择的动作A_{t1}的价值Q(S_{t1}, A_{t1})。γ是折扣因子衡量未来奖励的当前价值。Q(S_t, A_t)旧的价值估计。整个公式的含义是将Q(S_t, A_t)向“即时奖励 折扣后的下一状态实际动作价值”这个新目标进行小幅调整。第五步状态与动作迭代将当前状态更新为S_{t1}当前动作更新为A_{t1}然后循环回到第一步开始下一个时间步。这个五步循环构成了SARSA的核心。与Q-Learning更新公式Q(S_t,A_t) ← Q(S_t,A_t) α * [ R_{t1} γ * max_a Q(S_{t1}, a) - Q(S_t, A_t) ]对比最核心的区别就在于对未来价值的估计上SARSA用的是Q(S_{t1}, A_{t1})而Q-Learning用的是max_a Q(S_{t1}, a)。这个细微的差别导致了二者完全不同的行为特性。注意SARSA的更新必须发生在动作A_{t1}被选定之后。在代码实现中这通常意味着我们需要为“下一个状态”S_{t1}预选好动作A_{t1}然后才能用这五个值进行更新最后再将A_{t1}真正执行出去。这是一个常见的实现细节。3. 为什么是“在策略”学习SARSA与Q-Learning的哲学分野“在策略”这个术语是理解SARSA精髓的关键。所谓“在策略”指的是算法用于评估和改进的是它当前正在执行的那个策略。在SARSA中无论是选择动作A_t和A_{t1}还是更新价值Q(S_t, A_t)时对未来价值的估计Q(S_{t1}, A_{t1})都依赖于同一个行为策略如ε-greedy。让我们通过一个经典的“悬崖漫步”网格世界例子来直观感受两者的区别。想象一个4x12的网格最下面一行是“悬崖”掉下去会获得-100的奖励并回到起点起点在左下角终点在右下角每走一步获得-1的奖励鼓励尽快到达。Q-Learning离线学习它会学习到一条贴着悬崖边的最短路径因为从价值函数Q(s,a)的角度看那条路径的累积负奖励最少步数最少。即使它使用ε-greedy策略进行探索偶尔会掉下悬崖但它的更新目标max_a Q(s, a)始终盯着“最优动作”的价值。因此它最终学到的策略是“走悬崖边”。SARSA在策略学习由于它的更新依赖于实际采取的动作A_{t1}而它的行为策略ε-greedy有一定概率随机探索。当智能体处在悬崖边缘时如果它根据策略选择了一个“非最优”的探索动作比如向着悬崖方向那么这次更新就会降低“在悬崖边向里走”这个动作的价值因为下一个动作A_{t1}可能是危险的。SARSA会“感知”到探索带来的风险。最终SARSA学到的策略通常会选择一条远离悬崖的安全路径尽管这条路可能更长总奖励更负一些。因为它评估的是在带有探索的策略下每个动作的期望收益。这个例子清晰地展示了两种算法的哲学Q-Learning是乐观的。它假设智能体在未来总会做出最优选择因此它学习的是“最优策略”的价值函数。它分离了行为策略用于探索和目标策略用于更新被称为“离线策略”算法。SARSA是保守的。它评估的是在当前的探索策略下实际能获得的期望回报。它将探索的代价如掉下悬崖的风险考虑在内因此学习到的策略通常更稳健、更安全。它的行为策略和目标策略是同一个因此是“在策略”算法。下表总结了核心区别特性SARSAQ-Learning全称State-Action-Reward-State-Action-策略类型在策略评估和改进的是行为策略本身。离线策略评估和改进的是最优策略与行为策略可以不同。更新目标R γ * Q(S, A)其中A由行为策略产生。R γ * max_a Q(S, a)即最优动作的价值。学习目标学习当前可能带探索的策略下的动作价值。学习最优策略的动作价值。行为特点更保守会将探索的风险考虑在内策略通常更安全。更激进直接追求理论最优可能忽略探索过程中的危险。适用场景需要安全探索、在线学习、动作有连续风险的任务如机器人控制、交易。仿真环境、探索成本低、或可以大量离线训练的任务如游戏AI、广告推荐。在实际项目中选择SARSA还是Q-Learning往往取决于你对智能体“冒险精神”的要求以及环境的特性。4. 手把手实现一个完整的SARSA算法代码剖析理论说得再多不如一行代码。让我们用Python来实现一个解决“悬崖漫步”问题的SARSA智能体。我们将使用gym库中的CliffWalking-v0环境。这个环境完美复现了我们之前讨论的场景。首先是环境准备和参数定义import gym import numpy as np import matplotlib.pyplot as plt # 创建环境 env gym.make(CliffWalking-v0) n_states env.observation_space.n # 48个状态 (4*12) n_actions env.action_space.n # 4个动作 (0:上, 1:右, 2:下, 3:左) # 算法超参数 alpha 0.1 # 学习率 gamma 0.99 # 折扣因子 epsilon 0.1 # ε-greedy策略中的探索概率 episodes 1000 # 训练回合数 # 初始化Q表形状为 (状态数, 动作数) Q np.zeros((n_states, n_actions))接下来我们实现核心的ε-greedy策略函数。这个函数将用于SARSA中选择动作A_t和A_{t1}。def epsilon_greedy_policy(state, Q, epsilon): 根据ε-greedy策略选择动作。 参数: state: 当前状态索引 Q: 当前的Q表 epsilon: 探索概率 返回: action: 选择的动作索引 # 以 epsilon 的概率进行随机探索 if np.random.random() epsilon: return np.random.randint(n_actions) # 以 1-epsilon 的概率利用选择Q值最大的动作 else: # 处理多个动作Q值相同的情况随机选择一个 max_q np.max(Q[state]) actions_with_max_q np.where(Q[state] max_q)[0] return np.random.choice(actions_with_max_q)现在来到最关键的SARSA训练主循环。请注意我们是如何严格遵循S, A, R, S, A的顺序来获取数据并进行更新的。# 记录每个回合的总奖励用于评估学习效果 rewards_per_episode [] for episode in range(episodes): state, _ env.reset() # 重置环境获取初始状态S total_reward 0 # 根据当前策略选择初始动作A action epsilon_greedy_policy(state, Q, epsilon) done False while not done: # 执行动作A得到奖励R和下一个状态S next_state, reward, terminated, truncated, _ env.step(action) total_reward reward done terminated or truncated # **关键步骤**在状态S下根据当前策略选择下一个动作A next_action epsilon_greedy_policy(next_state, Q, epsilon) # 如果回合未结束使用S, A, R, S, A进行SARSA更新 if not done: # SARSA更新公式 td_target reward gamma * Q[next_state, next_action] td_error td_target - Q[state, action] Q[state, action] alpha * td_error else: # 如果回合结束到达终点或掉下悬崖则下一个状态S没有动作价值 # 此时更新目标仅为即时奖励 td_target reward td_error td_target - Q[state, action] Q[state, action] alpha * td_error # 回合结束跳出循环无需再选择next_action # 将状态和动作向前推进S - S, A - A state next_state action next_action rewards_per_episode.append(total_reward) # 可选每100回合输出一次进度 if (episode 1) % 100 0: avg_reward np.mean(rewards_per_episode[-100:]) print(fEpisode {episode1}, Avg Reward (last 100): {avg_reward:.2f}) env.close()提示在代码实现中处理回合结束的情况需要小心。当done为True时next_state是一个终止状态理论上其所有动作的Q值应为0。因此更新目标简化为reward。这是时序差分学习中的标准处理方式。训练完成后我们可以可视化学习曲线并测试学到的策略# 绘制学习曲线 plt.plot(rewards_per_episode) plt.xlabel(Episode) plt.ylabel(Total Reward) plt.title(SARSA Learning Curve on CliffWalking) plt.grid(True) plt.show() # 测试学到的策略使用贪婪策略即epsilon0 test_episodes 10 for test_ep in range(test_episodes): state, _ env.reset() done False steps 0 path [state] while not done and steps 100: # 防止无限循环 action epsilon_greedy_policy(state, Q, epsilon0.0) # 纯利用 state, reward, terminated, truncated, _ env.step(action) done terminated or truncated path.append(state) steps 1 print(fTest Episode {test_ep1}: Steps {steps}, Path (last 5 states): {path[-5:]})运行这段代码你会观察到SARSA智能体在经过几百个回合的训练后总奖励从-100左右逐渐提升并稳定在一个较高的值比如 -20 到 -30。通过测试路径你会发现它大概率学会了一条远离悬崖顶部的安全路径从上方绕行至终点。这正是其“在策略”保守特性的直观体现。5. 参数调优与实战心得让SARSA稳定工作的关键实现一个能跑的SARSA很简单但让它高效、稳定地学习却需要一些技巧。以下是我在实际项目中积累的一些关键参数调优经验和注意事项。1. 学习率α小步快跑还是大步迈进学习率α控制了新信息覆盖旧记忆的速度。值太大如0.5以上更新剧烈Q值波动大可能导致学习不稳定甚至无法收敛。智能体像得了健忘症新经验完全覆盖旧经验。值太小如0.01以下学习速度极慢需要大量经验才能收敛训练时间成本高。实践经验通常从0.1开始尝试是一个不错的起点。对于状态-动作空间较小、问题相对简单的情况0.1到0.3可能工作良好。对于更复杂的问题可能需要更小的学习率如0.01或0.05来保证稳定。一个高级技巧是使用衰减的学习率在训练初期用较大的α快速学习后期用较小的α进行微调例如alpha initial_alpha / (1 decay_rate * episode)。2. 探索率ε探索与利用的永恒博弈ε-greedy策略中的ε直接决定了智能体的探索欲望。值太大如0.5智能体大部分时间在随机游走无法有效利用已学到的知识学习效率低下最终策略性能差。值太小如0.01智能体过于贪婪可能过早陷入局部最优比如在CliffWalking中如果一开始随机探索没找到安全路它可能就永远只尝试那条会掉下悬崖的“最优”短路径。实践经验0.1是一个广泛使用的默认值。和α一样使用衰减的ε是常见且有效的方法。例如初始ε设为0.5或1.0鼓励充分探索然后随着回合数增加逐渐衰减到一个很小的值如0.01或0.001让智能体后期专注于利用学到的优秀策略。这模拟了人类“先广博后精深”的学习过程。3. 折扣因子γ目光有多长远折扣因子γ决定了智能体对未来奖励的重视程度。γ接近1如0.99, 0.999智能体是“长远规划者”非常重视未来的回报。这在回合制任务中很常见比如下棋或走迷宫最终结果远重于中间步骤。γ接近0如0.1, 0.5智能体是“短视者”只关心眼前的即时奖励。适用于一些即时反馈非常重要的连续任务。实践经验对于大多数有明确终止状态的 episodic 任务γ通常设置为0.9到0.99。你需要思考对于你的任务多步之后的奖励对当前决策还有多大意义在CliffWalking中我们设0.99因为即使多走几步最终安全到达的收益也远高于掉下悬崖的惩罚。4. Q表初始化从零开始还是乐观一点我们通常将Q表初始化为零。但这在ε-greedy策略下会带来一个问题所有动作初始价值相同第一次选择完全是随机的。如果某个动作一开始不小心获得了负奖励它的Q值会变负在贪婪选择时就不再被选中即使它可能是最优动作。这可能导致探索不足。乐观初始化技巧将Q表初始化为一个较小的正值比如5。这样所有动作在初始时都有被尝试的机会因为它们的Q值都高于初始化的“基线”。当某个动作获得负奖励后其Q值下降但可能仍是正数仍有被探索的机会。这可以鼓励智能体在早期更均匀地探索所有动作。在CliffWalking中乐观初始化有时能帮助智能体更快地发现安全路径。5. 收敛判断与可视化不要只盯着最终的总奖励曲线。以下监控手段能帮你更好地理解学习过程查看Q表变化定期如每100回合检查特定关键状态如起点、悬崖边缘状态的Q值看它们是否趋于稳定。观察策略变化定期用贪婪策略ε0测试一次记录其路径和步数。你会看到策略从随机游走到逐渐稳定的过程。分析探索率如果你使用了衰减的ε记录其变化确保在训练后期探索率足够低让策略得以收敛。一个常见的坑是学习曲线剧烈震荡长期无法稳定。这通常是学习率α过大或探索率ε衰减过快导致的。尝试降低α或让ε衰减得更平缓一些。记住强化学习训练本身具有一定随机性多次运行取平均性能是更科学的评估方式。6. 超越表格当SARSA遇见函数逼近与深度学习我们上面实现的SARSA被称为“表格型SARSA”因为它用一个二维数组表格Q[state, action]来存储每一个状态-动作对的价值。这种方法简单直观但有一个致命的缺陷它无法处理大规模甚至连续的状态/动作空间。想象一下如果状态是游戏的一帧图像像素矩阵或者是机器人传感器的连续读数表格法将因为维度灾难而完全失效。这时我们需要引入函数逼近。其核心思想是用一个参数化的函数Q(s, a; θ)来近似真实的动作价值函数其中θ是函数的参数比如神经网络的权重。SARSA算法可以与各种函数逼近器结合例如线性函数、决策树或者如今最流行的深度神经网络这就形成了Deep SARSA的雏形尽管Deep Q-Network更常见但思想相通。使用函数逼近后SARSA的更新目标不再只是更新表格中的一个单元格而是变成了一个监督学习问题调整参数θ使得Q(s, a; θ)的预测值更接近“目标值”y R γ * Q(s, a; θ)。注意这里的a仍然是根据当前策略由当前的Q(s, a; θ)网络定义在状态s下选择的动作。其梯度下降更新规则为θ ← θ α * [y - Q(s, a; θ)] * ∇_θ Q(s, a; θ)其中∇_θ Q(s, a; θ)是Q值对参数θ的梯度。实现Deep SARSA的注意事项经验回放与DQN一样为了打破数据间的相关性可以使用经验回放池。但需注意SARSA存入回放池的经验是(s, a, r, s, a)五元组。目标网络为了稳定训练通常也会引入一个目标网络来计算Q(s, a; θ-)其参数θ-定期从主网络θ同步而非实时更新。策略的连续性由于SARSA是“在策略”的用于生成a的策略网络与正在更新的网络是同一个或其延迟版本。这意味着策略在训练过程中是连续变化的。这与DQN的“离线策略”特性不同后者行为策略可以相对独立。虽然Deep SARSA在实际应用中没有DQN那么普遍因为其保守性和对策略连续性的要求但在一些对安全性、策略平滑性要求高的连续控制任务中如机器人 locomotion基于Actor-Critic框架的在策略算法如PPO、TRPO更为流行它们可以看作是SARSA思想在连续空间和高维问题上的高级演进。从表格型SARSA到函数逼近算法的核心哲学——依据实际采取的行动序列来评估当前策略——始终未变。理解了这个根本你就能更好地把握强化学习算法家族中“在策略”这一重要分支的设计脉络并在面对具体问题时做出更明智的算法选择。