想象你第一次教一个从没摸过车的人上路。手忙脚乱的他第一脚就把油门踩到底,方向盘左右乱甩,前方突然出现障碍物时,他猛打方向、急踩刹车,结果车子一头扎进树丛。教人开车,最怕的不是开得慢,而是“翻车不可挽回”。训练强化学习模型也是这样:模型一会儿觉得自己无所不能,一会儿又彻底摆烂,前一晚还收敛得好好的曲线,第二天早上就全面崩溃。根因往往不是“学得不够努力”,而是工程层面缺少一套稳定化的“驾驶守则”。本文用一份“老司机开车指南”,把 RL 训练中最常用的稳定性技巧讲给你听:怎么统一油门踏板(奖励归一化)、怎么校准仪表盘(观测标准化)、怎么加方向盘限位(梯度裁剪)、怎么让教练慢半拍(目标网络软更新)、以及怎么安排练车计划(学习率调度)。有了这套守则,你训练的模型会像一位稳重老司机一样,稳稳起步、稳稳过弯、最终稳稳到达目的地。

岗位关联

掌握这些稳定化技巧,等于给 RL 训练装上了六位一体的“安全气囊”——它能帮你避免返工、加快实验迭代,让算法工程师在面试和实际落地项目中都脱颖而出。

目录
  1. 可视化演示
  2. 训练崩溃根源
  3. 奖励归一化
  4. 观测标准化
  5. 梯度裁剪
  6. 熵正则探索
  7. 目标软更新
  8. 学习率调度
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示强化学习训练三大稳定性技巧:奖励归一化与截断控制奖励分布、梯度裁剪防止梯度爆炸、目标网络软更新解决移动目标问题,动态演示训练稳定化全流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

奖励归一化:控制奖励尺度与分布 红色为原始奖励(剧烈波动) 青色为归一化后(稳定) 橙色为截断阈值 r_norm = clip(r / (std+ε), -C, C) 奖励归一化将奖励缩放到合理范围并截断,防止价值函数估计不稳定

训练崩溃根源

新手上路最怕什么?不是开得慢,而是三件事同时发生:油门被一脚踩死、方向盘猛烈乱甩、判断完全依赖眼前那一瞬间。这三件事叠加起来,车子三分钟之内就能翻进沟里。RL 训练里也有同样的“翻车三连”:更新步长失控、策略剧烈摇摆、训练数据又来自策略自己。每一样踩雷,都可能让一条原本健康的曲线当场崩溃。

传统监督学习好比做练习册:题目和标准答案都是老师提前印好的,批改完再练下一本,样本之间彼此独立。RL 完全不同——模型当前策略生成交互轨迹,用这条轨迹算梯度,更新完策略再生成新的轨迹。旧策略稍微做错一个决策,后续收集到的数据全被带偏;而这种“自己给自己出题”的循环,会让一个小误差像滚雪球一样放大成一场灾难。科学家把这个过程称为“自举”:模型用自己的估计来更新自己的估计,偏差天然会累积。

策略梯度是这种脆弱性的核心。许多 RL 算法都会估计一个梯度方向,告诉参数“往哪边调、调多少”:

如果环境奖励整体放大 10 倍,优势估计也放大 10 倍,\hat{g} 成比例放大,参数更新步长便失控地冲向悬崖。更麻烦的是,翻车后你没法回到“上一把”重新来——因为数据已经全部变了。理解了这点,后面的每一个技巧都是在给这辆失控的车上加装专业设备:统一油门刻度、校准仪表盘、装方向盘限位器、请一位稳重的教练、再制定一套科学的练车计划。

