TL;DR
医師国家試験のデータで Qwen3 系列に post-training をかけるにあたり、まず Qwen3-30B-A3B (MoE, 48 Layer × 32 Head = 1,536 head) の内部を Path Patching で解剖しました。Path Patching = 「Attention Head を 1 個ずつ『別の入力から取ってきた活性値』で差し替えて、出力の変化を測る」手法です。
-
手法: Zhang et al. 2025 (arXiv:2502.11806) の枠組みを医療 MCQ に移植。counterfactual を 2 系統 (医療用語 → 一般語 / 推論キーワード置換) に分け、head ごとに 2 軸の impact (
medical_impact/reasoning_impact) を測定 - スケール: 8 ノード × 64 GPU 並列 × 医師国試 6,478 問 ≒ 約 995 万回の forward pass
- 結果: 両軸同符号の head は約 54% で、浅い層 = Positive 集中 / 深い層 = Negative 集中、しかも Negative 側の絶対値が大きい → モデルは「通常時に貢献している head」に強く依存
- 次回: 選定された Both Positive (378 head) / Both Negative (457 head) を Pinpoint Tuning の学習対象として使う
はじめに
RAMENチームとして医師国家試験のデータで Qwen3 系列に post-training をかけた一本になります。今回のテーマは、post-training sftをより良くするために、モデルの内部を分析します。
つまり、
「Qwen3 の内部構造 がタスク中でどのような影響をもたらしているかを Path Patching で炙り出す。」
ということになります。
この記事は、Chen24 (Fig 1) の Pinpoint Tuning の前段としてのPathPatchingというものです。:

① diagnose (Path Patching で「モデル内部のどこがタスクに寄与しているか」を特定) → ② optimize (その場所だけ狙って学習する) という 2 段構えの枠組みで、今回の記事は ① 部分、次記事が ② 部分に相当します。
なお図に写っている題材は Chen24 が扱った sycophancy (ユーザーに押し戻されると正解を撤回してしまう挙動) なので、本プロジェクトが扱う医療 QA とは対象タスクが違います。ここで拝借しているのは「診断 → 治療」の 2 段構えという枠組みの形だけで、我々自身が sycophancy を対象にしているわけではありません。
具体的には、Qwen3-30B-A3B (MoE, 48 Layer × 32 Head = 1,536 head) を対象に Path Patching を回し、どの層のどの head が医療 QA タスクにどう効いているかを可視化します。
目次
- Path Patching はなにをやっているのか?
- Path Patching を医療 QA タスクにどう当てはめたか?
- スケール: 1,536 head × 数千サンプルをどう回すか?
- 傾向: どの Layer / どの Head が効いているか
- 次にやること: 選んだ head を学習に使う
1. Path Patching は何をやっているのか
1.1 概要
Attention head を「1個だけ壊した」ときに出力がどれだけ変わるかを、全 head で測る手法です。壊し方は雑ではなく、別の入力を通したときの活性値を、そのヘッドの位置にだけ差し込みます。他のヘッドは正常のまま置いておきます。
つまり、
「もしこのヘッドだけが違う入力を見ていたらどうなるか」
を実験する形になります。
正常な forward (全 Head が正常な活性値で動く):
Head 1 だけ壊した forward (Head 1 の活性値だけ「壊した問題文から取ってきた活性値」に差し替え):
そしてこの 2 つのロジットの差から impact を計算します:
impact = (パッチ後のロジット - 元のロジット) / 元のロジット
ここで 「元の問題文」 = 事実的入力 (論文表記 X_f)、「壊した問題文」 = 反事実的入力 (X_cf)、と呼びます。
この 2 つをモデルに通したときの内部活性値がそれぞれ 「正常な活性値」 (H_f) と 「壊した活性値」 (H_cf) です。
「head を 1 個」の正確な意味 (Layer × Head の格子)。実際にはモデルは Layer 方向 (縦) × Head 方向 (横) の 2 次元格子になっています。
Qwen3-30B-A3B なら 48 Layer × 32 Head = 1,536 マスです。
Path Patching で 1 個差し替えるというのは、この格子のうち 1 マス (= 特定の Layer の、特定の Head) だけを「壊した活性値」に置き換える、という意味になります。
全 1,536 マスを 1 個ずつ順に潰していくので、出力は Layer × Head の 2 次元 impact マップになる、というのが基本イメージです。§4 で見る Layer 別分布はこの 2 次元マップの縦軸方向を眺めた話になります。
1.2 アルゴリズム (Zhang25 Algorithm 1)
擬似コードで書くと以下のようになります:
Input: dataset D = {(X_f^i, X_cf^i)}, model F, components C
Output: importance score δ_j for each component c^j
for each (X_f, X_cf) in D:
H_f ← activations of F(X_f) # 事実的活性化
H_cf ← activations of F(X_cf) # 反事実的活性化
for each component c^j:
H̃_f ← {H_cf[k] if k == j else H_f[k]} # c^j だけ差し替え
y_f ← F(X_f; H_f) # 元のロジット
ỹ_f ← F(X_f; H̃_f) # patched ロジット
δ_j^i ← (ỹ_f - y_f) / y_f
δ_j ← mean_i δ_j^i
return {δ_1, …, δ_m}
記号の意味 (このセクション用):
-
D: データセット (元の問題文と壊した問題文のペア集合) -
F: 対象モデル -
C: 測定対象コンポーネントの集合 (今回は全 Attention Head の集合) -
X_f/X_cf: 元の問題文 / 壊した問題文 -
H_f/H_cf: それぞれを通したときの内部活性値 -
H̃_f: 「c^j だけ壊した活性値、他は正常な活性値」というハイブリッドな内部状態 -
y_f/ỹ_f: 元のロジット / パッチ後のロジット -
δ_j: コンポーネントc^jの impact スコア (パッチによるロジットの相対変化)
Qwen3-30B-A3B の場合、|C| = 48 層 × 32 ヘッド = 1,536 です。データ 6,478 サンプル (医師国試 2001-2022 全問) で回すと 6,478 × 1,536 = 約 995 万回の forward pass が必要になります。
2. Path Patching を医療 QA タスクにどう当てはめたか?
まず前提として「Zhang25」という論文が本記事の下敷きになっているので、簡単に紹介します。
Zhang25 = Zhang et al. (2025) "Exploring Translation Mechanism of Large Language Models" arXiv:2502.11806。Path Patching を LLM の翻訳タスクに応用した論文です。本プロジェクトはこの Zhang25 の枠組み (counterfactual 設計・メトリクス) を、医療 QA タスクに移植する形で使っています。
その Zhang25 は具体的には翻訳タスク (「英語→フランス語」を通常入力、「英語→ドイツ語」を反事実入力とする、といった形) で Path Patching を回しました。本プロジェクトはこれを 医療の多肢選択問題 (医療 MCQ = Medical Multiple Choice Question) に持ち込みます。
医療 QA に持ち込む上で、決めることは 2 つあります:
-
どういう「壊した問題文」(
X_cf) を用意するか → §2.1 (実際に使った 2 種類) - その「壊した問題文」を使って何を測るか (impact メトリクス) → §2.2 (2 軸)
Path Patching の結果 = ①で作った「壊した問題文」 × ②のメトリクス の組み合わせで決まります。
その後 2.3 で impact の符号の読み方、2.4 で Zhang25 との対応表、という順で進みます。
2.1 事実的入力と反事実的入力
元の問題文 (事実的入力, X_f): 医師国試の選択問題 (正解を持つ通常の問題文) を使います。
壊した問題文 (反事実的入力, X_cf): Phase2 で実際に Path Patching を回した反事実は、以下の 2 種類です。
| 種類 | 内容 | 例 | 用途 |
|---|---|---|---|
| 医療用語 → 一般語 | 問題文中の医療用語を、上位カテゴリの一般語に置換する | 「胃癌」→「疾患」、「内視鏡検査」→「検査」 |
medical_impact 用 |
| 推論キーワード → 別の推論キーワード | 問いの推論・質問表現を、辞書内の別の推論表現に差し替える | 「〜について誤っているのはどれか」→「〜について診断されるのはどれか」 |
reasoning_impact 用 |
共通の目的は、「元の入力から、医療 QA を正しく解くための手がかりを削るか歪める」 ことです。Zhang25 の翻訳タスクで「翻訳先の言語を変える」ことで翻訳の意図を壊したのと同じ発想を、医療 QA 側に読み替えたものになります。ただし本プロジェクトでは 手がかりの種類を 2 系統に分けた のがポイントで:
- 医療用語 → 一般語 は、医療知識の手がかり (専門用語) を削る操作
- 推論キーワード置換 は、問いの構造 (「〜のはどれか」といった推論の指示) を歪める操作
粒度が違うので、それぞれを patch したときの出力変化を独立に測ることで、「医療知識に効いている head」と「推論の流れに効いている head」を別々に炙り出せる、という設計になっています。この 2 系統の「壊した問題文」が §2.2 の 2 メトリクス (medical_impact / reasoning_impact) と1対1で対応します。
2.2 impact メトリクスの設計 (2 軸)
2.1 で作った「壊した問題文」を使って、head を1つずつパッチしたときの出力変化を測ります。
| メトリクス | 使う「壊した問題文」 | 意味 |
|---|---|---|
medical_impact |
2.1 の 医療用語 → 一般語 のほう | その head が「医療知識」にどれだけ依存しているかの指標 |
reasoning_impact |
2.1 の 推論キーワード → 別の推論キーワード のほう | その head が「推論の流れ」にどれだけ寄与しているかの指標 |
各 impact は「正解と誤答の logit 差分がパッチによって相対的にどれだけ動いたか」で計算します:
impact = (パッチ後のロジット差分 - 元のロジット差分) / 元のロジット差分
= (ỹ_f - y_f) / y_f
記号の意味 (このセクション用):
-
y_f= 元のロジット差分 =logit(正解トークン) - logit(誤答トークン) -
ỹ_f= 対象 head を1つパッチした後の同じロジット差分
同じ head に対して 2 軸のスコアを持たせることで、「知識に依存する head」と「推論に依存する head」を分離して見られます。
2.3 符号の解釈
「壊した問題文」(X_cf) が「正解を壊した入力」なので、impact の符号は以下のように読みます:
- impact > 0: patch すると「正解 - 誤答」が広がる → 元の head は差を 縮める方向に効いていた
- impact < 0: patch すると「正解 - 誤答」が縮む → 元の head は差を 広げる方向に効いていた
「タスクへの貢献量」で見るなら |impact| が大きいほど効いていると読めます。「壊してみたら差が広がった」も情報で、こちらは「本来押し下げていた」というシグナルになります。
Zhang25 での扱い: 論文では fine-tuning 対象を "top-64 heads" と書いているだけで、正の impact のみ / 負の impact のみ / 絶対値ランキング のどれかは明示されていません (Figure 2 のヒートマップから正・負両方の impact が検出されていることは示唆されますが、選別基準は Section 7.3 でも Appendix D でも触れられていない)。おそらく
|impact|の上位で採用していたと推測されますが、確定はできません。
方針: 先行研究で符号の扱いが確定していないかつ「どちらを触れば性能が伸びるか」を事前に決めきれないので、次記事の Pinpoint Tuning では impact > 0 (378 heads) と impact < 0 (457 heads) の 2 系統を別々に学習して比較する形にしています。
2.4 Zhang25 との対応関係
| 観点 | Zhang25 (翻訳) | 本プロジェクト (医療 QA) |
|---|---|---|
元の問題文 (X_f) |
"Translate to English: 你好" | 医療 MCQ |
壊した問題文 (X_cf) |
"Translate to French: 你好" | 医療用語 → 一般語 / 推論キーワード → 別の推論キーワード |
| メトリクス | 単一 logit | 2 軸 (medical_impact + reasoning_impact) |
3. スケール: 1,536 head × 数千サンプルをどう回すか?
Qwen3-30B-A3B の場合、1 サンプルにつき 1,536 回の forward passが必要になります。データが 6,478 サンプル (医師国試 2001-2022 全問) だと、それだけで約 995 万回の forward pass です。素朴に 1GPU で回すと数週間レベルの処理時間がかかります。
3.1 並列化戦略
素直にデータを分割して並列化しています。1 つの forward pass は独立なので、embarrassingly parallel で扱えます。
実際の運用は 1 ノード × 8 GPU の SLURM job を 8 ノード投入 → 合計 64 プロセスで並列実行という形です。6,478 サンプルを 64 分割し、各プロセスがおよそ 101 サンプルずつを処理します。全プロセスが完了したら、サンプル数で重み付け平均を取って統合するので、6478 ÷ 64 が割り切れずに 102 サンプル / 101 サンプルの端数が出ても集約時に偏りません。
3.2 メモリと計算量
| 項目 | 値 |
|---|---|
| モデルロード (bfloat16) | ~60GB (MoE) |
| Path Patching 実行 (batch=2) | A100 80GB 必須 |
| 推奨 GPU | A100 / H100 80GB |
| 1 サンプル処理時間 | 十数秒 |
「正常な活性値」(H_f) と「壊した活性値」(H_cf) は「全層 × 全ヘッド × 全トークン」の活性値を保持することになるので、(層数, ヘッド数, seq_len, head_dim) の巨大テンソルになります。サンプルごとに順次処理・毎回破棄することでメモリを抑えています。
3.3 MoE 固有の考慮 (今回の研究対象外)
Qwen3-30B-A3B は 128 Expert × top-8 のルーティングを持ちますが、今回の Path Patching は Attention head 単位で impact を測る縛りを維持しています。Router を通ってどの Expert が選ばれたか / Expert 側の活性に patch を差し込むとどうなるか、といった MoE 固有のルーティング側の分析は 今回の研究対象からは外しています。
4. 傾向: どの Layer / どの Head が効いているか
ここが本題です。1,536 head の impact スコアが出そろったところで、「効く head はどこに集中しているか」を見ていきます。
4.1 2 軸の符号で仕分ける
medical_impact と reasoning_impact の 2 軸で impact を測定しました。両軸の符号が 一致した head だけを集めると以下のようになります:
| 集合 | 定義 | Head 数 | 割合 |
|---|---|---|---|
| Both Positive | medical > 0 かつ reasoning > 0 | 378 | 24.6% |
| Both Negative | medical < 0 かつ reasoning < 0 | 457 | 29.8% |
| 混在 (符号バラバラ) | それ以外 | 701 | 45.6% |
つまり 1,536 head のうち約半数が、2 軸で符号がきれいに一致します。これは偶然ではなく、medical_impact と reasoning_impact が独立ではなく、head レベルで「タスク全体への向き」がある程度統一されていることを意味します。
Impact 分布のスケール感
| Both Positive (378) | Both Negative (457) | |
|---|---|---|
| medical_impact 範囲 | +0.0000 〜 +0.0494 | -0.6238 〜 -0.0000 |
| medical_impact 平均 | +0.0061 | -0.0075 |
| reasoning_impact 範囲 | +0.0000 〜 +0.1291 | -0.1965 〜 -0.0000 |
| reasoning_impact 平均 | +0.0067 | -0.0076 |
Negative 側のほうが impact の絶対値が大きくなっています。上位を並べると:
Layer 9, Head 3: medical = -0.624 ← 単一 head で logit 差分が 60% スケールで動く計算
Layer 47, Head 28: medical = -0.081, reasoning = -0.197
Layer 46, Head 21: medical = -0.073, reasoning = -0.154
Layer 47, Head 31: medical = -0.058, reasoning = -0.126
Layer 9 Head 3 は他とスケールが違いすぎるので、外れ値疑いも含めて要注視です。もしこの数値が真に意味を持つのであれば、mechanistic interpretability で時折報告される「特定機能が単一 head に極端に集中する」パターン、つまり医療知識処理の大部分をこの 1 個の head が担っている可能性が考えられます。ただし impact は相対変化 (分母 = 元のロジット差分) で定義されているので、そのサンプルでたまたま分母が小さかったせいで見かけ上大きく出ただけ、という可能性も否定しきれず、本記事の分析ではどちらかの断定はできません。
それ以外の Negative top 群は Layer 46-47 の後段層に集まっており、絶対値の大きい head が深い層に偏って現れる、という傾向として読めます。
4.2 ヒートマップ
48 Layer × 32 Head の格子で、2 軸 (medical / reasoning) を並べて表示しています。
ヒートマップの見方
- 格子の縦軸 = Layer (0〜47)、横軸 = Head (0〜31)。1 マス = 1 個の Attention Head に対応
-
左パネル =
medical_impact(医療用語 → 一般語 で反事実を作ったときの impact) -
右パネル =
reasoning_impact(推論キーワード → 別の推論キーワード で反事実を作ったときの impact) -
セルの色 (
RdBu_rカラーマップ)- 🔴 赤系 = 正の impact (patch すると「正解 - 誤答」の差が広がる方向)
- 🔵 青系 = 負の impact (patch すると「正解 - 誤答」の差が縮む方向)
- ⚪ 白 = 0 近傍 (impact がほぼゼロ)
- 濃淡 = 絶対値の大きさ
-
🟩 緑の四角枠 (lime色) = 両軸が同符号の head をハイライト
- Both Positive 版: medical > 0 かつ reasoning > 0 の head を緑枠で表示 (378 個)
- Both Negative 版: medical < 0 かつ reasoning < 0 の head を緑枠で表示 (457 個)
Both Positive Heads (378 個 = 24.6%)

