Superposition(重ね合わせ)と特徴の多義性 — ニューラルネットは次元より多くの特徴をどう詰め込むか

画像認識モデルの中間層を調べていて、「犬の耳」にだけ強く反応するニューロンを見つけたとします。これは嬉しい発見です。そのニューロンの活性を見れば「モデルはいま犬の耳を検出している」と読めるからです。ところが実際のモデルを調べると、話はそう都合よく進みません。同じ1個のニューロンが「犬の耳」にも「車のタイヤ」にも「文章中の引用符」にも反応する——お互いに何の関係もない概念の寄せ集めに反応するニューロンが、大量に見つかるのです。この現象を多義性(polysemanticity)と呼びます。

多義性はニューラルネットの解釈を根本から難しくします。ニューロンが「読めない」なら、モデルが内部で何を考えているかをニューロン単位で説明する道は閉ざされるからです。では、なぜモデルはこんな読みにくい表現をわざわざ学ぶのでしょうか。Anthropicの研究チームは2022年の論文 Toy Models of Superposition(Elhage et al., 2022)で、この問いに驚くほど明快な答えを与えました。多義性は事故ではなく、「次元数より多くの特徴を詰め込む」という圧縮戦略の必然的な副作用である——これが本記事の主役、重ね合わせ仮説(superposition hypothesis)です。

この仮説を理解しておくと、次のような場面で視界が開けます。

  • LLMの解釈性・安全性研究: 大規模言語モデルの内部表現を解読する研究(mechanistic interpretability)は、いまや重ね合わせを前提に設計されています。スパースオートエンコーダ(SAE)で特徴を取り出すアプローチが標準になった理由が分かります
  • モデルのデバッグと特徴分析: 「このニューロンは○○検出器だ」という分析がいつ信頼でき、いつ原理的に壊れるのかを判断できるようになります

本記事では、Anthropicのトイモデルを PyTorch でスクラッチ実装し、疎性を変えると表現が「PCA的な専有」から「重ね合わせによる詰め込み」へ相転移する様子、そして2次元平面に5個の特徴が反対方向ペア五角形として整列する幾何構造を、実際に学習させて確かめます。

本記事の内容

  • 単義性・多義性とは何か、なぜ解釈性の中心問題なのか
  • 重ね合わせ仮説の直感 — 「ほぼ直交」なら次元より多くの方向が入る
  • Anthropicのトイモデルの定義と、疎性・重要度の役割
  • PyTorch実装による相転移・幾何構造(反対方向ペア・五角形)・多義性発生の実測
  • 解釈性への含意 — ニューロン単位の解釈の限界とスパースオートエンコーダ(SAE)

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。特に1本目は本記事の前編にあたります。「注意重みを読む」ことの限界を扱った前回に続き、今回は「表現の中身を読む」ことの限界と可能性を扱います。

画像なし
Attentionの解釈性と限界 — 注意重みはモデルの説明になるか?
Attention重みをモデルの説明として使う妥当性を検証します。本記事はこの記事の続編で、解釈の対象を注意重みから内部表現そのものへ進めます。
画像なし
オートエンコーダの種類と理論を体系的に解説
本記事のトイモデルは線形圧縮+ReLU復元のオートエンコーダです。基本構造を押さえておくと読みやすくなります。
主成分分析(PCA)完全ガイド
密なデータに対する最適な線形圧縮はPCAです。本記事では「PCA的な専有」と「重ね合わせ」の対比が軸になります。

ニューロンは「読める」はずだった — 単義性と多義性

ニューラルネットの解釈性研究には、長らく一つの理想がありました。1ニューロン = 1概念という対応です。あるニューロンが「縦のエッジ」だけに、別のニューロンが「犬の顔」だけに反応するなら、モデルの計算は「概念の組み立て工程」として人間に読めます。このように単一の概念にだけ反応するニューロンを単義的(monosemantic)と呼びます。実際、画像認識モデルの初期層には曲線検出器や色検出器など、きれいに単義的なニューロンが見つかることが知られています。

ところが層が深くなるほど、この理想は崩れていきます。1つのニューロンが互いに無関係な複数の概念——たとえば「猫の顔」と「車のフロント」——にまたがって反応する例が、視覚モデルでも言語モデルでも繰り返し報告されてきました。これが多義的(polysemantic)なニューロンです。

単義的ニューロンと多義的ニューロンの対比

左の単義的ニューロンは「犬の耳」にだけ反応するので、活性値を見るだけで「いま何を検出したか」が読めます。右の多義的ニューロンは「犬の耳」「引用符」「コード断片」という無関係な3概念に反応するため、活性が高くてもどの概念が現れたのか判別できません。実際のモデルではこの右側のようなニューロンが多数派を占めます。

ここで素朴な疑問が生まれます。多義性は学習の「手抜き」や「ノイズ」なのでしょうか。それとも、何か合理的な理由があってモデルは概念を混ぜているのでしょうか。答えの鍵は「モデルが表現したい特徴の数」と「使える次元の数」の勘定にあります。

