Cheney 2025-08-26 18:50 北京
四格漫画直观解读PPO的稳定优势
©作者 | Cheney
训练老是翻车,调参像在碰运气?多数时候问题出在策略更新的“步子”迈得太大。Policy Gradient 简单直接却极易不稳,TRPO 在稳定性上更有保障,却因复杂实现难以普及。
PPO 则在两者之间找到黄金分割点:它用“一刀剪”锁住策略更新的幅度,多轮复用同一批数据,提高效率,还不需要繁琐的二阶优化。这样的设计,让 PPO 成为学术界和工业界应用最广的强化学习算法之一。
接下来,我们先从 RL 的基本直觉出发,看看它为什么能迅速走红。
想象一下,你是一个刚刚出生的婴儿,对这个世界一无所知。你饿了会哭,舒服了会笑。你的父母(环境)会根据你的行为(哭/笑)给出反馈(喂奶/爱的抱抱)。通过无数次的尝试和反馈,你慢慢学会了如何与环境互动,如何让自己更舒服,如何获得更多“奖励”(比如糖果,或者父母的赞许)。
这就是强化学习(Reinforcement Learning,RL)的核心思想:一个智能体(Agent)在环境(Environment)中通过行动(Action)来最大化累积奖励(Reward)。
智能体根据当前状态(State)选择行动,环境则根据行动给出奖励和新的状态。这个过程周而复始,智能体通过不断试错来学习一个最优的策略(Policy),这个策略就是告诉智能体在任何给定状态下应该采取什么行动的“行为准则”。
1.2 策略梯度:直觉与挑战
在强化学习中,我们通常有两种学习策略的方式:
1. 基于价值(Value-based):先学习每个状态或状态-动作对的“价值”(即未来能获得的累积奖励),然后根据价值选择最优动作。比如 Q-learning、DQN。
2. 基于策略(Policy-based):直接学习一个策略函数,这个函数可以直接输出在某个状态下采取每个动作的概率。比如 REINFORCE、A2C。
策略梯度(Policy Gradient)方法就是基于策略学习的典型代表。它的核心思想非常直观:如果某个动作导致了高奖励,我们就增加这个动作在未来被选中的概率;如果导致了低奖励(或者惩罚),就降低它的概率。
数学上,我们希望最大化期望的累积奖励:
其中, 是一个轨迹(状态-动作-奖励序列), 是由参数 定义的策略, 是轨迹的总奖励。
策略梯度定理告诉我们,可以通过梯度上升来更新策略参数 :
这里, 是优势函数(Advantage Function),它衡量了在状态 下采取动作 比平均水平好多少。如果 ,我们就增加 的值(即增加 的概率);如果 ,就减少它。
听起来很美妙,对吧?直接优化策略,简单粗暴!但现实是骨感的,策略梯度方法有一个致命的问题:不稳定性。
1.3 为什么需要PPO?——“步子迈大了,容易扯着蛋”的问题
想象一下,你正在学习骑自行车。你尝试着蹬了一脚,结果车子歪了,你摔了个狗吃屎。按照策略梯度的逻辑,你可能会想:“哦,这一脚蹬得太猛了,下次轻点!”然后你下次真的轻点了,结果车子纹丝不动。你又想:“嗯,看来还是得猛点!”
问题在于,策略梯度方法在更新策略时,步长(学习率)的选择非常关键。如果步长太小,学习会非常慢,效率低下,就好像一个蜗牛在爬珠穆朗玛峰,猴年马月才能到顶啊。
但如果步长太大,策略可能会发生剧烈变化,导致性能急剧下降,甚至“灾难性遗忘”(Catastrophic Forgetting)。这就像你骑自行车,一不小心蹬得太猛,直接冲下悬崖,再也回不来了。
核心问题:如何才能在保证学习效率的同时,又避免策略更新过大导致“翻车”?
这就是 PPO 要解决的核心问题。它就像一个经验丰富的教练,在教你骑自行车时,既鼓励你大胆尝试,又在你快要摔倒时及时扶你一把,确保你始终在安全的范围内学习。
PPO解决了什么问题?——“稳健”与“高效”的平衡艺术
在深入讲解 PPO 的之前,我们先来明确它在强化学习的演进中扮演了什么角色,解决了哪些前人留下的“历史遗留问题”。
2.1 策略梯度方法的最大问题:不稳定性
我们前面提到了策略梯度方法的不稳定性。这是因为它们通常是在线(On-Policy)学习算法。这意味着它们用当前策略 收集数据,然后用这些数据来更新 。
一旦策略更新了,之前收集的数据就“过期”了,因为它们不再符合新的策略分布。如果新旧策略差异太大,那么用旧数据计算出的梯度方向可能对新策略来说是完全错误的,导致策略朝着错误的方向“狂奔”,最终“崩盘”。
这就像你用一套旧的、过时的地图(旧策略)来规划你的旅行路线(收集数据),然后根据这次旅行的经验(更新策略)来修改地图。如果你的修改幅度太大,地图变得面目全非,那么下次你再用这张新地图去旅行时,很可能发现自己迷失在荒野中,甚至掉进陷阱。
2.2 信赖域方法(TRPO)的“救赎”:理论优雅,实现复杂
为了解决策略梯度不稳定性问题,研究者们提出了信赖域策略优化(Trust Region Policy Optimization,TRPO)算法。
TRPO 的核心思想是:每次策略更新,都必须保证新策略与旧策略之间的差异在一个“信赖域”之内。这个差异通常用 KL 散度(Kullback-Leibler Divergence)来衡量,它量化了两个概率分布之间的距离。
TRPO 的目标函数可以概括为:
其中, 是 KL 散度, 是信赖域的半径。
TRPO 的理论非常优雅,它保证了每次更新都能提升策略性能,并且不会出现灾难性的性能下降。
然而,TRPO 有一个致命的缺点:实现起来非常复杂。它需要计算二阶导数(Hessian 矩阵),或者使用共轭梯度法(Conjugate Gradient)来近似求解,这在计算上非常昂贵,而且难以与深度学习框架(如 PyTorch)的自动微分机制完美结合。
2.3 PPO的登场:在优雅与实用之间找到“黄金分割点”
就在 TRPO 让大家望而却步的时候,OpenAI 在 2017 年推出了 PPO。PPO 的目标非常明确:在保持 TRPO 稳定性的同时,大大简化其实现难度,使其能够像普通的策略梯度方法一样,使用一阶优化器(如 Adam)进行训练。
PPO 不再需要计算复杂的二阶导数,而是通过一个巧妙的“剪裁(Clipping)”机制,在目标函数中直接限制新旧策略的差异。
PPO 解决了什么问题?
1. 策略梯度方法的不稳定性:通过限制策略更新的幅度,避免了“步子迈太大扯着蛋”的问题。
2. TRPO 的复杂性:用一个简单的一阶优化目标替代了复杂的二阶优化和约束求解,使得算法更容易实现和调优。
3. 在线学习的样本效率问题(部分解决):虽然 PPO 仍然是 On-Policy 算法,但它允许在收集到一批数据后,对这些数据进行多次迭代更新(Multiple Epochs),从而在一定程度上提高了样本利用率,减少了与环境的交互次数。
简而言之,PPO 在算法的稳定性、实现复杂度、以及样本效率之间找到了一个近乎完美的平衡点。它既不像 REINFORCE 那样“鲁莽”,也不像 TRPO 那样“高冷”,而是成为了一个“刚刚好”的算法,因此迅速成为了强化学习领域最受欢迎、应用最广泛的算法之一。
PPO用了什么方法?——优雅的策略修剪术
PPO 之所以能成为强化学习的“网红”,离不开它背后一系列巧妙而实用的设计。让我们一层一层地揭开它的神秘面纱。
3.1 核心思想:限制策略更新的幅度
PPO 的核心思想非常简单粗暴,但又极其有效:我允许你更新策略,但你不能“跑偏”太多!它不像 TRPO 那样通过 KL 散度来严格约束,而是通过一个更直接的方式——剪裁(Clipping),来限制新旧策略之间的比率。
想象一下,你是一个正在学习画画的学徒。你的老师(PPO)告诉你:“你可以自由发挥,但你的画风不能离我教你的基础风格(旧策略)太远。如果你画得太离谱,我就把你画的这部分‘剪掉’,只保留接近我风格的部分。”
3.2 策略比率(Probability Ratio):衡量“新旧”策略的差异
在 PPO 中,我们首先定义一个策略比率(Probability Ratio),用来衡量新策略 在给定状态 下采取动作 的概率,相对于旧策略 的比值:
这里的 是当前正在优化的策略参数,而 是在收集数据时使用的策略参数(通常是上一次迭代的策略参数)。
这个比率 告诉我们,新策略相对于旧策略,在 采取 的可能性是增大了还是减小了,以及增大了多少倍或减小了多少倍。
如果 ,说明新策略更倾向于采取 。
如果 ,说明新策略更不倾向于采取 。
如果 ,说明新旧策略对 的偏好程度一样。
3.3 剪裁(Clipping)机制:给策略更新戴上“紧箍咒”
PPO 最核心的创新点就在于这个“剪裁”机制。它修改了传统的策略梯度目标函数,引入了一个剪裁项,确保策略比率 不会超出某个预设的范围 。这里的 是一个超参数,通常取 0.1 或 0.2。
PPO 的目标函数(通常称为 PPO-Clip 目标)是这样的:
其中:
表示对时间步 的期望,实际上是在一批(batch)数据上求平均。
是策略比率。
是优势函数,衡量在状态 下采取动作 的“好坏”(稍后会详细解释)。
是一个剪裁函数,它将 的值限制在 之间。如果 ,则返回 ;如果 ,则返回 ;否则返回 。
这个目标函数看起来有点复杂,但我们来拆解一下它的含义。它包含两个项,然后取它们的最小值:
1. 原始策略梯度项:
这基本上就是传统的策略梯度目标,只是用比率 替代了 。
如果 (动作是好的),我们希望 越大越好(即新策略更倾向于这个动作)。
如果 (动作是坏的),我们希望 越小越好(即新策略更不倾向于这个动作)。
2. 剪裁后的策略梯度项:
这一项对策略比率 进行了剪裁。
如果 超出了 的范围,它就会被强制拉回到这个范围内。
3.3.2 剪裁的直观理解:为什么 min 函数是关键?
现在,关键来了:为什么我们要取这两个项的最小值(min)?
让我们分两种情况来讨论 的符号:
情况一:(当前动作 是“好”的,值得鼓励)
● 我们希望新策略 更多地采取这个动作,即希望 增大。
● 此时,目标函数是 。
● 由于 ,这个 操作实际上是在限制 的上限。
如果 增长得太快,超过了 ,那么 就会把 限制在 。
此时,目标函数会选择 。
这意味着,即使新策略非常非常想采取这个动作,它的奖励贡献也最多只能是 。PPO 会“惩罚”那些试图让 超过 的更新,因为这会导致目标函数不再增加。
直观理解:当一个动作是好的,PPO 鼓励你增加它的概率,但如果你增加得“太过分”了(超过 倍),PPO 就会说:“停!你已经足够好了,再好我也不会给你更多奖励了。别跑偏了!”这就像一个老师给学生加分,加到满分 100 分后,即使学生表现再好,也不会给他 101 分。
情况二:(当前动作 是“坏”的,应该避免)
● 我们希望新策略 更少地采取这个动作,即希望 减小。
● 此时,目标函数是 。
● 由于 ,这个 操作实际上是在限制 的下限。
如果 减小得太快,低于 ,那么 就会把 限制在 。
此时,目标函数会选择 。
这意味着,如果新策略试图让 变得非常小(小于 ),那么 会变得更大(因为 是负数,乘以一个更小的正数会得到一个绝对值更小的负数,也就是更大的值)。
直观理解:当一个动作是坏的,PPO 鼓励你减少它的概率,但如果你减少得“太过分”了(低于 倍),PPO 就会说:“停!你已经足够避免这个坏动作了,再避免我也不会给你更多惩罚了。别跑偏了!”这就像一个老师给学生扣分,扣到 0 分后,即使学生表现再差,也不会给他负 1 分。
总结一下:
当 时,PPO目标函数鼓励 增大,但会限制其上限在 。
当 时,PPO 目标函数鼓励 减小,但会限制其下限在 。
核心效果:PPO 通过这个巧妙的剪裁机制,确保了新策略相对于旧策略的改变不会太大。它在优化策略的同时,为策略更新设置了一个“安全区”,避免了灾难性的性能下降。这就像给策略更新加了一个“安全气囊”,或者说,给策略学习的“步子”设定了一个“最大步长”,防止“扯着蛋”。
3.4 优势函数(Advantage Function):评估行为的“好坏”
在 PPO 的目标函数中,我们看到了一个神秘的 。它就是优势函数(Advantage Function),是强化学习中一个非常重要的概念。它告诉我们,在某个特定状态下采取某个特定动作,到底比“平均水平”好多少。
3.4.1 价值函数(Value Function)与Q函数:行为的“预期收益”
在理解优势函数之前,我们先快速回顾一下价值函数(Value Function)和 Q 函数(Action-Value Function)。
状态价值函数 :表示在状态 下,遵循策略 能够获得的期望累积奖励。它衡量的是一个状态的“好坏”。
其中 是折扣因子,用于衡量未来奖励的重要性。
动作价值函数 : 表示在状态 下,采取动作 ,然后遵循策略 能够获得的期望累积奖励。它衡量的是在某个状态下采取某个动作的“好坏”。
在实际应用中,我们通常用神经网络来近似这些价值函数,称之为评论家(Critic)网络。
3.4.2 优势函数:超越“预期”的惊喜
现在,有了价值函数和 Q 函数,优势函数 就可以定义为:
直观理解:
● 是在状态 下采取动作 的预期收益。
● 是在状态 下,按照当前策略 随机选择动作的平均预期收益。
● 所以, 就表示:在状态 下,采取动作 比按照当前策略的平均水平,能多获得(或少获得)多少奖励。
如果 ,说明动作 比平均水平好,值得鼓励。
如果 ,说明动作 比平均水平差,应该避免。
优势函数是策略梯度方法中用来降低方差的关键。它通过引入一个基线(baseline,),使得梯度更新只关注那些“出乎意料”的好或坏的动作,而不是所有动作的绝对奖励。
3.4.3 广义优势估计(GAE):更精准的“惊喜”评估
在实际计算优势函数时,我们通常不会直接计算 和 ,而是使用时序差分(Temporal Difference,TD)误差来估计。最简单的是 TD(0) 误差:
这个 就是在时间步 观察到的即时奖励 加上对未来状态 的估计价值 ,与当前状态 的估计价值 之间的差值。它代表了当前价值估计的“惊喜”程度。
然而,TD(0) 误差只考虑了下一步的奖励和价值,这可能导致高偏差。而蒙特卡洛估计(Monte Carlo)虽然无偏,但方差很高。为了平衡偏差和方差,PPO 通常使用广义优势估计(Generalized Advantage Estimation,GAE)。
GAE 结合了 TD 误差和蒙特卡洛估计的优点,通过一个参数 来平衡:
其中 。
当 时,GAE 退化为 TD(0) 误差(高偏差,低方差)。
当 时,GAE 退化为蒙特卡洛估计(低偏差,高方差)。
GAE 允许我们通过调整 来权衡偏差和方差,从而获得更稳定和准确的优势估计。
3.5 熵(Entropy)奖励:鼓励探索,避免“偏执”
PPO 的目标函数通常还会加上一个熵(Entropy)奖励项:
其中:
是价值函数误差项(通常是均方误差),用于训练评论家网络:
这里的 可以是蒙特卡洛回报(未来总奖励),或者是 GAE 计算出的优势加上当前价值估计。
是策略 在状态 下的熵。熵衡量了策略的随机性或不确定性。
和 是超参数,用于平衡各个损失项的重要性。
为什么需要熵奖励?
在强化学习中,智能体需要不断地在探索(Exploration)和利用(Exploitation)之间做出权衡。
利用:智能体根据当前学到的最优策略,选择它认为能获得最高奖励的动作。
探索:智能体尝试一些它不确定结果的动作,以发现更好的策略或未知的奖励。
如果策略的熵太低,意味着策略变得过于“确定”,它总是选择相同的动作,即使这些动作可能不是全局最优的。这会导致智能体陷入局部最优,无法发现更好的策略。这就像一个餐馆老板,一旦他发现一道菜受欢迎,他就只买这道菜,不再尝试其他新菜品,最终可能错失做大做强的机会。
通过在目标函数中添加一个正的熵项(因为我们是最大化目标函数,所以是加号),PPO 鼓励策略保持一定的随机性,从而促进探索。这就像给餐馆老板一个“创新奖励”,鼓励他尝试新的食材和烹饪方法,推出新菜品,即使这些尝试不一定每次都成功。
3.6 多次迭代(Multiple Epochs):数据重用,提高效率
虽然 PPO 本质上是一个在线(On-Policy)算法,但它通过一个巧妙的机制,在一定程度上提高了样本效率:在收集到一批数据后,PPO 会用这些数据对策略网络和价值网络进行多次迭代更新(Multiple Epochs)。
传统的在线策略梯度方法(如 REINFORCE)在收集一批数据后,只用这些数据更新一次策略,然后就丢弃这些数据,重新与环境交互收集新数据。这导致样本效率非常低。
PPO 则不同。它在收集了一批数据(例如,1024 个时间步的经验)后,会计算这些数据的优势函数和目标价值,然后将这些数据打包成小批量(mini-batches),并对策略网络和价值网络进行多次梯度更新(例如,3-10 次)。
为什么可以这样做?正是因为 PPO 的剪裁机制,它保证了即使在多次更新中,新策略也不会离旧策略太远。这使得旧数据在一定程度上仍然是“有效”的,可以被重复利用。这种数据重用机制大大提高了 PPO 的样本效率,使其在与环境交互次数相同的情况下,能够学习到更好的策略。
3.7 整体架构:Actor-Critic的“双剑合璧”
PPO 的整体架构是经典的 Actor-Critic 模式。
Actor(行动者):负责学习策略 。它是一个神经网络,输入是状态 ,输出是每个动作的概率分布。Actor 的目标是最大化 PPO 的剪裁目标函数。
Critic(评论家):负责学习价值函数 。它也是一个神经网络,输入是状态 ,输出是该状态的估计价值。Critic 的目标是最小化价值函数误差(通常是均方误差)。
这两个网络通常共享一部分底层的神经网络层,但有各自独立的输出层。它们协同工作:
Critic 为 Actor 提供优势函数 的估计,帮助 Actor 判断哪些动作是好的,哪些是坏的,从而指导 Actor 更新策略。
Actor 在环境中执行动作,收集新的经验数据,这些数据又被用来训练 Critic,使其对价值的估计更加准确。
这种“双剑合璧”的架构,使得 PPO 能够同时利用价值函数来降低策略梯度的方差(Critic 的作用),又能够直接优化策略(Actor 的作用),从而实现稳定高效的学习。
PPO 的成功并非偶然,它背后蕴含着深刻的原理和巧妙的设计。让我们来深入剖析这些方法为何如此有效。
4.1 稳定性:剪裁带来的“安全气囊”
这是 PPO 最核心的优势,也是它能够广泛应用的关键。
● 避免灾难性更新:传统的策略梯度方法,如果学习率设置不当,一次更新就可能让策略发生巨大变化,导致智能体从一个“会走路”的状态瞬间变成一个“只会摔跤”的状态。PPO 的剪裁机制就像给策略更新装了一个“安全气囊”或者“护栏”。它确保了无论梯度有多大,新策略和旧策略之间的差异始终被限制在一个预设的范围内。
比喻:想象你在玩一个平衡游戏,你的目标是站在一个不断晃动的木板上。传统的策略梯度就像让你随意调整重心,一不小心就可能摔个大马趴。PPO 则像给你系上了一根安全绳,绳子的长度限制了你晃动的幅度,即使木板剧烈晃动,你也不会完全失去平衡。
平滑的性能提升:由于每次更新都在“安全区”内进行,PPO 的训练过程通常表现出更平滑、更稳定的性能提升曲线,而不是剧烈的波动。这对于调试和部署都非常有益。
4.2 简单性:告别二阶优化,拥抱一阶梯度
这是 PPO 能够普及的另一个重要原因。
● 易于实现:TRPO 虽然理论上很强大,但其复杂的二阶优化(Hessian矩阵、共轭梯度法)使得它难以与现代深度学习框架的自动微分功能无缝结合。PPO 则完全避免了这些复杂性。它的目标函数是可微分的,可以直接使用标准的一阶优化器(如 Adam、SGD)进行优化。
比喻:TRPO 就像一台需要专业工程师才能操作的精密仪器,而 PPO 则像一个傻瓜相机,任何人都能轻松上手,拍出不错的照片。
计算效率高:一阶优化比二阶优化在计算上要高效得多。这使得 PPO 能够在大规模问题上快速迭代和训练。
4.3 样本效率:有限的“离策略”能力
虽然 PPO 本质上是 On-Policy 算法,但它通过“多次迭代”的机制,在一定程度上提高了样本效率。
● 数据重用:允许在收集到一批数据后,对这些数据进行多次梯度更新。这减少了与环境交互的次数,因为每次交互收集到的数据可以被“榨干”更多的价值。
比喻:传统的 On-Policy 算法就像“一次性筷子”,用完就扔。PPO 则像“可重复使用的餐具”,洗洗还能再用几次,减少了浪费。
“伪离策略”:这种数据重用能力使得 PPO 在一定程度上具备了“离策略(Off-Policy)”算法的优点,即能够利用旧数据进行学习。但这种“离策略”是有限的,因为一旦新旧策略差异过大,剪裁机制就会发挥作用,阻止进一步的更新。这是一种“有限制的离策略”,或者说“近端离策略”。
4.4 鲁棒性:在实践中表现出色
PPO 在各种复杂的强化学习任务中都表现出了卓越的鲁棒性。
超参数不那么敏感:相比于其他一些策略梯度算法,PPO 对超参数(特别是学习率和剪裁参数 )的敏感度相对较低。这意味着它更容易调优,也更容易在不同的任务上泛化。
广泛适用性:从简单的控制任务(如 CartPole、Pendulum)到复杂的机器人操作、游戏 AI,PPO 都展现了强大的学习能力。
4.5 探索与利用的平衡:熵奖励的功劳
熵奖励项在 PPO 的成功中也扮演了重要角色。
● 防止局部最优:熵奖励鼓励策略保持一定的随机性,避免智能体过早地收敛到一个次优的局部策略。这使得智能体能够持续探索环境,发现更好的行为模式。
比喻:想象一个寻宝者。如果他只沿着一条他认为最有可能有宝藏的路走,他可能会错过其他隐藏着更大宝藏的路径。熵奖励就像鼓励他偶尔偏离主路,去探索一下旁边的灌木丛,说不定能有意外发现。
提高泛化能力:保持策略的随机性也有助于提高策略的泛化能力,使其在面对稍微不同的环境状态时,也能做出合理的决策。
综上所述,PPO 的成功在于它巧妙地结合了稳定性、简单性、样本效率和鲁棒性。它不是一个在理论上最完美的算法(比如 TRPO 在理论上更严谨),但它是一个在实践中表现最好、最容易使用、最值得信赖的算法之一。
没有哪个算法是完美的,PPO 也不例外。它有其闪耀的创新点,也有其固有的局限性。
5.1 创新点
5.1.1 实用主义的胜利PPO 最大的创新点在于它将 TRPO 的理论优势(稳定性)与传统策略梯度方法的实用性(一阶优化)完美结合。它证明了在深度强化学习中,一个“足够好”的近似方法,其在工程实现上的便利性,往往比理论上的完美性更重要。PPO 是实用主义的胜利。
5.1.2 工业界的新宠
由于其出色的性能和易用性,PPO 迅速成为工业界和研究领域最受欢迎的强化学习算法之一。
5.1.3 策略梯度算法的新里程碑
PPO 是策略梯度算法发展历程中的一个重要里程碑。它解决了传统策略梯度方法(如 REINFORCE、A2C)的稳定性痛点,同时又避免了 TRPO 的复杂性,为后续的策略梯度算法研究提供了新的思路和基准。它使得策略梯度方法在与基于价值的方法竞争时,拥有了更强的竞争力。
5.2 不足
5.2.1 仍然是“在线”学习(On-Policy)为主
尽管 PPO 通过多次迭代提高了样本效率,但它本质上仍然是一个在线(On-Policy)算法。
这意味着每次策略更新后,之前收集的数据就变得“不那么有效”了,需要重新与环境交互收集新数据。这导致 PPO 在需要大量环境交互才能学习的任务上,样本效率仍然不如真正的离线(Off-Policy)算法(如 SAC、DDPG)。
5.2.2 超参数敏感性
虽然 PPO 比 TRPO 更容易调优,但它仍然有一些关键的超参数需要仔细选择,例如:
剪裁参数 :决定了策略更新的“安全区”大小。太小可能导致学习缓慢,太大可能导致不稳定。
GAE 参数 :权衡偏差和方差。
学习率:经典的优化问题。
批次大小(Batch Size)和迭代次数(Number of Epochs):影响样本利用率和训练稳定性。
这些超参数的选择对 PPO 的性能有显著影响,通常需要进行大量的实验和调优。
5.2.3 理论上的“不完美”
PPO 的剪裁机制虽然在实践中表现出色,但它是一个启发式的方法,不像 TRPO 那样有严格的理论保证(如单调性能提升)。PPO 的目标函数在剪裁点处是不可导的,这在理论上可能带来一些问题(尽管在实践中通常不是大问题)。它更像是一个工程上的巧妙解决方案,而非严格的数学推导结果。
5.2.4 难以处理极端稀疏的奖励
和大多数策略梯度方法一样,PPO 在处理奖励非常稀疏(即智能体只有在完成任务的最后才获得奖励,中间过程没有任何反馈)的环境时,可能会遇到困难。
在这种情况下,智能体很难通过随机探索找到获得奖励的路径,从而导致梯度信号非常弱,学习效率低下。这通常需要结合其他技术,如奖励整形(Reward Shaping)或好奇心驱动探索(Exploration with Intrinsic Motivation)。
尽管存在这些不足,PPO 的优点仍然使其成为当前强化学习领域最受欢迎和最实用的算法之一。它的成功证明了在工程实践中,一个“足够好”且易于实现的算法,往往比一个理论上完美但难以操作的算法更有价值。
PPO的实现细节与代码示例(文字描述与伪代码)
为了让大家对 PPO 的实现有一个更直观的理解,这里我们提供一个高层次的算法流程和核心损失函数的伪代码描述。请注意,这是一个简化版本,实际的实现会涉及更多细节,如网络结构、环境交互、数据存储等。
6.1 算法流程概述
PPO 的训练过程可以概括为以下几个步骤:
1. 初始化:
初始化 Actor 网络(策略网络 )和Critic网络(价值网络 )。
初始化优化器(如Adam)和学习率。
设置 PPO 特有的超参数:剪裁参数 、GAE参数 、熵系数 、价值函数系数 、每次更新的迭代次数(epochs per update)。
2. 数据收集(Rollout):
使用当前的 Actor 网络 (即当前策略的参数 的一个副本)与环境进行交互,收集一批经验数据。
每一步经验包括:。
收集足够多的时间步(例如,2048 步)后停止。
3. 计算优势函数和目标价值:
对于收集到的每一步经验,使用当前的 Critic 网络 和 来估计价值。
根据奖励 和估计的价值,计算每一步的广义优势估计 。
计算每一步的目标价值 通常是 。
4. 策略和价值网络更新:
将收集到的经验数据(包括 )打包成小批量(mini-batches)。
重复多次(例如,10次)迭代:
对于每个小批量数据:
Actor更新:
计算当前策略 。
计算策略比率 。
计算 PPO-Clip 损失 。
计算策略熵 。
计算 Actor 的总损失:(注意,这里是负号,因为我们是梯度下降,而 PPO 目标是最大化)。
对 Actor 损失进行反向传播,更新 Actor 网络的参数 。
Critic 更新:
计算当前Critic网络 的预测值。
计算价值损失 。
对 Critic 损失进行反向传播,更新 Critic 网络的参数 。
5. 更新旧策略参数:
在完成所有小批量迭代后,将当前 Actor 网络的参数 复制到 ,以便在下一次数据收集中使用。
6. 重复步骤2-5,直到达到训练目标。
6.2 PPO损失函数的PyTorch实现(伪代码)
假设我们已经有了 Actor 网络(policy_net)和 Critic 网络(value_net),并且已经收集了一批数据 states, actions, old_log_probs, advantages, returns。
import torchimport torch.nn as nnimport torch.optim as optimfrom torch.distributions import Categorical # --- 假设的网络定义 (简化版) ---class Actor(nn.Module): def __init__(self, state_dim, action_dim): super(Actor, self).__init__() self.net = nn.Sequential( nn.Linear(state_dim, 64), nn.ReLU(), nn.Linear(64, action_dim) # Output logits for actions ) def forward(self, state): logits = self.net(state) return Categorical(logits=logits) # Returns a distributionclass Critic(nn.Module): def __init__(self, state_dim): super(Critic, self).__init__() self.net = nn.Sequential( nn.Linear(state_dim, 64), nn.ReLU(), nn.Linear(64, 1) # Output value ) def forward(self, state): return self.net(state)# --- PPO 损失函数计算 ---def calculate_ppo_loss(policy_net, value_net, states, actions, old_log_probs, advantages, returns, clip_epsilon, entropy_coeff, value_coeff): # 1. 计算当前策略的动作概率和log_prob current_dist = policy_net(states) current_log_probs = current_dist.log_prob(actions) # 2. 计算策略比率 r_t(theta) # old_log_probs 是从旧策略收集数据时记录的 log_prob ratio = torch.exp(current_log_probs - old_log_probs) # 3. 计算 PPO-Clip 目标函数 # term1: 原始策略梯度项 term1 = ratio * advantages # term2: 剪裁后的策略梯度项 clipped_ratio = torch.clamp(ratio, 1 - clip_epsilon, 1 + clip_epsilon) term2 = clipped_ratio * advantages # PPO-Clip 损失 (我们希望最大化这个目标,所以取负号进行梯度下降) actor_loss = -torch.min(term1, term2).mean() # 4. 计算价值函数损失 predicted_values = value_net(states).squeeze(-1) # Squeeze to match returns shape critic_loss = nn.MSELoss()(predicted_values, returns) # Mean Squared Error # 5. 计算策略熵奖励 entropy = current_dist.entropy().mean() # 熵越大越好,所以是负号加到总损失中 # 6. 组合总损失 # 注意:actor_loss 已经是负的,所以这里是加法 # critic_loss 是 MSE,我们希望最小化,所以是加法 # entropy_loss 是熵,我们希望最大化,所以是负号 total_loss = actor_loss + value_coeff * critic_loss - entropy_coeff * entropy return total_loss, actor_loss, critic_loss, entropy# --- 训练循环骨架 (简化版) ---def train_ppo(env, policy_net, value_net, actor_optimizer, critic_optimizer, num_episodes, rollout_steps, epochs_per_update, clip_epsilon, entropy_coeff, value_coeff, gamma, gae_lambda): # 初始化旧策略网络参数 old_policy_net = Actor(env.observation_space.shape[0], env.action_space.n) old_policy_net.load_state_dict(policy_net.state_dict()) for episode in range(num_episodes): states, actions, rewards, old_log_probs = [], [], [], [] # 1. 数据收集 (Rollout) state = env.reset() for t in range(rollout_steps): state_tensor = torch.FloatTensor(state).unsqueeze(0) # 从旧策略采样动作 with torch.no_grad(): # 不计算梯度,只用于数据收集 dist = old_policy_net(state_tensor) action = dist.sample() log_prob = dist.log_prob(action) next_state, reward, done, _ = env.step(action.item()) states.append(state_tensor) actions.append(action) rewards.append(reward) old_log_probs.append(log_prob) state = next_state ifdone: state = env.reset() # 环境重置 # 将收集到的数据转换为张量 states = torch.cat(states) actions = torch.cat(actions) old_log_probs = torch.cat(old_log_probs) rewards = torch.FloatTensor(rewards) # 2. 计算优势函数 (GAE) 和目标价值 # 这是一个简化的GAE计算,实际可能更复杂 with torch.no_grad(): values = value_net(states).squeeze(-1) # 计算蒙特卡洛回报作为GAE的基线 (简化) # 实际GAE需要从后往前计算,并考虑next_state_value returns = torch.zeros_like(rewards) advantages = torch.zeros_like(rewards) # 简化:使用蒙特卡洛回报作为目标价值 # 实际GAE计算会更复杂,这里仅为示意 R = 0 for i in reversed(range(len(rewards))): R = rewards[i] + gamma * R returns[i] = R # 优势函数简化为 (Returns - Values) advantages = returns - values # 实际GAE会更复杂,这里仅为示意 # GAE的计算通常需要一个循环,从轨迹末尾向前计算 # 例如:delta = r_t + gamma * V(s_{t+1}) - V(s_t) # advantage = delta + gamma * lambda * advantage_{t+1} # 3. 策略和价值网络更新 (多次迭代) # 将数据打乱,分成mini-batches dataset = torch.utils.data.TensorDataset(states, actions, old_log_probs, advantages, returns) dataloader = torch.utils.data.DataLoader(dataset, batch_size=64, shuffle=True) # 假设batch_size=64 for epoch in range(epochs_per_update): for batch_states, batch_actions, batch_old_log_probs, batch_advantages, batch_returns in dataloader: # 归一化优势函数 (可选但推荐) batch_advantages = (batch_advantages - batch_advantages.mean()) / (batch_advantages.std() + 1e-8) total_loss, actor_loss, critic_loss, entropy = calculate_ppo_loss( policy_net, value_net, batch_states, batch_actions, batch_old_log_probs, batch_advantages, batch_returns, clip_epsilon, entropy_coeff, value_coeff ) actor_optimizer.zero_grad() critic_optimizer.zero_grad() total_loss.backward() actor_optimizer.step() critic_optimizer.step() # 4. 更新旧策略参数 old_policy_net.load_state_dict(policy_net.state_dict()) # 打印训练进度 (例如,平均奖励) print(f"Episode {episode}, Avg Reward: {rewards.sum().item()}")更多阅读
#投 稿 通 道#
让你的文字被更多人看到
如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。
总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。
PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学术热点剖析、科研心得或竞赛经验讲解等。我们的目的只有一个,让知识真正流动起来。
📝 稿件基本要求:
• 文章确系个人原创作品,未曾在公开渠道发表,如为其他平台已发表或待发表的文章,请明确标注
• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰,无版权问题
• PaperWeekly 尊重原作者署名权,并将为每篇被采纳的原创首发稿件,提供业内具有竞争力稿酬,具体依据文章阅读量和文章质量阶梯制结算
📬 投稿通道:
• 投稿邮箱:hr@paperweekly.site
• 来稿请备注即时联系方式(微信),以便我们在稿件选用的第一时间联系作者
• 您也可以直接添加小编微信(pwbot02)快速投稿,备注:姓名-投稿
△长按添加PaperWeekly小编
🔍
现在,在「知乎」也能找到我们了
进入知乎首页搜索「PaperWeekly」
点击「关注」订阅我们的专栏吧
·
