【深度强化学习】DDPG+popart技巧(最详解)

news2024/12/24 14:33:10

文章目录

  • 前言
    • 遇到的困难
    • 参考
    • 最后实现情况
  • 一、popart 是什么?(论文解读)
    • 具体的理解
    • 关于mappo原代码中debiasing_term
  • 二、复刻popart
    • 主要进行了什么操作?
      • 1.art:
      • 2.pop:
      • 3.算法理解
      • 4.上述未考虑的部分(关键)
  • 三、代码实现
    • popart技巧关键代码
    • 疑问:


前言

本来是想根据论文和原代码复刻一遍MAPPO的,看完论文和代码后发现其运用到了很多收敛小技巧,这里在复刻第一个技巧popart时,遇到了很多困难,花了近两周时间才算是复刻成功。这里以RLz中的DDPG算法为基算法,加上popart技巧,来展示这个技巧如何添加到自己的其他RL算法中。

遇到的困难

1.参考1, 原论文并没提供原代码,参考2,3复刻的代码只告诉了对单个网络(Q网络或Critic网络)如何使用给出了代码。(并没有给出actor该如何更改)
2.参考4 mappo的实现巧妙利用ppo的actor的更新为近似的函数,不为-v。(后来复刻完后,发现在计算GAE时利用到v的计算)
mappo中r_mappo的179行
复刻关键点:denormalize
在这里插入图片描述
3.参考5,虽然readme上写是说在DDPG上实现了popart,可是下载后,进行跑了一遍Pendulum-v1的环境,一点没收敛,说明并未完成。
在这里插入图片描述
4.参考6 虽然是实现了,但是是tensorflow版本,不是pytorch版本。

参考

1.popart原论文
2.popart原论文的翻译和解读
3.popart原论文的实现
4.mappo原代码中关于popart的实现
5.github上未完成的DDPG_popart的实现
6.openai baselines中关于ddpg加入popart的tensorflow实现


最后实现情况

修改多次后(其他眼色),最后达到蓝色的收敛效果,其中黑色为原ddpg不加popart的效果。后对DDPG进行了修改,在 代码实现 中展示
环境为Pendulum-v1。
在这里插入图片描述

一、popart 是什么?(论文解读)

原论文写道:我们提出了一种方法来自适应地归一化学习更新中使用的目标。如果保证这些目标被规范化,则找到合适的超参数要容易得多。所提出的技术并不特定于 DQN,更普遍地适用于监督学习和强化学习。
即:Preserving Outputs Precisely, while Adaptively Rescaling Targets,自适应归一化目标的同时,保证精确的输出。

此算法是为了解决奖励跨度大的问题:例:吃豆人游戏中吃一个鬼魂有1600的奖励,而其他的奖励则很小。(之前有提出的办法是将奖励裁剪到(-1,1))

实验结果:DoubleDQN_popart 在Atari 57 games 中 使用DDQN_popart 在32个游戏中表现比DDQN的效果要好,(有一个是相等成绩),结果论文提及如下:
在这里插入图片描述

具体的理解

在参考2,3中已经解释的很详细了,可参考。

关于mappo原代码中debiasing_term

mappo代码:popart.py#L27

在原论文popart中的此处有提及,这里debiasing_term 的定义和下文的z_t的定义基本一致。(疑问:但是以下论文中并未提及该如何在popart中消除这个Adam初始化的影响,可能这里的mappo代码这里是解决了这个问题)
在这里插入图片描述

二、复刻popart

主要参考2,3的解释和代码实现,讲的十分好。

主要理解几个关键点:
论文目的:为了解决现有reward_clip解决方法并不能有效解决reward跨度大的问题
所以这里的技巧主要是与reward相关的操作。

主要进行了什么操作?

解决reward跨度大:使用归一化目标函数。

1.art:

动态归一化目标函数,在这里即Q_target,这个操作有点类似于在PPO中加入对reward进行nomalization技巧的操作。(但是两者的操作的地方不一致,前者是在更新中直接对target操作,后者是在对环境中step后的reward进行操作)两者trick的目标有些类似。(这样说比较好理解算法。)
有点类似于滑动平均的取mean和std