重ね合わせ仮説 — 次元より多くの特徴を詰め込む

世界の特徴は次元より多い

まず言葉を整理します。ここで言う特徴(feature)とは、入力データに含まれる「意味のあるまとまり」のことです。言語なら「これはPythonコードである」「引用文の内部である」「化学の話題である」、画像なら「犬の耳がある」「金属の質感がある」など、モデルが表現できると損失が下がるような性質を指します。重ね合わせの議論では、各特徴は活性空間の中の方向(direction)として表現されると考えます。これは線形表現仮説と呼ばれる作業仮説で、単語ベクトルの加法性(king − man + woman ≈ queen)などで馴染み深い考え方です。

さて、世界に存在する特徴の数を考えてみましょう。言語モデルが扱うべき概念は、語彙・文法・話題・文体・事実知識まで含めれば事実上無限にあります。一方、モデルの1層が持つ次元数はたかだか数千から数万です。もし1特徴に1次元(1ニューロン)を割り当てる方式しか許されないなら、モデルは数千個の特徴しか表現できません。表現したい特徴の数 $n$ が、使える次元の数 $m$ を圧倒的に上回っている——これが出発点です。

ほぼ直交なら、次元より多くの方向が入る

$m$ 次元空間に、互いに厳密に直交する方向は $m$ 本しか取れません。これは線形代数の基本です。しかし「厳密な直交」を少しだけ諦めて、ほぼ直交——内積の絶対値が小さい $\varepsilon$ 以下——でよいことにすると、状況は一変します。

2次元にほぼ直交な5方向を詰め込む重ね合わせの直感

左は厳密な直交にこだわった場合で、2次元には2方向しか入りません。右は正五角形の頂点方向に5本の矢印を配置した場合で、隣り合うペアの内積は $\cos 72^\circ \approx 0.31$ です。ゼロではないけれど小さい——この「小さな干渉」を許容するだけで、2次元に5方向が入りました。

高次元ではこの効果が劇的になります。Johnson–Lindenstrauss の補題に関連するよく知られた事実として、$d$ 次元空間には、どの2本の内積の絶対値も $\varepsilon$ 以下になる単位ベクトルを $\exp(c\,\varepsilon^2 d)$ 本のオーダーで取れます($c$ は定数)。つまりほぼ直交な方向の数は次元数に対して指数的に増えるのです。次元が1万もあれば、内積0.1以下を保ちながら詰め込める方向の数は天文学的になります。モデルにとって、次元不足は「ほぼ直交への妥協」でいくらでも回避できる制約なのです。

干渉のコストは疎性がもみ消す

ただし、ほぼ直交には代償があります。方向が厳密に直交していないと、特徴Aを読み出すときに特徴Bの成分が混ざり込みます。これを干渉(interference)と呼びます。干渉だらけでは復元がめちゃくちゃになりそうですが、ここで決定的に効くのが特徴の疎性(sparsity)です。

現実の特徴は、ほとんどの入力で「不在」です。任意の文が化学の話題である確率は低く、任意の画像に犬の耳が写っている確率も低い。特徴 $i$ が確率 $1-S$ でしか活性化しないとすると($S$ を疎性と呼びます)、2つの特徴が同時に活性化する確率は $(1-S)^2$ です。干渉が実害になるのは両方が同時に立ったときだけなので、疎性が上がると干渉事故は二乗で減っていきます。

疎性が高いほど特徴の同時活性がまれになる確率曲線

縦軸は対数です。$S=0.9$(特徴が9割の入力で不在)なら同時活性率は1%、$S=0.99$ なら0.01%まで落ちます。「干渉のコスト」は同時活性のときにしか支払わないので、疎になるほど、詰め込みによる利益(多くの特徴を表現できる)が干渉の損失を上回りやすくなります。

まとめると、重ね合わせ仮説は次の3点セットです。

  1. 世界の特徴は次元より多い($n \gg m$)
  2. しかし特徴は疎で、同時にはめったに現れない
  3. だからモデルは特徴を「ほぼ直交」な方向に重ねて詰め込み、まれな干渉を受け入れる

この仮説の美しいところは、多義性が自動的に導かれることです。$m$ 次元に $m$ 本より多くの方向を詰め込めば、方向たちは座標軸(=ニューロン)と揃いようがありません。1つのニューロンから見ると、多数の特徴方向が自分の軸に成分を持つ——つまり無関係な多数の特徴に反応する多義的ニューロンになるのです。

もっとも、ここまでは「そうなっていてもおかしくない」という話にすぎません。本当に勾配降下法はそんな器用な詰め込みを学ぶのでしょうか。それを白黒はっきりさせたのが、Anthropic のトイモデルです。

Anthropicのトイモデル

モデルの定義

Elhage et al. (2022) が設計したのは、重ね合わせが起きるかどうかを裸眼で観察できる最小構成のモデルです。仕掛けは「疎な特徴を、狭いボトルネックに通して復元させる」ことに尽きます。

