我是AI时代的无业游民,我游荡在现实与意念之间
一年前,我用 RL 造了一批非自回归决策模型

背景与痛点
一年前我在做一个实时竞价场景的决策系统。核心需求是:在 20ms 内,对每一次请求给出一个多维动作------出价、预算分配、投放时段选择。最初团队用的是自回归(autoregressive)序列生成:把动作拆成 token 序列,逐个解码。听起来很优雅,但上线后问题暴露得很彻底。
第一个问题是延迟不可控。自回归解码的步数与动作维度成正比,当动作空间扩展到 12 维时,P99 延迟直接冲到 80ms,远超预算。第二个问题是误差累积:前一个 token 的微小偏差会作为条件输入传给下一步,导致最终动作分布漂移。第三个问题更隐蔽------在 RL 训练中,自回归策略的 log-prob 计算需要对每个 token 做条件概率分解,这让优势函数的估计方差显著变大,训练极不稳定。
我们试过几种缓解手段:动作维度降维、用 KV Cache 加速、对 token 做 beam search 剪枝。但这些都是工程补丁,没有触及根本矛盾------自回归的串行依赖,与决策任务需要的并行、低延迟、全局一致性,本质上是冲突的。不解决这个矛盾,代价就是每次大促前都要重新调参,线上延迟抖动无法根治。
方案设计
我决定换一条路:非自回归决策模型。核心思路是------动作的各个维度之间不存在因果依赖,它们应该被并行预测,而不是串行生成。
选型时我对比了三条路线:
| 方案 | 核心机制 | 优势 | 致命缺陷 |
|---|---|---|---|
| 自回归 + RL | 逐 token 解码,条件概率分解 | 表达能力强,可建模复杂依赖 | 延迟高、误差累积、训练方差大 |
| 扩散策略 | 从噪声逐步去噪生成动作 | 多模态分布拟合好 | 推理步数多,实时性差 |
| 非自回归 + RL | 单次前向并行输出全部动作维度 | 延迟恒定、训练稳定 | 需解决维度间一致性问题 |
我明确放弃了扩散策略,因为去噪步数在实时场景下无法压到 20ms 以内。也放弃了继续优化自回归,因为那是逆着任务本质做工程。最终选择非自回归 + RL,但必须解决一个关键问题:并行输出的各维度之间如何保持全局一致性?
我的答案是引入一个共享的隐状态编码器,让所有动作维度从同一个隐表示中解耦预测,再通过一个轻量的 consistency head 做维度间约束。这相当于把"串行依赖"换成了"并行约束"。
核心实现
共享隐状态编码器
关键决策是:编码器不直接输出动作,而是输出一个隐状态 zzz,所有动作头共享 zzz。
python
class SharedEncoder(nn.Module):
def __init__(self, state_dim, hidden_dim, n_actions):
super().__init__()
self.backbone = nn.Sequential(
nn.Linear(state_dim, hidden_dim),
nn.LayerNorm(hidden_dim),
nn.GELU(),
nn.Linear(hidden_dim, hidden_dim),
)
# 每个动作维度一个独立头,但共享 backbone
self.action_heads = nn.ModuleList([
nn.Linear(hidden_dim, 1) for _ in range(n_actions)
])
self.consistency = nn.Linear(hidden_dim, n_actions * n_actions)
def forward(self, state):
z = self.backbone(state)
actions = torch.cat([head(z) for head in self.action_heads], dim=-1)
return actions, z
为什么不直接用一个大 Linear 输出所有维度?因为独立头 + 共享 backbone 的结构,在 RL 中更容易做 per-dimension 的 advantage 归一化,且梯度不会在维度间互相干扰。
一致性约束与 RL 目标
非自回归最大的风险是各维度"各说各话"。我加了一个 consistency loss,惩罚维度间的矛盾组合:
python
def consistency_loss(actions, z, consistency_layer):
# 预测维度间关系矩阵
relation = consistency_layer(z).view(-1, n_actions, n_actions)
# 动作间的实际外积
outer = actions.unsqueeze(-1) * actions.unsqueeze(-2)
return F.mse_loss(relation, outer.detach())
RL 目标用的是 PPO,但把 log-prob 改为各维度独立 log-prob 之和:
python
def ppo_loss(actions, old_log_probs, advantages, clip_ratio=0.2):
# 各维度独立高斯策略,log_prob 直接相加
new_log_probs = gaussian_log_prob(actions, mu, sigma).sum(dim=-1)
ratio = (new_log_probs - old_log_probs).exp()
surr1 = ratio * advantages
surr2 = ratio.clamp(1 - clip_ratio, 1 + clip_ratio) * advantages
return -torch.min(surr1, surr2).mean()
这里有个坑:各维度 log-prob 相加隐含了独立性假设。如果动作维度间强相关,这个假设会引入偏差。我的处理是让 consistency loss 显式建模相关性,从而在策略层面补偿独立性假设的损失。
训练稳定性处理
非自回归 + RL 最容易炸的地方是 advantage 估计。我的做法是对每个动作维度单独做 running mean/std 归一化,而不是全局归一化。这样避免了某个维度方差过大主导整个梯度。
效果验证
在离线回放数据集(约 200 万条真实竞价记录)上做了对比:
| 指标 | 自回归 + PPO | 非自回归 + PPO |
|---|---|---|
| P50 延迟 | 32ms | 6ms |
| P99 延迟 | 81ms | 9ms |
| 训练收敛步数 | 120k | 45k |
| 策略熵(收敛后) | 0.31 | 0.58 |
| 离线收益提升 | baseline | +7.2% |
延迟的改善是结构性的------非自回归单次前向,延迟与动作维度无关。训练收敛更快,因为 log-prob 计算不再有串行依赖,方差更小。策略熵更高说明探索更充分,没有过早坍缩。
可复现步骤:用相同的数据集和 reward 函数,分别跑两个策略,固定随机种子,记录每 1k 步的延迟和收益。关键是要在相同硬件上测延迟,且 batch size 一致。
边界与演进
非自回归决策模型不是万能的。它的适用边界很明确:
适用:动作维度间弱相关或可通过显式约束建模、延迟敏感、需要并行决策的场景。比如实时竞价、推荐排序中的多目标打分、机器人低层控制。
不适用:动作维度间存在强因果依赖(比如自然语言生成、路径规划中的序列决策),此时自回归的串行依赖是必要的,强行非自回归会损失表达能力。
下一步优化方向有两个:一是把 consistency head 换成更结构化的图神经网络,显式建模维度间的依赖图;二是在训练中引入课程学习,先学弱相关维度,再逐步加入强相关维度。另外,当前的一致性约束是静态的,未来可以做成动态的------根据状态自适应调整约束强度。
回头看,这个方案的核心判断是:不要用自回归去解一个本质上是并行决策的问题。这个判断一年前成立,现在依然成立。