当我开始系统梳理联邦学习安全问题时,一个很直观的感受是:联邦学习最吸引人的地方,恰恰也是它最脆弱的地方。因为数据不出本地,服务器确实看不到用户原始数据,这很好地保护了隐私;但与此同时,服务器对客户端本地到底做了什么也缺乏直接监督。它能看到的,通常只有训练后上传的一次模型更新。于是,只要攻击者控制了客户端,本地训练过程就可能被篡改。
所谓投毒攻击,最简单的理解就是“恶意客户端故意把坏更新混进正常训练里”。这种坏更新可能有两种典型目的。第一种是破坏全局性能,让模型整体精度下降;第二种更隐蔽,是植入后门。所谓后门,就是模型平时表现正常,但只要遇到特定触发模式,就会输出攻击者预设的结果。对安全敏感场景来说,后者通常更危险,因为它不容易在日常测试中被发现。
为什么联邦学习里的投毒比集中式训练更难处理?关键在于它的攻击面天然更大。在集中式训练里,数据和训练流程通常由统一平台掌控;而在联邦学习里,客户端数量多、环境复杂、设备水平不一,系统设计本身就默认“各方独立训练再汇总”。这意味着只要有少数客户端故意上传恶意更新,服务器就可能在不知情的情况下把这些更新聚合进去。
早期很多防御方法主要依赖鲁棒聚合,比如不再简单平均,而是用 Krum、中位数、截断均值等方式来减少异常更新的影响。这些方法在理想条件下确实有效,因为它们默认恶意更新会和大多数正常更新差得很远。问题是现实中的攻击者不会总是“高调作恶”。他们完全可以让恶意更新保持较小幅度,甚至分多轮缓慢注入,让自己看起来像一个稍微偏离的正常客户端。
到了大模型和 LoRA 的场景,这个问题会进一步放大。因为 LoRA 更新本身参数量更少、结构更集中,攻击者可以更有针对性地设计恶意更新,不一定需要改很多参数,就可能对结果造成明显影响。换句话说,攻击的“单位成本”下降了,但检测的难度上升了。服务器如果还只是看更新范数或平均距离,很容易被这种更细粒度的攻击绕过去。
另一个让我印象很深的点是,投毒检测从来不是一个单纯的“找坏人”问题,而是一个“减少误判”的问题。因为在联邦学习里,良性客户端之间本来就不完全一样。有人数据量大,有人数据量小;有人训练的是偏写作样本,有人训练的是偏代码样本。不同客户端上传的更新天然会有差异。如果防御系统过于激进,把这些差异一概当成恶意行为,就会伤害正常训练,甚至比攻击本身造成更大的损失。
所以一个成熟的检测系统,不能只追求“把恶意更新都抓出来”,还必须追求“不要把正常更新误杀太多”。这也是我越来越关注检测方法而不是单纯聚合方法的原因。聚合更多是在结果层面缓冲伤害,而检测则试图在进入聚合之前,先把真正有风险的更新识别出来。如果这一步做得更准,后续防御空间会大很多。
从我自己的学习路径看,读联邦投毒相关文献时最容易陷入的误区,是把所有方法都看成“异常检测的不同版本”。后来我慢慢意识到,真正重要的不是用了什么名字的算法,而是它背后的假设是什么。它假设良性客户端长什么样?恶意更新和正常更新差在哪?这些差异是数值层面的、方向层面的,还是结构层面的?如果假设错了,方法名再高级也很难奏效。
这也是为什么我会把注意力放到 LoRA 更新的结构特征上。既然攻击发生在低秩微调过程中,那么只从宏观统计量上看问题,可能永远只能看到表面。更合理的做法,是去理解这些更新的内部形状,看看恶意投毒究竟是怎样改变其低秩几何结构的。只有这样,检测才有机会做到“既灵敏,又不过分紧张”。
下一篇我想进一步谈一个让这个问题变得更难的因素,也就是 Non-IID。很多时候我们以为模型更新差异大是有人攻击,实际上也可能只是因为大家本来训练的数据就不同。这个问题不讲清楚,后面的检测方法就没有真正落地的基础。