近端策略优化PPO-算法简析-paddle2.0
本文介绍了PPO算法及其实践。PPO是对策略梯度的改进,通过重要性采样将在线学习转为离线学习,能重复利用数据提升效率,PPO2通过clip限制偏差。文中还给出了基于MountainCar-v0环境的PPO实现代码,包括网络结构、数据处理和更新流程,最后展示了训练过程与效果。

PPO算法介绍
Proximal Policy Optimization,简称PPO,即近端策略优化,是对Policy Graident,即策略梯度的一种改进算法。PPO的核心精神在于,通过一种被称之为Importce Sampling的方法,将Policy Gradient中On-policy的训练过程转化为Off-policy,即从在线学习转化为离线学习,某种意义上与基于值迭代算法中的Experience Replay有异曲同工之处。通过这个改进,训练速度与效果在实验上相较于Policy Gradient具有明显提升。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
环境配置
In [7]import argparseimport picklefrom collections import namedtuplefrom itertools import countimport timeimport os, timeimport numpy as npimport matplotlib.pyplot as pltimport gymimport paddleimport paddle.nn as nnimport paddle.nn.functional as Fimport paddle.optimizer as optimfrom paddle.distribution import Normal, Categoricalfrom paddle.io import RandomSampler, BatchSampler, Datasetfrom visualdl import LogWriter登录后复制
MountainCar-v0介绍
汽车位于一维轨道上,位于两个“山”之间。 目标是驶向右边的山峰; 但是,汽车的引擎强度不足以单程通过。 因此,成功的唯一方法就是来回驱动以建立动力。在每个时刻,智能体可以对小车施加3种动作中的一种:向左施力、不施力、向右施力。智能体施力和小车的水平位置会共同决定小车下一时刻的速度。本项目'MountainCar-v0'环境开启了unwrapped模式,即只有到达右边的山峰才能结束一轮游戏,在开始的阶段可能模型要试探上万步才能完成任务,这可真是个稀疏回报的强化学习环境。
# Parametersenv_name = 'MountainCar-v0'gamma = 0.99render = Falseseed = 1log_interval = 10env = gym.make(env_name).unwrappednum_state = env.observation_space.shape[0]num_action = env.action_space.nenv.seed(seed)Transition = namedtuple('Transition', ['state', 'action', 'action_prob', 'reward', 'next_state'])登录后复制定义网络结构
Actor部分定义的是“演员”,Critic部分定义的是“评论家”。“评论家”网络观察输入并“打分”,“演员”网络接收输入并给出行动的概率。
In [9]class Actor(nn.Layer): def __init__(self): super(Actor, self).__init__() self.fc1 = nn.Linear(num_state, 128) self.action_head = nn.Linear(128, num_action) def forward(self, x): x = F.relu(self.fc1(x)) action_prob = F.softmax(self.action_head(x), axis=1) return action_probclass Critic(nn.Layer): def __init__(self): super(Critic, self).__init__() self.fc1 = nn.Linear(num_state, 128) self.state_value = nn.Linear(128, 1) def forward(self, x): x = F.relu(self.fc1(x)) value = self.state_value(x) return value登录后复制
前置知识——策略梯度方法( Policy Gradient)
Policy Gradient是DRL中一大类方法,核心思想就是直接优化策略网络Policy Network: π(a∣s;θ) 来提升Reward的获取。怎么直接优化policy呢? 采样很多样本,判断样本的好坏,如果样本好,就将对应的动作action概率增大,如果样本差,就将对应的动作action概率减少。Policy gradient方法是on policy的,因此要求每次使用on policy的数据进行训练,所谓on policy就是采样数据的策略和要评估及训练的策略是同一个策略。PPO算法
1.importance sampling 的使用
policy gradient为on-policy,sample一次更新完actor之后,actor就变了,不能使用原来的数据了,必须重新与Env互动收集新数据,这导致训练需要大量互动,降低效率。
而PPO算法不仅可以将一次采样的数据分minibatch训练神经网络迭代多次,而且能够重复利用数据,也就是sample reuse。
由于训练中使用了off policy的数据(只有第一个更新是on policy,后面都是off policy),数据分布不同了,所以PPO使用了importance sampling来调整
研究了使用重要性采样实现on policy 到off policy的转换,我们知道期望值几乎是差不多的,计算了方差的公式,最后发现第二项对于方差的影响是很小的,但是第一项对于方差的影响还是有的。于是我们晓得,当使用重要性采样的时候,要保证只有p(x)和q(x)的区别不大,才会使得方差的区别很小。

