
1. 从“试错”到“策略”为什么我们需要策略学习聊到强化学习很多刚入门的朋友第一反应可能是“试错”——智能体在环境里瞎逛撞了南墙就回头运气好就找到宝藏。这确实是强化学习最直观的体现比如经典的“悬崖漫步”问题。但如果你真这么去实现很快就会发现效率低得令人发指。智能体像个无头苍蝇大部分时间都在重复犯错或者在一个局部最优解里打转。这背后的核心问题是我们只告诉了智能体“什么状态下的什么动作是好是坏”价值函数却没有直接告诉它“在某个状态下到底应该采取哪个动作”。这就是“策略学习”登场的时刻。如果说基于价值的强化学习Value-Based RL比如Q-Learning是在地图上标出每个位置的“潜在宝藏价值”然后让智能体自己根据价值去选择方向那么策略学习Policy-Based RL就是直接给智能体一张“行动指南”明确告诉它“站在这里你应该往左走。”这个“行动指南”就是我们要学习的策略函数 π(a|s)。它输入一个状态 s直接输出在该状态下采取每个动作 a 的概率分布。智能体根据这个概率分布进行抽样来决定实际执行的动作。为什么这很重要我举两个在实际项目中让我印象深刻的例子。第一个是多智能体协作游戏比如《王者荣耀》里的英雄配合。一个英雄的价值比如输出、生存高度依赖于队友的行动。用价值函数去刻画这种复杂的、动态的相互依赖关系极其困难因为状态空间会随着队友策略的变化而爆炸。但策略学习可以直接学习一个条件概率“看到队友冲上去了我有多大概率应该跟上控制有多大概率应该后撤保命”这种直接输出动作概率的方式在处理高维、连续动作空间比如机器人控制中机械臂每个关节的扭矩是一个连续值时优势更是碾压性的。你无法用一张离散的Q表去枚举所有可能的扭矩组合但一个策略网络可以输出一个高斯分布的均值和方差直接生成连续的动作。所以策略学习的核心价值在于它的直接性和灵活性。它绕过了学习复杂价值函数这一步直接优化我们最终想要的东西——行为策略。这对于动作空间复杂、随机策略最优比如石头剪刀布游戏固定出拳必输必须随机出、或者需要平滑行为变化的场景来说几乎是唯一的选择。接下来我们就深入这个“行动指南”的内部看看它是如何被构建和优化的。2. 策略函数的“心脏”参数化与梯度构建策略 π(a|s) 本身可以是一个简单的查找表对于极小状态空间但更普遍的是用一个带参数 θ 的函数来近似记作 π_θ(a|s)。这个函数就是策略的“心脏”。在深度学习时代它通常是一个神经网络我们称之为策略网络Policy Network。网络的输入是状态 s可能是图像像素、传感器数据、游戏画面等经过编码的向量输出则根据动作空间类型有所不同离散动作空间输出层通常是一个Softmax层每个神经元对应一个动作输出值代表选择该动作的概率。所有动作的概率之和为1。连续动作空间输出层通常输出一个概率分布的参数。最常见的是高斯分布正态分布此时网络输出两部分均值 μ 和方差 σ或对数方差 log_σ 以保证正值。动作 a 则从这个分布中采样得到a ~ N(μ, σ²)。这样做既保证了输出的连续性又通过采样引入了探索性。有了参数化的策略函数我们的目标就变成了找到一组参数 θ使得遵循这个策略所获得的期望总回报 J(θ)最大化。期望总回报 J(θ) 可以理解为从某个起始状态出发智能体按照策略 π_θ 与环境交互所得到的所有奖励考虑折扣因子 γ的平均值。我们的优化问题就是max_θ J(θ)。如何优化我们自然想到梯度上升Gradient Ascent。如果我们能计算出目标函数 J(θ) 关于参数 θ 的梯度 ∇_θ J(θ)那么就可以沿着梯度方向更新参数θ ← θ α * ∇_θ J(θ)其中 α 是学习率。这个梯度 ∇_θ J(θ)就是著名的策略梯度Policy Gradient。策略梯度的推导是策略学习的数学核心。其最终形式一种常见形式是 ∇_θ J(θ) E_τ~π_θ [ (Σ_{t0}^{T} ∇_θ log π_θ(a_t|s_t)) * (Σ_{t‘t}^{T} γ^{t’-t} r_{t‘}) ] 这个公式看起来复杂但直观理解非常关键它通过智能体实际走过的轨迹 τ 来估计梯度。公式中的 Σ_{t0}^{T} ∇_θ log π_θ(a_t|s_t) 是整条轨迹上每个时刻所选动作的对数概率关于参数 θ 的梯度之和。而 Σ_{t‘t}^{T} γ^{t’-t} r_{t‘} 是从时刻 t 到结束的回报Return它衡量了从时刻 t 开始往后所有决策的“好坏”。注意这里回报的计算方式有多种变体比如减去一个基线Baseline来减少方差这就是后续Actor-Critic框架的思想雏形。最朴素的方法是使用从t时刻开始的累计奖励。这个梯度的意义是什么它实际上在做一件非常符合直觉的事放大那些带来高回报的动作的概率抑制那些带来低回报的动作的概率。如果某条轨迹的最终回报很高那么这条轨迹上每一步动作的“功劳”都会被强化梯度为正更新会使这些动作的概率增加反之如果回报很低这些动作的概率就会被削弱。然而这个最朴素的策略梯度方法常被称为REINFORCE算法有一个致命缺点高方差High Variance。因为我们的梯度估计依赖于通过随机采样得到的单条或多条轨迹的回报而交互过程本身具有随机性环境动态、策略采样导致估计的梯度噪声很大训练极其不稳定收敛速度慢。这就引出了我们对策略梯度的一系列重要改进。3. 驯服“高方差”猛兽基线、Actor-Critic与信赖域直接使用蒙特卡洛回报估计的策略梯度方差太大这在实践中几乎是不可用的。我们必须想办法“驯服”这头方差猛兽。下面介绍三种核心且层层递进的技术。3.1 引入基线一个简单的“及格线”第一个直观的改进是引入基线Baseline。我们不再使用原始回报 G_t 作为权重而是使用 (G_t - b(s_t))。这里的 b(s_t) 是一个只与状态 s_t 有关与动作 a_t 无关的函数通常我们用一个价值函数 V(s_t) 的估计来充当基线。 梯度公式变为∇_θ J(θ) ≈ E [ Σ_t ∇_θ log π_θ(a_t|s_t) * (G_t - V(s_t)) ]。为什么减去基线能减少方差想象一下给学生打分。如果直接用绝对分数回报评价一个在简单题上得90分的学生可能比在难题上得70分的学生获得更高评价。但如果我们引入一个“平均分”或“期望分数”基线 V(s)用“优势”A_t G_t - V(s_t)来评价就能更好地衡量动作的“相对好坏”。一个动作带来的回报只要超过在该状态下的平均期望它就是“好”动作就应该被鼓励。减去基线不改变梯度的期望无偏估计但能显著降低方差因为优势值 A_t 的波动范围通常比原始回报 G_t 小得多。3.2 Actor-Critic框架价值网络的协同进化引入基线 V(s) 后一个自然的问题是如何得到这个 V(s)最经典和强大的答案就是Actor-Critic演员-评论家框架。这个框架将策略学习和价值学习融为一体Actor演员 即我们的策略网络 π_θ(a|s)负责生成动作。它根据Critic的评价来更新自己目标是最大化期望回报。Critic评论家 即我们的价值网络 V_φ(s)或 Q_φ(s, a)负责评价状态或状态-动作对的好坏。它通过时序差分TD等方法来学习目标是准确预测状态价值或优势。在Actor-Critic中策略梯度中的优势函数 A(s, a) 就由Critic来提供。例如使用TD误差 δ_t r_t γV(s_{t1}) - V(s_t) 作为优势函数 A_t 的估计。Actor的更新公式变为θ ← θ α * ∇_θ log π_θ(a_t|s_t) * δ_t。这个框架的美妙之处在于在线学习和单步更新。REINFORCE需要等到一个回合episode结束才能更新而Actor-Critic在每一步t都可以立即更新学习效率高得多。Critic不断学习更准确的价值估计为Actor提供更精准的“指导”Actor则根据指导调整策略产生新的数据供Critic学习形成一个良性循环。实操心得在实现Actor-Critic时一个常见的坑是Actor和Critic的学习率α_actor 和 α_critic设置不当。通常Critic需要学得更快、更准一些这样它给Actor的“评分”才可靠。我的一般经验是将 α_critic 设置为 α_actor 的2到5倍并密切监控价值损失Value Loss的收敛情况。如果价值损失震荡很大说明Critic还没学好此时Actor的更新方向可能是错的。3.3 信赖域与PPO让更新步伐更稳健即使有了Actor-Critic策略梯度方法还有一个深层次问题策略的更新步长学习率很难选择。步长太大一次更新可能让策略“跳”到一个性能差很多的新区域导致训练崩溃性能骤降步长太小学习速度又太慢。如何保证每次更新后新策略 π_θ’ 不会离旧策略 π_θ 太远从而稳定提升性能这就是信赖域策略优化Trust Region Policy Optimization, TRPO和其更流行的改进版近端策略优化Proximal Policy Optimization, PPO要解决的核心问题。TRPO通过复杂的二阶优化自然梯度来强制约束新旧策略的KL散度一种分布距离度量确保更新在“信赖域”内。虽然理论漂亮但实现复杂计算量大。PPO则提出了两种更工程化、更高效的解决方案来近似这个约束PPO-Clip裁剪 这是最流行的方法。它直接在新旧策略的概率比值上动手术。定义概率比 r_t(θ) π_θ(a_t|s_t) / π_θ_old(a_t|s_t)。如果这个比值偏离1太远说明策略变化太大。PPO-Clip的目标函数是L^{CLIP}(θ) E_t [ min( r_t(θ) * A_t, clip(r_t(θ), 1-ε, 1ε) * A_t ) ]。其中 ε 是一个小超参如0.1或0.2。这个 min 操作保证了当优势 A_t 为正时我们鼓励该动作但概率比最多增长到 (1ε)当 A_t 为负时我们抑制该动作但概率比最多减少到 (1-ε)。这样就巧妙地限制了每次更新的幅度。PPO-Penalty惩罚 在目标函数中直接添加一个KL散度的惩罚项L^{KLPEN}(θ) E_t [ r_t(θ) * A_t - β * KL[π_θ_old || π_θ] ]并通过自适应调整系数 β 来控制约束强度。在我经历的大多数项目中PPO-Clip因其实现简单、效果稳定成为了策略学习算法的首选。你几乎可以在任何主流的RL库如Stable-Baselines3, Ray RLlib中找到它的高效实现。它很好地平衡了采样效率、实现复杂度和训练稳定性。4. 策略学习实战以连续控制任务为例的完整流程理论说了这么多我们来看一个具体的实战例子让一个机械臂或一个模拟的机器人学习将末端移动到目标位置。这是一个典型的连续状态、连续动作的控制问题。4.1 环境与问题定义我们使用MuJoCo或PyBullet物理仿真环境中的HalfCheetah猎豹或Ant蚂蚁机器人模型。当然更简单的可以从OpenAI Gym的Pendulum-v1倒立摆开始。状态空间State Space 连续。包括关节角度、角速度、末端执行器坐标等。例如在Pendulum-v1中状态是[cos(θ), sin(θ), θ_dot]。动作空间Action Space 连续。每个关节的扭矩范围通常在[-1, 1]或[-2, 2]之间。奖励函数Reward 设计是关键。对于移动到目标点奖励通常包含到达目标的稀疏奖励如100、每一步距离目标减少的稠密奖励、以及惩罚过大动作幅度的项防止抖动。例如reward -distance_to_target - 0.1 * (action**2).sum()。4.2 策略与价值网络设计我们采用Actor-Critic框架用神经网络同时近似策略函数和价值函数。import torch import torch.nn as nn import torch.nn.functional as F class ActorNetwork(nn.Module): 策略网络Actor输出连续动作的高斯分布参数。 def __init__(self, state_dim, action_dim, hidden_dim256): super(ActorNetwork, self).__init__() self.fc1 nn.Linear(state_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.mu_head nn.Linear(hidden_dim, action_dim) # 均值 self.log_std_head nn.Linear(hidden_dim, action_dim) # 对数标准差 def forward(self, state): x F.relu(self.fc1(state)) x F.relu(self.fc2(x)) mu torch.tanh(self.mu_head(x)) * 2 # 假设动作范围[-2,2] log_std self.log_std_head(x) log_std torch.clamp(log_std, -20, 2) # 限制标准差范围防止数值不稳定 std torch.exp(log_std) return mu, std def sample_action(self, state): 根据状态采样一个动作并返回其对数概率。 mu, std self.forward(state) dist torch.distributions.Normal(mu, std) action dist.rsample() # 使用rsample以支持重参数化技巧和反向传播 log_prob dist.log_prob(action).sum(dim-1) # 由于tanh变换需要对概率进行修正如果mu是tanh输出此处简化处理 action torch.tanh(action) # 确保动作在有效范围内 return action.detach(), log_prob class CriticNetwork(nn.Module): 价值网络Critic评估状态价值V(s)。 def __init__(self, state_dim, hidden_dim256): super(CriticNetwork, self).__init__() self.fc1 nn.Linear(state_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.value_head nn.Linear(hidden_dim, 1) def forward(self, state): x F.relu(self.fc1(state)) x F.relu(self.fc2(x)) value self.value_head(x) return value4.3 训练循环与PPO-Clip实现核心训练采用经典的“收集数据-更新网络”循环。我们使用广义优势估计GAE来更平滑地估计优势函数 A_t。def compute_gae(rewards, values, next_value, dones, gamma0.99, gae_lambda0.95): 计算广义优势估计GAE。 advantages torch.zeros_like(rewards) gae 0 for t in reversed(range(len(rewards))): if t len(rewards) - 1: next_non_terminal 1.0 - dones[t] next_values next_value else: next_non_terminal 1.0 - dones[t] next_values values[t 1] delta rewards[t] gamma * next_values * next_non_terminal - values[t] gae delta gamma * gae_lambda * next_non_terminal * gae advantages[t] gae returns advantages values return advantages, returns # PPO-Clip 核心更新步骤伪代码逻辑 def ppo_update(actor, critic, optimizer_actor, optimizer_critic, states, actions, old_log_probs, returns, advantages, clip_epsilon0.2, value_coef0.5, entropy_coef0.01): # 将数据转换为Tensor states torch.FloatTensor(states) actions torch.FloatTensor(actions) old_log_probs torch.FloatTensor(old_log_probs).detach() returns torch.FloatTensor(returns).detach() advantages torch.FloatTensor(advantages).detach() # 归一化优势这是一个稳定训练的关键技巧 advantages (advantages - advantages.mean()) / (advantages.std() 1e-8) # 通常进行多轮如10轮小批量更新 for _ in range(10): # 重新计算当前策略下的动作概率和状态价值 mu, std actor(states) dist torch.distributions.Normal(mu, std) new_log_probs dist.log_prob(actions).sum(dim-1) entropy dist.entropy().sum(dim-1).mean() # 计算熵用于鼓励探索 values critic(states).squeeze() # 计算概率比 ratio torch.exp(new_log_probs - old_log_probs) # PPO-Clip 目标函数 surr1 ratio * advantages surr2 torch.clamp(ratio, 1.0 - clip_epsilon, 1.0 clip_epsilon) * advantages actor_loss -torch.min(surr1, surr2).mean() - entropy_coef * entropy # Critic 损失价值函数拟合 critic_loss F.mse_loss(values, returns) # 更新网络 optimizer_actor.zero_grad() actor_loss.backward() torch.nn.utils.clip_grad_norm_(actor.parameters(), max_norm0.5) # 梯度裁剪 optimizer_actor.step() optimizer_critic.zero_grad() critic_loss.backward() torch.nn.utils.clip_grad_norm_(critic.parameters(), max_norm0.5) optimizer_critic.step()4.4 关键超参数与调试经验策略学习尤其是PPO对超参数比较敏感。以下是一些经验值和个人调试心得折扣因子 γ 通常在0.99左右。它决定了未来奖励的重要性。对于回合制任务可以设为0.99或0.995对于没有明确终止的持续任务可能需要更接近1如0.999。GAE参数 λ 通常在0.95到0.98之间。λ1等价于蒙特卡洛回报λ0等价于单步TD误差。0.95是一个很好的起点它平衡了偏差和方差。裁剪系数 ε PPO-Clip的核心。通常设在0.1到0.3之间。0.2是最常用的默认值。调参心得如果训练初期性能提升很快但很快崩溃可能是ε太小更新太激进可以尝试增大到0.3。如果学习一直很慢可能是ε太大限制了更新可以尝试减小到0.1。学习率 Actor和Critic的学习率需要分别设置。通常Critic的学习率是Actor的2-5倍。例如Actor lr3e-4, Critic lr1e-3。使用学习率衰减策略如线性衰减通常有益。熵系数 用于鼓励探索。初始可以设一个较小的正值如0.01。随着训练进行策略趋于确定可以逐渐减小或衰减熵系数。一个常见技巧观察训练过程中策略的熵值如果熵下降过快可能导致早熟收敛到次优解此时可以适当增大熵系数或减缓其衰减速度。批量大小与更新轮数 每次从经验回放缓冲区或直接收集的轨迹中采样一个批次如64, 128, 256的数据然后对这个批次的数据进行多轮如5-10轮的PPO更新。批次越大梯度估计越准但内存消耗越大。更新轮数K太多可能导致过拟合当前批次的数据。踩坑记录在训练一个机械臂抓取任务时我曾遇到智能体很快学会快速抖动机械臂来获得微小但频繁的奖励因为奖励函数设计有缺陷包含了连续的正奖励而完全忽略了真正的抓取目标。这就是典型的奖励函数设计不当导致的“奖励黑客”Reward Hacking问题。解决方案是重新设计奖励函数大幅提高成功抓取的稀疏奖励并减少或移除可能导致短视行为的稠密奖励项。奖励函数的设计是强化学习成功的一半它需要精确地传达你的最终目标而不是中间过程的某些容易钻空子的指标。策略学习从最朴素的策略梯度出发通过引入基线、构建Actor-Critic框架、再到使用PPO等信赖域方法约束更新步伐一步步解决了高方差、采样效率低、训练不稳定等核心难题。它让我们能够直接优化复杂、高维甚至连续的动作策略成为解决机器人控制、游戏AI、自动驾驶等复杂决策任务的利器。理解其背后的“为什么”——为什么需要梯度、为什么方差高、为什么要裁剪——远比记住公式更重要。在实际操作中耐心地调试超参数、精心设计奖励函数、并利用TensorBoard等工具可视化训练过程如回报曲线、价值损失、策略熵是最终成功的关键。