g^=1N∑t=1N∇θlog⁡πθ(at∣st) A^t\displaystyle \hat{g} = \frac{1}{N}\sum_{t=1}^{N} \nabla_\theta \log \pi_\theta(a_t|s_t) \, \hat{A}_t
这个公式把“该往哪个方向调策略”和“这个方向该迈多大步”揉在一起,而“步长”恰好会随着奖励尺度的变化被整体放大或缩小。
  1. 对数策略梯度表示“要提高动作 a_t 的概率,参数 θ 应该往哪个方向挪”。方向是好的,但天然没有“限速”。
    ∇θlog⁡πθ(at∣st)\displaystyle \nabla_\theta \log \pi_\theta(a_t|s_t)
  2. 优势估计表示“这个动作比当前平均表现好了多少”。它把奖励值和基线做了减法,却仍然继承奖励的原始量纲。
    A^t=Rt−Vϕ(st)\displaystyle \hat{A}_t = R_t - V_\phi(s_t)
  3. 整体平均对一批样本取平均,得到对真实策略梯度的蒙特卡洛估计。
    g^=1N∑t=1N∇θlog⁡πθ(at∣st) A^t\displaystyle \hat{g} = \frac{1}{N}\sum_{t=1}^{N} \nabla_\theta \log \pi_\theta(a_t|s_t) \, \hat{A}_t

奖励归一化

一辆手动挡车的油门,踩到底和踩 10% 的“推背感”在不同车上是完全不同的。有的车轻点就窜,有的车踩一半才动。如果教练不统一刻度,学员每次上车都要重新适应手感。环境奖励就是这样:象棋赢一局给 +1,资源收集游戏每秒钟给你十几分,机器人关节奖励更是花样繁多。尺度差异巨大的奖励直接喂给策略,相当于让模型“每次换一辆手感完全不一样的车”。

解决思路很简单:给奖励装一个“统一踏板标度”。我们用一个滑动窗口持续记录近期奖励的均值 μ 和标准差 σ,然后把每个时间步的奖励换算成“相对平均水平高出几个标准差”。这样无论环境本身出手多大方,模型看到的奖励幅度都保持一致:

奖励归一化不是改变任务本身,而是把“油门踏板的物理行程”统一标定。模型不再需要猜测“这次碰到的环境到底是踩一点点还是踩一大半”,只要专心学相对表现的优劣。装上这层标定之后,同样的学习率在不同环境之间也能通用,训练曲线不再因为奖励量纲不同而忽上忽下。

r^t=rt−μσ+ϵ,rtclip=clip(r^t,−c,c)\displaystyle \hat{r}_t = \frac{r_t - \mu}{\sigma + \epsilon}, \qquad r^{\mathrm{clip}}_t = \mathrm{clip}(\hat{r}_t, -c, c)
前一半公式做标准化,把奖励变成均值为 0、方差为 1 的分布;后一半做截断,把过于极端的离群值限制在 [-c, c] 内,防止个别异常样本干扰训练。
  1. 原始奖励当前时间步 t 环境直接给出的奖励数值,尺度可能任意大。
    rt\displaystyle r_t
  2. 滑动均值最近一批奖励的平均水平,代表这个环境下“正常表现”的参照点。
    μ=1K∑i=1Krt−i+1\displaystyle \mu = \frac{1}{K}\sum_{i=1}^{K} r_{t-i+1}
  3. 滑动标准差衡量奖励波动的剧烈程度,是“这个环境平时给分给得多散”的标尺。
    σ=1K∑i=1K(rt−i+1−μ)2\displaystyle \sigma = \sqrt{\frac{1}{K}\sum_{i=1}^{K}(r_{t-i+1} - \mu)^2}
  4. 标准化与截断减去均值再除以标准差,最后用 clip 把极限值掐回来,避免离群点主导梯度。

观测标准化

车里的仪表盘五花八门:车速是 km/h,转速是每分钟几千转,油量是一格一格的刻度,水温又是摄氏度。如果教练让你直接看这些“原生态”数字,你一定会被转速表的大数值吸引全部注意力,而忽略油量那种小数值却可能更紧急的信号。神经网络的输入特征也是同理:一个特征是 0~1 的坐标,另一个特征是 0~10000 的电压,网络会默认大数值特征更重要,学起来自然偏科。

观测标准化就像一个“仪表盘校准师傅”,把每个特征都换算成均值 0、方差 1 的统一刻度。训练过程中用 running statistics(滚动统计量)在线维护每个特征的均值和方差,每来一个观测就顺手更新一次:

标准化之后,每个传感器读到的大数值不再天然压过小数值,神经网络可以公平地“看”到所有仪表。实际工程里还有一个小细节:训练初期统计量还没收敛,最好先用少量随机交互把均值和方差“预热”出来,再用它归一化正式训练数据。这不只是提升稳定性,也能显著加速收敛——仪表盘清楚了,新手才敢踩油门。

