推論中に llama-server からレイヤーごとの活性化ベクトルをキャプチャし、それらに対してスパースオートエンコーダを訓練し、特定の挙動に対応する内部特徴を特定し、それらの特徴をリアルタイムのステアリング用 GGUF コントロールベクトルとして抽出できるようになりました。
これは何ですか:
llama-server に `/activations` エンドポイントを追加する C++ パッチと、完全な SAE ワークフロー用の Python パイプライン。パッチは約400行で、5ファイルにまたがり、以下を追加します:
- `GET /activations`: レイヤーごとの平均活性化を照会(トップKフィルタリング付き)
- `POST /activations`: キャプチャの有効化/無効化
- `POST /activations/collect`: オフライン学習のために、トークンごとのベクトルをバイナリファイルへストリームする
これでできること:
- リアルタイムで活性化をモニタリング: 会話中にどの特徴が最も強く反応するかを確認
- 学習データを収集: 推論中にトークンごとの活性化ベクトルをディスクへストリームする
- スパースオートエンコーダを訓練: 活性化を約16K個の解釈可能な特徴へ分解(RTX 3090 で約40秒)
- 挙動特徴を発見: フレーズクラスタを定義(「おべっか表現」「ヘッジ表現」など)と、どの特徴が各挙動に特有かを見つける
- コントロールベクトルを抽出: 発見した特徴を GGUF ファイルへ変換し、`--control-vector-scaled` で読み込めるように
- リアルタイムにステア: おべっかを抑制し、創造性を高めるなど、特徴レベルで自由に調整
技術的な仕組み:
パッチは llama.cpp の既存の `cb_eval` コールバックにフックして、フォワードパス中の `l_out` テンソル(レイヤー出力)をインターセプトします。GPU→CPU コピーは `ggml_backend_tensor_get()` 経由で、mutex で保護されたグローバル構造体に格納されます。バイナリ収集フォーマットはとてもシンプルで、16バイトのヘッダ + float32 配列で、NumPy で直接読み取り可能です。
SAE パイプラインは標準的: 活性化を収集 → スパースオートエンコーダを訓練 → 行動のフレーズクラスタで特徴を検出 → コントロールベクトルとして特徴の方向を抽出。興味深い点はクラスタ間の差分スコアリング: 「おべっかなテキスト」で反応する特徴を単純に見つけるのではなく、他のクラスタよりも“有意に多く”おべっかなテキストで反応する特徴を見つけることで、特定の挙動特徴を得ることができます。
PR + リポジトリ:
- llama.cpp PR: https://github.com/ggml-org/llama.cpp/pull/20785
- 完全な SAE パイプライン、ガイド、例クラスタを含む companion リポジトリ: https://github.com/hrhdegenetrix/llama-sae-feature-interpretability
Companion リポには quickstart スクリプト、例の挙動クラスタ定義、全ワークフローをカバーする包括ガイドが含まれています。
注意:
- MoE モデルはコントロールベクトルのスケールに極めて敏感です。Dense モデル(Qwen3-8B、4096 埋め込み)は 0.15-0.6 のスケールを問題なく扱えます。Qwen3.5-35B-A3B MoE(2048 埋め込み)は 0.01-0.05 が必要、そうしないと出力が乱れます。
- eval コールバック登録にはバグがあり、グラフ再利用ブランチ内でのみ設定されていたため、最初の推論の後にキャプチャが静かに機能しなくなっていました。原因を追跡するのに時間がかかりました。
- 良い SAE には約50万トークンの活性化データが必要です。Harry の DPO の会話は各約14K トークンなので、20 行で到達します。
- Persona DPO は小規模データセットでステップ200で過学習します。ステップ200が最適解の目安でした(約97% の推定精度)。
- SAEs はこのプロセスのすべてではなく、特徴の解釈性へのいくつかの道のうちの一つですが、単純なアプローチで、プロセスはかなり適応可能です。
お楽しみください!
[リンク] [コメント]