由于上述的归一化target,有可能出现上一批的taget范围为[-1,1]归一化,而后一批taget的范围为[-10,10]归一化,那么原来给出的归一化范围可能会受到影响,为了解决这个问题,就干脆在原有Q网络的模型上后面再加一层要训练的网络,使这一层的weight和bias也一起更新,使得这个模型输出的范围和target的范围在差不多的范围内。(如下:见参考2)
在这里插入图片描述

2.pop:

保证精确的输出,即为了保证原函数拟合的结果,在原有的模型上再加一层线性层,然后使用合适的更新方法,就可以保证和原来一样的拟合结果(即加入此线性层更新后,原函数(无此线性层的函数)更新后的weight和bias不变)。
合适的更新方式为如下:
在这里插入图片描述
在参考3中证明了这点。(好像不是,是证明了提议3,这里是提议1)


3.算法理解

popart实现: Preserving Outputs Precisely, while Adaptively Rescaling Targets
1.初始化W(权重) = I , b(偏差) = 0, sigma(标准差) = 1 ,u(均值,代码写作mu) = 0
2.Use Y to compute new scale sigma_new and new shift mu_new
3.W = W * sigma / sigma_new b = (sigma * b + mu - mu_new) / sigma_new ; sigma = sigma_new mu = mu_new
4.得到拟合函数Q(s)的值
5.loss = W(Q(s))+b - (target_Q-mean)/std
5.用梯度下降更新Q的参数,再更新W和b的参数

在这里插入图片描述
根据上述1,2 两点,不难看出这里的计算归一化损失两者都是标准化过后的,W,b是标准化线性层,减均值除标准差也是标准化过程。

至于这句话,意思应该就是允许h函数可更新。
在这里插入图片描述


4.上述未考虑的部分(关键)

考虑到的部分:
1.用art来滑动平均取mean和std
2.原来的critic的函数不变,再新加入一层新的popart线性层当作输出层,此输出层的目的为标准化Q值输出。
3.更新时,先更新critic函数参数,再更新最后一层的线性层。
4.若是ppo,则在计算advantange时,若是以popart线性层当作输出,则应该反归一化其值->原始值来计算。
在这里插入图片描述
未考虑到的部分:
1.使用actor时,原本不加popart时,loss为-Q(以ddpg为例),加入后,若是以popart线性层当作输出,则应该反归一化其值->原始值,来计算。而不是单单用popart线性层上一层的Q函数来拟合。
(单单用popart线性层上一层的Q函数来当loss的-Q中的Q,效果如下)
在这里插入图片描述

原因:在上述计算loss时是用的标准化后的值,并且之后更新,其原始Q函数的规模(或范围)已经发生改变,若此时输出Q的值,就不正确了,而正确的Q(加入popart前的q)的计算方式应该为反归一化其现在线性层输出值。

2.ddpg使用了target函数来减小Q的过高估计,这里参考3,参考4,并没有此值,需要加入一个target的critic函数输出加入到popart线性层的类。

三、代码实现

为了展示popart对ddpg的核心部分修改了哪些,这里使用简单版本的DDPG_simple.py未基础,在此算法上加入popart技术来展示。

加入popart算法的代码在:DDPG_simple_with_tricks.py(欢迎star)

popart技巧关键代码

class UpperLayer(nn.Module):
    def __init__(self, H, n_out):
        super(UpperLayer, self).__init__()
        self.output_linear = nn.Linear(H, n_out)
        '''1.初始化W(权重) = I , b(偏差) = 0, sigma(标准差) = 1 ,u(均值,代码写作mu) = 0'''
        nn.init.ones_(self.output_linear.weight) # W = I
        nn.init.zeros_(self.output_linear.bias) # b = 0

    def forward(self, x):
        return self.output_linear(x)  