o^i=oi−μiVari+ϵ\displaystyle \hat{o}_i = \frac{o_i - \mu_i}{\sqrt{\mathrm{Var}_i + \epsilon}}
这里的下标 i 指第 i 个特征维度,逐维做标准化,而不是把整个观测向量混在一起算。
  1. 原始特征当前观测向量里的第 i 个分量,可能来自传感器、图像像素或游戏状态。
    oi\displaystyle o_i
  2. 特征均值历史数据中该特征的滑动平均值,代表“正常水平”。
    μi\displaystyle \mu_i
  3. 特征方差该特征历史波动的滑动方差,开根号后就是标准差。
    Vari\displaystyle \mathrm{Var}_i
  4. 数值稳定项ε 是极小的常数,防止方差为 0 时除零,同时避免数值溢出。
    ϵ≈10−8\displaystyle \epsilon \approx 10^{-8}

梯度裁剪

方向盘为什么不能想打多少打多少?因为高速状态下猛打一个 90 度,车子会直接失控。所以方向盘都有“限位器”,不论你怎么用力,最大转角就那么多。RL 参数更新也一样,偶尔一个异常样本会算出一个范数巨大的梯度,像一只突然伸过来的手把方向盘猛甩 90 度。

梯度裁剪就是给参数更新装一个“方向盘限位器”。最常用的是全局范数裁剪:先把所有参数的梯度拼成一个长向量,算出它的 L2 范数;如果这个范数超过阈值 c,就把整个向量等比例缩小,但方向寸步不移:

在工程中 c 一般取 0.5~5.0,过小会让模型学得拖沓,过大会失去保护意义。另一种更粗糙的按值裁剪(clip by value)是逐个参数限制,但会破坏梯度原有的比例关系,范数裁剪在实践中更受青睐。简单说:不管方向盘想怎么甩,限位器保证每次最多打半圈。加了这道限制,训练曲线很少再出现“瞬间跳崖”式的崩溃。

g′=g⋅min⁡(1,c∥g∥2)\displaystyle \mathbf{g}' = \mathbf{g} \cdot \min\left(1, \frac{c}{\lVert \mathbf{g} \rVert_2}\right)
这个公式的精髓是“只限步长、不改方向”。梯度是往东还是往西由损失函数说了算,裁剪刀只负责限制这次迈多远。
  1. 梯度向量把所有可训练参数的梯度首尾相连拼成的大向量,集中反映这次更新的整体走势。
    g=[∂L∂θ1,∂L∂θ2,…]⊤\displaystyle \mathbf{g} = \left[ \frac{\partial L}{\partial \theta_1}, \frac{\partial L}{\partial \theta_2}, \ldots \right]^{\top}
  2. L2 范数向量各分量的平方和再开根号,表示“这次梯度总共有多大劲”。
    ∥g∥2=∑igi2\displaystyle \lVert \mathbf{g} \rVert_2 = \sqrt{\sum_i g_i^2}
  3. 缩放因子取 1 和小一点的数的最小值。没超限时因子为 1,什么都不改;超限时把整个向量缩放回阈值以内。
    min⁡(1,c∥g∥2)\displaystyle \min\left(1, \frac{c}{\lVert \mathbf{g} \rVert_2}\right)
  4. 裁剪结果原梯度乘以缩放因子,方向和原来一致,只是“力气”被限制住。

熵正则探索

新手司机有个通病:一旦哪条路走熟了就再也不换,哪怕那条路其实又堵又远。教练会规定:“这一趟你必须绕一条新路线。”这个“绕路的规矩”在 RL 里就叫探索。没有探索,策略会过早认定某个动作最靠谱,然后死磕到底,哪怕环境稍有变化就彻底翻车。

策略分布的信息熵 H 恰好能衡量“犹豫程度”——如果所有动作概率几乎一样,说明策略还在广泛试探,熵很大;如果某个动作概率接近 1,说明策略已经极度自信,熵非常小。熵正则的做法是在损失函数里加一项负的熵惩罚,让网络在学目标的同时,保留一点“犹豫”的空间:

实践中 λ 可以从 0.01 线性退火到 0.0001。若 λ 设得太大,模型会一直“到处乱逛”学不会精细操作;设得太小,模型又过早变成“只走一条路的倔司机”。所以熵正则不是一味的“开新地图”,而是给策略系上一根安全绳:探索有余而不至于失控。等到模型对环境的理解成熟了,教练再悄悄松开这根绳子,让它专心把老路线跑精。

L(θ)=Lpolicy(θ)−λ⋅Es[H(πθ(⋅∣s))]\displaystyle L(\theta) = L_{\mathrm{policy}}(\theta) - \lambda \cdot \mathbb{E}_{s} \left[ \mathcal{H}\big(\pi_\theta(\cdot|s)\big) \right]
熵前面是负号,意味着熵越大、损失越小。网络在优化主损失的同时,还会被推着保持一定的随机性。
  1. 策略主损失训练策略用的核心目标,例如策略梯度的负对数似然项。
    Lpolicy(θ)\displaystyle L_{\mathrm{policy}}(\theta)
  2. 熵定义对动作概率取对数再加权平均的负值,分布越均匀熵越大。
    H(πθ(⋅∣s))=−∑aπθ(a∣s)log⁡πθ(a∣s)\displaystyle \mathcal{H}(\pi_\theta(\cdot|s)) = -\sum_a \pi_\theta(a|s) \log \pi_\theta(a|s)
  3. 负号与期望把熵通过负号加进损失,再对不同状态取平均,使模型整体倾向于保持探索。
    −λ⋅Es[H(πθ(⋅∣s))]\displaystyle - \lambda \cdot \mathbb{E}_{s} \left[ \mathcal{H}(\pi_\theta(\cdot|s)) \right]
  4. 权重系数λ 控制“探索欲”有多强。训练前期需要大探索,后期需要精细利用,所以 λ 通常随训练衰减。

目标软更新

副驾上坐着一位“教练”会很安心,但如果这位教练反应极快,学员每次打错方向他就立刻跟着修正目标,学员反而学不到东西——因为目标一直在晃。更合理的教练是“慢半拍”的:他不急于追随学员的每一个错误,而是慢慢吸收学员前后一段时间的表现,给出一个稳定一致的参考。

在 Q-learning 类算法(如 DQN、DDPG、SAC)里存在同样的难题:用一个网络同时计算目标值和更新自己,相当于学员一边开车一边给自己发指令,目标值里混入了自己刚刚犯的错,自举循环会导致震荡。解决办法是维护两个结构相同的网络:在线网络(online)负责更新,目标网络(target)负责计算稳定目标。目标网络不直接复制参数,而是每步朝在线网络“挪一小步”:

τ 如果取 1,目标网络瞬间变成在线网络的复制品,教练就完全失去缓冲作用;τ 取 0 则教练永远不学新东西,跟不上学员的成长。0.005 这样的小值能让目标网络在数千步内平稳逼近在线网络,既给训练提供了稳定锚点,又不至于拖慢学习。对比来看,回合更新的策略梯度方法通常不需要目标网络,因为数据本来就是逐步产生的;而一切依赖自举估计 Q 值的算法,都必须有这位“慢半拍教练”坐镇。

θtarget←τ θonline+(1−τ) θtarget\displaystyle \theta_{\mathrm{target}} \leftarrow \tau\, \theta_{\mathrm{online}} + (1-\tau)\,\theta_{\mathrm{target}}
这个公式是软更新的核心:目标网络每步只朝在线网络走 τ 那么远,一般 τ 取 0.005 或 0.001。
  1. 在线网络参数被优化器不断更新的“学徒网络”,学得最快但也不稳定。
    θonline\displaystyle \theta_{\mathrm{online}}
  2. 目标网络参数负责提供稳定学习信号的“教练网络”,更新步伐缓慢。
    θtarget\displaystyle \theta_{\mathrm{target}}
  3. 软更新系数τ 控制教练吸收新经验的速度,数值越小教练越“佛系”。
    τ∈(0,1]\displaystyle \tau \in (0, 1]
  4. 每步迭代新的目标参数是两套参数的加权平均,训练中每一步都执行这个混合操作。

学习率调度

练车不能一上来就上高速。新手先在封闭场地小油门练基础,熟悉后上城市道路逐步提速,考前再主动降速仔细过一遍易错点。学习率就是踩油门的力度——前期太重容易让模型冲进沟里,后期太重它会在最优点附近来回闯动就是停不下来。于是就有了学习率调度。

