强化学习之 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 自动驾驶 六、总结 一、引言 强化学习作为…...
vue-点击生成动态值,动态渲染回显输入框
1.前言 动态点击生成数值,回显输入框,并绑定。 2.实现 <template><div style"display:flex;align-items: center;flex-direction:row"><a-input:key"inputKey"v-model"uploadData[peo.field]"placehold…...
高性能 :OpenAI Triton Open-source GPU programming Language LINUX 环境配置
目录 配置triton环境cudabuild-essential带有pip的python环境直接安装pipanaconda 安装 triton 环境pip install tritonpip install torch 运行test示例vector-add.pylaunch.json 配置triton环境 cuda wget http://developer.download.nvidia.com/compute/cuda/11.0.2/local_…...
TCP 端口号为何位于首部前四个字节?协议设计的智慧与启示
知乎的一个问题很有意思:“为什么在TCP首部中要把TCP的端口号放入最开始的四个字节?” 这种问题很适合我这种搞历史的人,大年初一我给出了一个简短的解释,但仔细探究这个问题,我们将会获得 TCP/IP 被定义的过程。 文…...
HTML之JavaScript函数声明
HTML之JavaScript函数声明 1. function 函数名(){}2. var 函数名 function(){}<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1…...
R 数组:高效数据处理的基础
R 数组:高效数据处理的基础 引言 在数据科学和统计分析领域,R 语言以其强大的数据处理和分析能力而备受推崇。R 数组是 R 语言中用于存储和操作数据的基本数据结构。本文将详细介绍 R 数组的创建、操作和优化,帮助读者掌握 R 数组的使用技巧…...
git服务器搭建,gitea服务搭建,使用systemclt管理服务
文章目录 页面展示使用二进制文件安装git服务下载选择架构使用wget下载安装 验证 GPG 签名服务器设置准备环境创建systemctl文件 备份与恢复备份命令 (dump)恢复命令 (restore) 页面展示 使用二进制文件安装git服务 所有打包的二进制程序均包含 SQLite,MySQL 和 Po…...
Pdf手册阅读(1)--数字签名篇
原文阅读摘要 PDF支持的数字签名, 不仅仅是公私钥签名,还可以是指纹、手写、虹膜等生物识别签名。PDF签名的计算方式,可以基于字节范围进行计算,也可以基于Pdf 对象(pdf object)进行计算。 PDF文件可能包…...
嵌入式WebRTC压缩至670K,目标将so动态库压缩至500K,.a静态库还可以更小
最近把EasyRTC的效果发布出去给各大IPC厂商体验了一下,直接就用EasyRTC与各个厂商的负责人进行的通话,在通话中,用户就反馈效果确实不错! 这两天有用户要在海思hi3516cv610上使用EasyRTC,工具链是:gcc-2024…...
百度高德地图坐标转换
百度地图和高德地图的侧重点不太一样。同样一个地名,在百度地图网站上搜索到的地点可能是商业网点,在高德地图网站上搜索到的地点可能是自然行政地点。 高德地图api 在高德地图中,搜索地名,如“乱石头川”,该地名会出…...
ES 索引结构
ES 既不像 MySQL 这样有严格的 Schema,也不像 MongoDB 那样完全无 Schema,而是介于两者之间。 1️⃣ ES 的 Schema 模式 ES 默认是 Schema-less(无模式) 的,允许动态添加字段。 但 ES 也支持 Schema(映射 …...
HPM_SDK应用本地化——基于6750evkmini
文章目录 前言一、准备工作1、下载官方的SDK2、解压SDK 二、实操1、新建目标工程文件夹2、回到SDK中将相关文件复制1、Borad文件夹2、hello_world文件夹 三、实验现象总结 前言 为什么要对sdk进行应用本地化?在嵌入式开发中我们一般将官方提供的SDK作为参考&#x…...
【deepseek-r1本地部署】
首先需要安装ollama,之前已经安装过了,这里不展示细节 在cmd中输入官网安装命令:ollama run deepseek-r1:32b,开始下载 出现success后,下载完成 接下来就可以使用了,不过是用cmd来运行使用 可以安装UI可视化界面&a…...
查询语句来提取 detail 字段中包含 xxx 的 URL 里的 commodity/ 后面的数字串
您可以使用以下 SQL 查询语句来提取 detail 字段中包含 oss.kxlist.com 的 URL 里的 commodity/ 后面的数字串: <p><img style"max-width:100%;" src"https://oss.kxlist.com//8a989a0c55e4a7900155e7fd7971000b/commodity/20170925/20170…...
堆排序
目录 堆排序(不稳定): 代码实现: 思路分析: 总结: 堆排序(不稳定): 如果想要一段数据从小到大进行排序,则要先建立大根堆,因为这样每次堆顶上都能…...
【MySQL】我在广州学Mysql 系列—— 数据备份与还原
ℹ️大家好,我是练小杰,今天周一,过两天就是元宵节了,今年元宵节各位又要怎么过呢!! 本文主要对Mysql数据库中的数据备份与还原内容进行讨论!! 回顾:👉【MySQ…...
【LeetCode Hot100 双指针】移动零、盛最多水的容器、三数之和、接雨水
双指针 1. 移动零题目描述解题思路关键思路:步骤:时间复杂度:空间复杂度: 代码实现 2. 盛最多水的容器题目解析解题思路代码实现 3. 三数之和问题描述:解题思路:算法步骤:代码实现: …...
HTML应用指南:利用POST请求获取接入比亚迪业态的充电桩位置信息
在新能源汽车快速发展的今天,充电桩的分布和可用性成为了影响用户体验的关键因素之一。比亚迪作为全球领先的新能源汽车制造商,不仅在车辆制造方面取得了卓越成就,也在充电基础设施建设上投入了大量资源。为了帮助用户更方便地找到比亚迪充电桩的位置,本篇文章,我们将探究…...
Android车机DIY开发之软件篇(十二) AOSP12下载编译
Android车机DIY开发之软件篇(十二) AOSP12下载编译 sudo apt-get update sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib gmultilib libc6-dev-i386 lib32ncurses5-dev libx11-dev lib32z-dev ccache libgl1-mesa-…...
Jenkins+gitee 搭建自动化部署
Jenkinsgitee 搭建自动化部署 环境说明: 软件版本备注CentOS8.5.2111JDK1.8.0_211Maven3.8.8git2.27.0Jenkins2.319最好选稳定版本,不然安装插件有点麻烦 一、安装Jenkins程序 1、到官网下载相应的版本war或者直接使用yum安装 Jenkins官网下载 直接…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
Docker、Wsl 打包迁移环境
电脑需要开启wsl2 可以使用wsl -v 查看当前的版本 wsl -v WSL 版本: 2.2.4.0 内核版本: 5.15.153.1-2 WSLg 版本: 1.0.61 MSRDC 版本: 1.2.5326 Direct3D 版本: 1.611.1-81528511 DXCore 版本: 10.0.2609…...
【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...