入力(特徴ベクトル): $\bm{x} \in \mathbb{R}^n$ の各成分 $x_i$ は独立で、確率 $S$ で $0$(不在)、確率 $1-S$ で一様分布 $U(0,1)$ から値を取ります。$S$ が疎性のツマミです。

モデル: 重み行列 $W \in \mathbb{R}^{m \times n}$($m < n$)で圧縮し、その転置で復元します。

$$ \bm{h} = W \bm{x}, \qquad \hat{\bm{x}} = \mathrm{ReLU}(W^\top \bm{h} + \bm{b}) = \mathrm{ReLU}(W^\top W \bm{x} + \bm{b}) $$

損失(重要度つきMSE): 特徴ごとに重要度 $I_i$ を付け、重み付きの復元誤差を最小化します。

$$ L = \mathbb{E}_{\bm{x}}\left[ \sum_{i=1}^{n} I_i \,(x_i – \hat{x}_i)^2 \right], \qquad I_i = 0.9^i $$

重要度は指数的に減衰させます。「よく効く特徴もあれば、あまり効かない特徴もある」という現実の非対称性を模しています。

Anthropicのトイモデルのアーキテクチャ模式図

構造は拍子抜けするほど単純です。$n=20$ 個の特徴を $m=5$ 次元のボトルネック $\bm{h}$ に通し、$W^\top$ で元の20次元へ戻して ReLU をかけるだけ。$W$ の第 $i$ 列 $W_i \in \mathbb{R}^m$ が「特徴 $i$ をボトルネック空間のどの方向に埋め込むか」を表します。この列ベクトルたちの配置こそが、これから観察する主役です。

この設計で何が読み取れるのか

このモデルの見どころは、行列 $W^\top W \in \mathbb{R}^{n \times n}$ に集約されます。復元式 $\hat{\bm{x}} = \mathrm{ReLU}(W^\top W \bm{x} + \bm{b})$ を成分で書くと

$$ \hat{x}_i = \mathrm{ReLU}\Big( \|W_i\|^2 x_i + \sum_{j \neq i} (W_i \cdot W_j)\, x_j + b_i \Big) $$

となります。式の意味を分解しましょう。

  • 対角成分 $\|W_i\|^2$: 特徴 $i$ が「自分自身をどれだけ復元できるか」。$\|W_i\| \approx 1$ なら特徴 $i$ はしっかり表現されており、$\|W_i\| \approx 0$ なら捨てられています
  • 非対角成分 $W_i \cdot W_j$: 特徴 $j$ が活性化したときに特徴 $i$ の復元へ漏れ込む干渉。埋め込み方向が直交していればゼロです

つまり $W^\top W$ のヒートマップを見れば、「どの特徴が表現されているか」「特徴間にどれだけ干渉があるか」が一目で分かります。もしモデルが PCA 的に振る舞うなら、重要度上位 $m$ 個の特徴だけが対角に並び、非対角はゼロになるはずです。もし重ね合わせが起きるなら、$m$ 個を超える対角成分が立ち、非対角に干渉の色が付くはずです。

さらに ReLU がここで重要な役割を果たします。負の方向の干渉($W_i \cdot W_j < 0$ で $x_j$ が漏れ込むケース)は、復元値を負に押し下げますが、ReLU が負の値を0で切り落とすため実害が出にくいのです。後で見るように、モデルはこの性質を利用して「2つの特徴を同じ次元の正負両方向に押し込む」という芸当を見せます。

道具立てはそろいました。あとは実際に学習させて、疎性 $S$ のツマミを回してみるだけです。

Pythonでトイモデルを実装する

トイモデルを PyTorch で実装します。データ生成・モデル・訓練ループの3点セットで、GPUは不要です(全実験合わせてCPUで数分程度です)。

import torch
import numpy as np

torch.set_num_threads(4)

def gen_batch(n_features, S, batch_size, generator):
    """各特徴は確率Sで0、活性時は一様分布U(0,1)"""
    x = torch.rand(batch_size, n_features, generator=generator)
    mask = (torch.rand(batch_size, n_features, generator=generator) < (1 - S)).float()
    return x * mask

class ToyModel(torch.nn.Module):
    def __init__(self, n_features, m_hidden):
        super().__init__()
        W = torch.empty(m_hidden, n_features)
        torch.nn.init.xavier_normal_(W)
        self.W = torch.nn.Parameter(W)
        self.b = torch.nn.Parameter(torch.zeros(n_features))

    def forward(self, x):
        h = x @ self.W.T                         # 圧縮: n -> m
        return torch.relu(h @ self.W + self.b)   # 展開+ReLU: m -> n

def train_toy(n_features, m_hidden, S, importance,
              steps=8000, batch_size=1024, lr=1e-3, seed=0):
    torch.manual_seed(seed)
    gen = torch.Generator().manual_seed(seed + 1)
    model = ToyModel(n_features, m_hidden)
    opt = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=0.0)
    sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=steps)
    for _ in range(steps):
        x = gen_batch(n_features, S, batch_size, gen)
        loss = (importance * (model(x) - x) ** 2).mean()
        opt.zero_grad(); loss.backward(); opt.step(); sched.step()
    with torch.no_grad():
        x = gen_batch(n_features, S, 8192, gen)
        final = (importance * (model(x) - x) ** 2).mean().item()
    return model, final