最经典的组合是 warmup + 余弦衰减。warmup 让学习率从接近 0 的极小值线性爬升到峰值,相当于起步阶段先轻轻给油;爬升到峰值后,再按余弦曲线平滑地降速,让模型在后期的每一次更新都小心翼翼:

实际调参有个经验法则:先跑一条全程固定学习率的小实验,看曲线在哪个阶段开始震荡,把那个位置当作衰减的起点;warmup 步数一般占总步数的 1%~5%。学习率调度不是玄学,它只是把“什么时候该踩油门、什么时候该收油”这件事变成一张明确的时刻表。有了这张表,新司机也能稳稳妥妥地完成整个训练旅程。

lrt={lrmax⁡⋅tTw,t<Twlrmin⁡+lrmax⁡−lrmin⁡2(1+cos⁡π(t−Tw)T−Tw),t≥Tw\displaystyle lr_t =\begin{cases}lr_{\max} \cdot \dfrac{t}{T_w}, & t < T_w \\[8pt]lr_{\min} + \dfrac{lr_{\max} - lr_{\min}}{2} \left(1 + \cos \dfrac{\pi (t - T_w)}{T - T_w}\right), & t \geq T_w\end{cases}
先升温后降温,学习率大部分时间维持在一个令人安心的“巡航速度”,最后逐渐收油进入精细微调。
  1. 峰值学习率训练中允许的最大学习率,通常配合具体优化器一起调,最常见的是 3e-4 量级。
    lrmax⁡\displaystyle lr_{\max}
  2. warmup 段前 T_w 步内学习率随 t 线性从 0 升到峰值,避免开局就遭遇大梯度冲击。
    lrmax⁡⋅tTw\displaystyle lr_{\max} \cdot \frac{t}{T_w}
  3. 余弦衰减段从峰值逐步平滑下降到 lr_min,曲线在最高点和最低点都特别平缓,不会突然换挡。
    lrmax⁡−lrmin⁡2(1+cos⁡π(t−Tw)T−Tw)\displaystyle \frac{lr_{\max} - lr_{\min}}{2}\left(1 + \cos \frac{\pi(t - T_w)}{T - T_w}\right)
  4. 终点下限训练尾声学习率降到很小,让模型在最优点附近精细打磨而非反复横跳。
    lrmin⁡\displaystyle lr_{\min}

AI 应用场景

机器人机械臂分拣训练

阶段:策略训练阶段(训练稳定性保障)

解决的问题:机械臂在真实硬件上训练,一次较大的策略跳变就可能让力矩指令异常,损坏电机或夹爪。传统上若奖励尺度不统一,不同动作的回报差距悬殊,策略梯度会被个别大奖励带偏,训练曲线反复失控。

具体案例:某工厂分拣场景,先用仿真环境训练 300 万步。团队同时启用了奖励归一化、梯度裁剪和熵正则衰减,把 10 次训练中的成功率方差从 23% 压到 4%,仿真到真机的迁移成功率从 62% 提升到 91%。没有这些稳定化措施,仿真训练几乎每两次就有一次在中段发散。

游戏对战 AI 上线前评测

阶段:模型调优阶段(训练稳定性优化)

解决的问题:游戏 AI 训练末期胜率在 0.55 和 0.43 之间剧烈震荡,评测指标完全不可信,上线前无法判断模型真实水平。

具体案例:某 SLG 游戏 bot 的强化学习训练中,工程师加入了目标网络软更新和余弦学习率衰减,胜率曲线在最后 20 万步收敛到 0.52±0.01 的窄带区间。发布团队因此能放心用固定 checkpoint 做线上 A/B 测试,不再因为“昨天好今天差”的不稳定表现反复返工。

自动驾驶路径规划离线训练

阶段:离线模型训练阶段(稳定性与泛化)

解决的问题:多传感器观测量纲差异极大、不同天气场景数据分布漂移严重,训练很容易被大数值的激光雷达特征主导而忽视图像特征。

具体案例:某自动驾驶团队的路径规划模型,在训练管线里同时引入观测标准化和奖励归一化。40 个训练任务的平均奖励曲线从“发散到无法继续”变为“稳定收敛”,最终在雨天和夜间的泛化测试中,决策成功率分别提升了 18% 和 11%。稳定的训练动态让团队可以放心堆数据,而不必时刻提防“崩盘重训”。

Python 演示

python78 行
class StableKit:    """RL训练稳定性工具包: 奖励归一化 + 观测标准化 + 梯度裁剪 + 熵系数调度 + 软更新."""     def __init__(self, entropy_start=0.01, entropy_end=0.0001, total_steps=1000):        self.reward_mean = 0.0        self.reward_var = 1.0        self.reward_count = 0        self.obs_mean = None        self.obs_var = None        self.obs_count = 0        self.entropy_start = entropy_start        self.entropy_end = entropy_end        self.total_steps = total_steps     def add_reward(self, r):        self.reward_count += 1        delta = r - self.reward_mean        self.reward_mean += delta / self.reward_count        delta2 = r - self.reward_mean        self.reward_var += delta * delta2     def normalize_reward(self, r, eps=1e-8):        std = (self.reward_var / max(self.reward_count, 1)) ** 0.5        return (r - self.reward_mean) / (std + eps)     def add_obs(self, obs):        if self.obs_mean is None:            self.obs_mean = [0.0] * len(obs)            self.obs_var = [1.0] * len(obs)        self.obs_count += 1        for i, x in enumerate(obs):            d = x - self.obs_mean[i]            self.obs_mean[i] += d / self.obs_count            self.obs_var[i] += d * (x - self.obs_mean[i])     def normalize_obs(self, obs, eps=1e-8):        out = []        for i, x in enumerate(obs):            std = (self.obs_var[i] / max(self.obs_count, 1)) ** 0.5            out.append((x - self.obs_mean[i]) / (std + eps))        return out     def clip_gradients(self, grads, max_norm=0.5):        total = sum(float(g * g) for g in grads) ** 0.5        scale = min(1.0, max_norm / (total + 1e-8))        return [g * scale for g in grads]     def entropy_coeff(self, cur_step):        frac = min(cur_step / self.total_steps, 1.0)        return self.entropy_end + (self.entropy_start - self.entropy_end) * (1.0 - frac)     def soft_update(self, target_params, online_params, tau=0.005):        for tp, op in zip(target_params, online_params):            tp[:] = [tau * x + (1.0 - tau) * y for x, y in zip(op, tp)]  def demo():    kit = StableKit()    obs = [1.2, 340.0, -0.5]    target_params = [[0.0, 0.0]]    online_params = [[0.1, -0.2]]    for step in range(100):        kit.add_obs(obs)        obs = [x + 0.01 * step for x in obs]        reward = step * 0.5        kit.add_reward(reward)        r = kit.normalize_reward(reward)        o = kit.normalize_obs(obs)        grads = [0.2, -0.1, 0.05]        g = kit.clip_gradients(grads, max_norm=0.5)        beta = kit.entropy_coeff(step)        kit.soft_update(target_params, online_params, tau=0.005)        if step % 20 == 0:            print("step", step, "r", round(r, 3), "obs", [round(x, 3) for x in o], "beta", round(beta, 5))  if __name__ == "__main__":    demo()
目的:一个实际可跑的 Python 工具类,把奖励归一化、观测标准化、梯度裁剪、熵系数调度和目标网络软更新打包成可直接嵌入训练循环的组件。
  1. self.reward_mean += delta / self.reward_count用 Welford 增量法更新滑动均值,不必存下所有历史奖励。
  2. std = (self.reward_var / max(self.reward_count, 1)) ** 0.5从累积平方差恢复标准差,稳定且节省内存。
  3. scale = min(1.0, max_norm / (total + 1e-8))梯度范数超限时才缩放,保留原方向。
  4. self.entropy_end + (self.entropy_start - self.entropy_end) * (1.0 - frac)熵系数随训练进度线性退火到极小值。
  5. tp[:] = [tau * x + (1.0 - tau) * y for x, y in zip(op, tp)]原地完成目标网络软更新,不改变目标网络对象引用。

常见误区

误区“奖励归一化之后,原来大奖励和小奖励的区别就没了,模型会不会分不清好坏?”

正解奖励归一化只是改变数值的“尺子刻度”,不会改变奖励之间的相对大小。原本 +100 和 +10 的差距,归一化后变成 2.0 和 0.5,依然是第一个更好。真正会被抹平的只有绝对数值,而 RL 需要的恰恰是相对比较。唯一需要注意的是滑动统计量要足够稳定,窗口太小会让“尺子”本身乱跳。

为何常见:大家习惯性地把数值大小直接等同于信息量,忽略了一个事实:在梯度下降中,尺度的绝对值只会影响步长,而相对关系才决定优化方向。

误区“既然梯度裁剪是防崩溃的,那我把阈值设成 0.01 甚至更小,训练岂不是更稳定?”

正解裁剪阈值太小,梯度被压得极其微弱,模型每步只移动一点点,训练会慢到几乎无法收敛。它相当于给方向盘装了一个只能转 1 度的限位器,虽然绝对不会翻车,但也永远转不过弯。实际工程中一般从 0.5 试起,根据梯度范数的真实分布来定,让裁剪只在“大多数正常梯度之外”的尾部生效。

为何常见:把“防极端事件”误解成“压制一切事件”,忽略了裁剪应当只在阈值外起作用的设定初衷。

误区“熵正则鼓励探索,那我把它设到 0.1 甚至 0.5,策略就永远在探索,永远不会卡在局部最优。”

正解熵过大时策略几乎退化成均匀随机,一直在“每条路都试试”,根本学不会精细决策。这就好比教练让你每 10 次必须绕 9 次新路线,你连最近的一条上班路都记不住。正确做法是开始时用中等系数保证探索,后期把系数衰减到接近 0,让模型把已经发现的好路线“收紧”。

为何常见:大家容易把“探索”想成越多越好,却忘了训练后期更需要“利用”已学到的经验,探索和利用需要一个动态平衡。

误区“目标网络总是不跟手,我把它设成 0.5,让它快点跟上在线网络的进度。”

正解tau 正是为了“不跟手”而存在的。设成 0.5 甚至 1.0 后,目标网络几乎立刻变成在线网络的副本,自举循环的天然缓冲消失,Q 值估计会快速震荡。tau 取 0.005 的意思不是“慢”,而是“每一步吸收 0.5% 的新经验”,几千步之后它其实已经跟上了大趋势,只是不会跟着单个批次的噪声抖。

为何常见:直觉上认为“更快总比慢好”,忽略了稳定学习信号的核心要求是平滑而非及时。

误区“大家都在用 3e-4,我也一直用同一个学习率训到底,曲线最后有点震荡应该没关系。”

正解3e-4 只是很多实验的合理起点,不代表适合你的任务和总步数。训练后期还在用峰值学习率,策略会在最优点附近来回穿梭,表现为曲线高频震荡。加入 warmup 和余弦衰减后,同样的算法和步数通常能收获更干净的收敛曲线。固定学习率不是错,但“永远固定”会白白浪费后期微调的潜力。

为何常见:抄别人的超参最省事,可一旦训练分布或总步数改变,固定学习率的隐患就会在最后一公里爆发。

面试题

Q1. 奖励归一化的核心作用是什么?

单选题 · easy

  • A.消除奖励信号的相对大小差异← 正确答案
  • B.把奖励固定为常数便于调试
  • C.通过人为加大奖励让学习更快
  • D.让所有环境奖励都变成正数

答案:A。奖励归一化把原始奖励的绝对尺度统一到均值 0、方差 1 的分布上,让不同环境、不同量纲的奖励在梯度更新中处于相似的影响范围,但不会改变奖励之间的相对大小。B、C、D 都是对“归一化”字面意思的误解。

Q2. 按范数裁剪梯度时,下列哪种说法正确?

单选题 · easy

  • A.保留原方向并限制更新步长← 正确答案
  • B.随机改变梯度向量的方向
  • C.等价于把学习率固定缩小十倍
  • D.只裁剪第一个参数的梯度

答案:A。范数裁剪只在梯度范数超过阈值时等比例缩放整个向量,方向完全不变。它保护的是步长而非方向。学习率是全训练期统一的缩放因子,与逐时刻的裁剪不同;裁剪也不是只针对某个参数。

Q3. 目标网络软更新中 tau 一般取什么数量级?

单选题 · easy

  • A.0.005 这样的小值← 正确答案
  • B.0.5 这样的大值
  • C.直接设成 1.0 最快
  • D.由神经网络自动学习

答案:A。tau=0.005 意味着目标网络每步只向在线网络靠近 0.5%,几千步后能平滑跟上大趋势,同时过滤单步噪声。tau 取 0.5 或 1.0 会让目标网络几乎即时同步,丧失稳定作用。

Q4. warmup 阶段学习率的变化方式是什么?

单选题 · mid

  • A.从小值线性升到峰值← 正确答案
  • B.从峰值迅速降到零
  • C.保持最低值不变直到结束
  • D.随机波动没有规律

答案:A。warmup 的目的是让模型在初始不稳定阶段用小学习率起步,再逐步升到峰值,避免开局大梯度冲击。降为零是衰减阶段的事,保持最低值和随机波动都不是 warmup 的定义。

Q5. 纯 on-policy 策略梯度算法通常不需要下列哪一项?

单选题 · mid

  • A.目标网络软更新← 正确答案
  • B.奖励归一化
  • C.梯度裁剪
  • D.观测标准化

答案:A。目标网络软更新是为了缓解 value-based 算法中 Q 目标自举导致的震荡,而纯 on-policy 算法(如 PPO、A2C)用当前策略采样并单步更新,不依赖目标 Q 网络。奖励归一化、梯度裁剪和观测标准化在 on-policy 中同样常用。

Q6. 熵正则项在训练中的合理作用有哪些?

多选题 · mid

  • A.让策略保持一定随机性← 正确答案
  • B.让策略输出固定动作
  • C.消除观测中的传感器噪声
  • D.防止策略过早坍塌到单一动作← 正确答案

答案:AD。熵惩罚项鼓励策略分布保持平坦,从而维持探索、防止策略过于自信地收敛到单个动作。它不作用于观测信号,也无法消除传感器噪声;“输出固定动作”恰恰是熵正则要避免的现象。

Q7. 下列哪些技巧组合能有效提升 RL 训练的稳定性?

多选题 · mid

  • A.奖励归一化与截断← 正确答案
  • B.观测特征标准化← 正确答案
  • C.关闭梯度裁剪机制
  • D.增加目标网络软更新← 正确答案

答案:ABD。奖励归一化、观测标准化、目标网络软更新都能平滑训练动态。关闭梯度裁剪会移除防爆炸的最后一道保险,属于反向操作,会让训练更不稳定。

Q8. 训练后期 reward 曲线高频震荡,最可能的原因是?

单选题 · hard

  • A.学习率仍偏高导致过冲← 正确答案
  • B.观测标准化算错公式
  • C.目标网络 tau 等于零
  • D.梯度裁剪阈值设得太小

答案:A。训练后期策略已接近最优点,此时偏高的学习率会让参数在最优点两侧来回穿越,表现为高频震荡。观测标准化公式错误通常从一开始就会出问题,而不是只在后期;tau=0 会导致目标长期不更新但不会高频震荡;裁剪阈值太小会让更新过慢而非震荡。

Q9. 过度使用奖励截断可能带来哪些风险?

多选题 · hard

  • A.让超大奖励携带的信息丢失← 正确答案
  • B.导致所有算法无法训练
  • C.改变原有任务的难度结构← 正确答案
  • D.使梯度裁剪完全失效

答案:AC。截断把极端奖励压平,模型无法区分“特别好”和“一般好”,相当于压缩了任务难度信息。它并不是对任何算法都一票否决,也不会让梯度裁剪失效,只是需要根据任务特性谨慎使用。

Q10. 若把目标网络软更新系数 tau 直接设为 1,连续 Q 学习会发生什么?

单选题 · extreme

  • A.目标网络与在线网络同步,失去稳定缓冲← 正确答案
  • B.训练更稳定并且收敛速度翻倍
  • C.梯度裁剪机制自动失效
  • D.奖励归一化被目标网络覆盖

答案:A。tau=1 时,每步目标网络都被在线网络完全覆盖,目标值和被更新网络完全同步,自举循环的缓冲消失,Q 值估计容易震荡甚至发散。训练更稳定、梯度裁剪失效、奖励归一化被覆盖都不符合软更新的机制。