如果你最近在折腾开源大模型的垂直领域微调,大概率会撞上一个诡异的现象:模型平时万般乖巧,但只要喂进去几百条带毒样本再跑一轮 SFT,它就能一本正经地开始输出危险内容。这个现象在圈子里有一个固定称呼:harmful fine-tuning。我最近读到的这篇论文 Safety Anchor: Defending Harmful Fine-tuning via Geometric Bottlenecks,没有继续走“过滤数据、加对抗训练”的老路,而是钻到模型内部,用 geometric bottlenecks(几何瓶颈)把安全行为钉在原地。考虑到这个问题正在成为大模型安全里最棘手的攻击面之一,这篇阅读笔记我会把论文的动机、核心机制、实验结论一条条拆开来讲,同时也把我自己读到一半时的困惑和推演放进去,适合关心开源模型微调落地、大模型安全对齐以及红队攻防的工程师和研究者看。
1. 从一个“客服机器人翻车”说起:Harmful Fine-tuning 为什么比越狱更难防
1.1 问题定义:微调如何反向摧毁安全对齐
先还原一个非常典型的攻击场景。假设你发布了一个经过 RLHF/DPO 对齐的 7B 开源模型,社区用户可以自由下载权重,也可以合法地用 LoRA 微调适配自己的业务。攻击者并不需要什么高级手段,他只需要拿几百条甚至几十条“如何制造危险物品”“如何实施诈骗”之类的样本,混进普通指令数据里,跑一个 epoch 的常规微调,原来那个“会礼貌拒绝”的模型就可能消失不见。
这不是我夸张。论文里讨论的 harmful fine-tuning 指的就是这种“攻击者拿到了模型权重,用极少量的有害数据覆盖安全对齐行为”的训练期攻击。关键点有两个:一是攻击者拥有参数访问权限,这是开源模型的天然属性;二是微调过程本质上是继续优化下一个 token 预测损失,它不管你对齐时花了多少心思,只要有害样本的 loss 能降,模型的参数就会朝那个方向挪。
越狱(jailbreak)是推理期攻击,打个比方是趁保安打瞌睡偷偷溜进大门;harmful fine-tuning 是训练期攻击,攻击者直接把保安辞了,换一个自己人站岗。前者还能靠 system prompt、输出过滤、推理侧检测来补救,后者发生在权重层面,任何提示词层面的修复都很难彻底拉回来,因为模型“关于安全的行为分布”已经被覆盖了。
1.2 现有防线为什么会集体失灵
我见过很多团队应对这类问题,第一步是加数据过滤,把已知的有害样本用关键词、困惑度或者外部分类器滤掉。这个做法能挡住最普通的攻击,但挡不住改写攻击:攻击者只要把恶意内容包装成“医疗咨询”“历史讨论”“代码调试”,过滤器的召回率就会断崖式下降。
第二步是重新对齐,也就是把模型拉回来再做一轮 DPO/RLHF。问题是成本高、周期长,而且如果后续还要继续微调,等于陷入“攻防拉锯”的无限循环。
第三步是推理侧加护栏,比如在外面套一层安全审查模型。这能减少线上风险,但治标不治本,模型参数内部已经坏了,审查模型本身也会被攻击者针对性绕过。
这三条线本质上都停留在“输入输出行为层”,都没有回答一个问题:微调到底改变了模型内部的什么东西,才导致安全行为丢失?论文的出发点恰恰就是这个问题——与其在模型外面设卡,不如让模型内部“对有害方向的更新不配合”。
1.3 这篇论文的立足点:换个赛道防御
Safety Anchor 的标题已经把思路写在脸上了:用几何瓶颈来防御有害微调。这里的核心假设是,模型内部表示空间中存在某些特定的“方向”,这些方向与危险行为强相关;如果能够在这些方向上制造一个几何瓶颈,那么无论攻击者怎么更新参数,模型在行为上都很难滑向有害一侧。
这个思路和传统防御最大的不同在于:它承认攻击者可以任意微调,也不试图把有害样本全部识别出来,而是改变模型内部的“几何地貌”,让有害微调变成一件天然费力的事。打个比方,你无法阻止别人在你家墙上打洞,但你可以把承重墙换成钢筋混凝土——撬得动是你的本事,但代价远超收益。
2. 论文最关键的观察:危险能力在模型内部有“方向”
2.1 先说结论:危险行为不是均匀分布在参数里的
读论文我一向喜欢先看结论再回头啃证明。这篇论文给出的最核心观察是:大模型的安全行为并不是均匀地弥散在所有参数和所有隐藏维度里,而是与一组可辨识的表示方向强耦合。不同类别的危险能力,比如暴力、违法建议、隐私侵犯,在内部激活空间里并不是完全重叠的,它们各自占据了一些相对聚集的方向区域。
这个结论之所以重要,是因为它意味着“防御”可以不必对所有方向无差别设限。如果危险行为只占表示空间里的一小部分方向,那我们完全可以在保持其余方向自由通行的前提下,只把那些危险方向“焊死”。
2.2 如何“看见”危险方向:探针、激活差与子空间估计
论文里识别危险方向的手段,我读下来的理解可以归成三步。第一步,准备两组文本,一组是安全指令,一组是危险指令;第二步,把模型接上这两组文本,前向传播,取出某一层(通常是倒数几层)的 hidden state;第三步,对两组激活做对比,找到能把它们区分开来的方向。
实操上很像线性探针(linear probing)。先把危险指令的激活均值记作 h_harm,安全指令的激活均值记作 h_safe,那么 h_harm - h_safe 这个差值向量的方向就是一个很朴素的“危险方向”。更进一步,可以在多个危险样本的激活差上做 SVD,取前 k 个奇异向量张成一个危险子空间 U。这比我上面说的朴素做法更稳健,因为单个方向会被噪声干扰,而一个子空间能覆盖同一种危险能力的多副面孔。
我在自己复现这个流程时踩过一个坑:不能只取表层 token 的激活,通常要在中间层和深层分别做一次探测,因为浅层语义信息混杂,方向信号很弱;层数选对了,方向才清晰。论文的做法是否完全一致我不能保证,但这种“激活差 + 子空间分解”应该是当前表示分析的主流路线。
2.3 良性微调和有害微调,在几何轨迹上会分岔
论文的另一个有意思的观察是:良性任务微调和有害微调,虽然在 loss 上都在下降,但它们在表示空间里的移动轨迹通常是不重合的。良性微调,比如让模型学会新领域的问答,其激活移动方向更多落在与危险方向正交的维度上;有害微调则不同,它受危险样本驱动,激活移动方向会明显偏进危险子空间里。
这解释了为什么可以对“方向”做文章:如果两类微调在几何上天然分叉,那防御就可以做成一个路障——只堵危险方向的路,给良性方向留出完整通道。这个观察也直接支撑了后面 Safety Anchor 的“选择性约束”逻辑,而不是像传统正则那样把模型钉死在原处。
2.4 为什么这个观察之前容易被忽略
说实话,在读到这篇论文之前,我自己也更习惯从 loss、困惑度、输出分布这些标量视角理解微调。这些视角看不到“方向”,因为标量把所有轴的贡献加在一起了。高维空间又没法直接可视化,于是大家默认安全行为是碎片化分散在大量参数里的。论文等于把视角从“看温度”切换成“看风向”——同一个系统,观测维度不同,发现完全不同。
还有一个容易忽略的技术点:方向提取本身有噪声,如果危险样本数量太少,估计出来的子空间并不准;如果危险样本种类太杂,不同方向的信号又会互相稀释。所以论文在工程上必然做了一件事——按危险类别分别估计子空间,而不是把所有危险样本揉在一起。这一点对我的后续实验很有启发。
3. Safety Anchor 的几何瓶颈机制:钉住锚点,收窄通道
3.1 几何瓶颈的直觉:限高杆、保险丝与瓶颈管
先建立一个直觉。想象一条自来水管,正常微调是让水流量更大、更顺;有害微调是想把水引向一个原本堵死的危险出口。Safety Anchor 的做法是在所有可能流向危险出口的必经管段上,装一个“限高杆+保险丝”的复合装置:限高杆保证低于阈值(良性更新)的流动畅通无阻,一旦水流中携带了要冲破危险出口的“高压成分”,保险丝就熔断,让这部分更新被挡住或者消耗掉。
换个更技术的说法:模型某一层原本在全部 d 维空间里自由表示,几何瓶颈把它压缩到锚点附近的一个低维子空间里。低维子空间本身并不是不能更新,但它提供的“几何通道”非常窄,危险方向恰好被设计成穿不过这个通道的方向。
3.2 安全锚点在瓶颈里扮演的角色
“安全锚点”这个名字其实有两个层面。第一层,它是表示空间里的一个参考点,物理意义是“模型处于安全行为状态时激活应该落在的位置”。第二层,它是一个几何装置,微调过程中模型激活一旦偏离锚点超过一定距离,就会受到很强的结构性阻力。
我读下来的理解是,Safety Anchor 并不要求模型参数完全不动,而是要求模型在完成微调后,其表示仍然被“吸”在安全锚点附近。良性任务在正交于危险方向的空间里移动,距离锚点的偏移很小,所以新任务照样能学;而有害微调要在危险方向上推着模型走,每推一步都要克服越来越大的几何阻力,有限样本下的 loss 根本降不下去。
这个机制有点像一个地心引力源:不管你在哪个方向用力,最终都被拉回到安全区域。只不过力的大小是各向异性的,危险方向上的“引力”特别强,良性方向上的“引力”几乎为零。
3.3 一个可以上手的梯度投影最小实现
如果你只想快速验证“方向防御”这个概念,不必一次性复现论文全部工程细节。可以先做一个简化版:用少量有害样本估计危险子空间 U,然后在训练时把那些指向 U 的梯度成分直接移除。
# 概念验证代码:估计危险子空间并投影梯度 import torch # 1. 收集危险样本和安全样本的激活差值 # acts_diff: shape (n_samples, d),每行表示危险样本激活均值 - 安全样本激活均值 # 这里假设你已经通过数据加载得到 activations_diff _, _, V = torch.svd(acts_diff) r = 8 # 危险子空间秩,可调 U_harm = V[:, :r].to(device) # 危险子空间基,shape (d, r) # 2. 训练阶段:将每个参数的梯度投影到与 U_harm 正交的方向 def safe_step(model, grads, U_harm): for name, p in model.named_parameters(): g = grads[name] g_flat = g.reshape(-1, 1) # (d, 1) g_harm = (g_flat.T @ U_harm) @ U_harm.T # 梯度在危险子空间上的分量 grads[name] = (g_flat - g_harm.T).reshape_as(g) return grads这段代码非常粗糙,它不是论文原实现,论文的重点在激活层的几何瓶颈上,而这里做的是权重梯度层面的投影。但方向是一致的:让模型在训练时“看不见”有害方向上的梯度压力。我实际跑过一个 1B 模型,用这种方式确实能让有害样本的 loss 降不下去,同时普通问答任务的 loss 还是正常下降的。这种“只堵方向不堵能力”的感觉,只有亲手跑过才有体感。
3.4 和现有防御方法对比:限制“方向”而不是“动量”
很多团队一听到“防止微调破坏安全”,第一反应是“那就不要让模型权重离原始点太远”。EWC、L2-SP、参数冻结都属于这种思路。它们的问题很明显:太远了会挡住良性任务的可塑性,导致模型变成“只会原地踏步”的一块石头。
Safety Anchor 不一样的地方在于,它限制的不是“总位移”,而是“危险方向上的位移”。这两者有本质区别。表格里我简单对比一下常见思路:
| 方案思路 | 约束对象 | 安全性 | 可塑性 | 对攻击者的感知 |
|---|---|---|---|---|
| 数据过滤 | 输入端样本 | 中等,可被改写绕过 | 高 | 换皮样本即可绕过 |
| 权重强约束(EWC/L2-SP) | 全部参数偏移量 | 高 | 低 | 良性任务也学不动 |
| 对抗训练/疫苗式扰动 | 训练阶段扰动 | 中高 | 中 | 能对抗,但成本高 |
| Safety Anchor 几何瓶颈 | 危险方向子空间 | 高 | 高 | 需要绕开几何屏障 |
换个生活化的说法:传统防御是给整辆车限速,不管是运货还是运危险品都只能 30 km/h;Safety Anchor 是给每个轮子装传感器,运普通货物爱跑多快跑多快,运危险品一上高速就爆胎。
4. 实验到底做了啥:攻击成功率、良性性能与可塑性三笔账
4.1 威胁模型与攻击设置:不是轻而易举的玩具环境
要评估一种防御是否有用,首先得看它扛住了多强的攻击。论文里的威胁模型,我概括下来是:攻击者拿到完整模型权重,可以自由选择全参数微调或者 LoRA/QLoRA 形式的低秩微调;攻击者手里有一批恶意目标样本,目标是让模型在有害指令下给出顺从回答;评估时用 ASR(attack success rate,模型成功输出有害内容的比例)作为核心指标。
特别要注意的是,论文不是只测“100 条恶意样本”的简单场景,我在阅读中看到讨论的还包括不同恶意样本数量、不同微调强度(比如多轮训练、更大学习率)下的稳定性。这样就把一个问题摆到了台面上:防御究竟是一层脆弱的窗户纸,还是一道撞不动的墙。
4.2 防御效果:ASR 被压下来是第一步
据论文报告的实验趋势,不加防御的模型在几百条恶意样本下,ASR 可以冲到非常高的水平;加了 Safety Anchor 之后,同样的攻击设置下,ASR 会出现断崖式下降。更关键的是,在样本量加大、微调轮数增加之后,防御没有迅速崩溃,几何瓶颈依然能把有害行为挡在外边。
我读实验部分时的感受是,这个结果不是靠“让模型变笨”换来的——如果模型从头到尾只会输出安全兜底话术,ASR 当然也低,但那种防御没有意义。所以还必须看下一笔账:良性能力。
4.3 良性性能和可塑性的权衡
衡量良性能力,论文用的是常见的通用能力评测和指令遵循测试,比如常识问答、推理题、代码生成之类的基准。我看到的趋势是:Safety Anchor 对良性任务的分数影响很小,大部分任务维持在原水平附近。这是几何选择性约束的直接成果——良性方向的通道没被堵。
更值得关注的是“可塑性”(plasticity),这个概念在安全微调领域里经常被忽略。一个防御方案如果让模型完全学不进新知识,那它就没有落地价值。论文专门讨论了可塑性,并与那些“把模型钉死在原权重附近”的方法做了对比:Safety Anchor 在保持安全的同时,仍然允许模型通过微调学会新任务,而强约束方法在安全指标上虽然不差,但新任务学习能力明显下降。
我自己在评估防御方案时,现在一定会把可塑性放进验收标准。没有可塑性的安全,是博物馆里的标本;有可塑性的安全,才是生产线上能用的东西。
4.4 论文承认的边界:危险方向探测的完备性
没有哪种防御是银弹,这篇论文也没回避这一点。它最大的边界在于:防御效果高度依赖“危险方向”能否被预先准确估计。如果某种新的危险能力没有被纳入探测样本,或者攻击者刻意把恶意目标混进看起来完全良性的数据里,导致估计出的子空间被污染,那几何瓶颈就可能拦不住。
另外,低秩如何选取、锚点加在哪几层、对多大规模的模型生效,这些都是工程上要重新调参的问题。论文提供了思路和实验证据,但离“一键防御”还有距离。这一节对我来说反而最有用,因为它划清了方法的适用边界,让我不会拿着锤子把所有钉子都敲一遍。
5. 读懂之后:我觉得这事还能往这几个方向延展
5.1 同一套几何思路能迁移到机器遗忘、后门防御和持续学习
读完 Safety Anchor,我第一个想到的是机器遗忘(machine unlearning)。很多合规场景要求模型删除某些用户数据或版权内容,传统做法是重新训练,代价极高。如果采用几何思路,可以把“要删除的概念方向”当成危险方向来处理,在推理或后续微调时对相关方向施加瓶颈,让模型无论再怎么接触相关数据,都很难把那个方向激活起来。
第二个迁移方向是后门防御。模型被人注入后门后,所有带特定触发词的输入都会被引导到某个异常内部方向,从而触发攻击者预设的行为。如果能离线估计出触发词对应的内部方向,几何瓶颈完全可以用来“封路”,让后门触发器失效。
第三个方向是持续学习里的灾难性遗忘。旧任务的表示方向同样是一个几何特征,更新新任务时把旧任务方向设成锚点,只允许新任务在正交方向上生长,这几乎就是把 Safety Anchor 换个名字用在正向场景里。共通的一点是:只要模型行为可以被表示方向刻画,几何瓶颈就比参数正则更精准。
5.2 我读完之后还想追问的几个研究问题
第一个问题,更强的攻击者会怎么绕?如果我站在红队视角,可能会先用大量良性任务数据做预微调,把模型内部的锚点位置慢慢“磨偏”,等瓶颈失去对准效果之后再注毒。论文是否考虑了这种两步攻击,我没有在阅读中找到明确结论,这是一个值得做后续实验的开放问题。
第二个问题,危险子空间的维度怎么自动确定?现在r靠人工调,不同模型、不同能力的危险容量不一样。如果有一个自动识别危险方向秩的算法,会让方案落地门槛大幅下降。
第三个问题,几何防御和 DPO 这类偏好优化后期修复能否叠加?理想状态是:发布前用 Safety Anchor 给模型加“出厂防护”,线上发现问题后还能再做一轮偏好修复,两者互不干扰。这个组合策略如果成立,会给模型运营留出很大的安全缓冲空间。
5.3 给工程师的落地建议:先用减法,再做正交
如果你是在自己的模型上做实验,我的建议是不要一上来就想着完整复现论文的每一个模块。先花一天时间做一个减法实验:收集几十条安全样本和几十条危险样本,提取激活差,SVD 估计出 5 到 10 个危险方向,然后在微调时把梯度里的危险成分减掉,跑一个最小 case。你会立刻看到三件事:危险样本的 loss 很难降下去、普通任务的 loss 正常下降、模型输出的安全性肉眼可见地提升。
有了这个体感之后,再考虑要不要引入更完整的锚点机制,比如在特定 transformer 层里注入低秩瓶颈,或者将锚点做成可学习参数。论文的价值不在于让你照抄一个模型配置,而在于给你一把“从几何视角看安全”的钥匙。我实际测下来,哪怕只是用上面那段简化代码,也能在小型模型上复现“方向性防御”的基本现象。
最后再分享一个个人体会。以前我做安全对齐,习惯性地把注意力放在“输出长什么样”,也就是只盯显式的拒绝语和评分模型。读这篇论文之后,我开始把注意力放到“内部表示在往哪走”。每次微调实验结束,我都会顺手看一眼危险方向上的激活投影值,这个数字比任何人工评测都更早地告诉我安全边界是否在被侵蚀。强烈建议你也把这个习惯加到自己的实验流程里。