外观
调参与超参数优化
一句话定位:这一页教你一套"先诊断、后调参"的 RL 超参数方法论——先学会读学习曲线判断病灶,再按顺序动最少、最有效的旋钮;从默认值出发,到批量实验(Optuna)与 AutoRL。它解决"照着默认值跑、不涨,就乱调一通"的日常困境,读完你会有一个清晰的调参决策树。
先立一个认知:RL 对超参数的敏感度比监督学习高一个量级。监督学习里 learning_rate=1e-3 和 1e-2 往往只是收敛快慢的区别;RL 里同一个 1e-3,换一个算法实现(PPO 的 GAE 细节、奖励归一化做没做)都可能决定"能学"还是"学崩"。而 RL 调参还有一个监督学习没有的维度:同样的超参,不同 seed 表现差异巨大。这两件事叠加,决定了"凭感觉调参"在 RL 里几乎是必败策略。
本页与评估实践是姊妹篇:评估负责"如何知道实验结果的置信度",调参负责"如何高效找到好配置"。两者共享同一套实验基础设施。
一、敏感度真相:为什么 RL 调参这么难
| 事实 | 监督学习 | RL | 工程含义 |
|---|---|---|---|
| 对学习率敏感度 | 高(但范围宽) | 极高且范围窄 | 先查学习率 |
| 训练目标稳定性 | 静态损失 | 目标随策略漂移(非平稳) | 曲线会"先好后崩" |
| 评估噪声 | 验证集稳定 | 回报方差大 + seed 敏感 | 必须多 seed 确认 |
| 实现细节影响 | 有 | 巨大(奖励归一化、done 处理) | 先复现,再调参 |
| 一次实验成本 | 分钟级 | 分钟~天级 | 批量实验基建要早建 |
有两篇论文把这件事钉死了:Henderson et al. (2018) 证明 RL 算法对 seed 和实现细节的敏感度可以掩盖算法差异;Engstrom et al. (2020) 直接对 PPO 做消融,发现光改实现细节(学习率调度、奖励归一化)就能产生与"改进算法"同量级的性能差异。所以调参的第一守则:先确认你的"实验基准"正确,再谈调参。
最贵的坑:在"实现有 bug"的基础上调参
如果你的 PPO 忘记做 GAE mask、奖励没归一化、或者把 truncated 当失败——调参永远调不好,因为病灶不在参数。诊断调参问题的第一步永远是"跑一个已知基准(比如 CartPole 或官方默认配置)"确认管线本身是对的。见常见陷阱的复现诊断清单。
二、核心超参数速查表
按算法族分,主流的超参就这几组。表中"敏感度"指"动它影响有多大"。
1. 通用(所有算法)
| 参数 | 作用 | 典型范围 | 敏感度 | 调参直觉 |
|---|---|---|---|---|
learning_rate | 步长 | 3e-4 ~ 3e-3(PPO);1e-4 ~ 1e-3(SAC) | ★★★★★ | 第一大嫌疑犯,先查它 |
gamma (γ) | 折扣因子 | 0.9 ~ 0.999 | ★★★★ | 任务越长视(延迟奖励),γ 越接近 1 |
network width/depth | 容量 | 64~512 宽、2~3 层 | ★★★ | 过小欠拟合、过大慢且易过拟合 |
seed | 随机性 | 固定集合 | ★★★★ | 不是超参,但必须多 seed 确认 |
batch/minibatch size | 更新粒度 | 64~4096 | ★★★ | 大 batch 稳但慢 |
2. PPO 专属
| 参数 | 作用 | 典型范围 | 敏感度 | 调参直觉 |
|---|---|---|---|---|
n_steps | 每次更新的采样步数 | 512 ~ 2048(单环境);向量化后按总步数 | ★★★★ | 太小 → 更新太频繁不稳;太大 → 数据旧 |
gae_lambda (λ) | 优势估计偏差/方差权衡 | 0.9 ~ 0.99 | ★★★ | 接近 1 偏 MC(高方差);接近 0 偏 TD |
clip_range (ε) | 单轮更新步长上限 | 0.1 ~ 0.3 | ★★★ | 越大更新越激进 |
update_epochs | 复用同一批数据的轮数 | 3 ~ 10 | ★★★ | 过多 → 同一批数据过拟合 |
ent_coef | 熵正则强度 | 0 ~ 0.05 | ★★★★ | 探索不足调大;训练崩/随机化调小或归零 |
3. SAC/DDPG 等 off-policy 专属
| 参数 | 作用 | 典型范围 | 敏感度 | 调参直觉 |
|---|---|---|---|---|
buffer_size | 回放容量 | 1e5 ~ 1e6 | ★★ | 越大样本越多样,但陈旧 |
tau | 目标网络软更新系数 | 0.005 ~ 0.01 | ★★ | 越小目标更新越慢 |
ent_coef / target_entropy | 熵项 | 自动(auto) | ★★★ | 首选自动温度,见Actor-Critic 家族 |
learning_starts | 回放预热步数 | 1e3 ~ 1e4 | ★★ | 回放没攒够就学会振荡 |
从默认值出发,先别碰这些
SB3/CleanRL 的默认值都是经过调优的(SB3 的 PPO 默认 lr=3e-4、n_steps=2048、clip=0.2,是 RL 社区公认的"稳健起跑点")。第一轮实验只动一个参数,其余全默认。 你调的不是"最好配置",而是"确认每个旋钮的影响方向"。
三、调参顺序:奖励 → 环境 → 算法 → 超参
调参最大的误区是一上来就动 learning_rate。正确的优先级是从最上游的病灶开始:
text
① 奖励 奖励稀疏/写歪/量级不对 → 算法再好也没用
│ (先检查:平均回报量级、奖励项来源,见奖励工程)
② 环境 环境 bug / 观测缺信息 / 动作空间错误
│ (先检查:环境冒烟测试、启发式基线能打几分)
③ 算法 算法族选错(该用 off-policy 用了 on-policy)
│ (先检查:样本预算、动作空间、稳定需求)
④ 超参 前三步没问题,才是动旋钮的时候
(按第二节表从敏感度最高的开始)每一步都有"一句话检查":
| 层 | 检查问题 | 如果异常 |
|---|---|---|
| 奖励 | 启发式策略能拿到多少回报?奖励有界吗? | 先修奖励,别调参(奖励工程) |
| 环境 | 随机策略冒烟测试通过吗?观测维度正确吗? | 先修环境 |
| 算法 | 样本预算够 off-policy 发挥吗?动作空间匹配吗? | 先换算法族(Actor-Critic 家族) |
| 超参 | 学习曲线形态是哪种病? | 按第四节诊断表对症下药 |
为什么顺序这么重要
一个真实案例:某团队把 PPO 的 lr 从 3e-4 调到 3e-3、clip 从 0.2 调到 0.1,折腾两周毫无起色——最后发现奖励函数漏了一个负项,导致所有策略都在"同分"区域打转。在奖励没修好之前调超参,等于给一栋地基倾斜的楼换瓷砖。
四、诊断工具:学习曲线形态分析
调参要"对症下药",先学会读曲线。以下五种形态是 RL 调参最常见的"病",每种对应明确药方:
text
① 根本不涨 ② 涨了又崩 ③ 剧烈振荡
回报 回报 回报
│ │┐ │╱╲╱╲╱╲
│ ───────── │╱└──┐ │╱╲╱╲╱╲╱
│ ▁▁▁▁▁ │ ╲╲ │╱╲╱╲╱╲
└──────────────步数 └────────步数 └────────步数
④ 缓慢爬升(没收敛) ⑤ 前期飞涨后停滞
回报 回报
│ ───── │ ────────
│ ╱ │ ╱
│ ╱ │ ╱
│╱ │╱
└────────────步数 └────────────步数| 形态 | 主要病因 | 首选药方 | 次选 |
|---|---|---|---|
| ① 根本不涨 | 奖励/环境问题,或学习率过小 | 回到第三节排查奖励与环境 | lr 上调一档 |
| ② 涨了又崩 | 学习率过大 / clip 过大 / 熵太小 | lr 降一档(PPO 还可降 clip) | 加梯度裁剪 |
| ③ 剧烈振荡 | 高方差(on-policy + 小 batch) | 增大 n_steps/batch | 增大 gae_lambda 或熵正则 |
| ④ 缓慢爬升 | 学习率过小 / 熵过大 / 网络太小 | lr 上调 | 加宽网络 |
| ⑤ 飞涨后停滞 | 卡在局部最优 / 探索不足 | 调大熵正则或初始探索 | 换 seed 确认是方差还是真停滞 |
曲线诊断要在"多 seed"上做
单条曲线"涨了又崩"可能是真崩,也可能只是这个 seed 运气差。形态判断至少看 3 个 seed 的重叠曲线,形态一致才下诊断。这也是评估实践把 IQR 带作为默认视图的原因——IQR 带本身就是最好的诊断图。
补充诊断量:不止看回报
回报是最终结果,但中间量能提前告诉你病灶在哪:
| 诊断量 | 正常形态 | 异常形态 → 病因 |
|---|---|---|
| 策略熵 | 缓慢下降 | 骤降 → 过早收敛;不降 → 没在学 |
| 价值损失 / TD 误差 | 缓慢下降 | 剧烈波动 → 学习率过大或 bootstrap 不稳 |
| 概率比(PPO) | 集中在 1 附近 | 远离 1 → clip 频繁生效,更新过猛 |
| 平均回报 | 上升 | 见上表 |
五、随机搜索 vs 贝叶斯优化(Optuna)
当"对症单点调参"到瓶颈、或者要探一个陌生配置空间时,上批量搜索。
| 方法 | 原理 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| 网格搜索 | 每个参数取若干档全组合 | 简单、可解释 | 组合爆炸、浪费 | 参数很少时 |
| 随机搜索 | 在范围里随机采样 N 组 | 比网格高效(高维更明显)、简单 | 不利用历史结果 | 起步默认 |
| 贝叶斯优化(Optuna/TPE) | 用历史结果建概率模型,选最可能提升的点 | 样本效率高、自动剪枝 | 实现复杂度高、单点串行 | 预算有限、二次调优 |
实践要点:
- 先随机搜索探路:用随机搜索跑 30~50 组,确认"什么范围的配置能学",同时拿到一个粗糙上界。
- 再贝叶斯收尾:把随机搜索的结果喂给 Optuna,让它围绕好区域精挖。
- 每次搜索都记录:每组配置 + 结果存表,搜索结束时你得到的不只是最优配置,还有"哪个参数影响最大"(Optuna 的
importance分析)。
Optuna 最小示例(PPO-CartPole)
python
import optuna
from stable_baselines3 import PPO
from stable_baselines3.common.env_util import make_vec_env
def objective(trial):
# 每次 trial 采样一组超参
lr = trial.suggest_float("learning_rate", 1e-4, 1e-2, log=True)
gae = trial.suggest_float("gae_lambda", 0.9, 0.99)
ent = trial.suggest_float("ent_coef", 0.0, 0.05)
env = make_vec_env("CartPole-v1", n_envs=4)
model = PPO("MlpPolicy", env, learning_rate=lr,
gae_lambda=gae, ent_coef=ent, seed=0)
model.learn(total_timesteps=20_000)
# 评估(用固定评估种子,别用训练时的轨迹)
eval_env = make_vec_env("CartPole-v1", n_envs=1)
obs = eval_env.reset()
total = 0.0
for _ in range(500):
action, _ = model.predict(obs, deterministic=True)
obs, r, done, info = eval_env.step(action)
total += r.item()
if done.all():
break
return total # Optuna 最大化这个值
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=30)
print("best params:", study.best_params)Optuna 的隐藏陷阱
- 固定 seed 陷阱:上面的示例固定
seed=0——如果它对某个参数组合运气好,Optuna 会追着运气跑。正规做法是每组参数跑 3 个 seed,用中位数当目标值。 - 预算一致性:trial 之间
total_timesteps必须一致,否则是在比预算不是比参数。 - 剪枝要小心:Optuna 的 early stopping 会砍掉"前期差"的配置,但 RL 的某些配置恰恰是"先差后好"(如熵大、探索久)。剪枝请谨慎。
六、固定 seed 陷阱与多 seed 确认
这是 RL 调参里被坑得最惨的一条。完整背景见常见陷阱与反模式,这里只给操作规则:
| 场景 | 允许的 seed 用法 | 不允许 |
|---|---|---|
| 快速试参(探路) | 单 seed 看形态 | 把单 seed 结果当最终结论 |
| 两配置对比 | 同一组 seed,成对比较 | A 用 seed 0-4、B 用 seed 5-9 |
| 最终结论 | 3~5+ seed,中位数 ± IQR | 报告"最好的一次运行" |
最阴的坑:用测试配置调参
如果你先跑了一组配置、把 seed=0 跑得最好的那组选出来,然后再用 seed=0 验证——你已经在同一份随机性上"过拟合"了。验证必须换新的 seed 集合。这就是"seed 过拟合":你的"调优"其实在记忆一组随机数。
七、AutoRL 简介
AutoRL 试图把整个"算法 + 超参 + 结构"的搜索自动化——本质是用元学习/进化在 RL 训练循环的外层再套一层优化。几个方向:
| 方向 | 做法 | 代表工作 | 现状 |
|---|---|---|---|
| 超参搜索 | Optuna/BOHB 等跑批量实验 | SB3 + Optuna 教程 | 成熟、工程可用 |
| 训练中动态调参 | 训练过程中按指标调 lr/熵 | PBT(Population Based Training) | 论文常用,工程少见 |
| 学习目标自动设计 | 让算法自动学更新规则/目标函数 | Learned Policy Gradient、Meta-RL | 研究前沿 |
工程视角的判断:
- **PBT(群体并行训练)**是 AutoRL 里最"划得来"的:一群训练并行跑,表现差的个体复制好个体的权重和超参再变异。OpenAI 当年就用它调出了多个基准上的 SOTA(Lilian Weng 博客有详细介绍)。
- 别指望 AutoRL 替代你的理解:搜索只是把"调参手艺"外包给算力,它需要你先把搜索空间、目标函数、评估协议定对——这些恰恰是前几节教的东西。
AutoRL 与搜索的边界
你以为的 AutoRL:"随便给个环境,自动找到能打 SOTA 的算法。"实际的 AutoRL:"你把搜索空间、评估协议、预算、seed 方案全部定好,它帮你穷举。"前者不存在,后者价值很大。 务实路线:先手动诊断 + 随机搜索,再 Optuna 收尾;PBT 只在团队有分布式基建时考虑。
八、实验管理:配置、日志、复现
调参效率的最终决定因素不是"调得快",而是"每轮实验不浪费"。一个可追溯的实验系统让你敢大规模搜索——否则每次调参都是一次"盲盒"。
1. 配置即代码:Hydra
yaml
# config.yaml(一次实验的完整身份证)
seed: 42
env_id: HalfCheetah-v4
algorithm:
name: ppo
learning_rate: 3.0e-4
gamma: 0.99
gae_lambda: 0.95
clip_range: 0.2
ent_coef: 0.0
search: # 若是批量搜索,记录搜索方法
method: random
n_trials: 40用 Hydra 或简单的 yaml 加载器,命令行覆盖任意字段(python train.py algorithm.learning_rate=1e-3),每个实验自动存档完整 config——"复现"就退化成"重跑一个目录"。
2. 日志与版本
- 训练时记录:每 N 步写一行
(step, train_return, eval_return, entropy, loss, lr)到 CSV 或 W&B。 - 代码版本:实验目录里记下
git commit。 - 依赖版本:记
pip freeze或 lockfile。
3. 一次调参会话的最小纪律
text
每一轮实验(建议画成 checklist):
□ 只改一个变量(或明确记录改了哪些)
□ 记录"改前/改后"的超参与结果
□ 至少 3 个 seed 确认形态
□ 把结论写进实验日志(哪怕一句话:"lr 3e-4→1e-3 崩了,形态③")"只改一个变量"是纪律不是教条
批量搜索(Optuna)同时改多个变量没问题——那是系统化的探索。真正要避免的是"同时手改 5 个参数还说不清为什么"的随机式调参。纪律 = 每个改动都有记录、每个结论都有依据。
九、调参决策树总结
text
策略学不动?
├─ 先跑已知基准(CartPole/官方默认)确认管线 OK? ── 否 → 修实现,别调参
├─ 奖励有问题? ── 是 → 修奖励
├─ 环境有问题? ── 是 → 修环境
├─ 算法族选错? ── 是 → 换算法
└─ 读学习曲线形态
├─ 不涨 → lr 上调
├─ 涨了崩 → lr/clip 下调,加梯度裁剪
├─ 振荡 → 加大 n_steps/batch
├─ 爬升慢 → lr 上调 / 熵下调
└─ 停滞 → 熵上调 / 换 seed 确认
之后:多 seed 确认 → 批量搜索(随机 → Optuna)→ 记录 + 报告延伸阅读
- 策略梯度方法 —— PPO 超参(clip、熵、GAE)的原理出处
- Actor-Critic 家族 —— SAC 自动温度、TD3 双 Q 等 off-policy 超参的原理
- 从零搭一套 RL 评估 —— 调参与评估共享的实验基建与统计严格度
- 常见陷阱与反模式 —— seed 过拟合、算力错觉等调参路上的翻车点
- 框架与工具怎么选 —— SB3/CleanRL/Optuna 等工具链的选型
参考资料
- Henderson, P. et al. (2018). Deep Reinforcement Learning that Matters. AAAI 2018. arXiv:1709.06560
- Engstrom, L. et al. (2020). Implementation Matters in Deep RL: A Case Study on PPO and TRPO. ICLR 2020. arXiv:1805.08292
- Islam, R. et al. (2017). Reproducibility of Benchmarked Deep Reinforcement Learning Tasks. arXiv:1708.04133
- Bergstra, J. & Bengio, Y. (2012). Random Search for Hyper-Parameter Optimization. JMLR 13, 281–305.(随机搜索优于网格搜索的经典论证)
- Optuna 官方文档:optuna.org;SB3 的 Optuna 集成示例见 github.com/DLR-RM/rl-baselines3-zoo
- Jaderberg, M. et al. (2017). Population Based Training of Neural Networks. arXiv:1711.09846(PBT 论文)
- Lilian Weng (2019). A Gentle Introduction to PBT / AutoRL 综述. lilianweng.github.io
- Hydra 官方文档:hydra.cc