近端策略优化(PPO)算法的理论基础与PyTorch代码详解

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
实时计算 Flink 版,5000CU*H 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
简介: 近端策略优化(PPO)是深度强化学习中高效的策略优化方法,广泛应用于大语言模型的RLHF训练。PPO通过引入策略更新约束机制,平衡了更新幅度,提升了训练稳定性。其核心思想是在优势演员-评论家方法的基础上,采用裁剪和非裁剪项组成的替代目标函数,限制策略比率在[1-ϵ, 1+ϵ]区间内,防止过大的策略更新。本文详细探讨了PPO的基本原理、损失函数设计及PyTorch实现流程,提供了完整的代码示例。

近端策略优化(Proximal Policy Optimization, PPO)算法作为一种高效的策略优化方法,在深度强化学习领域获得了广泛应用。特别是在大语言模型(LLM)的人类反馈强化学习(RLHF)过程中,PPO扮演着核心角色。本文将深入探讨PPO的基本原理和实现细节。

PPO属于在线策略梯度方法的范畴。其基础形式可以用带有优势函数的策略梯度表达式来描述:

策略梯度的基础表达式(包含优势函数)。

这个表达式实际上构成了优势演员-评论家(Advantage Actor-Critic)方法的基础目标函数。PPO算法可以视为对该方法的一种改进和优化。

PPO算法的损失函数设计

PPO通过引入策略更新约束机制来提升训练稳定性。这种机制很好地平衡了更新幅度:过大的策略更新可能导致训练偏离优化方向,而过小的更新则可能降低训练效率。为此,PPO采用了一个特殊的替代目标函数,该函数由裁剪项和非裁剪项组成,并取两者的最小值。

PPO的损失函数结构。

替代损失函数的非裁剪部分分析

损失函数中的非裁剪部分示意图。

在PPO中,比率函数定义为在状态st下执行动作at时,当前策略与旧策略的概率比值。

策略概率比率r(θ)的定义。

这个比率函数r(θ)为我们提供了一个度量新旧策略差异的有效工具,它可以替代传统策略梯度目标函数中的对数概率项。非裁剪部分的损失通过将此比率与优势函数相乘得到。

非裁剪部分损失计算示意图。

替代损失函数的裁剪机制

为了防止过大的策略更新,PPO引入了裁剪机制,将策略比率r(θ)限制在[1-ϵ, 1+ϵ]的区间内。其中ϵ是一个重要的超参数,在PPO的原始论文中设定为0.2。这样,我们可以得到完整的PPO目标函数:

PPO完整目标函数,包含非裁剪项和裁剪项。

PPO的最终优化目标是在这两部分中取较小值,从而实现稳定的策略优化。

算法实现流程

 1. 系统初始化
   a. 设置随机种子
   b. 初始化演员网络与评论家网络的优化器
   c. 配置损失追踪器和奖励记录器
   d. 加载超参数配置

 2. 训练回合迭代
   a. 环境重置
   b. 回合内循环:
     i. 通过演员网络预测动作概率分布并采样
     ii. 记录动作的对数概率(作为old policy的参考)
     iii. 执行环境交互,获取转移数据
   c. 计算衰减回报
   d. 存储回合经验数据
   e. 按更新频率执行网络优化:
     i. 评估状态价值
     ii. 计算优势估计
     iii. 构建PPO损失函数
     iv. 执行梯度优化

 3. 训练监控
   a. 记录并可视化平均损失指标

PyTorch实现详解

1、初始化**

 torch.manual_seed(self.cfg['train']['random_seed'])  
 actor_optim = optim.Adam(self.actor.parameters(), lr=self.cfg['train']['lr'], betas=self.cfg['train']['betas'])  
 critic_optim = optim.Adam(self.critic.parameters(), lr=self.cfg['train']['lr'], betas=self.cfg['train']['betas'])  
 avg_actor_losses = []  
 avg_critic_losses = []  
 actor_losses = []  
 critic_losses = []  
 eps = np.finfo(np.float32).eps.item()  
 batch_data = []

2、回合循环

2.1 重置环境

 for episode in range(self.cfg['train']['n_epidode']):  
     rewards = []  
     log_probs = []  
     actions = []  
     states = []  
     state_values = []  
     self.actor.train()  
     self.critic.train()  
     terminated, truncated = False, False # 初始化终止和截断标志
     state, info = self.env.reset()  
     # 转换为张量
     state = torch.FloatTensor(state).to(self.device)

2.2 当回合未结束时:

 timesteps = 0  
 # 遍历时间步
 while not terminated and not truncated:  
     timesteps += 1

