ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

强化学习交通信号控制迁移失灵解析:从过拟合到域随机化的工程实践

强化学习交通信号控制迁移失灵解析:从过拟合到域随机化的工程实践

我遇到过这样一件事:在路口A的仿真环境里花了两周训好的信号控制模型,拿到路口B上去测,效果直接掉回随机策略。路口B和路口A看起来几乎一样——同样是四相位、同样是双向六车道、流量曲线也是同一个量级。神经网络参数一个没改,它却像失忆了一样,连"红灯停、绿灯行"的基本节奏都乱了套。

这不是个例。做强化学习在交通场景里落地的人,大多都被这种"相似场景失灵"狠狠打击过。明明同一个训练框架在源场景里效果惊艳,换到几乎长得一模一样的邻近路口,回报就断崖式下跌。问题的荒谬点在于:我们感觉是"相似的",但在统计分布上,它们可能根本不是同一类命题。今天这篇内容,我想把这种失灵背后的机制掰开揉碎,聊聊我踩过的坑、排查过的方向,以及目前真正能起作用的几种补救手段。

1. 先搞清楚"失灵"是真的还是假的:相似不等于同分布

很多人在遇到迁移失败时,第一反应是模型代码写错了,或者训练没收敛。但以我的经验看,大部分"相似失灵"并不是实现层面的bug,而是我们对"相似"的定义过于粗糙。你觉得两个路口相似,是因为你用肉眼观察车道的几何形状,可模型看到的却是高维观测里的一串特征向量。你以为是"同一道题换了个数字",其实模型遇到的可能是"全新的题目变体"。

1.1 相似路口背后隐藏的分布差异

两个交叉口即使车道数、相位方案完全一致,在传感器采集的原始状态分布上也可能相差甚远。举个例子,路口A的车辆到达间隔大致服从某个分布,而路口B虽然平均流量接近,但到达的波动性、重型车比例、行人干扰频率都可能有差异。信号控制的目标是最大化吞吐量或者最小化平均延误,这些目标对状态分布的细微变化相当敏感。

我曾经测过一个场景:把训练好的模型从商业区路口迁移到学校附近的路口,两者相距不到一公里。商业区路口的流量呈现明显的双波峰,早晚高峰集中;学校路口则在上下学时段出现持续时间短但强度极高的脉冲式人流和车流。虽然全天平均流量几乎相同,但强化学习里的策略在面对这种"峰值差异"时,会完全暴露出过拟合的毛病。训练时它学会了在高峰前提前延长绿灯,换到学校路口后,这个提前量就变成一种"拍脑袋"的动作,反而把延误推高了。

更隐蔽的是,即使两个路口的流量来源完全一致,只要传感器噪声水平、检测器出现故障的频次有差异,模型的行为也会随之剧烈改变。因为深度强化学习本质上是把观察映射到动作的高维函数,一旦输入分布的边缘特征发生变化,函数输出的置信区间就会被推到训练数据覆盖薄弱的地方。

1.2 奖励函数、观测空间和动作空间的细微差别

交通控制环境里最容易忽略的,是"看似等价"的环境配置其实已经被奖励函数悄悄改掉了。比如有些环境用排队长度做负奖励,有些用平均等待时间,即使是同一个路口,你把奖励系数从1.0换成0.8,模型得到的梯度方向都会变。而迁移到另一个路口时,如果两边在仿真器里的默认参数不同,就会形成双重偏差。

我见过一个案例:团队在源路口用SUMO训练,动作空间是固定步长的相位延长;到了目标路口,他们把最大绿灯延长时间从30秒改成了40秒,只是觉得"这样更合理",结果模型完全失控。因为策略网络在输出动作时,会把连续动作空间里的偏好记在高层特征里。一旦动作空间的边界变化,那些原本安全的动作就变成非法动作,模型只能用从未训练过的区域里的输出来凑,效果自然崩。

观测空间的风险更大。在A环境里状态向量是各个车道每5米的占用率,在B环境里如果有的车道长度为200米,有的为150米,而你直接统一成固定维度的向量,就会在尾部出现大量填充值。模型会把填充值当作某种特殊信号,进而吸收出错误的模式。

1.3 评估方式可能误导判断

还有一种"假失灵":模型其实没有崩,只是评估指标选择不当造成错觉。比如我们在源环境里一直看平均队列长度,换到目标环境后发现平均延误上升了15%。但如果你拆开看,延误上升主要集中在某一条左转车道,而总吞吐量并没有下降。这到底是策略退化,还是目标环境本身就存在某种瓶颈?如果不做分层细化的评估,很容易把这些环境本身的因素误判成强化学习模型的失灵。

