强化学习之 PPO 算法:原理、实现与案例深度剖析
目录
- 一、引言
- 二、PPO 算法原理
- 2.1 策略梯度
- 2.2 PPO 核心思想
- 三、PPO 算法公式推导
- 3.1 重要性采样
- 3.2 优势函数估计
- 四、PPO 算法代码实现(以 Python 和 PyTorch 为例)
- 五、PPO 算法案例应用
- 5.1 机器人控制
- 5.2 自动驾驶
- 六、总结
一、引言
强化学习作为机器学习中的一个重要领域,旨在让智能体通过与环境交互,学习到最优的行为策略以最大化长期累积奖励。近端策略优化(Proximal Policy Optimization,PPO)算法是强化学习中的明星算法,它在诸多领域都取得了令人瞩目的成果。本文将深入探讨 PPO 算法,从原理到代码实现,再到实际案例应用,力求让读者全面掌握这一强大的算法。
二、PPO 算法原理
2.1 策略梯度
在强化学习里,策略梯度是一类关键的优化方法,你可以把它想象成是智能体在学习如何行动时的 “指南针”。假设策略由参数 θ \theta θ 表示,这就好比是智能体的 “行动指南” 参数,智能体在状态 s s s 下采取行动 a a a 的概率为 π θ ( a ∣ s ) \pi_{\theta}(a|s) πθ(a∣s) ,即根据当前的 “行动指南”,在这个状态下选择这个行动的可能性。
策略梯度的目标是最大化累计奖励的期望,用公式表示就是: J ( θ ) = E s 0 , a 0 , ⋯ [ ∑ t = 0 T γ t r ( s t , a t ) ] J(\theta)=\mathbb{E}_{s_0,a_0,\cdots}\left[\sum_{t = 0}^{T}\gamma^{t}r(s_t,a_t)\right] J(θ)=Es0,a0,⋯[t=0∑Tγtr(st,at)]
这里的 γ \gamma γ 是折扣因子,它的作用是让智能体更关注近期的奖励,因为越往后的奖励可能越不确定,就像我们在做决策时,往往会更看重眼前比较确定的好处。 r ( s t , a t ) r(s_t,a_t) r(st,at) 是在状态 s t s_t st 下采取行动 a t a_t at 获得的奖励,比如玩游戏时,在某个游戏场景下做出某个操作得到的分数。
根据策略梯度定理,策略梯度可以表示为: ∇ θ J ( θ ) = E s , a [ ∇ θ log π θ ( a ∣ s ) A ( s , a ) ] \nabla_{\theta}J(\theta)=\mathbb{E}_{s,a}\left[\nabla_{\theta}\log\pi_{\theta}(a|s)A(s,a)\right] ∇θJ(θ)=Es,a[∇θlogπθ(a∣s)A(s,a)]
这里的 A ( s , a ) A(s,a) A(s,a) 是优势函数,它表示采取行动 a a a 相对于平均策略的优势。简单来说,就是判断这个行动比一般的行动好在哪里,好多少,帮助智能体决定是否要多采取这个行动。
2.2 PPO 核心思想
PPO 算法的核心是在策略更新时,限制策略的变化幅度,避免更新过大导致策略性能急剧下降。这就好像我们在调整自行车的变速器,如果一下子调得太猛,可能车子就没法正常骑了。
它通过引入一个截断的目标函数来实现这一点: L C L I P ( θ ) = E t [ min ( r t ( θ ) A ^ t , clip ( r t ( θ ) , 1 − ϵ , 1 + ϵ ) A ^ t ) ] L^{CLIP}(\theta)=\mathbb{E}_{t}\left[\min\left(r_t(\theta)\hat{A}_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1+\epsilon)\hat{A}_t\right)\right] LCLIP(θ)=Et[min(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)]
其中 r t ( θ ) = π θ ( a t ∣ s t ) π θ o l d ( a t ∣ s t ) r_t(\theta)=\frac{\pi_{\theta}(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)} rt(θ)=πθold(at∣st)πθ(at∣st) 是重要性采样比,它反映了新策略和旧策略对于同一个状态 - 行动对的概率差异。 A ^ t \hat{A}_t A^t 是估计的优势函数, ϵ \epsilon ϵ 是截断参数,通常设置为一个较小的值,如 0.2 。这个截断参数就像是给策略更新幅度设定了一个 “安全范围”,在这个范围内更新策略,能保证策略既有所改进,又不会变得太糟糕。
三、PPO 算法公式推导
3.1 重要性采样
重要性采样是 PPO 算法中的关键技术之一。由于直接从当前策略采样数据效率较低,我们可以从旧策略 π θ o l d \pi_{\theta_{old}} πθold 采样数据,然后通过重要性采样比 r t ( θ ) r_t(\theta) rt(θ) 来校正数据的分布。 E s ∼ π θ [ f ( s ) ] ≈ 1 N ∑ i = 1 N π θ ( s i ) π θ o l d ( s i ) f ( s i ) \mathbb{E}_{s\sim\pi_{\theta}}[f(s)]\approx\frac{1}{N}\sum_{i = 1}^{N}\frac{\pi_{\theta}(s_i)}{\pi_{\theta_{old}}(s_i)}f(s_i) Es∼πθ[f(s)]≈N1i=1∑Nπθold(si)πθ(si)f(si)
比如我们要了解一群鸟的飞行习惯,直接去观察所有鸟的飞行轨迹很困难,那我们可以先观察一部分容易观察到的鸟(旧策略采样),然后根据这些鸟和所有鸟的一些特征差异(重要性采样比),来推测整个鸟群的飞行习惯。
3.2 优势函数估计
优势函数 A ( s , a ) A(s,a) A(s,a) 可以通过多种方法估计,常用的是广义优势估计(Generalized Advantage Estimation,GAE): A ^ t = ∑ k = 0 ∞ ( γ λ ) k δ t + k \hat{A}_t=\sum_{k = 0}^{\infty}(\gamma\lambda)^k\delta_{t + k} A^t=k=0∑∞(γλ)kδt+k
其中 δ t = r t + γ V ( s t + 1 ) − V ( s t ) \delta_{t}=r_t+\gamma V(s_{t + 1})-V(s_t) δt=rt+γV(st+1)−V(st) 是 TD 误差, λ \lambda λ 是 GAE 参数,通常在 0 到 1 之间。优势函数的估计就像是给智能体的行动打分,告诉它每个行动到底有多好,以便它做出更好的决策。
四、PPO 算法代码实现(以 Python 和 PyTorch 为例)
import torchimport torch.nn as nnimport torch.optim as optimimport gymclass Policy(nn.Module):def __init__(self, state_dim, action_dim):super(Policy, self).__init__()self.fc1 = nn.Linear(state_dim, 64)self.fc2 = nn.Linear(64, 64)self.mu_head = nn.Linear(64, action_dim)self.log_std_head = nn.Linear(64, action_dim)def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))mu = torch.tanh(self.mu_head(x))log_std = self.log_std_head(x)std = torch.exp(log_std)dist = torch.distributions.Normal(mu, std)return distclass Value(nn.Module):def __init__(self, state_dim):super(Value, self).__init__()self.fc1 = nn.Linear(state_dim, 64)self.fc2 = nn.Linear(64, 64)self.v_head = nn.Linear(64, 1)def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))v = self.v_head(x)return vdef ppo_update(policy, value, optimizer_policy, optimizer_value, states, actions, rewards, dones, gamma=0.99,clip_epsilon=0.2, lambda_gae=0.95):states = torch.FloatTensor(states)actions = torch.FloatTensor(actions)rewards = torch.FloatTensor(rewards)dones = torch.FloatTensor(dones)values = value(states).squeeze(1)returns = []gae = 0for i in reversed(range(len(rewards))):if i == len(rewards) - 1:next_value = 0else:next_value = values[i + 1]delta = rewards[i] + gamma * next_value * (1 - dones[i]) - values[i]gae = delta + gamma * lambda_gae * (1 - dones[i]) * gaereturns.insert(0, gae + values[i])returns = torch.FloatTensor(returns)old_dist = policy(states)old_log_probs = old_dist.log_prob(actions).sum(-1)for _ in range(3):dist = policy(states)log_probs = dist.log_prob(actions).sum(-1)ratios = torch.exp(log_probs - old_log_probs)advantages = returns - values.detach()surr1 = ratios * advantagessurr2 = torch.clamp(ratios, 1 - clip_epsilon, 1 + clip_epsilon) * advantagespolicy_loss = -torch.min(surr1, surr2).mean()optimizer_policy.zero_grad()policy_loss.backward()optimizer_policy.step()value_loss = nn.MSELoss()(value(states).squeeze(1), returns)optimizer_value.zero_grad()value_loss.backward()optimizer_value.step()def train_ppo(env_name, num_episodes=1000):env = gym.make(env_name)state_dim = env.observation_space.shape[0]action_dim = env.action_space.shape[0]policy = Policy(state_dim, action_dim)value = Value(state_dim)optimizer_policy = optim.Adam(policy.parameters(), lr=3e-4)optimizer_value = optim.Adam(value.parameters(), lr=3e-4)for episode in range(num_episodes):states, actions, rewards, dones = [], [], [], []state = env.reset()done = Falsewhile not done:state = torch.FloatTensor(state)dist = policy(state)action = dist.sample()next_state, reward, done, _ = env.step(action.detach().numpy())states.append(state)actions.append(action)rewards.append(reward)dones.append(done)state = next_stateppo_update(policy, value, optimizer_policy, optimizer_value, states, actions, rewards, dones)if episode % 100 == 0:total_reward = 0state = env.reset()done = Falsewhile not done:state = torch.FloatTensor(state)dist = policy(state)action = dist.meannext_state, reward, done, _ = env.step(action.detach().numpy())total_reward += rewardstate = next_stateprint(f"Episode {episode}, Average Reward: {total_reward}")if __name__ == "__main__":train_ppo('Pendulum-v1')
五、PPO 算法案例应用
5.1 机器人控制
在机器人控制领域,PPO 算法可以用于训练机器人的运动策略。例如,训练一个双足机器人行走,机器人的状态可以包括关节角度、速度等信息,行动则是关节的控制指令。通过 PPO 算法,机器人可以学习到如何根据当前状态调整关节控制,以实现稳定高效的行走。
5.2 自动驾驶
在自动驾驶场景中,车辆的状态包括位置、速度、周围环境感知信息等,行动可以是加速、减速、转向等操作。PPO 算法可以让自动驾驶系统学习到在不同路况和环境下的最优驾驶策略,提高行驶的安全性和效率。
六、总结
PPO 算法作为强化学习中的优秀算法,以其高效的学习能力和良好的稳定性在多个领域得到了广泛应用。通过深入理解其原理、公式推导,结合代码实现和实际案例分析,我们能够更好地掌握和运用这一算法,为解决各种复杂的实际问题提供有力的工具。
相关文章:
强化学习之 PPO 算法:原理、实现与案例深度剖析
目录 一、引言二、PPO 算法原理2.1 策略梯度2.2 PPO 核心思想 三、PPO 算法公式推导3.1 重要性采样3.2 优势函数估计 四、PPO 算法代码实现(以 Python 和 PyTorch 为例)五、PPO 算法案例应用5.1 机器人控制5.2 自动驾驶 六、总结 一、引言 强化学习作为…...
Docker 部署 MongoDB | 国内阿里镜像
一、简易单机版 1、镜像拉取 docker pull registry.cn-hangzhou.aliyuncs.com/farerboy/mongo:8.0.5-rc1 2、运行镜像 docker run -it --name mongodb \ -e MONGO_INITDB_ROOT_USERNAMEmongoroot \ -e MONGO_INITDB_ROOT_PASSWORDmongoroot \ -v /wwwroot/opt/docker/mong…...
1.1 Spring Security 概述
Spring Security 概述 1. 什么是 Spring Security? Spring Security 是 Spring 生态中专注于应用安全的核心框架,为 Java 企业应用提供认证(Authentication)、授权(Authorization)以及安全攻击防护&#x…...
Kotlin协程详解——协程上下文
目录 一、上下文结构 get()获取元素 minusKey()删除元素 fold()元素遍历 plus()添加元素 CombinedContext Key 二、协程名称CoroutineName 三、上下文组合 四、协程作用域CoroutineScope 五、典型用例 协程的上下文,它包含用户定义的一些数据集合&#x…...
手写一个C++ Android Binder服务及源码分析
手写一个C Android Binder服务及源码分析 前言一、 基于C语言编写Android Binder跨进程通信Demo总结及改进二、C语言编写自己的Binder服务Demo1. binder服务demo功能介绍2. binder服务demo代码结构图3. binder服务demo代码实现3.1 IHelloService.h代码实现3.2 BnHelloService.c…...
今日AI和商界事件(2025-02-10)
今日AI领域的相关事件包括: 一、技术与应用进展 全球首例AI驱动供应链攻击曝光: 网络安全机构披露一起新型供应链攻击事件,攻击者利用AI技术生成高度仿真的供应商邮件,诱骗目标企业员工下载恶意软件,进而渗透至大众汽…...
全面理解-c++中的异常处理机制
C 的异常处理机制是一种用于处理程序运行时错误的结构化方法,通过分离正常逻辑与错误处理代码,提高代码的可读性和可维护性。以下是其核心组成部分和工作原理的详细说明: 1. 异常处理的三大关键字 1.1 try 块 作用:包裹可能抛出异…...
Deep Dive into LLMs like ChatGPT - by Andrej Karpathy
https://www.youtube.com/watch?v7xTGNNLPyMIhttps://www.youtube.com/watch?v7xTGNNLPyMIDeep Dive into LLMs like ChatGPT - by Andrej Karpathy_哔哩哔哩_bilibilihttps://www.youtube.com/watch?v7xTGNNLPyMI转载自Andrej Karpathy Youtube ChannelThis is a general a…...
react实例与总结(一)
目录 一、简单认识 1.1、特点 1.2、JSX语法规则 1.3、函数组件和类式组件 1.4、类组件三大属性state、props、refs 1.4.1、state 1.4.2、props 1.4.3、refs 1.5、事件处理 1.6、收集表单数据—非受控组件和受控组件 1.7、高阶函数—函数柯里化 1.8、生命周期—新旧…...
51单片机(国信长天)矩阵键盘的基本操作
在CT107D单片机综合训练平台上,首先将J5处的跳帽接到1~2引脚,使按键S4~S19按键组成4X4的矩阵键盘。在扫描按键的过程中,发现有按键触发信号后(不做去抖动),待按键松开后,在数码管的第一位显示相应的数字:从左至右&…...
在cursor/vscode中使用godot C#进行游戏开发
要在 Visual Studio Code(VS Code)中启动 C#Godot 项目,可以按照以下步骤进行配置: 1.安装必要的工具 • 安装 Visual Studio Code:确保你已经安装了最新版本的 VS Code。 • 安装.NET SDK:下载并安装.NET 7.x SDK(…...
机器学习怎么学习,还有算法基本的源代码
1.scikit-learn官方文档,中文版/英文版 中文社区:https://scikit-learn.org.cn/ 中文官方文档:https://scikitlearn.com.cn/ 英文版:https://scikit-learn.org/stable/(翻墙) 2.菜鸟教程:AI&a…...
STM32 RTC亚秒
rtc时钟功能实现:rtc模块在stm32内部,由电池或者主电源供电。如下图,需注意实现时仅需设置一次初始化。 1、stm32cubemx 代码生成界面设置,仅需开启时钟源和激活日历功能。 2、生成的代码,需要对时钟进行初始化,仅需…...
【Linux】深入理解linux权限
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:Linux 目录 前言 一、权限是什么 二、用户和身份角色 三、文件属性 1. 文件属性表示 2. 文件类型 3. 文件的权限属性 四、修改文件的权限属性和角色 1. …...
json格式,curl命令,及轻量化处理工具
一. JSON格式 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于一个子集的JavaScript编程语言,使用人类易于阅读的文本格式来存储和表示数据。尽管名字中有“JavaScript”,但JSON是语言无关的,几…...
DeepSeek模拟阿里面试——java面向对象
作为一位阿里高级Java程序员面试官,我会围绕Java面向对象编程的核心概念、实际应用以及设计原则设计问题,以全面评估候选人的理解和应用能力。以下是可能的面试问题: 基本概念与实现方式 请解释Java中封装、继承、多态的基本概念及其在Java中…...
web直播弹幕抓取分析 signature
声明: 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 前言 最近遇到太多难点了卡了很久&am…...
【04】RUST特性
文章目录 隐藏shadowing所有权ownership堆区&栈区所有权规则变量&数据Copy Trait与Drop TraitCopy TraitDrop Trait移动克隆函数参数与返回值的所有权参数引用可变引用悬垂引用slice生命周期隐藏shadowing 有点像同名覆盖 let mut guess = String::new();let guess: u3…...
PL/SQL块结构
目录 一、声明部分(declare) 二、执行部分(begin end) 三、异常处理部分 (Exception end) 四、代码示例 PL/SQL(Procedural Language/Structured Query Language)是Oracle数据库…...
基于 FFmpeg 和 OpenGLES 的 iOS 视频预览和录制技术方案设计
基于 FFmpeg 和 OpenGLES 的 iOS 视频预览和录制技术方案设计 在 iOS 上实现一个基于 FFmpeg 和 OpenGLES 的视频预览和录制功能,需要结合 FFmpeg 的强大音视频处理能力和 OpenGLES 的高效图形渲染能力。以下是一个完整的技术方案设计,包含项目的架构设计、模块划分、技术选…...
【LeetCode 刷题】贪心算法(4)-区间问题
此博客为《代码随想录》贪心算法章节的学习笔记,主要内容为贪心算法区间问题的相关题目解析。 文章目录 55. 跳跃游戏45. 跳跃游戏 II452. 用最少数量的箭引爆气球435. 无重叠区间763. 划分字母区间56. 合并区间 55. 跳跃游戏 题目链接 class Solution:def canJu…...
提示工程 | 目的 | 常用技巧
什么是提示工程 提示工程也叫指令工程,Prompt就是你发给大模型的指令,比如:画幅画,写首诗等。貌似简单,但意义非凡,Prompt是AGI时代的编程语言,Prompt工程是AGI时代的软件工程,提示…...
ABP框架9——自定义拦截器的实现与使用
一、AOP编程 AOP定义:面向切片编程,着重强调功能,将功能从业务逻辑分离出来。AOP使用场景:处理通用的、与业务逻辑无关的功能(如日志记录、性能监控、事务管理等)拦截器:拦截方法调用并添加额外的行为,比如…...
Generate html
"Generate HTML"(生成 HTML)指的是通过程序或工具自动创建 HTML 代码的过程。HTML(超文本标记语言)是用于创建网页内容和结构的标准语言。生成 HTML 通常意味着通过某些方式自动化地构建或生成网页的结构和元素…...
CUDA 计算平台 CUDA 兼容性【笔记】
在 b 站看过的两个关于 CUDA 的技术分享,整理分享下对自己有用的课件。 20231130 2023第9期 聊一聊常见的AI计算平台库_哔哩哔哩_bilibili20230831 2023第6期 聊一聊CUDA兼容性_哔哩哔哩_bilibili 文章目录 CUDA 计算平台CUDA 函数库介绍英伟达三大护城河࿱…...
移动(新)魔百盒刷机教程[M301A_YS]
刚刚成功刷了一个坏的魔百盒,简单记录一下。 刷电视盒子有两种:卡刷和线刷。 线刷 一、线刷准备 1.刷机工具 Amlogic USB Burning Tool 晶晨线刷烧录工具 2.固件 根据盒子的型号、代工等找到对应的固件 二、线刷步骤 电脑打开下好的 Amlogic US…...
最新消息 | 德思特荣获中国创新创业大赛暨广州科技创新创业大赛三等奖!
2024年12月30日,广州市科技局公开第十三届中国创新创业大赛(广东广州赛区)暨2024年广州科技创新创业大赛决赛成绩及拟获奖企业名单,德思特获得了智能与新能源汽车初创组【第六名】【三等奖】的好成绩! 关于德思特&…...
基于机器学习的DDoS检测系统实战
基于机器学习的DDoS检测系统实战(PythonScikit-learn)|毕业设计必备 摘要:本文手把手教你从0到1实现一个轻量级DDoS攻击检测系统,涵盖数据预处理、特征工程、模型训练与可视化分析。 一、项目背景与意义 DDoS&#x…...
ubuntu安装VMware报错/dev/vmmon加载失败
ubuntu安装VMware报错/dev/vmmon加载失败,解决步骤如下: step1:为vmmon和vmnet组件生成密钥对 openssl req -new -x509 -newkey rsa:2048 -keyout VMW.priv -outform DER -out VMW.der -nodes -days 36500 -subj "/CNVMware/"ste…...
使用条件随机场(CRF)进行文本分类并评估模型性能
目标: 使用条件随机场(CRF)模型对文本数据进行分类,并评估模型的性能。任务包括读取数据、划分训练集和测试集、训练CR # 1.数据读取与预处理: # o使用open函数读取包含文本和标签的CSV文件。 # o将每一行数据分为文本…...