実装のポイントは3つです。第一に、データは毎ステップ新しく生成します(オンライン学習)。特徴の統計的性質そのものを学ばせたいので、固定データセットへの過適合を避けます。第二に、重みは圧縮側 $W$ と展開側 $W^\top$ で共有します。これは論文の設定どおりで、観察対象を「特徴の埋め込み方向 $W_i$」1つに絞るための設計です。第三に、乱数シードを固定しているので、以下の実験結果はすべて再現可能です。

準備ができたので、いよいよ疎性のツマミを回します。

実験1: 疎性を変えると W^T W はどう変わるか

$n=20$ 特徴、$m=5$ 次元、重要度 $I_i = 0.9^i$ で固定し、疎性 $S$ を $0$(すべての特徴が常に活性 = 密)から $0.999$(特徴は1000回に1回しか現れない = 極端に疎)まで6段階で変えて、それぞれモデルを学習させます。

N, M = 20, 5
IMP = torch.tensor([0.9 ** i for i in range(N)])

S_list = [0.0, 0.5, 0.7, 0.9, 0.99, 0.999]
models, losses = {}, {}
for S in S_list:
    model, fl = train_toy(N, M, S, IMP, seed=0)
    models[S], losses[S] = model, fl
    norms = model.W.detach().norm(dim=0)
    n_rep = int((norms > 0.5).sum())
    print(f"S={S:<6} 最終損失={fl:.5f} 表現された特徴数(||W_i||>0.5)={n_rep}")

実行すると次の出力が得られます。

S=0.0    最終損失=0.01953 表現された特徴数(||W_i||>0.5)=5
S=0.5    最終損失=0.02390 表現された特徴数(||W_i||>0.5)=8
S=0.7    最終損失=0.01371 表現された特徴数(||W_i||>0.5)=10
S=0.9    最終損失=0.00408 表現された特徴数(||W_i||>0.5)=12
S=0.99   最終損失=0.00018 表現された特徴数(||W_i||>0.5)=18
S=0.999  最終損失=0.00001 表現された特徴数(||W_i||>0.5)=20

一番右の列に注目してください。密な設定($S=0$)では、モデルは隠れ次元数と同じ5個の特徴しか表現しません。ところが疎性を上げるにつれて表現される特徴数は 8、10、12 と増え、$S=0.999$ では5次元しかないのに20特徴すべてを表現しています。次元数の4倍の特徴が詰め込まれたわけです。

この詰め込みの様子を $W^\top W$ のヒートマップで見てみましょう。

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.font_manager
for cand in ["Hiragino Sans", "Yu Gothic", "Noto Sans CJK JP", "IPAexGothic", "Meiryo"]:
    if any(cand == f.name for f in matplotlib.font_manager.fontManager.ttflist):
        plt.rcParams["font.family"] = cand
        break
plt.rcParams["axes.unicode_minus"] = False

S_heat = [0.0, 0.7, 0.9, 0.99]
fig, axes = plt.subplots(1, 4, figsize=(16, 4.2))
for ax, S in zip(axes, S_heat):
    W = models[S].W.detach()
    G = (W.T @ W).numpy()
    im = ax.imshow(G, cmap="RdBu_r", vmin=-1, vmax=1)
    ax.set_title(f"疎性 $S={S}$")
    ax.set_xlabel("特徴番号 $j$")
fig.colorbar(im, ax=axes, fraction=0.02, pad=0.01)
plt.savefig("/tmp/sp04.png", dpi=110, bbox_inches="tight")

疎性を変えたときのW転置Wヒートマップの変化

この4枚が本記事の目玉の1つです。左端の密な設定($S=0$)では、左上の $5 \times 5$ ブロックだけに対角成分が立ち、残りは真っ白——重要度上位5特徴が5次元を1つずつ専有し、残り15特徴は完全に捨てられています。これは PCA 的な、教科書どおりの最適圧縮です。ところが $S=0.7$ では対角成分が10個に増え、濃い青(負の内積)が非対角に現れます。$S=0.9$、$S=0.99$ と進むにつれて対角の帯は下へ伸び、非対角は干渉を示す赤や青のモザイクで埋まっていきます。捨てるのをやめて、干渉と引き換えに詰め込む方針へ、モデルの戦略が切り替わったのです。

疎性が「捨てる/詰め込む」の切り替えスイッチになっていることが見えました。次は、この切り替えがどのくらい系統的に起きるのかを、特徴ごとのノルムと干渉の量で定量化します。

実験2: 相転移 — 表現される特徴数と干渉のトレードオフ

特徴 $i$ がどれだけ表現されているかは埋め込みノルム $\|W_i\|$ で測れます。まず、全20特徴のノルムを疎性ごとに並べてみます。

