RL策略崩塌的复盘
- 深度学习
一次 RL 策略崩塌的复盘:为什么已经接近 100% 正确的模型,后来会突然固定走同一个错误出口?
1. 背景
这次实验是一个带有“线索—出口对应关系”的玩具强化学习任务。
环境大致规则是:
- 场景里有一个线索;
- 线索的形状/类型决定哪个出口是正确的;
- agent 需要先观察或记住线索;
- 然后根据线索选择对应的出口;
- 如果走到正确出口,获得奖励;
- 如果走到错误出口,失败或获得低奖励/惩罚。
也就是说,这个任务不是简单的“看到出口就走”,而是需要完成一条信息链:
观察线索 -> 记住线索 -> 在出口处根据线索做选择
更抽象一点:
cue = 当前 episode 的关键信息
正确动作 = f(cue)
比如:
如果 cue 是 A,则正确出口是 TOP
如果 cue 是 B,则正确出口是 BOTTOM
训练早期,模型需要通过探索慢慢学会这条规则。
训练中期,模型成功率逐渐上升,最后一度接近甚至达到 100%。
表面上看,这说明模型已经完全学会任务了。
但奇怪的是,继续训练之后,模型没有保持稳定,而是出现了策略崩塌。
2. 观察到的现象
训练到某个阶段时,模型表现很好:
成功率 ≈ 1.0
这说明它不是靠固定走某一个出口,而是真的在根据线索选择出口。
因为如果正确出口会随线索变化,那么固定走某个出口最多只能达到大约 50% 成功率。
所以成功率为 1 的阶段,模型策略应该近似是:
如果线索对应 TOP,则走 TOP
如果线索对应 BOTTOM,则走 BOTTOM
也就是:
π(action | state, cue)
模型行为依赖 cue。
但是继续训练之后,模型突然变成了:
不管线索是什么,都走同一个出口
例如:
永远走 TOP
于是成功率从接近 1 跌到接近 0.5,甚至在某些评估分布下更低。
这让我一开始困惑:
之前模型明明已经学会了线索和出口的对应关系,为什么后来会突然变成固定走一个出口? 又没有什么信号告诉它“永远走 TOP”。
后来我理解到,问题不是模型“重新学了一个固定出口策略”,而是:
模型原本依赖线索的那条通道退化了;
线索通道退化后,输出层剩下的默认偏置主导了行为。
所以崩塌不是:
模型重新变随机
而是:
模型掉进了一个确定性的捷径策略:永远走同一个出口
3. 一个简化的数学图像
可以用一个很简单的形式理解模型在出口处的决策。
假设模型只需要在两个出口之间选择:
TOP 或 BOTTOM
定义一个 logit 差:
z = score_TOP - score_BOTTOM
如果:
z > 0,则选 TOP
z < 0,则选 BOTTOM
模型对线索的使用可以抽象成:
z = k · h_cue + b
其中:
h_cue:模型内部表示出来的线索特征;k:模型使用这个线索特征的强度;b:模型自身对某个出口的默认偏置。
如果线索是二值的,可以进一步简化为:
cue = +1 表示正确出口是 TOP
cue = -1 表示正确出口是 BOTTOM
于是:
z = k · cue + b
在模型表现正常时,可能是:
k = 10
b = 2
那么:
cue = +1: z = 10 * 1 + 2 = 12 -> TOP
cue = -1: z = 10 * (-1) + 2 = -8 -> BOTTOM
此时虽然模型内部有一个偏向 TOP 的 b = 2,但线索项 k · cue 很强,所以模型还是能根据线索正确选择出口。
这就是成功率为 1 的阶段。
但是如果继续训练后,线索通道被削弱了,例如:
k = 1
b = 2
那么:
cue = +1: z = 1 * 1 + 2 = 3 -> TOP
cue = -1: z = 1 * (-1) + 2 = 1 -> TOP
此时不管线索是什么,模型都会选 TOP。
这就是策略崩塌。
关键点是:
固定走 TOP 并不需要一个明确的奖励信号。
它可能只是因为:
线索项变弱以后,默认偏置 b 主导了决策。
4. 为什么成功率接近 100% 之后反而危险?
一开始我以为:
模型成功率都 100% 了,说明它已经完全学会了,继续训练应该只会更稳。
但实际不是这样。
当模型已经几乎总是做对时,强化学习里的有效纠错信号会变得很弱。
策略梯度可以粗略理解成:
策略更新 ∝ advantage · 探索产生的对比 · 对参数的梯度
更具体地,在二分类动作里,如果模型对正确动作的概率已经非常接近 1:
P(correct action) ≈ 0.999
那么它几乎不会采样错误动作。
这会导致几个问题:
4.1 缺少错误对比
如果模型总是做对,它很少得到“这条路错了,那条路对了”的对比信号。
它只知道:
当前行为可以成功
但它不一定持续知道:
成功是因为我正确使用了线索
成功行为里可能包含很多步骤:
移动到某处
观察线索
记忆线索
走向出口
选择出口
最终奖励只告诉模型整个轨迹成功了。
但它不一定能精确地区分:
到底是哪一部分对成功最关键?
尤其当策略已经稳定成功后,模型不会反复测试“如果不看线索会怎样”、“如果记错线索会怎样”。
于是,使用线索这条内部机制缺少持续维护。
4.2 策略太确定后,纠错梯度会变小
假设模型已经非常确信应该走 TOP:
P(TOP) ≈ 0.999
如果它采样到了 TOP,并且这次 TOP 是错的,理论上应该降低 TOP 的概率。
但由于策略已经非常饱和,实际梯度可能很小。
直观理解:
模型越自信,越不容易被一次失败拉回来。
在 softmax 策略里,梯度里会出现类似:
(a - p)
这样的项。
如果模型选择了 TOP,并且:
p = P(TOP) ≈ 0.999
那么:
a - p ≈ 1 - 0.999 = 0.001
这个值很小。
所以即使这次失败了,降低 TOP logit 的梯度也可能很弱。
这和监督学习不太一样。
在监督学习里,如果标签明确告诉模型“正确答案是 BOTTOM”,模型通常可以直接得到很强的纠错信号。
但在强化学习里,失败只告诉它:
这条轨迹不好
不一定直接告诉它:
你应该在那个具体状态选择 BOTTOM
更不一定告诉它:
你应该重新恢复线索记忆通道。
4.3 其它梯度还在继续改变模型
虽然维持线索通道的有效梯度变弱了,但模型参数并不是停止变化。
训练还在继续,模型仍然会受到各种更新影响:
- batch 随机噪声;
- critic/value loss;
- step penalty;
- optimizer 的动量;
- Adam 的自适应学习率;
- 环境中的路径长度差异;
- 网络共享表征带来的干扰;
- 探索或采样带来的波动。
这些更新不一定是“错误”的,也不一定完全和任务目标无关。
更准确地说:
这些梯度未必维护“记住线索”这条回路。
比如 step penalty 可能会鼓励 agent 尽量少走路。
如果去看线索、记住线索、再去出口比直接冲出口更费步数,那么模型可能会逐渐偏向一种短路策略:
别管线索,直接去某个出口试试
如果任务里两个出口平均正确率是 50%,而直接冲出口路径短,那么在某些奖励设定下,这个短路策略并不一定会被强烈压制。
于是就可能出现:
维持线索的梯度很弱
其它目标/噪声/偏置还在推模型
线索通道慢慢退化
5. 我的理解:模型忘掉的不是动作,而是“为什么这个动作正确”
我对这个现象的理解是:
训练成功后,模型一直执行正确动作。
因为它已经几乎 100% 符合环境规则,所以结果对某些关键输入细节不再敏感。
模型知道:
看到某种大致特征 -> 走某个出口
但它未必持续保持对线索细节的敏感。
一开始,模型可能真的依赖了线索的关键数值或方向。
后来,因为一直成功,它不需要反复验证这些细节。
在持续训练中,噪声或其它梯度让内部表征发生漂移。
只要漂移还没有影响最终动作,模型仍然会成功。
于是问题不会立刻暴露出来。
这就像:
线索特征的数值慢慢变了;
但只要符号还没变,输出动作仍然正确;
因此训练过程不会强烈纠正这个漂移。
比如可以想象:
logit = k · h_cue + b
只要:
k · h_cue
还足够大,模型就能压过默认偏置 b,继续正确。
但随着漂移继续发生:
h_cue 变小
或者 k 变小
或者 b 变大
最终某一刻:
k · h_cue < b
于是默认偏置接管决策。
此时模型不再根据线索选择出口,而是固定输出一个动作。
崩塌发生后,即使模型开始拿到惩罚,也未必容易恢复。
因为此时:
线索表征已经弱了
策略已经非常确定
模型很少探索另一个动作
失败奖励也不直接告诉它该恢复哪条内部通道
所以它可能陷入一个局部稳定的坏策略。
6. 手机话费类比
我觉得可以用“手机交话费上网”来类比这个问题。
假设规则是:
只有话费金额 > 0,手机才能上网
一开始,每次拿到新手机,我都会:
交话费 -> 上网
反复多次之后,我形成了一个经验:
只要执行了“交话费”这个动作,后面就能上网。
但这里真正关键的不是“有没有点缴费按钮”,而是:
缴费金额是否大于 0
如果长期以来,每次缴费金额都足够,那么“金额检查”这件事就不会被特别强调。
后来由于各种噪声或外部影响,我每次交的钱数开始变化。
一开始金额变化不影响结果:
交 50 元 -> 能上网
交 30 元 -> 能上网
交 10 元 -> 能上网
交 1 元 -> 能上网
因为它们都满足:
金额 > 0
于是我更加觉得:
金额具体是多少不重要。
直到某一次,金额变成了:
0 元
结果上不了网了。
但问题是,我的决策系统里已经只保留了:
是否执行了“交话费”这个动作
而没有保留:
交了多少钱
所以我会困惑:
我明明已经交过话费了,为什么还是不能上网?
对应到模型里就是:
模型还在执行类似的流程
但真正关键的线索数值/线索表征已经不参与决策了
于是失败后,它也不知道该修复哪里。
7. 消防演练类比
另一个更贴切的类比是消防演练。
现实中有很多规矩,一开始看起来很麻烦:
为什么要定期消防演练?
为什么要检查逃生通道?
为什么不能堆东西挡住门?
为什么要保留应急预案?
如果一个地方几十年没发生火灾,人们很容易觉得这些规则没必要。
因为日常数据告诉大家:
不演练也没事
不检查也没事
不维护也没事
但问题是,一旦真正发生火灾,这些规则就是关键。
长期不演练的结果是:
系统看起来正常
但应对异常的能力已经退化
这和 RL 模型很像。
当模型长期成功后,它可能慢慢忘掉:
为什么这个动作是正确的
只保留:
这个动作过去经常成功
所以需要某种“消防演练”机制,让模型持续保留关键能力。
对应到 RL 里,可以是:
- 保持探索;
- 使用熵正则;
- 偶尔随机动作;
- 强制访问线索;
- 增加辅助任务;
- 在 hidden state 上预测 cue;
- 对策略变化加 KL 限制;
- 使用 replay 或 curriculum 复习旧场景。
这些机制的共同目的不是让模型一直犯错,而是:
让模型持续知道:成功依赖哪些关键条件。
8. 玩具实验设计:如何复现这个现象
下面设计一个尽量简单的实验,用来复现“成功后继续训练导致策略崩塌”的现象。
8.1 环境结构
可以做一个小型 gridworld。
地图示例:
+-------------------+
| TOP EXIT |
| |
| |
| CUE |
| |
| |
| AGENT |
| |
| BOT EXIT |
+-------------------+
环境元素:
- agent 初始位置随机;
- 地图中有一个 cue;
- 有两个出口:TOP 和 BOTTOM;
- cue 类型随机:
- cue = A:TOP 正确;
- cue = B:BOTTOM 正确;
- agent 需要先看到 cue,再去正确出口。
也可以让 cue 是不同形状:
circle -> TOP
triangle -> BOTTOM
或者不同颜色:
red -> TOP
blue -> BOTTOM
关键是 cue 和正确出口每个 episode 随机变化。
8.2 观测设计
为了让任务需要记忆,可以限制 agent 视野。
例如:
agent 只能看到周围 3x3 或 5x5 的局部区域
这样 agent 在出口处看不到 cue。
它必须在之前看到 cue 后,把 cue 存到 hidden state 里。
观测可以包括:
局部地图 one-hot
agent 朝向
上一步动作
如果使用 RNN,可以输入:
obs_t -> encoder -> GRU/LSTM -> policy/value head
8.3 动作空间
动作可以设为:
0: 上
1: 下
2: 左
3: 右
4: 停留/交互
如果 agent 到达出口格子,则 episode 结束。
8.4 奖励设计
简单奖励:
到达正确出口: +1
到达错误出口: 0 或 -1
每步惩罚: -0.01
超时: 0 或 -0.5
为了更容易出现崩塌,可以使用:
正确出口: +1
错误出口: 0
每步惩罚: -0.01
因为这会让“直接冲某个出口猜一下”变得有一定吸引力。
如果直接猜出口成功率是 50%,而路径更短,那么它可能在某些阶段和“先看线索再走出口”竞争。
8.5 模型结构
一个简单结构:
局部观测 obs
↓
CNN / MLP encoder
↓
GRU / LSTM
↓
policy head + value head
伪代码:
class Agent(nn.Module):
def __init__(self):
super().__init__()
self.encoder = CNN_or_MLP()
self.rnn = nn.GRU(hidden_dim, hidden_dim)
self.policy = nn.Linear(hidden_dim, num_actions)
self.value = nn.Linear(hidden_dim, 1)
def forward(self, obs, hidden):
x = self.encoder(obs)
h, hidden = self.rnn(x, hidden)
logits = self.policy(h)
value = self.value(h)
return logits, value, hidden
8.6 训练方法
可以用 PPO / A2C / REINFORCE。
为了观察崩塌,建议做几组对比:
实验 A:无熵正则
entropy_coef = 0
继续训练很久,观察成功率是否先上升到接近 1,然后在后期下降。
实验 B:有熵正则
entropy_coef = 0.01 或 0.005
看是否可以延缓或避免策略坍缩。
实验 C:熵正则退火到 0
entropy_coef 从 0.01 逐渐降到 0
观察退火后是否更容易崩塌。
实验 D:保留少量 ε-greedy
训练时:
以 1 - ε 概率按策略采样
以 ε 概率随机动作
例如:
ε = 0.02 或 0.05
看是否比完全无探索更稳定。
实验 E:加入 cue 辅助损失
在模型 hidden state 上加一个辅助头:
cue_pred = Linear(hidden)
训练它预测当前 episode 的 cue 类型。
辅助损失:
L_aux = CE(cue_pred, cue_label)
总损失:
L = L_RL + λ_aux · L_aux
例如:
λ_aux = 0.1
这个实验用来验证:
只要持续检查 hidden state 里是否还保留 cue,崩塌会不会减少。
9. 需要记录的指标
为了确认是不是“线索通道退化导致默认偏置接管”,不要只看成功率。
建议记录下面这些指标。
9.1 成功率
success_rate
观察是否出现:
先升到 1.0,然后后期下降
9.2 出口选择分布
分别统计:
cue=A 时选择 TOP 的比例
cue=A 时选择 BOTTOM 的比例
cue=B 时选择 TOP 的比例
cue=B 时选择 BOTTOM 的比例
理想策略:
cue=A: TOP ≈ 100%
cue=B: BOTTOM ≈ 100%
崩塌策略:
cue=A: TOP ≈ 100%
cue=B: TOP ≈ 100%
或者:
cue=A: BOTTOM ≈ 100%
cue=B: BOTTOM ≈ 100%
这能直接看出模型是不是变成了固定出口策略。
9.3 policy entropy
记录出口决策点的策略熵:
H(π) = -Σ π(a|s) log π(a|s)
如果崩塌时熵接近 0,说明模型不是随机猜,而是非常确定地固定选择某个动作。
9.4 logit margin
记录出口决策点:
score_TOP - score_BOTTOM
分别按 cue 类型画图。
正常情况下应该是:
cue=A: score_TOP - score_BOTTOM > 0
cue=B: score_TOP - score_BOTTOM < 0
崩塌后可能变成:
cue=A: score_TOP - score_BOTTOM > 0
cue=B: score_TOP - score_BOTTOM > 0
也就是两个 cue 下 logit 同号。
9.5 hidden state 的 cue 可解码性
训练一个 probe,从 hidden state 预测 cue。
比如收集出口决策点的 hidden state:
h_exit
训练一个简单线性分类器:
probe(h_exit) -> cue
如果成功期:
probe accuracy ≈ 100%
崩塌前后下降到:
probe accuracy ≈ 50%
说明模型在决策点的 hidden state 已经不包含 cue 信息了。
这是验证“线索表征退化”的关键指标。
9.6 是否访问 cue
统计每个 episode 中:
agent 是否看到过 cue
agent 到达 cue 附近的频率
agent 从看到 cue 到出口之间间隔多少步
如果崩塌后 agent 不再访问 cue,说明问题可能发生在更早阶段:
不是记忆坏了,而是它根本不再去看线索。
如果它仍然访问 cue,但 hidden state 不含 cue,则说明:
它看到了,但没记住/没用上。
10. 如何区分几种崩塌原因
策略固定走一个出口只是表面现象,背后可能有几种原因。
10.1 不再去看线索
表现:
cue visitation rate 下降
说明 agent 学会了短路:
直接冲出口,不看线索
解决方向:
- 给看线索一点辅助奖励;
- 降低 step penalty;
- curriculum 中强制必须看线索;
- 增加错误出口惩罚。
10.2 看了线索但没记住
表现:
cue visitation 正常
hidden state probe accuracy 下降
说明问题在记忆通道。
解决方向:
- 加 cue prediction auxiliary loss;
- 增大 RNN hidden size;
- 用 attention/memory;
- 在训练中加入长延迟场景;
- 保持探索。
10.3 记住了线索但输出头不用
表现:
probe 能预测 cue
但 action 不随 cue 变化
说明 hidden state 里有信息,但 policy head 没使用。
解决方向:
- 加出口动作辅助监督;
- 加 KL 限制;
- 降低 policy head 学习率;
- 使用 regularization 防止 output bias 接管。
10.4 策略过度确定,失败也拉不回来
表现:
entropy 接近 0
logit margin 极大
解决方向:
- 熵正则;
- ε-greedy;
- PPO clip / KL penalty;
- 限制 logit magnitude;
- label smoothing 类似思想;
- 降低学习率。
11. 为什么“不一定必须用熵正则”
熵正则的作用是让策略不要太早变成:
P(action) ≈ 1
它的好处是:
保留探索
保留纠错机会
防止策略过早饱和
但它不是唯一办法。
真正需要的是:
持续保留有效的纠错信号。
所以也可以用其它方法。
11.1 ε-greedy
训练时保持少量随机动作:
ε = 0.02 ~ 0.05
即:
95%~98% 按模型策略行动
2%~5% 随机动作
这相当于定期让模型“试错”。
但注意:
如果使用 policy gradient,随机动作来自行为策略,而不是纯当前策略,需要在实现上注意 off-policy 偏差。
简单实验中可以先不严格处理,但如果要严谨,最好记录行为策略概率或使用适合 off-policy 的方法。
11.2 偶尔强制访问线索
如果崩塌原因是 agent 不再看线索,那么只在出口处随机动作不够。
因为问题可能发生在前面:
它根本没有获取 cue。
这时可以设计:
部分 episode 中强制或鼓励 agent 先经过 cue 区域
例如:
- curriculum 前期必须看到 cue;
- 看到 cue 给一个小辅助奖励;
- 没看到 cue 到出口不给奖励;
- 随机初始化让看 cue 成为自然路径的一部分。
11.3 辅助任务:预测 cue
我认为这是最像“消防演练”的方法。
即使主任务一直成功,也定期检查:
模型内部还记不记得 cue?
做法是在 hidden state 上加辅助头:
hidden_state -> cue_pred
训练目标:
预测当前 episode 的 cue 类型
这样即使主奖励不给出强梯度,辅助损失也会持续维护 cue 表征。
11.4 偶尔故意走错出口可以吗?
直觉上可以,因为这样能让模型保持试错。
但要注意两点。
第一,如果动作是外部强行改的,不能简单当作模型自己采样的动作来做 on-policy 更新,否则会引入偏差。
第二,只在出口处故意走错,不一定能维护完整的信息链:
去看线索 -> 记住线索 -> 根据线索选出口
更好的方式是:
让模型偶尔探索整个过程
或者直接监督它记住 cue
所以“故意走错”不是最推荐的方案。
更推荐:
少量 ε-greedy + cue auxiliary loss
12. 我对这次现象的最终总结
这次策略崩塌可以总结成一句话:
模型成功后,使用线索的回路因为缺少探索和错误对比而缺乏维护;
其它更新力继续改变参数,导致线索项逐渐变弱;
一旦默认偏置压过线索项,策略就固定走一个出口;
由于策略已经高度确定,失败样本也很难产生足够梯度把线索回路修回来。
更直观地说:
模型不是忘了“该怎么走”,而是忘了“为什么这么走”。
成功期:
根据线索选择出口
崩塌期:
线索不用了,只剩默认动作
这说明对于需要记忆、线索、条件决策的任务,仅仅看成功率是不够的。
成功率为 1 不代表内部机制稳定。
还要检查:
模型是否仍然访问线索
hidden state 是否仍然包含线索
policy 是否仍然随线索变化
entropy 是否过低
logit 是否过度饱和
否则模型可能已经在内部慢慢失去关键能力,只是表面上还没暴露。
13. 以后遇到类似问题时的检查清单
如果一个 RL 模型先成功,后崩塌,可以按这个顺序检查:
行为层面
- 是否还去看 cue?
- 是否固定走某个出口?
- 不同 cue 下动作分布是否不同?
- 是否变成了短路策略?
策略层面
- 出口决策点 entropy 是否接近 0?
- logit margin 是否越来越大?
- 策略是否过早饱和?
表征层面
- hidden state 能否线性解码 cue?
- cue 信息是在什么时候丢失的?
- 是没看到 cue,还是看了没记住?
优化层面
- entropy coef 是否过早降为 0?
- step penalty 是否让看 cue 太亏?
- value loss 是否干扰 encoder?
- 学习率是否过大?
- PPO clip / KL 是否过松?
- 是否训练太久导致漂移?
修复方案
优先尝试:
1. 保留小熵正则
2. 保留小 ε-greedy
3. 加 cue prediction auxiliary loss
4. 记录 hidden state cue probe
5. 降低 step penalty 或增加错误出口惩罚
6. 使用 KL/PPO clip 限制策略漂移
7. 定期评估不同 cue 下的动作分布
14. 最后的一句话
这个实验给我的最大启发是:
长期成功本身也可能让模型忘记成功依赖的条件。
如果一个能力长期不被测试,它就可能在内部退化。
所以对 RL agent 来说,训练不只是让它学会正确行为,还要让它持续记得:
正确行为为什么正确。
这就是为什么需要探索、辅助任务、probe、演练和稳定性约束。