「緑枠が付いた + セルの色が赤系」= 両軸ともしっかり正の impact、という head を目視で拾えます。
Both Negative Heads (457 個 = 29.8%)

「緑枠 + セルの色が青系」= 両軸ともしっかり負の impact、という head。Layer 47 付近に濃い青の緑枠が集まっているのが目立ちます。
目視でわかる分布の違い
- Positive 側: 浅い層 (Layer 0-2) と 中間層 (Layer 22-26) に緑枠が多い
- Negative 側: 深い層 (Layer 40-47) に緑枠が濃く集中し、色も濃い (絶対値が大きい)
同じ Path Patching メトリクスで符号を分けただけで、緑枠の Layer 方向の分布が明らかに違うのが視覚的に確認できます。次の 4.3 で数値化します。
4.3 Layer 別の分布: 符号によって集中する層が違う
Both Positive と Both Negative で、Layer への集中の仕方が違います:
| Both Positive | Both Negative | |
|---|---|---|
| Head を含む Layer 数 | 46 / 48 (Layer 43, 45 除外) | 48 / 48 (全層) |
| Top5 集中層 | L24: 18, L22: 17, L1: 16, L0: 15, L26: 14 | L45: 23, L47: 21, L44: 20, L16: 20, L43: 19 |
| 浅い層 (0-15) の Head 数 | 多い (L0: 15, L1: 16) | 少ない (L0: 3, L1: 1) |
| 深い層 (38-47) の Head 数 | 少ない (1-6 heads/layer) | 多い (12-23 heads/layer) |
- Both Positive = 浅い層 + 中間層に集中 (L0-1, L22-26)
- Both Negative = 深い層に集中 (L43-47)
同じ Path Patching メトリクスで符号を分けただけで、層方向の分布が明確に別れる形になっています。読み方としては:
- 浅い層 = 用語のマッチングや、単語レベルの表面パターン処理に絡む head が集中 → patch で符号が上向く方に多い
- 深い層 = MCQ の推論・回答生成に絡む head が集中 → patch で符号が下向く方に多い
という層方向の役割分布として読めます。Zhang25 の翻訳タスクでも「早期層で source encoding、後期層で target generation」という同種の傾向が報告されており、似ている傾向が得られました。
4.4 まとめ: 傾向として言えること
Path Patching の結果として、head 分布については以下の観察が得られました:
- impact が明確に非ゼロな head は全体の 20-30% 程度。半数以上は impact がゼロ近傍 or 符号が混在
- 層方向で符号分布が別れる — 浅い層と深い層で impact の符号がきれいに偏る
- 絶対値が突出した head が少数存在 — Layer 9 Head 3 の medical_impact = -0.624 が代表例。「単一 head への機能集中」の可能性も考えられるが、分母が小さかったせいで見かけ上大きく出ただけの可能性も残るため断定はできない (詳細は §4.1)
- Negative 側のほうが impact の絶対値が大きい — Positive 平均 +0.006 / Negative 平均 -0.008、範囲も負側が -0.62 まで伸びる (意味と Pinpoint Tuning への含意は §5 で)
5. 次にやること: 選んだ head を学習に使う
Path Patching の結果として、符号で分けた head 集合が 2 つ手元にある状態になりました:
- Both Positive (378 head, 24.6%): 浅い〜中間層に寄っている
- Both Negative (457 head, 29.8%): 深い層に寄っている、絶対値も大きい
ここで 4.4 の観察 (4) — Negative 側の絶対値のほうが大きいという非対称 — が効いてきます。これは「モデルは "壊すと悪化する head" (= 通常時に貢献している head) に強く依存している」ことを意味し、逆に「壊すと改善する head」(Positive 側) は数も効きも小さいことを示唆します。事前学習で head が「タスクに貢献する方向」に最適化された結果として自然に読める絵で、Pinpoint Tuning で Negative 側のほうが学習で強く動く可能性が想定されます。
この選ばれた head だけを学習対象にしたら、モデルの挙動はどう変わるのか?
これが Pinpoint Tuning の設計で、次回の記事の主題になります。
謝辞
この成果は、NEDO(国立研究開発法人新エネルギー・産業技術総合開発機構)の 委託業務(JPNP25006)の結果得られたものです。
参考文献
- Wang et al. (2022) "Interpretability in the Wild: a Circuit for Indirect Object Identification in GPT-2 small" arXiv:2211.00593
- Zhang et al. (2025) "Exploring Translation Mechanism of Large Language Models" arXiv:2502.11806
- Chen et al. (2024) "From Yes-Men to Truth-Tellers: Addressing Sycophancy in Large Language Models" arXiv:2409.01658
関連記事
リポジトリ
https://github.com/weblab-llm-m/singularity-post-training-medical
学習済みモデル (HuggingFace)