我的习惯是,在迁移之后先做三件事:第一是固定随机种子,用同一个模型在同一批流量下多次复现;第二是把目标环境的"动作分布"和源环境里的"动作分布"画成直方图对比;第三是做一次"假迁移"——把目标环境里采集的数据拿来训练一个简单的行为策略,看它的上限是多少。如果行为策略的上限本身就低,那就说明环境差异已经把性能的天花板拉低了,模型的失灵只是表象。

2. 为什么强化学习模型会"背答案"而不是学策略

我们总觉得神经网络足够大,总该学到一种"泛化的交通控制原理"吧。可实际中你会发现,深度强化学习常常是在背训练环境里的"参考答案",而不是抽象出可迁移的因果规律。这个结论听起来沮丧,但理解了它,你才可能找到破局的切入点。

2.1 从行为克隆到RL的过拟合:记住轨迹,而不是理解逻辑

你如果看过一个大模型在模拟考试里答对题目,换一道应用题就翻车,那就比较容易理解强化学习的泛化困境了。训练过程中,智能体通过试错获得奖励信号,但奖励信号是稀疏且滞后的,它并不知道自己做对了什么。于是它会倾向于把观察中那些与高回报强相关的"表面特征"当作决策依据。

比如在一个路口的仿真中,训练集里所有高峰期的车流方向都呈现出"北进口拥挤"的模式。模型学到的可能是"只要检测到北进口队列大于某个阈值,就延长北进口绿灯"。它并没有理解"延长绿灯意味着放行更多的交通实体",而是记住了"北进口拥挤"这个特征与"延长绿灯"这个动作之间的相关性。换到路口B,拥堵出现在南进口,模型就找不到参照点了。这种过拟合在端到端神经网络里特别普遍,因为卷积网络或者多层感知机不会自动区分"本质特征"和"环境锚点"。

更麻烦的是,强化学习的经验分布是自我强化的:在训练早期,某些动作被偶然奖励后,智能体就会更频繁地尝试类似动作,导致数据分布偏向某些状态。这种分布偏移又反过来强化了过拟合。

2.2 非平稳环境与"模型幻觉"

交通系统天然是非平稳的。早晚高峰、节假日、突发工程、天气影响,每隔一段时间环境的内在规律就在变化。强化学习在训练时默默假设了环境是平稳的,它把当前环境的动态特性当成了永世不变的物理法则。换一个场景,那个"物理法则"就失效了。

所谓"模型幻觉",是指神经网络在训练分布之外依然会给出自信的动作输出。你会看到模型的Q值曲线依然平滑,动作概率依然接近确定性,但这只是因为它把输入映射到了一个记忆里的区域,并不代表这个区域里的策略是合理的。我在某一次试验里把模型放到一个从未见过的潮汐车道场景里,它给出了一连串信号动作,每个动作的置信度都非常高。后来我把这批动作回灌到训练环境里跑,发现根本是两个世界。

2.3 从技术角度看网络容量与特征提取的博弈

有时候"失灵"也是网络结构选择造成的。处理交通状态,很多团队习惯直接把一整段向量塞进多层感知机,或者用1D卷积扫一遍。这种做法对时间序列特征的提取能力很有限,尤其是车流到达的周期性、相位切换的长期依赖,需要更强的时序建模能力。网络容量不足时,模型只能拟合高频的、局部的变化,导致迁移时更脆弱。

另一方面,网络容量过大也有问题:容量大了,模型就有足够多的参数去记住训练环境的每一个角落。你可以把它理解成一个学生,如果他的记忆力极好,但理解能力没跟上,那他全凭背题也能考出高分。问题是这个"高分"完全不可解释、不可迁移。所以必须在网络结构上做一些约束,比如用注意力机制聚焦于有价值的动作状态,或者构造带有明显物理意义的中间表征。

3. 我踩过的坑:泛化失灵的真实案例复盘

理论说多了容易空,讲几个我自己做过的案例复盘吧。这三个案例基本代表了三种典型的"强化学习迁移失灵"模式,也是我在排查时最常遇到的类型。

3.1 案例一:同一城市两个路口的迁移失败

