transformer-lens-interpretability
transformer-lens-interpretability について
TransformerLensは、GPTスタイルの言語モデルの内部メカニズムを研究するためのPythonライブラリです。各活性化に設けられたHookPointを通じて、内部の検査や介入、回路分析を行えます。
主な機能
- 01モデル活性化へのフック
- 02中間活性化のキャッシュ
- 03活性化パッチングと因果追跡
- 04注意パターンと回路の分析
- 05直接ロジット帰属
活用シーン
- 01出力に影響する活性化の追跡
- 02注意パターンと情報の流れの検査
- 03誘導ヘッドやIOI回路の分析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ orchestra-research/ ai-research-skills --skill transformer-lens-interpretabilityインストール後、Claude Code で/transformer-lens-interpretabilityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api.mdREADME.mdtutorials.md詳細
TransformerLens: Transformer のメカニスティック解釈可能性
TransformerLens は、GPT 系の言語モデルに対するメカニスティック解釈可能性の研究における事実上の標準ライブラリです。Neel Nanda が作成し、Bryce Meyer が保守しています。すべてのアクティベーションに HookPoint を設けることで、モデルの内部を調べたり操作したりするための分かりやすいインターフェースを提供します。
GitHub: TransformerLensOrg/TransformerLens(スター数 2,900 以上)
TransformerLens を使うべき場合
次のような場合は TransformerLens を使用してください:
- 学習中に獲得されたアルゴリズムをリバースエンジニアリングしたい
- アクティベーションパッチング / 因果トレーシングの実験を行いたい
- アテンションパターンと情報の流れを調べたい
- 回路(例: induction head、IOI 回路)を分析したい
- 中間アクティベーションをキャッシュして調べたい
- 直接ロジット帰属を適用したい
次のような場合は代替手段を検討してください:
- Transformer 以外のアーキテクチャを扱う必要がある → nnsight または pyvene を使用
- Sparse Autoencoder を学習・分析したい → SAELens を使用
- 超大規模モデルをリモートで実行する必要がある → NDIF 付きの nnsight を使用
- より高レベルな因果介入の抽象化が必要 → pyvene を使用
インストール
pip install transformer-lens
開発版の場合:
pip install git+https://github.com/TransformerLensOrg/TransformerLens
コアコンセプト
HookedTransformer
すべてのアクティベーションに HookPoint を設けて Transformer モデルをラップする中心的なクラスです:
from transformer_lens import HookedTransformer
# Load a model
model = HookedTransformer.from_pretrained("gpt2-small")
# For gated models (LLaMA, Mistral)
import os
os.environ["HF_TOKEN"] = "your_token"
model = HookedTransformer.from_pretrained("meta-llama/Llama-2-7b-hf")
対応モデル(50 以上)
| ファミリー | モデル |
|---|---|
| GPT-2 | gpt2, gpt2-medium, gpt2-large, gpt2-xl |
| LLaMA | llama-7b, llama-13b, llama-2-7b, llama-2-13b |
| EleutherAI | pythia-70m から pythia-12b まで, gpt-neo, gpt-j-6b |
| Mistral | mistral-7b, mixtral-8x7b |
| その他 | phi, qwen, opt, gemma |
アクティベーションのキャッシュ
モデルを実行し、中間アクティベーションをすべてキャッシュします:
# Get all activations
tokens = model.to_tokens("The Eiffel Tower is in")
logits, cache = model.run_with_cache(tokens)
# Access specific activations
residual = cache["resid_post", 5] # Layer 5 residual stream
attn_pattern = cache["pattern", 3] # Layer 3 attention pattern
mlp_out = cache["mlp_out", 7] # Layer 7 MLP output
# Filter which activations to cache (saves memory)
logits, cache = model.run_with_cache(
tokens,
names_filter=lambda name: "resid_post" in name
)
ActivationCache のキー
| キーのパターン | 形状 | 説明 |
|---|---|---|
resid_pre, layer | [batch, pos, d_model] | アテンション前の残差 |
resid_mid, layer | [batch, pos, d_model] | アテンション後の残差 |
resid_post, layer | [batch, pos, d_model] | MLP 後の残差 |
attn_out, layer | [batch, pos, d_model] | アテンションの出力 |
mlp_out, layer | [batch, pos, d_model] | MLP の出力 |
pattern, layer | [batch, head, q_pos, k_pos] | アテンションパターン(softmax 後) |
q, layer | [batch, pos, head, d_head] | クエリベクトル |
k, layer | [batch, pos, head, d_head] | キーベクトル |
v, layer | [batch, pos, head, d_head] | バリューベクトル |
ワークフロー 1: アクティベーションパッチング(因果トレーシング)
破損させた入力の実行に対して、クリーンなアクティベーションをパッチとして適用することで、モデルの出力に因果的に影響するアクティベーションを特定します。
ステップごとの手順
from transformer_lens import HookedTransformer, patching
import torch
model = HookedTransformer.from_pretrained("gpt2-small")
# 1. Define clean and corrupted prompts
clean_prompt = "The Eiffel Tower is in the city of"
corrupted_prompt = "The Colosseum is in the city of"
clean_tokens = model.to_tokens(clean_prompt)
corrupted_tokens = model.to_tokens(corrupted_prompt)
# 2. Get clean activations
_, clean_cache = model.run_with_cache(clean_tokens)
# 3. Define metric (e.g., logit difference)
paris_token = model.to_single_token(" Paris")
rome_token = model.to_single_token(" Rome")
def metric(logits):
return logits[0, -1, paris_token] - logits[0, -1, rome_token]
# 4. Patch each position and layer
results = torch.zeros(model.cfg.n_layers, clean_tokens.shape[1])
for layer in range(model.cfg.n_layers):
for pos in range(clean_tokens.shape[1]):
def patch_hook(activation, hook):
activation[0, pos] = clean_cache[hook.name][0, pos]
return activation
patched_logits = model.run_with_hooks(
corrupted_tokens,
fwd_hooks=[(f"blocks.{layer}.hook_resid_post", patch_hook)]
)
results[layer, pos] = metric(patched_logits)
# 5. Visualize results (layer x position heatmap)
チェックリスト
- 最小限だけ異なるクリーンな入力と破損した入力を定義する
- 振る舞いの差を捉える指標を選ぶ
- クリーンなアクティベーションをキャッシュする
- (layer, position) の組み合わせをすべて体系的にパッチする
- 結果をヒートマップとして可視化する
- 因果的なホットスポットを特定する
ワークフロー 2: 回路分析(間接目的語の特定)
「Interpretability in the Wild」からの IOI 回路の発見を再現します。
ステップごとの手順
from transformer_lens import HookedTransformer
import torch
model = HookedTransformer.from_pretrained("gpt2-small")
# IOI task: "When John and Mary went to the store, Mary gave a bottle to"
# Model should predict "John" (indirect object)
prompt = "When John and Mary went to the store, Mary gave a bottle to"
tokens = model.to_tokens(prompt)
# 1. Get baseline logits
logits, cache = model.run_with_cache(tokens)
john_token = model.to_single_token(" John")
mary_token = model.to_single_token(" Mary")
# 2. Compute logit difference (IO - S)
logit_diff = logits[0, -1, john_token] - logits[0, -1, mary_token]
print(f"Logit difference: {logit_diff.item():.3f}")
# 3. Direct logit attribution by head
def get_head_contribution(layer, head):
# Project head output to logits
head_out = cache["z", layer][0, :, head, :] # [pos, d_head]
W_O = model.W_O[layer, head] # [d_head, d_model]
W_U = model.W_U # [d_model, vocab]
# Head contribution to logits at final position
contribution = head_out[-1] @ W_O @ W_U
return contribution[john_token] - contribution[mary_token]
# 4. Map all heads
head_contributions = torch.zeros(model.cfg.n_layers, model.cfg.n_heads)
for layer in range(model.cfg.n_layers):
for head in range(model.cfg.n_heads):
head_contributions[layer, head] = get_head_contribution(layer, head)
# 5. Identify top contributing heads (name movers, backup name movers)
チェックリスト
- 明確な IO/S トークンを設定してタスクを用意する
- ベースラインのロジット差を計算する
- アテンションヘッドごとの寄与に分解する
- 主要な回路の構成要素(name mover、S-inhibition、induction)を特定する
- アブレーション実験で検証する
ワークフロー 3: Induction Head の検出
[A][B]...[A] → [B] のパターンを実装する induction head を見つけます。
from transformer_lens import HookedTransformer
import torch
model = HookedTransformer.from_pretrained("gpt2-small")
# Create repeated sequence: [A][B][A] should predict [B]
repeated_tokens = torch.tensor([[1000, 2000, 1000]]) # Arbitrary tokens
_, cache = model.run_with_cache(repeated_tokens)
# Induction heads attend from final [A] back to first [B]
# Check attention from position 2 to position 1
induction_scores = torch.zeros(model.cfg.n_layers, model.cfg.n_heads)
for layer in range(model.cfg.n_layers):
pattern = cache["pattern", layer][0] # [head, q_pos, k_pos]
# Attention from pos 2 to pos 1
induction_scores[layer] = pattern[:, 2, 1]
# Heads with high scores are induction heads
top_heads = torch.topk(induction_scores.flatten(), k=5)
よくある問題と解決策
問題: デバッグ後もフックが残る
# WRONG: Old hooks remain active
model.run_with_hooks(tokens, fwd_hooks=[...]) # Debug, add new hooks
model.run_with_hooks(tokens, fwd_hooks=[...]) # Old hooks still there!
# RIGHT: Always reset hooks
model.reset_hooks()
model.run_with_hooks(tokens, fwd_hooks=[...])
問題: トークン化の落とし穴
# WRONG: Assuming consistent tokenization
model.to_tokens("Tim") # Single token
model.to_tokens("Neel") # Becomes "Ne" + "el" (two tokens!)
# RIGHT: Check tokenization explicitly
tokens = model.to_tokens("Neel", prepend_bos=False)
print(model.to_str_tokens(tokens)) # ['Ne', 'el']
問題: 分析で LayerNorm が無視される
# WRONG: Ignoring LayerNorm
pre_activation = residual @ model.W_in[layer]
# RIGHT: Include LayerNorm
ln_scale = model.blocks[layer].ln2.w
ln_out = model.blocks[layer].ln2(residual)
pre_activation = ln_out @ model.W_in[layer]
問題: 大規模モデルでのメモリ爆発
# Use selective caching
logits, cache = model.run_with_cache(
tokens,
names_filter=lambda n: "resid_post" in n or "pattern" in n,
device="cpu" # Cache on CPU
)
主要クラスリファレンス
| クラス | 用途 |
|---|---|
HookedTransformer | フック付きのメインモデルラッパー |
ActivationCache | アクティベーションを格納する辞書型のキャッシュ |
HookedTransformerConfig | モデルの設定 |
FactoredMatrix | 効率的な因数分解行列の演算 |
SAELens との統合
TransformerLens は、スパースオートエンコーダー分析のために SAELens と統合されています:
from transformer_lens import HookedTransformer
from sae_lens import SAE
model = HookedTransformer.from_pretrained("gpt2-small")
sae = SAE.from_pretrained("gpt2-small-res-jb", "blocks.8.hook_resid_pre")
# Run with SAE
tokens = model.to_tokens("Hello world")
_, cache = model.run_with_cache(tokens)
sae_acts = sae.encode(cache["resid_pre", 8])
リファレンスドキュメント
詳細な API ドキュメント、チュートリアル、高度な使い方については、references/ フォルダーを参照してください:
| ファイル | 内容 |
|---|---|
| references/README.md | 概要とクイックスタートガイド |
| references/api.md | HookedTransformer、ActivationCache、HookPoints の完全な API リファレンス |
| references/tutorials.md | アクティベーションパッチング、回路分析、ロジットレンズのステップバイステップのチュートリアル |
外部リソース
チュートリアル
- メインデモノートブック
- アクティベーションパッチングのデモ
- ARENA Mech Interp コース - 200 時間以上のチュートリアル
論文
- A Mathematical Framework for Transformer Circuits
- In-context Learning and Induction Heads
- Interpretability in the Wild (IOI)
公式ドキュメント
バージョンに関する注意
- v2.0: HookedSAE を削除(SAELens に移行)
- v3.0 (alpha): 任意の nn.Module を読み込むための TransformerBridge