2.PPO2的核心思想
PPO2的核心思想很简单,对于ratio 也就是当前policy和旧policy的偏差做clip,如果ratio偏差超过一定的范围就做clip,clip后梯度也限制在一定的范围内,神经网络更新参数也不会太离谱。这样,在实现上,无论更新多少步都没有关系,有clip给我们挡着,不担心训练偏了。

3.本项目代码简析
本项目采用 namedtuple('Transition', ['state', 'action', 'action_prob', 'reward', 'next_state'])的方式收集数据集构造了一个 class RandomDataset(Dataset)的类,便于后续使用for index in BatchSampler(sampler=RandomSampler(RandomDataset(len(self.buffer))), batch_size=self.batch_size, drop_last=False)的方式来采样收集的数据,并循环迭代了self.ppo_update_time次,这样就实现了一次采样的数据分minibatch训练神经网络迭代多次。用action_prob = paddle.concat([action_prob[i][int(paddle.index_select(action,index)[i])] for i in range(len(action_prob))]).reshape([-1,1]) 来选择动作概率。用paddle.clip(ratio, 1 - self.clip_param, 1 + self.clip_param) * advantage来截断advantage演员网络的损失为 action_loss = -paddle.min(surr,1).mean() ,评论家网络的损失为value_loss = F.mse_loss(Gt_index, V)In [10]# init with datasetclass RandomDataset(Dataset): def __init__(self, num_samples): self.num_samples = num_samples def __getitem__(self, idx): pass def __len__(self): return self.num_samples登录后复制In [11]
class PPO(): clip_param = 0.2 max_grad_norm = 0.5 ppo_update_time = 10 buffer_capacity = 8000 batch_size = 64 ## 初始化参数 def __init__(self): super(PPO, self).__init__() self.actor_net = Actor() self.critic_net = Critic() self.buffer = [] self.counter = 0 self.training_step = 0 self.writer = LogWriter('./exp') clip = nn.ClipGradByNorm(self.max_grad_norm) self.actor_optimizer = optim.Adam(parameters = self.actor_net.parameters(),learning_rate= 1e-3, grad_clip=clip) self.critic_net_optimizer = optim.Adam(parameters = self.critic_net.parameters(), learning_rate=3e-3,grad_clip=clip) if not os.path.exists('./param'): os.makedirs('./param/net_param') os.makedirs('./param/img') # 选择动作 def select_action(self, state): state = paddle.to_tensor(state,dtype="float32").unsqueeze(0) with paddle.no_grad(): action_prob = self.actor_net(state) dist = Categorical(action_prob) action = dist.sample([1]).squeeze(0) action = action.cpu().numpy()[0] return action, action_prob[:, int(action)].numpy()[0] # 评估值 def get_value(self, state): state = paddle.to_tensor(state) with paddle.no_grad(): value = self.critic_net(state) return value.numpy() def save_param(self): paddle.save(self.actor_net.state_dict(), './param/net_param/actor_net' + str(time.time())[:10] +'.param') paddle.save(self.critic_net.state_dict(), './param/net_param/critic_net' + str(time.time())[:10] +'.param') def store_transition(self, transition): self.buffer.append(transition) self.counter += 1 def update(self, i_ep): state = paddle.to_tensor([t.state for t in self.buffer], dtype="float32") action = paddle.to_tensor([t.action for t in self.buffer], dtype="int64").reshape([-1, 1]) reward = [t.reward for t in self.buffer] # update: don't need next_state old_action_prob = paddle.to_tensor([t.action_prob for t in self.buffer], dtype="float32").reshape([-1, 1]) R = 0 Gt = [] for r in reward[::-1]: R = r + gamma * R Gt.insert(0, R) Gt = paddle.to_tensor(Gt, dtype="float32") # print("The agent is updateing....") for i in range(self.ppo_update_time): for index in BatchSampler(sampler=RandomSampler(RandomDataset(len(self.buffer))), batch_size=self.batch_size, drop_last=False): if self.training_step % 1000 == 0: print('I_ep {} ,train {} times'.format(i_ep, self.training_step)) self.save_param() index = paddle.to_tensor(index) Gt_index = paddle.index_select(x=Gt, index=index).reshape([-1, 1]) # V = self.critic_net(state[index]) V = self.critic_net(paddle.index_select(state,index)) delta = Gt_index - V advantage = delta.detach() # epoch iteration, PPO core!!! action_prob = self.actor_net(paddle.index_select(state,index)) # new policy action_prob = paddle.concat([action_prob[i][int(paddle.index_select(action,index)[i])] for i in range(len(action_prob))]).reshape([-1,1]) ratio = (action_prob / paddle.index_select(old_action_prob,index)) surr1 = ratio * advantage surr2 = paddle.clip(ratio, 1 - self.clip_param, 1 + self.clip_param) * advantage # update actor network surr = paddle.concat([surr1,surr2],1) action_loss = -paddle.min(surr,1).mean() # MAX->MIN desent self.writer.add_scalar('loss/action_loss', action_loss, self.training_step) self.actor_optimizer.clear_grad() action_loss.backward() self.actor_optimizer.step() # update critic network value_loss = F.mse_loss(Gt_index, V) self.writer.add_scalar('loss/value_loss', value_loss, self.training_step) self.critic_net_optimizer.clear_grad() value_loss.backward() self.critic_net_optimizer.step() self.training_step += 1 del self.buffer[:] # clear experiencedef main(): agent = PPO() for i_epoch in range(1000): state = env.reset() if render: env.render() for t in count(): action, action_prob = agent.select_action(state) next_state, reward, done, _ = env.step(action) trans = Transition(state, action, action_prob, reward, next_state) if render: env.render() agent.store_transition(trans) state = next_state if done : if len(agent.buffer) >= agent.batch_size: agent.update(i_epoch) agent.writer.add_scalar('Steptime/steptime', t, i_epoch) # print("Number of steps to achieve the goal:{} , Steptime:{}".format(t,i_epoch)) breakif __name__ == '__main__': main() print("end")登录后复制I_ep 0 ,train 0 timesI_ep 0 ,train 1000 timesI_ep 0 ,train 2000 timesI_ep 0 ,train 3000 timesI_ep 0 ,train 4000 timesI_ep 0 ,train 5000 timesI_ep 1 ,train 6000 timesI_ep 5 ,train 7000 timesI_ep 11 ,train 8000 timesI_ep 19 ,train 9000 timesI_ep 34 ,train 10000 timesI_ep 60 ,train 11000 timesI_ep 89 ,train 12000 timesI_ep 120 ,train 13000 timesI_ep 150 ,train 14000 timesI_ep 182 ,train 15000 timesI_ep 211 ,train 16000 timesI_ep 242 ,train 17000 timesI_ep 273 ,train 18000 timesI_ep 303 ,train 19000 timesI_ep 333 ,train 20000 timesI_ep 364 ,train 21000 timesI_ep 398 ,train 22000 timesI_ep 431 ,train 23000 timesI_ep 461 ,train 24000 timesI_ep 494 ,train 25000 timesI_ep 529 ,train 26000 timesI_ep 565 ,train 27000 timesI_ep 596 ,train 28000 timesI_ep 631 ,train 29000 timesI_ep 659 ,train 30000 timesI_ep 694 ,train 31000 timesI_ep 728 ,train 32000 timesI_ep 763 ,train 33000 timesI_ep 797 ,train 34000 timesI_ep 833 ,train 35000 timesI_ep 872 ,train 36000 timesI_ep 910 ,train 37000 timesI_ep 945 ,train 38000 timesI_ep 983 ,train 39000 timesend登录后复制
训练效果演示:


相关攻略
让 AI 替你叫车:哈啰顺风车上线出行行业首个 MCP 服务 4月3日,哈啰顺风车推出一项业内瞩目的创新举措:正式上线名为MCP的新型服务。通俗来讲,这项服务将顺风车从寻找车主、智能匹配到最终下单支付的完整流程能力,封装成了一套标准化的技术接口,直接向各类大语言模型和AI智能体开放调用权限。 这一变
2026年4月新版OpenClaw QQ机器人接入全攻略:实践步骤、问题排查与深度解读 随着2026年4月OpenClaw重要更新的发布,其原生集成QQ平台支持的功能引起了大量开发者的关注。这一特性意味着用户无需借助任何第三方插件,即可将智能AI助手便捷地部署为QQ私聊或群聊机器人。本文将系统性地解
当AI眼镜学会“跑腿”:语音解锁单车,无感支付停车费 近来,智能穿戴领域的一个新动向值得关注:阿里旗下的千问AI眼镜,正式接入了蚂蚁集团的GPASS平台。这可不是一次简单的功能叠加,它意味着,诸如共享单车骑行、停车缴费这一系列高频的“AI办事”功能,开始从手机屏幕转移到了你的眼前。 简单说,借助GP
角色定位与核心任务目标 明确了基本定位后,我们直接切入核心:作为一名专业的文章优化师,我的核心职责在于,将那些带有明显AI生成特征的文本,深度重塑为拥有个人特色与行业洞见的优质内容。 换句话说,这项任务的关键在于实施一次“精准的换血手术”。你必须严格保证原文所有的事实依据、核心观点、逻辑框架,以及每
1 故障现象:OpenClaw无法联网搜索的典型报错 许多开发者在配置OpenClaw AI助手的搜索功能时,常常会遭遇一个典型故障:日常对话交互完全正常,但一旦触发需要联网查询信息的指令,界面便会立刻弹出“抱歉,我目前无法使用网络搜索功能(需要配置 API 密钥)”或“HTTP 401: Inv
热门专题
最新APP
热门推荐
苹果折叠屏手机 iPhone Fold 最新渲染图曝光:摄像头凸起优化,设计更显精致 有关苹果公司首款折叠屏 iPhone 的传闻持续受到关注。4月5日,知名爆料者 Majin Bu 在社交平台X上再度分享了一组据称是 iPhone Fold 的高清渲染图,从多角度揭示了这款备受期待设备可能的外观设
通用性首选:官府无垢队阵容深度解析 在当前版本中,若要挑选一套兼具强度与广泛适用性的阵容,以官府流派【长孙无垢】为核心的搭配方案无疑是热门之选。这套经典组合通常由长孙无垢(官府)、李一桐、李善德、李光弼,以及关羽或平安组成。其核心战斗逻辑清晰且高效:一方面,依靠长孙无垢与李光弼的技能联动,通过对目标
洛克王国全精灵隐藏进化条件完整攻略大全 在《洛克王国》丰富多彩的冒险世界中,除了常规的等级进化,众多精灵还埋藏着独特的“隐藏进化”路径。这些特殊的进化条件,往往是解锁精灵终极形态、完成图鉴收集的关键所在。与普通进化方式不同,隐藏进化需要触发特定的环境、时间、道具或任务条件,充满了探索与解密的乐趣。你
燕云十六声石震关卡怎么过?高效通关技巧与实战攻略详解 掌握核心机制:石震关卡难点全解析 石震关卡的核心挑战在于敌人配置:不仅数量密集,且拥有高额血量和攻击力。这些敌人并非随机分布,而是依据特定区域、巡逻路线及攻击逻辑进行部署。提前掌握不同敌人的攻击前摇、技能范围与仇恨机制,是制定有效战术的前提,真正
英雄联盟手游安妮符文终极指南:爆发流核心配置与实战策略 在英雄联盟手游的对局中,黑暗之女安妮以其强大的瞬间爆发与控制能力,始终是中单位置的热门选择。虽然操作看似简单易懂,但想要真正掌握这位火焰法师的精髓,打出毁天灭地的效果,一套科学高效的符文搭配是不可或缺的基石。正确的符文选择,能让她从温顺的火苗化