class PopArt:
    def __init__(self, mode, LowerLayers, LowerLayers_target,H, n_out, critic_lr):
        super(PopArt, self).__init__()
        self.mode = mode.upper() # 大写
        assert self.mode in ['ART', 'POPART'], "Please select mode from  'Art' or 'PopArt'."
        self.lower_layers = LowerLayers
        self.lower_layers_target = LowerLayers_target
        self.upper_layer  = UpperLayer(H, n_out).to(device)
        self.sigma = torch.tensor(1., dtype=torch.float)  # consider scalar first
        self.sigma_new = None
        self.mu = torch.tensor(0., dtype=torch.float)
        self.mu_new = None
        self.nu = self.sigma**2 + self.mu**2 # second-order moment
        self.beta = 10. ** (-0.5) 
        self.lr = 1e-3  
        self.loss_func = torch.nn.MSELoss()
        self.loss = None

        self.opt_upper = torch.optim.Adam(self.upper_layer.parameters(), lr = self.lr)
        self.opt_lower = torch.optim.Adam(self.lower_layers.parameters(), lr = critic_lr)


    def art(self, y):
        '''2.Use Y to compute new scale sigma_new and new shift mu_new 相当于滑动式更新均值和标准差'''
        self.mu_new = (1. - self.beta) * self.mu + self.beta * y.mean()
        self.nu = (1. - self.beta) * self.nu + self.beta * (y**2).mean()
        self.sigma_new = torch.sqrt(self.nu - self.mu_new**2)
        

    def pop(self):
        '''3.W = W * sigma_new / sigma b = (sigma * b + mu - mu_new) / sigma_new ; sigma = sigma_new , mu = mu_new '''
        relative_sigma = (self.sigma / self.sigma_new)
        self.upper_layer.output_linear.weight.data.mul_(relative_sigma)
        self.upper_layer.output_linear.bias.data.mul_(relative_sigma).add_((self.mu-self.mu_new)/self.sigma_new)

    def update_stats(self):
        # update statistics
        if self.sigma_new is not None:
            self.sigma = self.sigma_new
        if self.mu_new is not None:
            self.mu = self.mu_new

    def normalize(self, y):
        return (y - self.mu) / self.sigma

    def denormalize(self, y):
        return self.sigma * y + self.mu

    def backward(self):
        '''4.更新拟合函数theta
        5.用梯度下降更新W,b的参数
        '''
        self.opt_lower.zero_grad()
        self.opt_upper.zero_grad()
        self.loss.backward()

    def step(self):
        torch.nn.utils.clip_grad_norm_(self.lower_layers.parameters(), 0.5)
        self.opt_lower.step()
        torch.nn.utils.clip_grad_norm_(self.upper_layer.parameters(), 0.5)
        self.opt_upper.step()
        


    def forward(self, o,a, y):
        if self.mode in ['POPART', 'ART']:
            self.art(y)
        if self.mode in ['POPART']:
            self.pop()
        self.update_stats()
        y_pred = self.upper_layer(self.lower_layers(o,a))
        self.loss = self.loss_func(y_pred, self.normalize(y))
        self.backward()
        self.step()

        return self.loss , self.lower_layers 

    def output(self, x, u):
        return self.upper_layer(self.lower_layers(x, u))
    
    def output_target(self, x, u):
        return self.upper_layer(self.lower_layers_target(x, u))

其中self.nu 论文中未明确(论文中的符号为vt) 但是说明 vt - µ^2 is positive
于是这里选择了参考3中的代码self.sigma**2 + self.mu**2,而mappo中则初始化这个值为0。

在Pendulum-v1环境下效果如下:两者参数一致(其中 sigma=1 batch_size=265)
黑色为原DDPG_simple,而蓝色为初始nu为0,黄色初始化为self.sigma2 + self.mu2,可见两者差别并不大。
在这里插入图片描述
换成MountainCarContinuous-v0下测试:黄色为原ddpg_simple,紫色为加入popart后。
(参数均一致,其中sigma=1,batch_size=64)
效果如下:发现效果竟不如原来的算法。

self.nu = self.sigma**2 + self.mu**2 # second-order moment 二阶矩 用于计算方差
self.beta = 10. ** (-0.5) 
self.lr = 1e-3 #10. ** (-2.5)  

在这里插入图片描述
将上述的self.opt_upper = torch.optim.Adam(self.upper_layer.parameters(), lr = self.lr)的最后一层学习率改成和论文中一样的10**(-2.5)