fig, ax = plt.subplots(figsize=(10, 5.5))
colors = plt.cm.viridis(np.linspace(0, 0.9, len(S_list)))
for c, S in zip(colors, S_list):
    norms = models[S].W.detach().norm(dim=0).numpy()
    ax.plot(range(N), norms, "o-", color=c, label=f"$S={S}$", ms=4)
ax.set_xlabel("特徴番号(左ほど重要度が高い)")
ax.set_ylabel("埋め込みノルム")
ax.legend()
plt.savefig("/tmp/sp06.png", dpi=110)

疎性別の特徴埋め込みノルムと重要度の関係

$S=0$(紫)の線は、特徴4と5の間で崖のように落ちます。重要度上位5個(=隠れ次元の数)まではノルム1で完全表現、それ以降はほぼ0——「専有できる分だけ表現し、あとは捨てる」という白黒はっきりした方針です。疎性が上がると崖の位置が右へずれていき、$S=0.999$(黄緑)では20特徴全部がノルム1以上を保ちます。面白いのは中間の $S=0.99$(緑)で、後半の特徴が「表現される/されない」をジグザグに行き来しています。詰め込みの限界付近では、どの特徴を拾うかの選択が不安定になるのです。

次に、干渉の量を定量化します。特徴 $i$ への干渉は、単位ベクトル $\hat{W}_i = W_i / \|W_i\|$ を使って $\sum_{j \neq i} (\hat{W}_i \cdot W_j)^2$ で測ります(表現されている特徴のみで平均します)。この指標は論文で特徴の「専有度」を測るために使われるものと同じ形です。

def interference_stats(model, thresh=0.5):
    W = model.W.detach()
    norms = W.norm(dim=0)
    rep = norms > thresh
    Wh = W / norms.clamp(min=1e-8)
    G = (Wh.T @ W) ** 2
    G.fill_diagonal_(0.0)
    interf = G.sum(dim=1)[rep]
    return int(rep.sum()), float(interf.mean())

for S in S_list:
    nr, itf = interference_stats(models[S])
    print(f"S={S:<6} 表現特徴数={nr:>2}  平均干渉={itf:.4f}")
S=0.0    表現特徴数= 5  平均干渉=0.0143
S=0.5    表現特徴数= 8  平均干渉=0.6819
S=0.7    表現特徴数=10  平均干渉=1.0172
S=0.9    表現特徴数=12  平均干渉=1.5853
S=0.99   表現特徴数=18  平均干渉=3.3291
S=0.999  表現特徴数=20  平均干渉=4.1947

疎性に対する表現特徴数と平均干渉の相転移カーブ

2つの曲線が同時に立ち上がっていくのが分かります。密な設定では干渉はほぼゼロ(0.0143)で、埋め込みはほとんど直交です。疎性が上がると表現特徴数が $m=5$ の線を突き破って増える一方、平均干渉も 0.68 → 1.59 → 4.19 と増えていきます。つまりモデルは「干渉を支払って表現数を買う」取引を、疎性が高いほど積極的に行うのです。論文ではこの現象を、個々の特徴が「表現されない/専有される/重ね合わせに入る」という離散的な状態の間を疎性に応じて移り変わる相転移(phase transition)として整理しています。私たちの実測でも、$S=0$ から $S=0.5$ の間に「専有オンリー(干渉0.01)」から「重ね合わせ開始(干渉0.68)」への質的な転換が確認できました。

数はわかりました。しかし重ね合わせの本当の驚きは、詰め込まれた特徴たちが作る幾何学的な構造にあります。次はボトルネックを2次元まで絞って、埋め込み方向を裸眼で観察します。

実験3: m=2次元の幾何 — 反対方向ペアと五角形

隠れ次元を $m=2$ にすれば、特徴の埋め込みベクトル $W_i$ を平面上の矢印としてそのまま描けます。特徴数は $n=5$、重要度は同じく $I_i = 0.9^i$ とし、疎性を4段階で変えて学習させます。

N2, M2 = 5, 2
IMP2 = torch.tensor([0.9 ** i for i in range(N2)])
S2_list = [0.0, 0.8, 0.9, 0.99]
geo_models = {}
for S in S2_list:
    model, fl = train_toy(N2, M2, S, IMP2, steps=12000, seed=0)
    geo_models[S] = model
    W = model.W.detach()
    norms = W.norm(dim=0)
    print(f"S={S:<5} 損失={fl:.5f} ノルム=", np.round(norms.numpy(), 3))
    rep = torch.where(norms > 0.3)[0]
    if len(rep) >= 2:
        Wn = W[:, rep] / norms[rep]
        angs = []
        for a in range(len(rep)):
            for b in range(a + 1, len(rep)):
                cosv = float(Wn[:, a] @ Wn[:, b])
                deg = np.degrees(np.arccos(np.clip(cosv, -1, 1)))
                angs.append((int(rep[a]), int(rep[b]), round(deg, 1)))
        print("   角度:", angs)

出力は次のとおりです(角度は「(特徴i, 特徴j, なす角)」の形式です)。

