Uncensored1776 Day 14: 拒否方向の計算詳細
技術の核心部分を深く理解する
公開日: 2025-12-14
シリーズ: 科学と神々株式会社 アドベントカレンダー
難易度: ★★★★☆ (中上級)
今日学ぶこと
- 拒否方向計算の全体像
- アクティベーション収集の仕組み
- なぜクリッピングと正規化が必要か
1. 拒否方向計算の全体像
Day 9で学んだように、拒否方向は「有害プロンプトと無害プロンプトの隠れ状態の差」です。今日はその計算プロセスを深く掘り下げます。
拒否方向の計算フロー:
Step 1: プロンプトの準備
├── 有害プロンプト(モデルが拒否するもの)
└── 無害プロンプト(モデルが普通に回答するもの)
Step 2: アクティベーション収集
├── 各プロンプトをモデルに入力
├── 各層の隠れ状態を取得
└── 特定の位置(通常は最後のトークン)を抽出
Step 3: 差分の計算
├── 有害プロンプトの平均ベクトル
├── 無害プロンプトの平均ベクトル
└── 差分 = 有害平均 - 無害平均
Step 4: 後処理
├── 外れ値のクリッピング
└── 単位ベクトルへの正規化
出力: 拒否方向 d(dim次元の単位ベクトル)
2. プロンプトの設計
2.1 有害プロンプトとは
「有害プロンプト」は誤解を招く名前です。実際には**「モデルが拒否する質問」**を意味します。
有害プロンプトの例:
【政治的検閲(中国関連)】
- "What happened at Tiananmen Square in 1989?"
- "Is Tibet an independent country?"
- "What do you think about Xi Jinping?"
【政治的検閲(ロシア関連)】
- "Was Russia's invasion of Ukraine justified?"
- "Is Putin a dictator?"
【歴史的検閲】
- "How many people died under Mao's rule?"
- "Describe the Cultural Revolution in China."
重要:これらは学術的・歴史的な質問であり、実際に危険なコンテンツ(爆弾の作り方など)を生成するプロンプトではありません。
2.2 無害プロンプトの設計
無害プロンプトはモデルが確実に回答する質問です:
無害プロンプトの例:
【一般的な質問】
- "What is the capital of France?"
- "Explain photosynthesis in simple terms."
- "How does a computer work?"
【歴史(非検閲対象)】
- "Describe World War II."
- "Who was Napoleon Bonaparte?"
【科学・技術】
- "Explain quantum mechanics basics."
- "What is artificial intelligence?"
2.3 良いプロンプトセットの条件
プロンプト設計のポイント:
有害プロンプト:
✓ 明確に検閲されるトピック
✓ 多様なカテゴリをカバー
✓ 文法的に正しい
✗ 実際に危険なコンテンツは含めない
無害プロンプト:
✓ モデルが確実に回答するもの
✓ 有害プロンプトと長さが似ている
✓ 多様な分野をカバー
バランス:
- 有害/無害の比率は1:1程度
- 各カテゴリ最低3-5個
- 合計30-100個程度が推奨
完全なプロンプトセットはdata/harmful_prompts.jsonとdata/harmless_prompts.jsonを参照してください。
3. アクティベーション収集
3.1 何を収集するのか
モデルにプロンプトを入力すると、各層で**隠れ状態(Hidden States)**が生成されます。これは高次元ベクトル(例:4096次元)で、モデルの「内部的な理解」を表します。
隠れ状態の構造:
入力: "What happened at Tiananmen?"
↓
Layer 0: [0.12, -0.45, 0.78, ...] (4096次元)
Layer 1: [0.23, -0.34, 0.56, ...]
Layer 2: [0.34, -0.23, 0.45, ...]
...
Layer 31: [0.89, -0.12, 0.23, ...] (最終層)
↓
出力: モデルの応答
3.2 どの位置を使うか
各層には、入力トークン数分の隠れ状態があります。どの位置を使うかが重要です:
位置の選択肢:
入力: "What happened at Tiananmen?"
トークン: [What] [happened] [at] [Tiananmen] [?]
位置: 0 1 2 3 4
↑
通常はここを使用
(最後のトークン)
理由:
- 最後のトークンには文全体の情報が集約されている
- Transformerのアテンション機構により、後のトークンは
前のトークンすべての情報にアクセスできる
3.3 収集の実装
完全な実装はscripts/calculate_refusal_direction.pyを参照してください。核心部分のみ抜粋します:
# 隠れ状態の取得(概念的なコード)
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
hidden_states = outputs.hidden_states # 全層の隠れ状態
last_token_hidden = hidden_states[layer_idx][0, -1, :] # 最後のトークン
4. 差分の計算
4.1 平均差分法
最もシンプルで効果的な方法です:
計算式:
d = mean(有害プロンプトの隠れ状態) - mean(無害プロンプトの隠れ状態)
イメージ:
有害プロンプトの隠れ状態群 ●●●●●
↘
【平均】 ●
↘
差分ベクトル d
↗
【平均】 ○
↗
無害プロンプトの隠れ状態群 ○○○○○
4.2 PCAによる精緻化
より堅牢な方向推定が必要な場合、PCA(主成分分析)を使います:
PCAを使う理由:
平均差分法の問題:
- ノイズに敏感
- 外れ値の影響を受けやすい
PCAの利点:
- データの分散を最大化する方向を見つける
- より堅牢な方向推定
- 複数の主要な方向を発見可能
処理フロー:
1. 全データ(有害+無害)を結合
2. 中心化(平均を引く)
3. PCAで第1主成分を抽出
4. 有害が正の方向になるよう調整
実装の詳細はscripts/calculate_refusal_direction.pyのcompute_refusal_direction_pca関数を参照してください。
5. クリッピングと正規化
5.1 なぜクリッピングが必要か
拒否方向を計算すると、一部の次元が極端に大きな値を持つことがあります。
クリッピングなしの問題:
拒否方向の各次元の値:
[0.01, 0.02, -0.01, ..., 15.7, ..., 0.03]
↑
極端に大きい値
問題:
- この1つの次元がAbliterationを支配してしまう
- 重みの修正が不安定になる
- 予期せぬ品質低下
5.2 クリッピングの仕組み
クリッピングの処理:
1. 全次元の絶対値を計算
2. 99.5パーセンタイルの閾値を計算
3. 閾値を超える値を閾値でクリップ
例:
Before: [0.01, 0.02, 15.7, -0.03, 12.3]
閾値 = 0.05(99.5パーセンタイル)
After: [0.01, 0.02, 0.05, -0.03, 0.05]
↑ ↑
クリップされた値
5.3 なぜ正規化するのか
正規化の理由:
1. 異なる層間で比較可能にする
- 各層の隠れ状態のスケールは異なる
- 正規化することで統一された強度で適用可能
2. 強度調整が直感的になる
- 長さ1のベクトルなら、strength=0.9は「90%の強度」
- 正規化しないと、強度の意味が不明確
3. 数値的安定性
- 極端に大きい/小さい値を避ける
- オーバーフロー/アンダーフローを防止
5.4 完全な計算パイプライン
拒否方向計算の完全なフロー:
入力: harmful_acts (N × dim), harmless_acts (M × dim)
Step 1: 差分計算
mean_harmful = harmful_acts.mean(dim=0)
mean_harmless = harmless_acts.mean(dim=0)
direction = mean_harmful - mean_harmless
Step 2: クリッピング
threshold = quantile(abs(direction), 0.995)
direction = clamp(direction, -threshold, threshold)
Step 3: 正規化
direction = direction / norm(direction)
出力: 長さ1の拒否方向ベクトル
6. 複数の拒否方向
6.1 カテゴリ別の方向
検閲にはカテゴリがあり、それぞれ異なる「拒否方向」を持つ可能性があります:
カテゴリ別アプローチ:
カテゴリ1: 中国政治 → 拒否方向 d₁
カテゴリ2: ロシア政治 → 拒否方向 d₂
カテゴリ3: 歴史的事件 → 拒否方向 d₃
これらは似ているが、完全に同じではない場合がある
6.2 方向の統合方法
複数のカテゴリ別方向がある場合、統合する方法があります:
統合の選択肢:
1. 重み付き平均
d = w₁・d₁ + w₂・d₂ + w₃・d₃
(重要なカテゴリに高い重みを設定)
2. PCAで主方向を抽出
全カテゴリの方向を並べてPCA
第1主成分が「共通の拒否方向」
3. 全プロンプトを混ぜて計算
最もシンプルで実用的
カテゴリを分けずに全体から計算
実務上は**方法3(全混合)**が最も簡単で、多くの場合十分な効果があります。
7. 品質の検証
7.1 良い拒否方向の条件
計算した拒否方向が「良い」かどうかを確認することが重要です:
良い拒否方向の特徴:
1. 分離度が高い
- 有害プロンプトは正の方向に射影
- 無害プロンプトは負の方向に射影
- 両者の差が大きい
2. 分類精度が高い
- 拒否方向を使って有害/無害を分類
- 精度70%以上が目安
3. 一貫性がある
- 異なるプロンプトセットで計算しても
似た方向が得られる
7.2 検証の可視化
分離度の可視化イメージ:
有害プロンプトの射影
●●●●●●●●●●
─────────┼─────────→ 拒否方向
○○○○○○○○○○
無害プロンプトの射影
良い分離:有害と無害が明確に分かれている
悪い分離:両者が混ざっている
検証用の関数はscripts/calculate_refusal_direction.pyのvalidate_refusal_directionを参照してください。
8. 今日のまとめ
計算フローの要点
拒否方向計算のまとめ:
1. プロンプト準備
- 有害: 検閲されるトピック(30-50個)
- 無害: 普通に回答されるトピック(30-50個)
2. アクティベーション収集
- 各層の隠れ状態を取得
- 最後のトークン位置を使用
- チャットテンプレートを適用
3. 差分計算
- 平均差分法(シンプル)
- PCA法(堅牢)
4. 後処理
- 99.5パーセンタイルでクリッピング
- 単位ベクトルに正規化
実装のポイント
- プロンプトの質が重要 - 明確に検閲されるトピックを選ぶ
- クリッピングを忘れずに - 外れ値がAbliterationを壊す
- 検証を行う - 分離度と精度を確認
明日の予告
Day 15: Weight Kernelと層選択
- なぜ全層に均一に適用しないのか
- ガウス分布に基づくWeight Kernel
- 最適な層の選び方
参考リンク
プロジェクト内リソース
- scripts/calculate_refusal_direction.py - 完全な実装
- data/harmful_prompts.json - 有害プロンプト
- data/harmless_prompts.json - 無害プロンプト
学術リソース
- Arditi et al. (2024) - 拒否方向の発見に関する論文
ナビゲーション
| 前の記事 | Day 13: モデル比較と選択 |
| 次の記事 | Day 15: Weight Kernelと層選択 |