self.nu = self.sigma**2 + self.mu**2 # second-order moment 二阶矩 用于计算方差
self.beta = 10. ** (-0.5) 
self.lr = 10. ** (-2.5)  

效果如下:为下图的橙色
在这里插入图片描述

    将lr改回1e-3,beta改成0.99999 
self.nu = self.sigma**2 + self.mu**2 # second-order moment 二阶矩 用于计算方差
self.beta = 0.99999#10. ** (-0.5) 
self.lr = 1e-3 #10. ** (-2.5)  

效果如下,为下图的的粉色:
在这里插入图片描述

self.nu = 0#self.sigma**2 + self.mu**2 # second-order moment 二阶矩 用于计算方差
self.beta = 0.99999#10. ** (-0.5) 
self.lr = 1e-3 #10. ** (-2.5)  

效果为如下的蓝色

在这里插入图片描述

疑问:

这里的MountainCarContinuous-v0环境的奖励如下:
在这里插入图片描述
到达目的地后得到一个大的奖励值,和前文论文中说要解决的问题是同种问题(奖励的差值大),可是效果最终测试还是不如原版。

论文中加入此算法的展示效果为在一半的环境下有增益效果。

原因可能是这里使用了Adam,并没有使用SGD?
(使用Adam可能需要使用和参考4一样的初始化方法。)

希望能在评论区看到解答。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2194231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

传感器模块编程实践(三)舵机+超声波模块融合DIY智能垃圾桶模型

文章目录 一.概要二.实验模型原理1.硬件连接原理框图2.控制原理 三.实验模型控制流程四.智能感应垃圾桶模型程序五.实验效果视频六.小结 一.概要 随着科技的飞速发展和环保意识的日益增强,智能垃圾桶成为了城市生活的新宠,智能垃圾桶人们无需接触垃圾桶…

【Linux系统编程】第二十八弹---构建基础文件操作库与理解标准错误流(stderr)在C与C++中的应用

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、封装简单的库 1.1、定义文件结构 1.2、打开文件 1.3、刷新缓冲区 1.4、写文件 1.5、关闭文件 1.6、各文件代码 2、s…

医院管理新趋势:Spring Boot技术引领

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式,是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示: 图4-1系统工作原理…

Github优质项目推荐 - 第五期

文章目录 Github优质项目推荐 - 第五期一、【localsend】,47.5k stars - 附近设备文件互传二、【Pake】,29.9k stars - 网页变成桌面应用三、【laravel-crm】,10.7k stars - CRM 解决方案四、【localstack】,55.7k stars - 本地 A…

【C++】二叉搜索树+变身 = 红黑树

🚀个人主页:小羊 🚀所属专栏:C 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 前言一、定义与性质二、红黑树节点的定义三、新增节点插入四、验证红黑树五、AVL树和红黑树比较 前言 本文仅适合了…

【算法】DFS系列之 FloodFill 算法

【ps】本篇有 7 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1)图像渲染 .1- 题目解析 .2- 代码编写 2)岛屿数量 .1- 题目解析 .2- 代码编写 3)岛屿的最大面积 .1- 题目解析 .2- 代码编写 4)被围绕的区域 .1-…

allegro精确画圆形边框

1.显示原点位置: 2.class-subclass依次选择Board Geometry-Outline 3.菜单ADD---Circle,右侧option,依次设置如下,如图可设置为圆心(0,0),半径为42mm的边框,不要忘了右键Done,完成绘…

海量数据去重的hash,布隆过滤器Bloom Filter,一致性hash

文章目录 一、⭐hash函数如何选取hash函数?冲突处理 二、⭐海量数据去重布隆过滤器**布隆过滤器介绍**布隆过滤如何应用 三、⭐一致性哈希基本原理: 四、⭐一道面试题 一、⭐hash函数 hash函数的作⽤:避免插⼊的时候字符串的⽐较&#xff0c…

怎么把店铺地址弄上高德地图?

随着互联网的高度普及,如今的营销方式也发生了巨大的变化。以前的商家要想提高店铺销售额,只需要在线下举办促销活动就可以吸引周边的顾客上门消费,但现在,商家如果想获取更多客流量,就必须在网上进行曝光,…