演员预测动作概率并从分布中采样动作。

 # 演员层输出动作概率,因为演员神经网络在输出层有softmax
 action_prob = self.actor(state)  

 # 我们知道我们不直接使用分类交叉熵损失函数,而是手动构建以获得更多控制。
 # PyTorch中的分类交叉熵损失函数使用softmax将logits转换为概率到分类分布,
 # 然后计算损失。所以通常不需要显式地将softmax函数添加到神经网络中。在这项工作中,
 # 我们在神经网络上添加softmax层并计算分类分布。

 # categorical函数可以从softmax概率或从logits(输出中没有softmax层)生成分类分布,
 # 将logits作为属性
 action_dist= Categorical(action_prob)  

 # 采样动作
 action = action_dist.sample()  
 actions.append(action)

获取对数概率,这被视为比率的log pi_theta_old

 # 获取对数概率以得到log pi_theta_old(a|s)并保存到列表中
 log_probs.append(action_dist.log_prob(action))

在环境中执行动作,获取下一个状态、奖励和终止标志。

 # 动作必须从张量转换为numpy以供环境处理
 next_state, reward, terminated, truncated, info = self.env.step(action.item())  
 rewards.append(reward)  

 # 将下一个状态分配为当前状态
 state = torch.FloatTensor(next_state).to(self.device)

2.3 计算折扣回报

 R = 0  
 returns = [] # 用于保存真实值的列表

 # 使用环境在回合中返回的奖励计算每个回合的回报
 for r in rewards[::-1]:  
     # 计算折扣值
     R = r + self.cfg['train']['gamma'] * R  
     returns.insert(0, R)  

 returns = torch.tensor(returns).to(self.device)  
 returns = (returns - returns.mean()) / (returns.std() + eps)

2.4 将每个回合的经验存储在batch_data中

 # 存储数据
 batch_data.append([states, actions, returns, log_probs, state_values])

2.5 每update_freq回合更新网络:

 if episode != 0 and episode%self.cfg['train']['update_freq'] == 0:  
   # 这是我们更新网络一些n个epoch的循环。这个额外的for循环
   # 改善了训练效果
   for _ in range(5):  
       for states_b, actions_b, returns_b, old_log_probs, old_state_values in batch_data:             
         # 将列表转换为张量
         old_states = torch.stack(states_b, dim=0).detach()  
         old_actions = torch.stack(actions_b, dim=0).detach()  
         old_log_probs = torch.stack(old_log_probs, dim=0).detach()

计算状态值

 state_values = self.critic(old_states)

计算优势。

 # 计算优势
 advantages = returns_b.detach() - state_values.detach()  
 # 规范化优势在理论上不是必需的,但在实践中它降低了我们优势的方差,
 # 使收敛更加稳定和快速。我添加这个是因为
 # 解决一些环境问题如果没有它会太不稳定。
 advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-10)

计算演员和评论家的PPO损失。

 # 现在我们需要计算比率(pi_theta / pi_theta__old)。为了做到这一点,
 # 我们需要从存储的状态中获取所采取动作的旧策略,并计算相同动作的新策略
 # 演员层输出动作概率,因为演员神经网络在输出层有softmax
 action_probs = self.actor(old_states)  
 dist = Categorical(action_probs)  
 new_log_probs = dist.log_prob(old_actions)  
 # 因为我们取对数,所以我们可以用减法代替除法。然后取指数将得到与除法相同的结果
 ratios = torch.exp(new_log_probs - old_log_probs)  

 # 替代损失函数的非裁剪部分
 surr1 = ratios * advantages  

 # 替代损失函数的裁剪部分
 surr2 = torch.clamp(ratios, 1 - self.cfg['train']['clip_param'], 1 + self.cfg['train']['clip_param']) * advantages  

 # 更新演员网络:loss = min(surr1, surr2)
 actor_loss = -torch.min(surr1, surr2).mean()  
 actor_losses.append(actor_loss.item())  

 # 使用Huber损失计算评论家(价值)损失
 # Huber损失对数据中的异常值比平方误差损失更不敏感。在基于价值的RL设置中,
 # 推荐使用Huber损失。
 # Smooth L1损失与HuberLoss密切相关
 critic_loss =  F.smooth_l1_loss(state_values, returns_b.unsqueeze(1)) #F.huber_loss(state_value, torch.tensor([R]))  
 critic_losses.append(critic_loss.item())

使用梯度下降更新网络

 actor_optim.zero_grad()  
 critic_optim.zero_grad()  

 # 执行反向传播
 actor_loss.backward()  
 critic_loss.backward()  

 # 执行优化
 actor_optim.step()  
 critic_optim.step()

完整代码

https://avoid.overfit.cn/post/ff4d892c414a4b9c9391a1812690eceb

作者:Dhanoop Karunakaran