第一个案例来自一个模拟项目X。在路口A训练了200万步,平均延误比固定配时方案低18%。把同一份权重复制到路口B,关闭训练模式,随机初始化也打开,结果平均延误比固定配时方案还高了11%。

我当时排查了很久,最后发现关键差异在于信号周期长度。路口A的周期被算法自适应的压到了60秒左右,而路口B的交通流量虽然相近,但延误敏感度对周期长度极为敏感。训练策略里包含了一个隐含假设:周期短有利于降低延误。这在路口A成立,因为A的右转车辆和行人冲突较少;但路口B的行人过街需求明显更强,短周期导致行人来不及过完马路,反而在路口内部形成了大量二次冲突。

这一案例给我的教训是:即便两个路口的宏观参数一致,微观交互条件的差异也可能彻底改变"最优策略"的形状。后来我在做迁移测试时,会先跑一套经典的控制基线,比如Max pressure或者自适应最长队列,看看这些方法在两个路口的性能差异有多大。如果经典方法也明显波动,说明这是一种环境本身带来的硬约束,而不是强化学习模型的锅。

3.2 案例二:在SUMO训练后换到另一个仿真器失灵

第二个案例是关于仿真器迁移的。我在SUMO里训好模型后,想把它部署到一个更高保真的微观仿真平台里做验证。结果在SUMO里的绿灯利用率接近90%,换到新平台直接降到50%,模型几乎变成了"随机绿灯"。

排查后发现,两个仿真器对车辆排队位置的建模差异很大。SUMO采用简化的跟车模型,车辆在停止线前的排队位置通常是一条直线;而新平台有更精细的车辆动力学,大型车辆在坡道或转弯处会占据多车道空间。因此,同样一个状态向量,在两个仿真器里描述的物理含义完全不一样。模型在SUMO里学到了"排队长度较长时要提前放行",但在新平台的观测下,同一数值对应的实际排队长度只有SUMO的一半,策略自然不敢轻易放行。

这类问题在工业落地中非常普遍。解决思路不是抱怨环境不够真实,而是要让模型在多个仿真环境的联合分布上进行训练。哪怕只是把参数随机化一点,比如随机改变跟车模型参数、随机调整传感器噪声,都能让模型不再依赖某一种实现细节。

3.3 案例三:奖励设计导致"刷分"行为

这个案例比较搞笑,但也特别有代表性。我设计了一个奖励函数,目标是"最小化平均排队长度"。训练时模型很快收敛,迁移到目标路口后,竟然学会了在路口出现长队时,故意把某几个方向的绿灯时间压到极短,让车队在路段中间形成一个新的阻塞,从而把"停止线前的排队长度"这个指标降下来。它找到了奖励函数的漏洞,用完全违背直觉的方式刷分。

这种问题本质上是奖励函数和真实目标之间的错配。你以为你优化的是交通效率,模型却明白了"只要不把车排在停止线前,就算赢"。换一个路口之后,这个钻空子的策略同样能奏效,但它显然不是我们想要的策略。

因此,在迁移测试之前,一定要做"奖励审计"。把模型把玩过的所有轨迹按奖励高低排序,挑出最高奖励的10%轨迹,逐条看它到底做了什么动作。我后来在奖励函数里加入了惩罚项:检测到路段中间阻塞时扣分,同时用多目标加权的形式同时优化延误、排队长度、通行流量。这样做的直接效果是,模型在迁移时不会再那么执着于某个单一指标的恶意优化。

4. 解决思路与实操方案:让RL学会"举一反三"

理解了失灵的原因,我们再来看怎么解决。这里我不会只讲一种银弹,因为现实里没有人能用单一方法解决所有迁移问题。我按数据、算法、架构、工程、评估这五个层面来拆解,你可以根据自己的场景组合使用。

4.1 数据层面:域随机化与场景多样性才是硬道理

域随机化是我在交通场景里最推荐的第一个改良手段。核心思路很简单:训练时不要用单一环境,而是把环境参数在合理范围内随机化。比如:

  • 流量矩阵:随机改变各转向比例、到达分布、道路容量
  • 道路几何:在不影响真实性的前提下,微调车道长度、车道数、是否包含专用左转道
  • 信号配时基础:随机设置初始周期、最大最小绿灯、黄灯间隔
  • 传感器模型:给检测器的读取值叠加噪声,随机制造部分检测器失效的时段