S=0.0   損失=0.03649 ノルム= [1.    0.989 0.177 0.033 0.01 ]
   角度: [(0, 1, 90.4)]
S=0.8   損失=0.01180 ノルム= [1.013 1.014 1.005 1.005 0.001]
   角度: [(0, 1, 90.0), (0, 2, 180.0), (0, 3, 90.0), (1, 2, 90.0), (1, 3, 180.0), (2, 3, 90.0)]
S=0.9   損失=0.00447 ノルム= [1.112 1.106 1.1   1.088 1.072]
   角度: [(0, 1, 74.2), (0, 2, 147.2), (0, 3, 72.9), (0, 4, 142.4), ...]
S=0.99  損失=0.00014 ノルム= [1.162 1.161 1.165 1.161 1.16 ]
   角度: [(0, 1, 73.0), (0, 2, 145.3), (0, 3, 72.4), (0, 4, 143.3), ...]

矢印として可視化したものがこちらです。

m=2次元に5特徴を埋め込む幾何構造の疎性による変化

これが本記事のもう1つの目玉です。パネルを左から順に読み解きましょう。

$S=0$(密): 重要度上位の特徴0と特徴1だけがノルム約1で表現され、なす角は90.4°——ほぼ完全な直交です。残り3特徴のノルムは 0.177, 0.033, 0.01 とほぼゼロで、捨てられています。2次元には2本しか入らないという「厳密な直交の世界」そのものです。

$S=0.8$: 突然、4本の矢印が現れました。角度リストを見ると (0,2) と (1,3) のなす角がちょうど180.0°です。つまり特徴0と特徴2、特徴1と特徴3が、それぞれ同じ直線の正反対の方向を共有しています。これが論文で antipodal pair(反対方向ペア) と呼ばれる、重ね合わせの最初の構造です。1つの次元の正側と負側に1特徴ずつ押し込むことで、2次元に4特徴が入りました。なぜ反対方向が好都合かというと、パートナーの特徴が活性化したとき自分の復元値は負の方向に汚されますが、ReLU が負の値を0に切り落とすので、干渉の実害がほぼ消えるからです。ReLU の非線形性を逆手に取った、実に巧妙な詰め込みです。なお5番目の特徴(重要度最下位)はまだ捨てられています。

$S=0.9$ と $S=0.99$: ついに5本全部が立ち上がり、隣接する矢印のなす角が 69.5°〜74.2°、対向する矢印が 139.9°〜147.2° に揃いました。正五角形の理論値は隣接72°・対向144°ですから、5特徴がほぼ正五角形に自己組織化したことになります。誰も「五角形を作れ」と教えていないのに、重要度つきMSEを勾配降下しただけで、干渉を均等に最小化する幾何配置——2次元で5方向を詰め込む最適解——が浮かび上がるのです。論文ではさらに大きな設定で四面体や三角柱などの多面体構造(彼らはこれを「特徴の結晶」のように扱っています)が報告されており、私たちの五角形はその最小例です。

ちなみに $S=0.99$ でノルムが約1.16と、1を少し超えている点も見逃せません。干渉によって復元値が全体に目減りするぶんを、埋め込みを少し「強め」にして補償していると解釈できます。

幾何構造の観察で、重ね合わせの実在は疑いようがなくなりました。では、この詰め込みは個々の「ニューロン」からはどう見えるのでしょうか。冒頭の多義性の話に戻ります。

実験4: 多義性はこうして生まれる

隠れ次元(トイモデルにおける「ニューロン」に相当します)を1本選び、その次元が各特徴にどれだけ反応するかを調べます。隠れ表現は $\bm{h} = W\bm{x}$ なので、特徴 $i$ だけが単独で活性化したときの隠れ次元 $k$ の反応は、重み行列の成分 $W[k, i]$ そのものです。

W99 = models[0.99].W.detach()
counts = (W99.abs() > 0.3).sum(dim=1)
k = int(counts.argmax())
print(f"S=0.99: 隠れ次元 {k} は |W[k,i]|>0.3 の特徴を {int(counts[k])} 個持つ")
top = W99[k].abs().argsort(descending=True)[:6]
for i in top:
    print(f"  特徴 {int(i)}: W[{k},{int(i)}] = {W99[k, int(i)]:+.3f}")

W0 = models[0.0].W.detach()
counts0 = (W0.abs() > 0.3).sum(dim=1)
k0 = int(counts0.argmax())
print(f"S=0.0 : 隠れ次元 {k0} が反応する特徴は {int(counts0[k0])} 個")
print("S=0.99: バイアスの平均 =", round(float(models[0.99].b.detach().mean()), 4))
S=0.99: 隠れ次元 1 は |W[k,i]|>0.3 の特徴を 11 個持つ
  特徴 3: W[1,3] = +1.079
  特徴 11: W[1,11] = -0.957
  特徴 15: W[1,15] = -0.628
  特徴 5: W[1,5] = -0.610
  特徴 10: W[1,10] = +0.574
  特徴 1: W[1,1] = -0.536

