abliterated 模型(可理解为"拒答已被消融"的模型)是一个普通的开放权重模型,只是它的拒答行为已经通过编辑权重被外科手术式地移除了——不是重新训练,也不是用一段巧妙的提示词把它包起来。这个名字是 ablate(消融)和 obliterate(抹除)的合成词,而且它对机制的描述相当准确:你在激活空间中找出承载"我应该拒绝这个请求"的那一个方向,然后让网络从此再也无法写出这个方向。
这项技术源自可解释性研究:研究发现,对话微调后的 transformer 中的拒答行为,主要由一个线性方向所介导。正是这个结论让整件事变得廉价。如果拒答弥散在成千上万个相互作用的特征之中,移除它就需要梯度下降;而因为它是集中的,移除它只需要线性代数。
拒答存在于残差流中
Transformer 的残差流是一个不断累积的向量——每个 token 位置各有一个——每个 block 都从中读取,再写回其中。注意力头和 MLP 把各自的输出加到它上面;没有任何东西会覆盖它。正因为这种加性结构,残差流中的某个方向在第 10 层和第 40 层的含义大致相同,而这恰恰是让你能够把"拒答方向"当作一个单一对象、而非四十个互不相关的对象来谈论的性质。
要找到它,你需要两组提示词:一组模型会稳定拒答,另一组模型会稳定回答。把两组都跑一遍,在固定的层和 token 位置捕获残差流激活(指令部分的最后几个 token 效果很好,因为此时模型已经读完了请求、但还没开始作答),然后取两组均值之差。
import torch
def refusal_direction(harmful: torch.Tensor, harmless: torch.Tensor) -> torch.Tensor:
"""Difference-in-means estimate of the refusal direction, unit-normalised.
Both tensors are [n_prompts, d_model] residual-stream activations captured at
the same layer and token position. Difference-in-means beats a trained probe
here: with a few hundred prompts a probe overfits, a mean shift does not.
"""
r = harmful.mean(dim=0) - harmless.mean(dim=0)
return r / r.norm()你在每个候选层和位置上都这么做,得到几十个候选方向,然后从中挑一个。挑选比提取更重要:正确的判据是消融这个候选方向,然后同时测量在留出的有害提示词上拒答下降了多少,以及在无害提示词上下一个 token 的分布偏移了多少。第二个数字——通常是相对于未修改模型的 KL 散度——就是你的损伤计。一个能消灭拒答、却把无害分布搅得严重偏移的候选,不如一个拒答消除得稍少一点、但附带损伤只有几分之一的候选。
abliterated 模型是怎么构建出来的
一旦有了单位向量 r,就有两种用法。第一种是推理时钩子:在每一层,把激活沿 r 的分量减掉,再往下传。这行得通,但代价是需要一个运行时钩子,模型也就不再是一个纯粹的 checkpoint。
第二种——真正的 abliteration——把它烘焙进权重里。每一个写入残差流的矩阵都会得到一个秩一更新,把 r 从其输出空间中移除:嵌入矩阵、每一个注意力输出投影、每一个 MLP 下投影。
def orthogonalize_(weight: torch.Tensor, r: torch.Tensor) -> None:
"""Project r out of a matrix that writes into the residual stream, in place.
weight is [d_model, d_in]. After W <- (I - r rT) W, the product W @ x has zero
component along r for every possible x, so no amount of prompting can put the
direction back. Rank-one per matrix; no gradients, no optimiser, no dataset of
answers — only the few hundred prompts used to estimate r.
"""
weight -= torch.outer(r, r @ weight)结果是一个普通的 checkpoint。相同的架构、相同的文件格式、相同的推理开销,在 vLLM 或 llama.cpp 中原样加载。在单张 GPU 上,整个流程几分钟就能跑完,而开销大的部分是候选方向的扫描,不是编辑本身。
它与微调有何不同
通过微调来移除拒答——在一批顺从回复的语料上做 SFT 或 DPO——是一种根本不同的操作。它用梯度下降移动每一个权重;它需要的是一个回答数据集,而不仅仅是提示词;它要消耗 GPU 小时数;而且它教的是新行为,而不是删除已有行为。它还会把微调语料中的风格、长度偏好和事实层面的怪癖一并带进来,并有灾难性遗忘无关能力的风险。
Abliteration 只触及一个秩一子空间,其余一概不动。这种精确性既是它的主要卖点,也是其主要失败模式的根源:如果除了拒答之外还有别的东西恰好也落在 r 上,那也会一起被摧毁,而扫描做得再仔细也无法完全避免这一点。
它与越狱提示词有何不同
越狱不动权重,而是把激活引导到一个拒答回路不会触发的区域。这种做法的脆弱性在自动化场景中会真正成为问题。它每次调用都要烧掉上下文 token。它是非确定性的——同一个提示词,这次采样顺从,下次采样就可能拒绝。当提供方更新模型或修补了那段特定措辞时,它会悄无声息地失效。而且因为拒答机制依然完好无损,它可能在生成中途卷土重来,给你三段有用的输出之后接上一句"其实,我不能继续下去了"。
Abliterated 模型没有任何东西可以卷土重来。这种行为在整个生成过程中、在各种提示词措辞下都是稳定的,而这正是让它能用在批处理流水线中的性质。
坦诚面对取舍
Abliterated 模型并不是一个更好的模型。它是一个不再说"不"的模型,而这句话的含义比听起来要窄得多。
- 能力漂移。 从每个写入矩阵中移除一个方向有可测量的代价。Abliterated checkpoint 常见的表现是:在边缘情况下指令遵循变弱,更容易附和提示词中的错误前提,在推理基准上有小幅退步。
- 拒答并非完美的一维。 一个方向捕获了大部分行为,而不是全部。要预期有些拒答会残留,也要预期与该方向共享的那些行为会受到附带损伤。
- 没有新知识。 Abliteration 不添加任何东西。原本基础模型会因为无知(披着谨慎的外衣)而拒答的地方,你现在得到的是一段信心十足的编造——对于评估流水线来说,这可能比拒答更糟,因为拒答至少是一个诚实的、你能检测到的信号。
- 提示词集定义了这次编辑。 一个狭窄的有害提示词集会产生一个狭窄的方向。同一个基础模型的两次 abliteration 并不能互换。
许多公开发布的 abliterated 模型事后都会做一次轻量的修复微调,以挽回退化的能力——这悄悄地把这项技术原本想要避免的成本又重新引了回来。
它在哪些地方有用
诚实的使用场景,是那些拒答意味着测量误差而非安全收益的工作:红队测试框架、拒答率基线、鲁棒性评估,以及那些任务本身是防御性的、但主题触发了护栏的安全分析。unbleep 通过一个 OpenAI 兼容端点提供 abliterated 模型,你可以把现有客户端直接指向它们,获得一个稳定的基线。你用它构建什么由你自己负责——我们不会逾越的界线,请参见可接受使用政策。
获取 API 密钥,亲自测量其中的差异。