目录
相关文章
|
9天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于生物地理算法的MLP多层感知机优化matlab仿真
本程序基于生物地理算法(BBO)优化MLP多层感知机,通过MATLAB2022A实现随机数据点的趋势预测,并输出优化收敛曲线。BBO模拟物种在地理空间上的迁移、竞争与适应过程,以优化MLP的权重和偏置参数,提升预测性能。完整程序无水印,适用于机器学习和数据预测任务。
|
2天前
|
算法 安全 数据安全/隐私保护
基于BBO生物地理优化的三维路径规划算法MATLAB仿真
本程序基于BBO生物地理优化算法,实现三维空间路径规划的MATLAB仿真(测试版本:MATLAB2022A)。通过起点与终点坐标输入,算法可生成避障最优路径,并输出优化收敛曲线。BBO算法将路径视为栖息地,利用迁移和变异操作迭代寻优。适应度函数综合路径长度与障碍物距离,确保路径最短且安全。程序运行结果完整、无水印,适用于科研与教学场景。
|
9天前
|
算法 数据安全/隐私保护
基于二次规划优化的OFDM系统PAPR抑制算法的matlab仿真
本程序基于二次规划优化的OFDM系统PAPR抑制算法,旨在降低OFDM信号的高峰均功率比(PAPR),以减少射频放大器的非线性失真并提高电源效率。通过MATLAB2022A仿真验证,核心算法通过对原始OFDM信号进行预编码,最小化最大瞬时功率,同时约束信号重构误差,确保数据完整性。完整程序运行后无水印,展示优化后的PAPR性能提升效果。
|
1天前
|
机器学习/深度学习 存储 算法
基于遗传优化SVM支持向量机的数据分类算法matlab仿真,SVM通过编程实现,不使用工具箱
本内容包含基于遗传算法优化支持向量机(SVM)的数据分类算法,适用于数据分类与回归分析。算法使用Matlab2022a运行,提供无水印运行效果预览。完整代码含详细中文注释及操作视频。理论部分介绍通过遗传算法全局搜索能力优化SVM参数(如惩罚参数C、核函数参数),提升分类性能。具体步骤包括设定参数范围、种群规模等,利用适应度函数评估性能并引导搜索方向,最终实现最优参数组合的确定。
|
3天前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
6天前
|
机器学习/深度学习 资源调度 算法
基于入侵野草算法的KNN分类优化matlab仿真
本程序基于入侵野草算法(IWO)优化KNN分类器,通过模拟自然界中野草的扩散与竞争过程,寻找最优特征组合和超参数。核心步骤包括初始化、繁殖、变异和选择,以提升KNN分类效果。程序在MATLAB2022A上运行,展示了优化后的分类性能。该方法适用于高维数据和复杂分类任务,显著提高了分类准确性。
|
1天前
|
算法 数据可视化 调度
基于NSGAII的的柔性作业调度优化算法MATLAB仿真,仿真输出甘特图
本程序基于NSGA-II算法实现柔性作业调度优化,适用于多目标优化场景(如最小化完工时间、延期、机器负载及能耗)。核心代码完成任务分配与甘特图绘制,支持MATLAB 2022A运行。算法通过初始化种群、遗传操作和选择策略迭代优化调度方案,最终输出包含完工时间、延期、机器负载和能耗等关键指标的可视化结果,为制造业生产计划提供科学依据。
|
3天前
|
机器学习/深度学习 存储 算法
基于MobileNet深度学习网络的活体人脸识别检测算法matlab仿真
本内容主要介绍一种基于MobileNet深度学习网络的活体人脸识别检测技术及MQAM调制类型识别方法。完整程序运行效果无水印,需使用Matlab2022a版本。核心代码包含详细中文注释与操作视频。理论概述中提到,传统人脸识别易受非活体攻击影响,而MobileNet通过轻量化的深度可分离卷积结构,在保证准确性的同时提升检测效率。活体人脸与非活体在纹理和光照上存在显著差异,MobileNet可有效提取人脸高级特征,为无线通信领域提供先进的调制类型识别方案。
|
8天前
|
资源调度 算法 数据可视化
基于IEKF迭代扩展卡尔曼滤波算法的数据跟踪matlab仿真,对比EKF和UKF
本项目基于MATLAB2022A实现IEKF迭代扩展卡尔曼滤波算法的数据跟踪仿真,对比EKF和UKF的性能。通过仿真输出误差收敛曲线和误差协方差收敛曲线,展示三种滤波器的精度差异。核心程序包括数据处理、误差计算及可视化展示。IEKF通过多次迭代线性化过程,增强非线性处理能力;UKF避免线性化,使用sigma点直接处理非线性问题;EKF则通过一次线性化简化处理。
|
7天前
|
算法 数据安全/隐私保护 计算机视觉
基于sift变换的农田杂草匹配定位算法matlab仿真
本项目基于SIFT算法实现农田杂草精准识别与定位,运行环境为Matlab2022a。完整程序无水印,提供详细中文注释及操作视频。核心步骤包括尺度空间极值检测、关键点定位、方向分配和特征描述符生成。该算法通过特征匹配实现杂草定位,适用于现代农业中的自动化防控。