性能测试学习6:jmeter安装与基本配置/元件

一.JDK安装 官网:https://www.oracle.com/ 二.Jmeter安装 官网:http://jmeter.apache.org/download_jmeter.cgi 下载zip包,zip后缀那个才是Windows系统的jmeter 三.Jmeter工作目录介绍 四.Jmeter功能 1)修改默认配置-汉化 2&am…

市场价值99的likeyun付费进群插件

市场价值99的likeyun付费进群插件 这款是引流宝的付费插件 要搭配引流宝使用 多模版 最近发现好多人再倒卖大佬的付费插件 为了减少这种情况,在征得大佬的同意下决定免费发出来 此版本是付费进群最新1.0版本 不提供后续更新服务,有能力的还是建议支持…

每日OJ题_牛客_乒乓球筐_哈希_C++_Java

目录 牛客_乒乓球筐_哈希 题目解析 C代码 Java代码 牛客_乒乓球筐_哈希 乒乓球筐__牛客网 (nowcoder.com) 描述: nowcoder有两盒(A、B)乒乓球,有红双喜的、有亚力亚的……现在他需要判别A盒是否包含了B盒中所有的种类&#…

深入理解 Spring Cache 的工作原理及集成其它第三方缓存

目录 1、Spring Cache 简介2、常用注解2.1、常用注解介绍2.2、常用注解的主要参数 3、缓存注解上 SPEL 表达式可使用的元数据4、入门案例4.1、引入依赖4.2、开启缓存功能4.3、使用缓存4.3.1、新建一个 UserServiceImpl4.3.2、新建一个 UserController 5、工作原理5.1、缓存自动…

Java建筑行业智能化管理系统源码,PC端、手机端、大屏端源码,智慧工地管理平台源码,智慧建设平台 智慧住建平台

智慧工地平台全套源码合作 智慧工地是指运用现代信息技术,如物联网(IoT)、大数据、人工智能(AI)、云计算、移动互联网等,对传统建筑工地进行智能化改造和管理的新型工地。它通过高度集成的系统和设备&#…

GeoCue与Xer Technologies合作推动无人机测绘技术革新

GeoCue与Xer Technologies合作推动无人机测绘技术革新 近期,LiDAR测绘硬件和软件开发商GeoCue与瑞士长航时混合动力无人机制造商Xer Technologies AG携手合作,成功将GeoCue的TrueView 720 LiDAR和图像传感器集成至Xer X8无人机平台。这一里程碑式的合作不仅标志着无人机测绘技…

Python手绘五星红旗,庆75周年

环境 pip install matplotlib pip install numpy 代码 import matplotlib.pyplot as plt import numpy as np# 中国国旗的标准尺寸比例是 3:2 width, height 300, 200 # 这里可以调整为任何满足3:2比例的尺寸# 创建一个新图形 fig, ax plt.subplots(figsize(width/100, h…

js 字符串下划线转驼峰 驼峰转下划线

一、下划线转驼峰 1、效果 2、示例 function underscoreToCamelCase(str) {return str.replace(/(_\w)/g, function(match) {return match[1].toUpperCase();}).replace(/^_/, ); }// 示例 let snakeCaseStr "hello_world"; let camelCaseStr underscoreToCamelC…

【Diffusion分割】Cold SegDiffusion:医学图像分割的扩散模型

Cold SegDiffusion: A novel diffusion model for medical image segmentation 摘要: 随着深度学习的发展,扩散模型在医学图像分割任务中表现出了卓越的性能。然而,传统的分割扩散模型通常采用随机高斯噪声生成分割掩膜,导致分割…

【华为HCIP实战课程七】OSPF邻居关系排错MTU问题,网络工程师

一、MTU MUT默认1500,最大传输单元,一致性检测 [R3-GigabitEthernet0/0/1]mtu 1503//更改R3的MTU为1503 查看R3和SW1之间的OSPF邻居关系正常: 默认华为设备没有开启MTU一致性检测! [R3-GigabitEthernet0/0/1]ospf mtu-enable //手动开启MTU检测 [SW1-Vlanif30]ospf mtu…