深度强化学习调参Tricks合集
刚入门深度强化学习的人会问「DDPG算法参数如何调节?( https://www.zhihu.com/question/309162916 ) (或其他算法如何调参)」「DRL算法那么多,我该选哪个算法?」为了帮助更多的人,我把这些总结发到网上。
写在前面
-
深度强化学习 Deep Reinforcement Learning 简称为DRL
-
运行DRL算法代码(实际使用+调整参数),需要更多DL基础
-
阅读DRL算法论文(理解原理+改进算法),需要更多RL基础
深度强化学习算法能训练能智能体: 机械臂取物、飞行器避障、控制交通灯、机器人移动、交易股票、训练基站波束成形选择合适的权重超越传统算法。实际使用时,问题却很多:
-
一开始会问:算法那么多,要选哪个?训练环境怎么写?
-
选完后会问: 算法怎么调参? 收益函数 reward function 要怎么改?(看的人多,有空再写,太长了)
第一个问题 请看姊妹篇 ↓ 你可以根据目录引导,只看与你任务相关的算法。
曾伊言:如何选择深度强化学习算法?MuZero/SAC/PPO/TD3/DDPG/DQN/等
https://zhuanlan.zhihu.com/p/342919579
后一个问题,就是当前这篇文章 深度强化学习调参技巧:以D3QN、TD3、PPO、SAC算法为例,我没有看到满意的文章,所以才自己写。第一版 2021-1-24,第二版 2021-01-28。
目录
-
训练环境怎么写?循序渐进,三个阶段
-
算法怎么调参?
————————————
-
在off-policy算法中常见的超参数
-
在on-policy算法中常见的超参数
-
与离散动作探索有关的超参数
-
与连续动作探索有关的超参数
-
探索衰减、退火
————————————
-
D3QN特有的超参数
-
TD3特有的超参数
-
PPO+GAE特有的超参数
-
SAC特有的超参数
————————————
-
为什么我的算法越训练越差?
-
备用层,用于回复评论
-
对知乎上“DRL超参数调优”的一些观点进行点评
训练环境怎么写?
强化学习里的 env.reset() env.step() 就是训练环境。其编写流程如下:
初始阶段:
-
不要一步到位,先写一个简化版的训练环境。把任务难度降到最低,确保一定能正常训练。
-
记下这个正常训练的智能体的分数, 与随机动作、传统算法得到的分数做比较 。DRL算法的分数应该明显高于随机动作(随机执行动作)。DRL算法不应该低于传统算法的分数。如果没有传统算法,那么也需要自己写一个局部最优的算法(就算只比随机动作的算法高一点点都可以,有能力的情况下,要尽量写好)。
-
评估策略的性能: 大部分情况下,可以直接是对Reward Function 给出的reward 进行求和得到的每轮收益episode return作为策略评分。有时候可以需要直接拿策略的实际分数作为评分(移动速度/股票收益/目标完成情况 等)。
-
需要保证这个简化版的代码:高效、简洁、可拓展
改进阶段:
-
让任务难度逐步提高,对训练环境env 进行缓慢的修改,时刻保存旧版本的代码
-
同步微调 Reward Function,可以直接代入自己的人类视角,为某些行为添加正负奖励。注意奖励的平衡(有正有负)。注意不要为Reward Function 添加太多额外规则,时常回过头取消一些规则,避免过度矫正。
-
同步微调 DRL算法,只建议微调超参数,但不建议对算法核心进行修改。因为任务变困难了,所以需要调整超参数让训练变快。同时摸清楚在这个训练环境下,算法对哪几个超参数是敏感的。有时候为了节省时间,甚至可以为 off-policy 算法保存一些典型的 trajectory(不建议在最终验证阶段使用)。
-
每一次修改,都需要跑一下记录不同方法的分数,确保: 随机动作 < 传统方法 < DRL算法 。这样才能及时发现代码逻辑上的错误。要极力避免代码中出现复数个的错误,因为极难排查。
收尾阶段:
-
尝试慢慢删掉Reward Function 中一些比较复杂的东西,删不掉就算了。
-
选择高低两组超参数再跑一次,确认没有优化空间。
下面是我在网上搜索到的相关文章,尽管它们标题在说“强化学习调参”技巧,但文不对题,他们讨论的都是:如何写一个训练环境。写得不错,真的做过项目的人才写得出来。我不反对去看这两篇。
深度强化学习有哪些调参技巧? – 何之源 – 2020-02( https://www.zhihu.com/question/365294045/answer/995032858 ) 我认同他的观点,这是成功做过 custom env 的人该持有的观点:编写自定义的训练环境,初始阶段:降低任务难度, 先写出一个简单且没有bug的环境 ,保证代码运行效率,第二阶段:确定一个指标(分数)后, 迭代修改训练环境以及强化学习算法 。在这个过程中,可以用上深度学习的trick。
银河中的太阳系:强化学习RL调参技巧 2020-11( https://zhuanlan.zhihu.com/p/296700661 ) 这篇的观点与上面何之源的相近,写得对(3个赞同?太少了)
算法怎么调参?
知乎有人提问:DDPG算法参数如何调节?(或其他算法如何调参),我下面的内容也是对这个问题的回答:
这个问题非常不好。很多DRL库会把算法也当成一个超参数,那么我为DDPG调参的时候,第一步就是换掉这个老旧的DDPG. 2016算法。然后为自己的任务选择一个合适的算法。请看 曾伊言:如何选择深度强化学习算法?MuZero/SAC/PPO/TD3/DDPG/DQN/等(已完成) ,里面解释了: 为何DDPG DQN算法只适合入门而不适合使用。
无论是什么任务,你选择的算法必定与DQN变体、TD3、PPO、SAC这四种算法有关,它们占据不同的生态位,请根据实际任务需要去选择他们,在强化学习的子领域(多智能体、分层强化学习、逆向强化学习也会以它们为基础开发新的算法):
-
离散动作空间推荐: Dueling DoubleQN(D3QN)
-
连续动作空间推荐:擅长调参就用 TD3 ,不擅长调参就用 PPO或SAC ,如果训练环境 Reward function 都是初学者写的,那就用PPO
所以下面我以这四种算法为例,讲这四种算法的调参技巧。我当然可以直接跟你说遇到什么state,正确的action 是什么,但是 我希望多写一些推理过程 。
我小时候,我身边的人说“不能在昏暗环境下看书,对眼睛不好”。后来我经常在昏暗的环境下看书,而视力却一直是顶级的——我开始怀疑这句话的正确性。经过思考,这句话其实是“在昏暗环境下看书,普通人类需要更靠近纸张以获取更大进光量,眼睛长时间看近处的东西,最终导致疲劳,长此以往导致视力下降”。不开灯的昏暗情况下,如果夜视力正常,且眼睛与书本保持合适距离,别看太久,那么完全可以正常看书。(不接受任何看不到视力表最下一行的人的反驳)
在DRL中也一样,我如果直接告诉你某种情况下 折扣因子 gamma 就该选 0.95 0.99 0.999,而不说推理过程,那么不懂的人照样不懂。我也希望各位 抱着怀疑眼光来看我接下来的所有推理 。
DRL超参数的默认值无法给出,但是我可以给出不同任务下的超参数推荐组合,见「强化学习库:小雅 AgentRun.py」( https://github.com/Yonv1943/ElegantRL/blob/master/AgentRun.py )文件的 类 class Arguments 以及 函数 train__demo( )。
写在前面:我极力反对过度调参:在实验室中人肉搜索找出一组能刷出虚高分数的超参数并没有用。请不要沉迷调参。
在off-policy算法中常见的超参数
-
网络宽度:network dimension number。DRL 全连接层的宽度(特征数量)
-
网络层数:network layer number。一个输入张量到输出需要乘上w的次数
-
随机失活:dropout
-
批归一化:batch normalization
-
记忆容量:经验回放缓存 experimence replay buffer 的最大容量 max capacity
-
批次大小:batch size。使用优化器更新时,每次更新使用的数据数量
-
更新次数:update times。使用梯度下降更新网络的次数
-
折扣因子:discount factor、gamma
【网络宽度、网络层数】越复杂的函数就需要越大容量的神经网络去拟合。在需要训练1e6步的任务中,我一般选择 宽度128、256,层数小于8的网络(请注意,乘以一个w算一层,一层LSTM等于2层,详见曾伊言:LSTM入门例子( https://zhuanlan.zhihu.com/p/94757947 ))。使用ResNet等结构会有很小的提升。一般选择一个略微冗余的网络容量即可,把调整超参数的精力用在这上面不划算,我建议这些超参数都 粗略地选择2的N次方 ,因为:
-
防止过度调参,超参数选择x+1 与 x-1并没有什么区别,但是 x与2x一定会有显著区别
-
2的N次方大小的数据,刚好能完整地放进CPU或GPU的硬件中进行计算,如Tensor Core
过大、过深的神经网络不适合DRL,因为:
-
深度学习可以在整个训练结束后再使用训练好的模型。而强化学习需要在几秒钟的训练后马上使用刚训好的模型。这导致DRL只能用比较浅的网络来保证 快速拟合 (10层以下)
-
并且强化学习的训练数据不如有监督学习那么稳定,无法划分出训练集测试集去避免过拟合,因此DRL也不能用太宽的网络(超过1024),避免参数过度冗余导致 过拟合 。
【dropout、批归一化】她们在DL中得到广泛地使用,可惜不适合DRL。如果非要用,那么也要选择非常小的 dropout rate(0~0.2),而且要注意在使用的时候关掉dropout。我不用dropout。
-
好处:在数据不足的情况下缓解过拟合;像Noisy DQN那样去促进策略网络探索
-
坏处:影响DRL快速拟合的能力;略微增加训练时间
【批归一化】经过大量实验,DRL绝对不能直接使用批归一化,如果非要用,那么就要修改Batch Normalization的动量项超参数。详见 曾伊言:强化学习需要批归一化(Batch Norm)吗?( https://zhuanlan.zhihu.com/p/210761985 )
【记忆容量】经验回放缓存 experimence replay buffer 的最大容量 max capacity,如果超过容量限制,它就会删掉最早的记忆。在简单的任务中(训练步数小于1e6),对于探索能力强的DRL算法,通常在缓存被放满前就训练到收敛了, 不需要删除任何记忆 。然而,过大的记忆也会拖慢训练速度,我一般会先从默认值 2 ** 17 ~ 2 ** 20 开始尝试,如果环境的随机因素大,我会同步增加记忆容量 与 batch size、网络更新次数,直到逼近服务器的内存、显存上限(放在显存训练更快)
当然,我不反对你探索新的记忆删除机制,我试过将“删除最早的数据”改成 “越早的数据”或“和其他记忆重复的数据”有更大概率被删除 。只能消耗一点CPU资源,在特定任务上得到比较好的效果,而且实现很复杂。如果你们探索出更好的方法记得告诉学界,先谢过了。
【批次大小、更新次数】一般我会选择与网络宽度相同、或略大的批次大小batch size。我一般从128、256 开始尝试这些2的N次方。在off-policy中,每往Replay 更新几个数据,就对应地更新几次网络,这样做简单,但效果一般。(深度学习里)更优秀的更新方法是: 根据Replay中数据数量,成比例地修改更新次数 。Don’t Decay the Learning Rate, Increase the Batch Size. ICLR. 2018( https://arxiv.org/abs/1711.00489 ) 。,经过验证,DRL也适用。我根据Replay中数据个数来调整 batch size 和 update times:
replay_max = 'the maximum capacity of replay buffer'
replay_len = len(ReplayBuffer)
k = 1 + replay_len / replay_max
batch_size = int(k * basic_batch_size)
update_times = int(k * basic_update_times)
for _ in range(update_times):
data = ReplayBuffer.random_sample(batch_size)
...
【折扣因子】discount factor(或者叫 discount-rate parameter),gamma 。这个值很容易确定,请回答“你希望你的智能体每做出一步,至少需要考虑接下来多少步的reward?”如果是t 步:
gamma ** t = 0.1 # 0.1 对于当前这一步来说,t步后的reward的权重
gamma = 0.1 ** (1/t)
0.93 ~= 0.1 ** (1/32)
0.98 ~= 0.1 ** (1/128)
0.99 ~= 0.1 ** (1/256)
0.995 ~= 0.1 ** (1/512)
可以看到 0.93, 0.98, 0.99, 0.995 的gamma值
分别对应 32, 128, 256, 512 的步数
gamma绝对不能选择1.0。尽管在入门DRl任务选择gamma=0.1 甚至能训练得更快,但是gamma等于1 有“让Q值无限大”的风险。gamma值一般选择0.99即可,在某些任务上需要调整。
详见《Reinforcement Learning An Introduction – Richard S. Sutton》的 Chapter 12 Eligibility Traces。尽管下图讲的是 lambda,但它和 gamma 起作用的原理差不多。若还是不懂,就等我有时间再补充。这本书的pdf版可以在斯坦福网站下载。
https://web.stanford.edu/class/psych209/Readings/SuttonBartoIPRLBook2nd Ed.pdf

图片来自 einforcement Learning An Introduction》 Chapter 12 Eligibility Traces
在on-policy算法中常见的超参数
同策略(A3C、PPO、PPO+GAE)与异策略(DQN、DDPG、TD3、SAC)的主要差异是:
-
异策略off-policy:ReplayBuffer内可以存放“由 不同策略 ”收集得到的数据用于更新网络
-
同策略on-policy:ReplayBuffer内只能存放“由 相同策略 ”收集得到的数据用于更新网络
因此以下超参数有不同的选择方法:
-
记忆容量:经验回放缓存 experimence replay buffer 的最大容量 max capacity
-
批次大小:batch size。使用优化器更新时,每次更新使用的数据数量
-
更新次数:update times。使用梯度下降更新网络的次数
【记忆容量】on-policy 算法每轮更新后都需要删除“用过的数据”,所以on-policy的记忆容量应该大于等于【单轮更新的采样步数】,随机因素更多的任务需要更大的单层采样步数才能获得更多的 轨迹 trajectory,才能有足够的数据去表达环境与策略的互动关系。详见下面PPO算法的【单轮更新的采样步数】
【批次大小】on-policy 算法比off-policy更像深度学习,它可以采用稍大一点的学习率(2e-4)。因为【单轮更新的采样步数】更大,所以它也需要搭配更大的batch size(2**9 ~ 2**12)。如果内存显存足够,我建议使用更大的batch size,我发现一些很难调的任务,在很大的batch size(2 ** 14) 面前更容易获得单调上升的学习曲线(训练慢但是及其稳定,多GPU分布式)。请自行取舍。
【更新次数】一般我们不直接设置更新次数,而是通过【单轮更新的采样步数】、【批次大小】和【数据重用次数】一同算出【更新次数】,详见下面PPO算法的【数据重用次数】
update_times = steps * reuse_times / batch_size
与离散动作探索有关的超参数
离散动作的探索就是要如何选择Q值最大以外的动作。
【epslion-Greedy 策略】按照贪婪策略探索离散动作:每次都从 已经被强化学习算法加强过的Q值中,选择Q值最大的那个动作去执行。为了探索,有很小的概率 epslion 随机地执行某个动作。Q Network(Critic) 的更新需要采集所有离散动作的样本,epslion-Greedy保证了Replay可以收集到足够丰富的训练数据。超参数 执行随机动作的概率 epslion我一般选择 0.1 ,然后根据任务需要什么程度的探索强度再修改。如果 离散动作很多 ,我会尽可能选择大一点的 epslion。
【Noisy DQN 的探索策略】在网络中间添加噪声,得到带噪声的Q值,接着沿用贪婪策略,智能体会选择Q值最高的动作去执行。这种模式比起 epslion-Greedy的好处是:能保证多探索Q值高的动作,增加探索效率。坏处是Q值过低的动作可能很难被覆盖到(所幸Q Network 输出的Q值一般比较接近)。超参数就是噪声大小,一般我从方差0.2开始调起,然后print出不同动作 Q值们的方差 辅助我调参。
【输出离散动作的执行概率】在MuZero 算法中,让预测器(大号的 Q Network)额外输出动作的执行概率(使用softmax),详见 曾伊言:如何选择深度强化学习算法?MuZero/SAC/PPO/TD3/DDPG/DQN/等(已完成) (搜索 MuZero),这种增加计算量的方法效果会更好。SAC for Discrete Action Space( https://arxiv.org/abs/1910.07207 )也有类似的思想。这种方法不需要调参,智能体会自己决定自己的探索方式。
与连续动作探索有关的超参数
连续动作的探索几乎都通过加噪声完成。区别在于噪声的大小要如何决定。当你的任务困难到一加噪声就影响智能体的探索,那么加噪声也可以与epsilon-Greedy 同时使用:你可以选择合适加噪声,何时不加。
【OU-noise】普通的噪声是独立的高斯分布,有小概率会一直出现相同符号的噪声。OU-noise产出的噪声更为平缓,适用于惯性系统的任务。详见 强化学习中Ornstein-Uhlenbeck噪声是鸡肋吗?( https://zhuanlan.zhihu.com/p/96720878 ) 我个人认为是鸡肋,DDPG作为一个早期的不成熟算法,探索能力弱,因此很需要OU-noise。然而,TD3算法明确在论文中写明“TD3不用OU-noise”,PPO SAC算法也没用它,我也不用。 它给DRL引入了更多超参数 ,导致它经常能在实验室内调出自欺欺人的虚胖成绩,不适合实际使用
【TD3的探索方式】动作过激活函数tanh后,加上clip过的噪声,然后再clip一次,详见下方的【TD3的探索方式】
【SAC的探索方式】动作加上噪声后,经过激活函数,详见下方【SAC的探索方式】
请注意,当你使用任何连续动作空间的DRL算法,请让它输出的 动作空间规范为(-1,+1) ,不需要为了物理含义而选择其他区间,因为任何区间都能通过线性变换获得(如0~1, -2~+2等)。选择不规范的空间百害而无一利:计算策略熵困难,神经网络拟合起来不舒服,clip不方便代,码实现不方便,不方便print出某个值与其他算法比较、等等。
探索衰减、退火
与探索有关的超参数都可以使用 衰减、退火。在离散动作中,探索衰减表现为逐步减小执行随机动作的概率 在连续动作中,探索衰减表现为逐步减小探索噪声的方差,退火同理。
-
衰减就是单调地减小(固定、不固定,比例、定值 ),直至某个限度后停止。在比较简单的环境里,算法可以在前期加强探索,后期减少探索强度,例如在训练前期使用一个比较大的 epslion,多执行随机动作,收集训练数据;训练中期让epslion逐步减小,可以线性减小 或者 乘以衰减系数,完成平缓过渡;训练后期干脆让epslion变为一个极小的数,以方便收敛。我建议适度地使用探索衰减,能不用尽量不用。(我不建议0,这会降低RelapyBuffer中数据的多样性,加大过拟合风险)
-
退火就是减小后,(缓慢、突然)增大,周期循环。比衰减拥有更多的超参数。我不推荐使用,除非万不得已。
探索衰减一定会有很好的效果,但这种“效果好”建立在人肉搜索出衰减超参数的基础之上。成熟的DRL算法会自己去调整自己的探索强度。比较两者的 调参总时间 ,依然是使用成熟的DRL算法耗时更短。
D3QN特有的超参数
D3QN即 Dueling DoubleDQN。先按上面的提示,选择一个略微冗余的Q Netwrok,详见上面的【在off-policy算法中常见的超参数】,然后从默认值0.1 开始调整 epsilon-Greedy的探索概率 epsilon,详见上面的【epslion-Greedy 策略】
TD3特有的超参数
-
探索噪声方差 exploration noise std
-
策略噪声方差 policy noise std
-
延迟更新频率 delay update frequency
同样作为确定策略梯度算法,不存在某个任务DDPG会表现得比TD3好。除了教学,没有理由使用DDPG。如果你擅长调参,那么可以可以考虑TD3算法。如果你的算法的最优策略通常是边界值,那么你首选的算法就是TD3(例如:不需要考虑能耗的移动速度;在金融交易任务中每轮交易的最大金额设计得太小)

MADDPG 使用的 捕食者-被捕食者 环境,multiagent-particle-envs 最佳策略总在动作边界
【TD3的探索方式】让她很容易在探索「边界动作」:
-
策略网络输出张量,经过激活函数 tanh 调整到 (-1, +1)
-
为动作添加一个clip过的高斯噪声,噪声大小由人类指定
-
对动作再进行一次clip操作,调整到 (-1, +1)
好处:一些任务的最优策略本就存在存在大量边界动作,TD3可以很快学得很快。 坏处: 边界动作都是 -1或 +1,这会降低策略的多样性,网络需要在多样性好数据上训练才不容易过拟合。对于clip 到正负1之间的action,过大的噪声方差会产生大量边界动作

不同标准差的正态分布,红线std2=0.2,蓝线std2=0.5
【探索噪声方差 exploration noise std】就是上图中的s。我们需要先尝试小的噪声方差(如0.05),然后逐渐加大。大的噪声方差刻意多探索边界值,特定任务下能让探索更快。且高噪声下训练出来的智能体更robust(稳健、耐操)。请注意:过大的噪声方差(大于上图蓝线的0.5)并不会让探索动作接近随机动作,而是让探索动作更接近单一的边界动作。此外,过大的噪声会影响智能体性能,导致她不容易探索到某些state。
因此,合适的探索噪声方差只能慢慢试出来,TD3适合愿意调参的人使用。在做出错误动作后容易挽回的环境,可以直接尝试较大的噪声。我们也可以模仿 epslion-Greedy,设置一个使用随机动作的概率,或者每间隔几步探索就不添加噪声,甚至也在TD3中使用探索衰减。这些操作都会增加超参数的数量,慎用。
【策略噪声方差 policy noise std】确定了探索噪声后,策略噪声只需要比探索噪声稍大(1~2倍)。TD3对策略噪声的解释是“计算Q值时,因为相似的动作的Q值也是相似的,所以TD3也为动作加一个噪声,这能使Q值函数更加光滑,提高训练稳定性 。见论文的 Target Policy Smoothing Regularization”。详见 曾伊言:强化学习算法TD3论文的翻译与解读 ( https://zhuanlan.zhihu.com/p/86297106 )。我们还能多使用几个添加噪声的动作,甚至使用加权重要性采样去算出更稳定的Q值期望。在确定策略梯度算法里的这种“在计算Q值时,为动作加noise的操作”,让TD3变得有点像随机策略梯度。无论这里的 是否有clip,策略噪声方差最大也不该超过0.5。
【延迟更新频率 delay update frequency】TD3认为:引入目标网络进行 soft update 就是为了提高训练稳定性,那么既然 network 不够稳定,那么我们应该延迟更新目标网络 target network,即多更新几次 network,然后再更新一次target network。从这个想法再拓展出去,我们甚至可以模仿TTUR的思想做得更细致一点,针对双层优化问题我们能做:
-
环境随机因素多,则需要尝试更大的延迟更新频率,可尝试的值有 1~8
-
提供策略梯度的critic可以多更新几次,再更新一次actor,可尝试的值有 1~4
-
提供策略梯度的critic可以设计更大的学习率,例如让critic的学习率是actor 的 0.1~1倍
-
由于critic 需要处理比 actor 更多的数据,因此建议让critic网络的宽度略大于actor
TTUR的思想,详见 曾伊言:从双层优化视角理解对抗网络GAN 的 TTUR (Two Time-Scale Update Rule)( https://zhuanlan.zhihu.com/p/162990438 )
对于一个n维度的连续动作,我们将一个常数设为动作方差过于朴素,为了刷榜 (这不道德),我甚至可以使用PPO、SAC搜索出来的探索噪声方差(也是n维的向量)给TD3使用。 甚至在PPO搜出来的噪声方差的基础上,根据物理意义对方差进行微调。其实这相当于在SAC这种使用了从TD3学来TwinCritic结构的算法,在训练后期固定噪声方差进行训练。
PPO+GAE特有的超参数
PPO比其他算法更robust(稳健),这与她使用了 Minorize-Maximization (MM algorithm)有很大关联,这保证了PPO每次更新策略 总能让性能获得单调的提升,详见RL — Proximal Policy Optimization (PPO) Explained – 2018-07 – Jonathan Hui( https://jonathan-hui.medium.com/rl-proximal-policy-optimization-ppo-explained-77f014ec3f12 ) 这 是介绍PPO算法极好的文章,写在版权保护意识很强的 Medium网站,大陆不能正常访问。