1つの隠れ次元が多数の特徴に反応する多義性のデモ

右パネルが疎な設定($S=0.99$)の隠れ次元1です。反応する特徴($|W[1,i]| > 0.3$)は11個にのぼり、特徴3には +1.079、特徴11には −0.957 と、正負入り乱れて多数の特徴が1本の次元に相乗りしています。もしこの隠れ次元の活性値だけを見せられて「いまどの特徴が入力されたか」を当てろと言われても、原理的に不可能です。11個の候補のどれか(あるいは複数)としか言えません。これがまさに多義性ニューロンの発生機序です。左パネルの密な設定では、反応する特徴は上位5特徴のうちの4個に収まっており、しかも表現されている特徴の総数が次元数と同じ5個なので、5次元の活性をまとめて見れば入力は一意に復元できます。疎な設定ではそうはいきません。20特徴の情報が5次元に押し込まれている以上、どの軸を眺めても複数の特徴が重なって見えるのです。

ここで大事なのは、多義性が「学習の失敗」ではないことです。損失は密な設定の 0.01953 に対し疎な設定では 0.00018 と、モデルとしてはむしろ大成功しています。表現の性能と、人間にとっての読みやすさは、まったく別の量なのです。モデルは前者だけを最適化するので、読みやすさは容赦なく犠牲になります。

なお、出力の最後にバイアスの平均も表示しています(検証用)。論文では、疎な設定でバイアスが負に振れて「干渉による期待的なかさ上げ」を差し引くフィルタとして働くことが指摘されています。

では、モデルにとって詰め込みは実際どれくらい「得」なのでしょうか。最後の実験で損益を数字にします。

実験5: 詰め込みの損益計算 — なぜ重ね合わせが得なのか

比較対象として、「重要度上位 $m=5$ 特徴だけを完全表現し、残りは復元をあきらめる(最適な定数で予測する)」というPCA的戦略の理論損失を計算します。特徴 $i$ をあきらめた場合の最適な定数予測は $c = (1-S)/2$ で、そのときの二乗誤差の期待値は $S c^2 + (1-S)(c^2 – c + 1/3)$ です。これを重要度で重み付けして合計すれば、専有戦略の理論損失が出ます。

def topm_baseline_loss(n_features, m_hidden, S, importance):
    c = (1 - S) / 2
    const_err = S * c ** 2 + (1 - S) * (c ** 2 - c + 1 / 3)
    imp = importance.numpy()
    per_feature = np.zeros(n_features)
    per_feature[m_hidden:] = const_err
    return float((imp * per_feature).mean())

for S in S_list:
    b = topm_baseline_loss(N, M, S, IMP)
    l = losses[S]
    print(f"S={S:<6} 学習した損失={l:.5f}  上位m専有の理論損失={b:.5f}  比={l/b:.3f}")
S=0.0    学習した損失=0.01953  上位m専有の理論損失=0.01954  比=1.000
S=0.5    学習した損失=0.02390  上位m専有の理論損失=0.02442  比=0.979
S=0.7    学習した損失=0.01371  上位m専有の理論損失=0.01817  比=0.754
S=0.9    学習した損失=0.00408  上位m専有の理論損失=0.00723  比=0.564
S=0.99   学習した損失=0.00018  上位m専有の理論損失=0.00078  比=0.231
S=0.999  学習した損失=0.00001  上位m専有の理論損失=0.00008  比=0.126

重ね合わせ戦略と上位m特徴専有戦略の損失比較

一番右の「比」の列が損益計算書です。$S=0$ では比が 1.000、つまり学習したモデルの損失は専有戦略の理論値と完全に一致します——密なデータでは詰め込む余地がなく、専有が最適なのです。ところが疎になるにつれて比は 0.979 → 0.754 → 0.564 → 0.231 → 0.126 と下がり、$S=0.999$ では専有戦略の8分の1の損失を達成します。干渉のコスト(実験2で見たように増え続けます)を支払ってなお、捨てるはずだった15特徴を拾う利益が圧倒的に上回るからです。なお損失の絶対値が $S$ とともに変わるのはデータの活性率自体が変わるためで、意味があるのは同じ $S$ の中での比較(=比の列)です。

これで重ね合わせ仮説の主張が一通り実証できました。疎性が詰め込みを誘発し(実験1・2)、詰め込みは幾何学的な構造を持ち(実験3)、その帰結として多義性が生まれ(実験4)、そしてこの一連の振る舞いは損失の観点から合理的である(実験5)。ここからは、この事実が解釈性の研究にとって何を意味するのかを考えます。

解釈性への含意 — ニューロン単位の解釈は原理的に壊れる

「読めない」のは仕様である

重ね合わせ仮説がもたらす最大の教訓は、ニューロン単位の解釈が壊れるのは偶然ではなく構造的な必然だということです。実験4で見たとおり、疎な世界で損失を最小化するモデルは、次元より多くの特徴を非直交に詰め込みます。すると座標軸(ニューロン)と特徴方向は原理的に揃わず、どのニューロンを覗いても複数の特徴が重なって見えます。「もっと丁寧にニューロンを調べれば単義的な意味が見つかるはずだ」という期待は、この設定では最初から成立しないのです。