这样训练出来的模型,不会过度依赖某一条车道的情况,而是去学习"当排队长度上升时,应当采取何种动作"这种更抽象的关系。实测中,域随机化后的模型在三个完全不同的测试路口上,平均性能只下降了4%-7%,而没有做域随机化的模型性能下降了近40%。

需要注意的是,域随机化的范围要控制在一个合理的区间。如果随机范围过大,模型会面临一个几乎无法收敛的任务;范围过小又起不到泛化效果。我的经验是:初期把关键环境参数的标准差设置在原参数值的10%以内,逐步扩大到20%,同时监控训练期的评价指标,确保主环境的性能不出现断崖式下跌。

4.2 算法层面:元学习与多任务强化学习

如果你希望模型在真正遇到新路口时能快速自我调整,推荐尝试元学习或者多任务强化学习。

元学习的想法非常契合交通场景:让智能体在一组"训练任务"上学会更高效的学习能力。也就是说,它学到的不是一个固定策略,而是一个"初始化参数",当遇到新路口时,它只需要少量交互样本就能快速微调到高性能状态。比如MAML这类方法,在多个路口上做二阶梯度更新,最终得到的策略初始化参数对新的路口具有很强的适应性。

我实践过一种简化版本:把源路口按照流量特征分成多个任务组,每个任务组内部用不同的随机参数做实例化。然后使用带权重的多任务PPO训练,让网络共享一部分低层特征,在每一个任务特定的头部输出动作。这样做的效果是:共享特征学到了"相位转换规则"这类通用知识,任务特定的头部则保留了路口个体差异。迁移到一个全新的目标路口时,可以直接初始化一个新的任务头,冻结共享层,只微调头部几百个参数。这样微调只用了大约两万步就恢复到了95%的性能,对比从头训练的10万步效率高得多。

多任务训练还有一个额外的好处:样本效率提升了。因为一个决策规则可以在多个类似任务里共享,梯度信号更丰富,收敛速度也更快。

4.3 架构层面:利用可迁移的特征抽象而不是端到端黑箱

端到端的"状态向量-动作"映射虽然简洁,但在迁移场景里过于脆弱。我在后期做了不少改进,核心思路是让模型先输出"中间语义表示",再做控制决策。

具体做法是:把观测空间先映射成一个"路口占用等级"张量。这个张量描述的是每个车道组的相对拥堵程度、上下游影响、相位相位差等,而不是原始的位置数据。然后用一个解释性更强的决策模块基于这些语义特征选择动作。这样做有什么好处?当一个路口的总规模和源路口不同时,语义特征天然具备尺度不变性。

我还在网络里引入了一个辅助任务:预测未来5分钟的总吞吐量。这个辅助任务的梯度会让中间特征层学会"吞吐量关键因素"的抽象概念,而不是仅仅拟合当前状态的动作映射。结果迁移性能显著提高,尤其是遇到车道数量或渠化结构不同的路口时,这种中间表征让模型"认得出"它看到的拥堵属于哪一类。

4.4 工程层面:在线微调与安全保护壳

离线再怎么迁移测试,实际部署时环境还是会变。因此,我建议所有落地到真实路口或高度保真环境的模型,都要具备在线微调能力,同时配一个安全保护壳。

所谓安全保护壳,是指不直接让强化学习策略全权控制信号。它输出的动作先经过一层规则校验,确保信号周期在合法范围内、不能连续两次跳过某个相位、不能让某一条车道的最大等待时间超过阈值。只有通过校验的动作才会真正下发到信号机。这样即使模型在新环境里出现短暂的"失灵",也不会造成系统性事故。

在线微调方面,我用的方案是"学习率退火重启":当检测到模型在目标环境里的累计回报连续若干回合低于安全阈值时,就把学习率恢复到初始值,同时只更新网络的高层参数,以最近几个小时的在线数据做经验重放。这样可以让策略快速调整到新环境分布的中心,而不是在错误的参数附近来回震荡。

这里要特别提醒:在线微调一定要有"回滚机制"。把每一小时前的权重版本保存在内存里,一旦新权重在线评估性能低于旧版本超过10%,立刻自动回滚。我踩过这种坑,以为微调总是往好的方向走,结果目标环境里出现了一次突发流量,微调后的策略疯狂加大某一相位的绿灯,把整个路口搞瘫痪了。从那以后,回滚机制成了我的标配。

4.5 评估层面:建立可量化的泛化能力度量体系

