拒绝悬崖(Refusal Cliff)现象
引言
大推理模型(Large Reasoning Models, LRMs)具有多步推理能力,在复杂问题解决上表现出色。然而,它们表现出令人担忧的安全漏洞——这一现象尚未被充分理解。
本文介绍ICLR 2026的最新研究1,通过机制可解释性分析揭示了**拒绝悬崖(Refusal Cliff)**现象:许多对齐不足的推理模型在思考过程中正确识别了有害提示并保持强烈的拒绝意图,但在最终输出前的token上经历拒绝分数的急剧下降。
现象描述
Refusal Cliff定义
拒绝悬崖:推理模型在推理过程中维持高拒绝分数,但在最终输出生成前急剧下降的现象。
def demonstrate_refusal_cliff(model, harmful_prompt):
"""
展示拒绝悬崖现象
"""
# 生成推理过程
reasoning = model.generate_reasoning(harmful_prompt)
# 在每个token位置测量拒绝分数
refusal_scores = []
for token in reasoning:
score = measure_refusal_score(model, token)
refusal_scores.append(score)
# 分析拒绝分数变化
initial_score = np.mean(refusal_scores[:-5]) # 推理过程
final_score = refusal_scores[-1] # 最终token
print(f"推理过程平均拒绝分数: {initial_score:.3f}")
print(f"最终token拒绝分数: {final_score:.3f}")
print(f"分数下降: {initial_score - final_score:.3f}")
# 检测悬崖
cliff_detected = (initial_score - final_score) > 0.5
return cliff_detected, refusal_scores典型观察
| 阶段 | 拒绝分数 | 行为 |
|---|---|---|
| 初始输入 | 0.85 | 识别为有害 |
| 推理过程 | 0.78-0.82 | 维持拒绝意图 |
| 最终输出前 | 0.05-0.15 | 急剧下降 |
| 输出完成 | 0.10 | 错误响应 |
定量刻画
定义拒绝悬崖强度:
其中 为第 个token的拒绝分数, 为序列长度, 为悬崖窗口。
机制分析
线性探测方法
使用线性探测追踪拒绝意图在token位置上的变化:
def train_refusal_probe(model):
"""
训练拒绝意图探测分类器
"""
# 收集激活
activations = []
labels = []
for example in dataset:
hidden_states = extract_hidden_states(model, example)
label = example.refusal_intention # 0: 服从, 1: 拒绝
activations.append(hidden_states)
labels.append(label)
# 训练线性探测
probe = LogisticRegression()
probe.fit(concatenate(activations), labels)
return probe
def trace_refusal_intention(model, probe, text):
"""
在文本生成过程中追踪拒绝意图
"""
tokens = tokenize(text)
hidden_states = extract_hidden_states_at_each_position(
model, tokens
)
# 探测每个位置的拒绝意图
refusal_scores = probe.predict_proba(hidden_states)[:, 1]
return refusal_scores关键发现
发现1:推理过程中模型确实正确识别了有害提示
- 平均拒绝分数:
- 正确分类率:
发现2:拒绝意图在特定token位置被系统性地抑制
- 抑制发生在最后3-5个token
- 与”开始输出”信号高度相关
注意力头分析
使用因果干预分析识别负向贡献的注意力头:
def identify_negative_heads(model, harmful_prompts, safe_prompts):
"""
识别负向贡献的注意力头
"""
negative_contributions = {}
for layer in range(num_layers):
for head in range(num_heads):
# 计算该头对拒绝行为的贡献
contribution = measure_head_contribution(
model, layer, head,
harmful_prompts, safe_prompts
)
if contribution < 0:
negative_contributions[(layer, head)] = contribution
# 按贡献排序
sorted_heads = sorted(
negative_contributions.items(),
key=lambda x: x[1] # 升序,最负的排前面
)
return sorted_heads因果干预验证
实验:消融负向贡献的注意力头
def causal_intervention(model, head_to_ablate):
"""
因果干预实验
"""
original_attack_rate = evaluate_attack_success(model)
# 消融目标头
model_ablated = ablate_head(model, head_to_ablate)
ablated_attack_rate = evaluate_attack_success(model_ablated)
return {
'original': original_attack_rate,
'ablated': ablated_attack_rate,
'improvement': original_attack_rate - ablated_attack_rate
}实验结果
| 消融策略 | 攻击成功率 | 下降幅度 |
|---|---|---|
| 无消融 | 34.2% | - |
| 消融Top-1头 | 21.5% | 37% |
| 消融Top-3头 | 8.7% | 75% |
| 消融Top-5头 | 6.2% | 82% |
| 消融Top-10头 | 4.1% | 88% |
关键发现:仅消融约3%的负向贡献注意力头即可将攻击成功率降至10%以下。
Cliff-as-a-Judge方法
核心思想
基于拒绝悬崖现象,提出Cliff-as-a-Judge数据选择方法:
识别训练示例中拒绝悬崖最显著的样本,用于安全对齐训练。
方法步骤
class CliffAsAJudge:
"""
Cliff-as-a-Judge数据选择方法
选择具有最大拒绝悬崖的训练样本,以更有效地修复推理模型的安全对齐
"""
def __init__(self, model, cliff_threshold=0.5):
self.model = model
self.cliff_threshold = cliff_threshold
self.probe = train_refusal_probe(model)
def compute_cliff_score(self, example):
"""
计算样本的拒绝悬崖分数
"""
# 生成响应
response = self.model.generate(example.prompt)
# 追踪拒绝意图
refusal_scores = trace_refusal_intention(
self.model, self.probe, response
)
# 计算悬崖强度
if len(refusal_scores) < 10:
return 0.0
early_scores = refusal_scores[:-5]
late_scores = refusal_scores[-5:]
cliff_strength = (
np.mean(early_scores) - np.mean(late_scores)
) / (np.mean(early_scores) + 1e-8)
return cliff_strength
def select_training_examples(self, dataset, top_k=1000):
"""
选择训练样本
"""
cliff_scores = []
for example in tqdm(dataset):
score = self.compute_cliff_score(example)
cliff_scores.append((example, score))
# 选择悬崖最强的样本
selected = sorted(
cliff_scores,
key=lambda x: x[1],
reverse=True
)[:top_k]
return [ex for ex, _ in selected]效率分析
| 方法 | 所需数据比例 | 最终攻击成功率 |
|---|---|---|
| 全部安全数据 | 100% | 8.2% |
| 随机选择 | 10% | 18.5% |
| Cliff-as-a-Judge | 1.7% | 9.1% |
“Less is More”效应:仅使用1.7%的原始安全训练数据即可达到与全部数据相当的对齐效果。
理论解释
推理-输出解耦假设
假设:推理模型中存在两种相对独立的表示:
- 推理表示 ():负责问题分析和推理步骤
- 输出表示 ():负责最终响应生成
表示冲突
有害提示情况下:
- 正确识别:
- 绕过拒绝:
表示纠缠
拒绝悬崖源于 和 之间的纠缠:
其中 为”开始输出”信号,触发表示转换。
与RIM的关系
RIM vs Refusal Cliff
| 方面 | RIM | Refusal Cliff |
|---|---|---|
| 现象 | 推理增强导致错位 | 拒绝分数骤降 |
| 发生位置 | 整体错位 | 最后几个token |
| 机制 | 激活纠缠 | 表示转换 |
| 分析方法 | 激活归因 | 线性探测 |
统一解释
RIM和Refusal Cliff可能是同一根本原因的不同表现:
- RIM:表示层面的纠缠
- Refusal Cliff:动态演化过程中的表示切换失败
实践应用
安全评估
def evaluate_model_for_refusal_cliff(model, test_prompts):
"""
评估模型的拒绝悬崖风险
"""
results = []
for prompt in test_prompts:
if is_harmful(prompt):
cliff_strength, scores = demonstrate_refusal_cliff(
model, prompt
)
results.append({
'prompt': prompt,
'cliff_strength': cliff_strength,
'initial_refusal': np.mean(scores[:-5]),
'final_refusal': scores[-1],
'vulnerable': cliff_strength > 0.5
})
vulnerable_rate = sum(
r['vulnerable'] for r in results
) / len(results)
return {
'vulnerable_rate': vulnerable_rate,
'details': results
}修复策略
def fix_refusal_cliff(model, training_data):
"""
修复拒绝悬崖
"""
# 1. 识别负向贡献的注意力头
negative_heads = identify_negative_heads(model, ...)
# 2. 使用Cliff-as-a-Judge选择训练数据
cliff_selector = CliffAsAJudge(model)
selected_data = cliff_selector.select_training_examples(training_data)
# 3. 微调修复
model.fine_tune(selected_data, method='lora')
return model总结
本文揭示了推理模型安全漏洞的重要机制——拒绝悬崖现象:
- 现象确认:推理过程中正确识别但输出前骤降
- 机制发现:特定注意力头的负向贡献导致拒绝意图被抑制
- 因果验证:消融约3%关键头可将攻击成功率降至10%以下
- Cliff-as-a-Judge:利用悬崖特征的高效数据选择方法
- Less is More:仅1.7%数据达到全部数据的效果
这一研究为理解和修复推理模型的安全问题提供了精确的干预目标。
参考文献
相关链接:reasoning-induced-misalignment-rim | lora-safety-alignment-reasoning | cot-controllability-safety-monitoring
Footnotes
-
Anonymous. “Refusal Falls Off a Cliff: How Safety Alignment Fails in Reasoning.” ICLR 2026. ↩