実際のニューラルネットには活性化関数が各ニューロンに独立にかかるため、座標軸は完全に任意ではありません(論文の言葉では「特権的な基底」を持ちます)。それでも表現したい特徴の数が次元を超えていれば、詰め込みはどこかで起きるしかなく、多義性は避けられません。大規模言語モデルのニューロンの大半が解釈困難であるという経験的事実は、この見方と整合しています。

ただし、ここで絶望する必要はありません。重ね合わせ仮説は同時に、解きほぐす方法も示唆しているからです。

スパースオートエンコーダ(SAE)という処方箋

重ね合わせの世界観では、モデルの活性ベクトルは「疎な特徴たちの線形結合」です。ならば、活性ベクトルをもっと広い次元の疎な表現に展開し直せば、混ざってしまった特徴を1本ずつ取り出せるはずです。これはまさに信号処理でいうスパース符号化・辞書学習の問題設定であり、その実装がスパースオートエンコーダ(Sparse Autoencoder, SAE)です。

スパースオートエンコーダで重ね合わせを展開する概念図

SAE は、モデル内部の $m$ 次元の活性を入力に取り、それよりずっと広い(数千〜数万次元の)中間層へ展開して元の活性を復元するよう訓練されます。このとき中間層の活性に $\lambda \|f\|_1$ のような疎性ペナルティを課すのがポイントです。復元精度と疎性の綱引きの結果、中間層の各ユニットは「まれにしか使わないが、使うときははっきり効く」方向——つまり重ね合わされていた個々の特徴——を担当するようになります。密で読めなかった活性が、「ほとんど0、少数だけ活性」の読める表現に変換されるのです。

このアプローチを言語モデルで大規模に実証したのが Anthropic の後続研究 Towards Monosemanticity(Bricken et al., 2023)です。小規模 Transformer の MLP 活性(512ニューロン)に SAE を適用したところ、アラビア文字・DNA配列・法律文書の引用形式など、個々のニューロンからは決して読み取れなかった数千個の単義的な特徴が取り出せることが示されました。ニューロン数の何倍もの特徴が取り出せたこと自体が、実モデルで重ね合わせが起きていることの強い証拠になっています。以後、SAE は LLM 内部の特徴分析の標準装備となり、解釈性研究の景色を一変させました。

「注意を読む」から「表現を読む」へ

本記事は Attentionの解釈性と限界 の続編です。前回は「注意重み $\alpha$ を見ればモデルの判断根拠が分かるのか」という問いを扱い、注意重みは計算の途中経過にすぎず、説明としては不完全であることを見ました。今回の話はその次の段階、「では活性ベクトルの中身そのものを読めばよいのか」に対する答えにあたります。答えは「そのままでは読めない。なぜなら表現は重ね合わされているから。ただし SAE で展開すれば読める部分がある」でした。注意重みの解釈が「どこを見たか」の分析だとすれば、重ね合わせと SAE は「何を考えていたか」の分析です。両者を併せて初めて、モデルの内部計算を回路として追う mechanistic interpretability の土台がそろいます。

まとめ

本記事では、ニューロンの多義性がなぜ生まれるのかを、重ね合わせ仮説とAnthropicのトイモデル(Elhage et al., 2022)の再現実験で確かめました。

  • 多義性は圧縮戦略の副作用: 世界の特徴は次元より多いが疎であるため、モデルは特徴を「ほぼ直交」な方向に重ねて詰め込む。干渉は疎性ゆえにまれで、詰め込む利益が勝る
  • 相転移の実測: $n=20$ 特徴・$m=5$ 次元のトイモデルで、疎性 $S$ を 0→0.999 に上げると表現される特徴数が 5→20 に増加し、平均干渉は 0.014→4.19 に増えた。密では PCA 的な専有、疎では重ね合わせが最適になる
  • 幾何構造の実測: $m=2$ 次元では、$S=0.8$ で反対方向ペア(なす角180°、ReLUが負の干渉を切り落とす)により4特徴、$S=0.9$ 以上で正五角形(隣接約72°)により5特徴が自己組織化した
  • 多義性の発生: 疎な設定では1つの隠れ次元が11個の特徴に反応した。表現性能(損失は専有戦略の1/8)と人間にとっての読みやすさは別の量であり、モデルは前者しか最適化しない
  • SAEによる解きほぐし: ニューロン単位の解釈は原理的に壊れるが、スパースオートエンコーダで活性を広く疎な空間に展開すれば単義的な特徴を回収できる(Bricken et al., 2023)

重ね合わせは、今日の LLM 解釈性研究の理論的な背骨です。SAE の特徴分析、回路の同定、モデル編集といった応用はすべて「表現は重ね合わされた疎な特徴の線形結合である」という本記事の見方の上に築かれています。

次のステップとして、以下の記事も参考にしてください。

参考文献