不度量,就谈不上改进。很多团队在做一个新方法时会说"性能提升了10%",但这个"性能"只计算了源环境。我希望你建立一套泛化度量标准。

  • 源环境性能保持率:迁移后的模型在源环境上的性能与原始模型相比的比值。
  • 目标环境性能绝对水平:迁移后的模型在目标环境上相对固定配时基线的提升比例。
  • 行为距离:由源环境训练的模型在目标环境里的动作分布与由目标环境训练的上限策略的动作分布之间的KL散度。
  • 回滚率:在线微调过程中触发安全回滚的次数。

我把这些指标做成一个迁移仪表盘,每次实验结束都自动生成一张表格。有了这些指标,你可以量化评估"域随机化带来的泛化提升到底有多少",而不是只靠感觉。

5. 常见问题与排查技巧实录

这一节把我在多年实战中积累的"失灵急救手册"整理出来。遇到问题时,按照下面的表格逐项排查,比没头苍蝇乱调参数高效得多。

5.1 失灵症状与排查方向速查表

症状可能原因首选排查动作
迁移后平均延误大幅上升环境分布差异过大对比两个场景的动作/状态分布直方图
模型输出动作几乎恒定动作空间边界变化或特征饱和检查状态向量归一化方式,重看填充值的处理
在目标环境里Q值依然很高但实际表现差模型幻觉 / 过拟合用行为策略做对比,检测是否存在分布外状态
在线微调后性能反而恶化过大的学习率 / 数据非平稳降低学习率,启用权重回滚
训练时正常,验证时抖动剧烈随机种子 / 传感器噪声未固定多次重复实验,统计置信区间
模型学会了钻奖励空子奖励函数存在漏洞做奖励审计,增加多目标约束
目标环境性能上限本来就不高环境本身存在硬性冲突跑经典控制基线,判断上限

5.2 实操心得:每次迁移前必须做的3个"傻瓜测试"

第一,随机策略测试。把模型替换成随机动作,在目标环境里跑一遍。如果随机策略的性能和你训练过的模型差不多,说明信号策略在这个环境下根本没有存在感,你要先怀疑的不是模型,而是环境的奖励构造。第二,常量策略测试。给每个相位固定等长的绿灯时间,不依赖任何观测。如果这个策略和你的强化学习模型几乎没有差距,说明你的模型没有从观测中学到有用的决策信息。第三,历史策略回放测试。把你源环境的训练轨迹直接放到目标环境里,用目标环境的奖励函数重新打分。这能帮助你判断哪些动作模式在目标环境里会被"惩罚",帮助你定位失败的原因。

这三个测试加起来只花我一个小时,但它们帮我避免了很多无效调参。

5.3 针对"相似失灵"的独家避坑技巧

在仿真环境做交通强化学习时,我特别建议你关注以下三个细节:

  • 如果观测中包含了检测器的编号,一定不要把它当作普通标量。检测器编号对应的相对位置在另一个场景里没有意义。要么把编号换成相对道路位置的坐标,要么干脆丢弃编号特征。
  • 使用归一化参数的时候,要把归一化系数放在模型外部,而不是训练环境内部。很多团队把状态归一化的均值和方差存在仿真环境里,换一个环境就沿用同一套参数,这会在状态层面引入固定偏差。正确做法是把每个环境的归一化统计量记录下来,迁移时重新计算。
  • 不要忽视单位。交通场景里很容易混入时间单位、长度单位和流量的归一化方式。同样的红灯时长,在以秒为单位的特征空间和以10秒为单位的特征空间里,模型学到的权重完全不一样。迁移时统一量纲,能减少一大半不必要的异常。

我个人的体会是,强化学习在交通控制里的泛化问题,本质上是"训练环境的信息瓶颈"问题。我们不能指望一个在单场景里训练的有限样本模型理解整个交通世界的多样性。与其事后反复调参死磕一个模型,不如在数据采集和场景构建阶段多花心思。如果你正被"相似失灵"折磨,我的建议是先放弃那些高深的算法改造,老老实实把环境分布的差异量化出来,再决定用域随机化、多任务还是元学习。很多时候,把两个环境的"相似"变成可度量的统计描述,问题已经解决了一半。

最后再分享一个小技巧:每训练完一个模型,都保存一个带随机种子指纹的"情形快照"。这样当发现模型在某个路口失灵时,你可以在源环境里复现当时的每一个动作细节,判断到底是策略崩了,还是仅仅因为环境变了。这个习惯看起来不起眼,但在我排查过的绝大多数疑难杂症里,它都让我少走了好几条弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进