🔬 SAE 特征索引到语义的对应机制

核心问题

SAE(稀疏自编码器)产生的特征索引(0-32767)只是一个数字,
如何知道它代表什么语义?

答案:SAE 特征没有预定义的语义标签!

语义不是"查找"出来的,而是通过实验"发现"的。

📊 四种发现语义的方法

1

激活模式分析

Activation Patterns / Top Activations

观察"哪些 token 激活了这个特征",推断其语义

最常用
2

特征比较

Feature Comparison

对比两段文本的激活差异,找出显著不同的特征

对比分析
3

Steering 干预

Causal Intervention

增强/抑制特征,观察模型输出变化

最强验证
4

自动化命名

Automated Naming

用 LLM 总结 Top Activations,自动命名特征

领域通用

🔥 方法一:激活模式分析(热力图)

原理

每个 SAE 特征在特定输入上会被激活。通过观察"哪些 token 激活了这个特征",可以推断其语义。

代码实现

# qwen_scope_app.py 第 295-413 行 def feature_heatmap_to_html(tokens, features, top_k, skip_first): """热力图:行=特征,列=Token,颜色=激活强度""" seq_len, sae_width = features.shape # 计算每个特征在所有位置的平均激活值 mean_activation = features.mean(dim=0) # [sae_width] topk_indices = mean_activation.topk(top_k).indices # [top_k] # 对每个 Top-K 特征,展示其在每个 token 位置的激活值 for feat_idx in topk_indices: row_activations = features[:, feat_idx] # [seq_len] # 颜色映射:白→红,按行最大值归一化

示例:特征 #1234 的激活热力图

Token
The
capital
of
China
is
Beijing
特征 #1234
0.1
0.3
0.2
0.9
0.1
0.85

→ 该特征在 "China""Beijing" 上激活最强 → 推断:可能表示"中国相关概念"

⚖️ 方法二:特征比较

原理

比较两段文本的 SAE 激活差异,找出在一段文本中激活强、另一段中激活弱的特征。

示例

文本1
"我喜欢编程,写代码很有趣"
文本2
"我喜欢游泳,运动很健康"
特征索引文本1 激活文本2 激活差异推断语义
#56780.850.12+0.73可能与"编程"相关
#90120.100.78-0.68可能与"运动"相关
#34560.650.62+0.03通用概念(如"喜欢")

🎯 方法三:Steering 干预(因果验证)

原理

这是最强的语义验证方法。人为增强或抑制某个特征的激活,观察模型输出变化。

数学本质

# SAE 解码器权重 W_dec: [sae_width, d_model] # W_dec[feat_idx] 是该特征在隐藏层中的"语义方向" # Steering 操作: hidden_new = hidden_old + strength × W_dec[feat_idx] # 这相当于在隐藏层的"语义空间"中,沿着该特征的方向移动

代码实现

# qwen_scope_app.py _steer_hook 函数 def _steer_hook(module, inp, out): hidden = out[0].clone() # 关键操作:将 SAE 解码器权重对应的特征向量加到隐藏状态 hidden[:, pos, :] += strength * sae['W_dec'][feat_idx] return (hidden,) + out[1:]

示例

原始
Prompt: "The capital of France is"
输出: "Paris"
增强特征 #1234 后
Prompt: "The capital of France is"
输出: "Beijing" 或中国相关内容
→ 确认特征 #1234 与"中国"语义相关

🤖 方法四:自动化命名

⚠️ Qwen-Scope 本身未实现此功能,但这是 SAE 解释领域的通用方法。

标准流程

① 在大规模语料库上运行模型,收集每个特征的 Top Activations
② 对每个特征,收集激活最强的 100-1000 个 token/句子
③ 用 LLM 自动命名:"这个特征在以下文本上激活最强:[列表],请给它一个语义名称"
④ 人工审核命名结果

示例

特征 #5678 的 Top Activations: "dog" (0.92), "cat" (0.89), "pet" (0.85), "animal" (0.82), "fish" (0.78)... LLM 命名:"宠物/动物相关概念"

📋 完整工作流程

Step 1: 选择特征索引(如 #12345)
Step 2: 激活模式分析 → 什么文本触发了它?
Step 3: 特征比较 → 它在什么场景下激活更强?
Step 4: Steering 干预 → 增强/抑制它,模型行为如何变化?
Step 5: 语义命名 → 用自然语言描述该特征的语义

📁 关键代码位置索引

功能函数名行号说明
SAE 特征计算compute_sae_features252-265隐藏状态 → SAE 特征
热力图生成feature_heatmap_to_html295-413特征×Token 激活热力图
特征分析回调cb_analyze506-517入口:分析文本的特征激活
Steering 强度_steering_strength_from_mode521-545映射 Light/Medium/Strong
Steering Hook_steer_hook~820修改隐藏状态注入特征
特征比较cb_compare1082+对比两段文本的特征差异
比较结果渲染compare_to_html859+渲染差异特征表

💡 类比理解

SAE 特征索引到语义的对应,就像化学中发现新元素——你不能查字典知道元素 118 的性质,你需要通过实验来发现它。

特征索引 #12345 本身只是一个数字,它的语义由它在什么输入上激活以及它对模型输出有什么影响来定义。

基于 Qwen-Scope 源代码分析 | SAE 特征解释机制研究