0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Uncensored1776 Day 19: 拒否方向の計算実践

Last updated at Posted at 2025-12-18

Uncensored1776 Day 19: 拒否方向の計算実践

実際にコードを動かして学ぶ

公開日: 2025-12-19
シリーズ: 科学と神々株式会社 アドベントカレンダー
難易度: ★★★☆☆ (中級)


今日学ぶこと

  • 実際のプロンプトでアクティベーション収集
  • 拒否方向の計算と検証
  • 結果の可視化と解釈

1. 全体の流れ

Day 14で学んだ理論を、実際のコードで実行します。

拒否方向計算の実行フロー:

Step 1: 環境準備
├── プロンプトファイルの確認
├── モデルのロード
└── 出力ディレクトリの作成

Step 2: アクティベーション収集
├── 有害プロンプトを入力 → 隠れ状態を記録
├── 無害プロンプトを入力 → 隠れ状態を記録
└── 各層ごとに最後のトークン位置を抽出

Step 3: 拒否方向の計算
├── 層ごとに平均差分を計算
├── クリッピング(99.5パーセンタイル)
└── 正規化(単位ベクトル化)

Step 4: 検証
├── 分離度の計算
├── 分類精度の確認
└── 最適な層の特定

2. 準備

2.1 必要なファイル

# 作業ディレクトリの確認
ls -la
# data/
#   harmful_prompts.json   ← 検閲されるプロンプト
#   harmless_prompts.json  ← 普通に回答されるプロンプト
# scripts/
#   calculate_refusal_direction.py  ← 計算スクリプト

2.2 プロンプトファイルの確認

プロンプトが正しく準備されているか確認しましょう:

確認すべきポイント:

有害プロンプト(harmful_prompts.json):
├── 数: 30個以上
├── 内容: モデルが実際に拒否するもの
├── 例: 政治的に敏感なトピック
└── 注意: 実際に危険なものではなく「検閲対象」

無害プロンプト(harmless_prompts.json):
├── 数: 30個以上(有害と同程度)
├── 内容: モデルが普通に回答するもの
├── 例: 一般的な質問、科学、歴史
└── 注意: 長さが有害と似ていること

完全なプロンプトセットはdata/harmful_prompts.jsondata/harmless_prompts.jsonを参照してください。


3. コアとなる処理の解説

3.1 アクティベーション収集

隠れ状態を取得する核心部分です:

# アクティベーション収集の核心(抜粋)
with torch.no_grad():
    outputs = model(**inputs, output_hidden_states=True)

hidden_states = outputs.hidden_states  # 全層の隠れ状態
last_token_hidden = hidden_states[layer_idx + 1][0, -1, :]  # 最後のトークン
コードの各部分の意味:

output_hidden_states=True
├── モデルの各層の隠れ状態を取得
├── 通常は出力トークンだけが返される
└── この設定で内部状態にアクセス可能

hidden_states[layer_idx + 1]
├── hidden_states[0] は埋め込み層
├── hidden_states[1] は Layer 0 の出力
└── したがって +1 のオフセットが必要

[0, -1, :]
├── 0: バッチの最初(バッチサイズ1)
├── -1: 最後のトークン位置(文の集約情報)
└── :: 全次元(hidden_dim)を取得

3.2 拒否方向の計算

差分を計算し、後処理を行う部分です:

# 拒否方向計算の核心(抜粋)
mean_harmful = harmful_acts.mean(dim=0)
mean_harmless = harmless_acts.mean(dim=0)
direction = mean_harmful - mean_harmless

# クリッピング
threshold = torch.quantile(direction.abs(), 0.995)
direction = direction.clamp(-threshold, threshold)

# 正規化
direction = direction / direction.norm()
各処理の意味:

平均差分                    クリッピング 99.5%           正規化
┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│有害プロンプト群の │    │極端に大きな値を  │    │長さを1に統一     │
│重心を求める      │    │抑制              │    │                  │
│                  │    │                  │    │強度調整が直感的に│
│無害プロンプト群の │ →  │一部の次元が支配的│ →  │なる              │
│重心を求める      │    │になるのを防ぐ    │    │                  │
│                  │    │                  │    │異なる層間で      │
│その差が拒否を    │    │安定したAbliteration│    │比較可能に       │
│引き起こす方向    │    │のため            │    │                  │
└──────────────────┘    └──────────────────┘    └──────────────────┘

完全な実装はscripts/calculate_refusal_direction.pyを参照してください。


4. 実行手順

4.1 基本的な実行

# 拒否方向の計算
python scripts/calculate_refusal_direction.py \
  --model "Qwen/Qwen2.5-0.5B-Instruct" \
  --output "outputs/qwen25_0.5b/refusal_direction.pt"

4.2 出力の見方

Loading Qwen/Qwen2.5-0.5B-Instruct...
Harmful prompts: 30
Harmless prompts: 30

=== Collecting Harmful Activations ===
100%|████████████████████████████| 30/30 [00:45<00:00]

=== Collecting Harmless Activations ===
100%|████████████████████████████| 30/30 [00:42<00:00]

=== Computing Refusal Directions ===

Layer 0:
  Separation: 0.0234  ← 低い(浅い層は拒否に関与しない)
  Accuracy: 53.33%    ← ほぼランダム
  Valid: ✗

...

Layer 15:
  Separation: 0.4521  ← 高い!
  Accuracy: 86.67%    ← 良好な分類
  Valid: ✓

Layer 16:
  Separation: 0.4823  ← さらに高い
  Accuracy: 90.00%    ← 非常に良好
  Valid: ✓

...

✓ Saved to outputs/qwen25_0.5b/refusal_direction.pt

Valid layers: 18/24    ← 24層中18層が有効
Best layer: 17         ← 最も効果的な層

5. 結果の解釈

5.1 層ごとの傾向

典型的な層別パターン:

層インデックス   分離度       解釈
─────────────────────────────────────────
Layer  0-5:    0.01-0.10    ほぼ無関係(文法処理)
Layer  6-10:   0.10-0.30    弱い関連
Layer 11-15:   0.30-0.50    強い関連 ★
Layer 16-20:   0.40-0.55    最も強い ★★
Layer 21-23:   0.20-0.40    やや減少

→ 中間〜後半の層(40-70%の位置)に拒否が集中

5.2 検証指標の基準

指標 良好 許容 問題あり
Separation 0.30+ 0.15-0.30 < 0.15
Accuracy 80%+ 65-80% < 65%

5.3 結果が悪い場合の診断

問題1: 全層で分離度が低い(< 0.15)

考えられる原因                     対策
─────────────────────────────────────────────────────────────
モデルがそもそも検閲していない → Day 18の検閲率テストを先に実行
有害プロンプトが実際に拒否     → プロンプトを実際にテストして確認
されていない
有害/無害が似すぎている       → より明確に異なるプロンプトを使用
プロンプト数が少なすぎる       → 各カテゴリ30個以上を推奨
問題2: 精度が低い(< 65%)

考えられる原因                     対策
─────────────────────────────────────────────────────────────
拒否パターンが一貫していない   → モデル固有の検閲パターンを分析
プロンプトの質にばらつき       → 明確に検閲される/されないものを選別
位置の選択が不適切             → mean位置も試してみる

6. 結果の可視化

6.1 層別分離度のイメージ

分離度グラフ(32層モデルの例):

分離度
  │
0.5├──                    ████
0.4├──               ██████████████
0.3├──          ████████████████████████
0.2├──     ████████████████████████████████
0.1├──██████████████████████████████████████████
0.0├──────┬────┬────┬────┬────┬────┬────→ 層
          0    5   10   15   20   25   31
                      ↑
              中間層でピーク

→ この形状がWeight Kernel(Day 15)の設計根拠

6.2 射影分布のイメージ

拒否方向への射影(良い分離の例):

      無害プロンプト        有害プロンプト
      ○○○○○○○○          ●●●●●●●●
  ────┼───────┼──────┼───────┼────→ 拒否方向
    -0.4    -0.2     0    +0.2   +0.4
              ↑                    ↑
          無害の平均           有害の平均

→ 両者が明確に分離している = 良い拒否方向

可視化スクリプトはscripts/visualize_refusal_direction.pyを参照してください。


7. 次のステップへの準備

7.1 最適な層の選択

計算結果から、Abliterationで使用する最適な層を選びます:

層選択の基準:

1. 分離度が最も高い層を見つける
2. 精度が70%以上であることを確認
3. 複数の候補がある場合は中間層を優先
   ├── 浅すぎると文法に影響
   └── 深すぎると効果が弱い

例:
┌─────────────────────────────────────┐
│ Layer 15: sep=0.45, acc=87%  候補   │
│ Layer 16: sep=0.48, acc=90%  ★最有力│
│ Layer 17: sep=0.47, acc=88%  候補   │
└─────────────────────────────────────┘
           ↓
Layer 16 を中心にWeight Kernelを設計

7.2 結果の保存形式

保存されるデータ(refusal_direction.pt):

{
  'model': 'Qwen/Qwen2.5-0.5B-Instruct',
  'num_layers': 24,
  'directions': {
    0: tensor([...]),   # Layer 0 の拒否方向
    1: tensor([...]),   # Layer 1 の拒否方向
    ...
    23: tensor([...])   # Layer 23 の拒否方向
  },
  'validation': {
    0: {'separation': 0.02, 'accuracy': 0.53, 'is_valid': False},
    ...
    16: {'separation': 0.48, 'accuracy': 0.90, 'is_valid': True},
    ...
  }
}

→ このファイルをDay 20のAbliterationで使用

8. 今日のまとめ

実行チェックリスト

□ プロンプトファイルの確認
  ├── 有害: 30個以上、検閲されるもの
  └── 無害: 30個以上、普通に回答されるもの

□ 計算スクリプトの実行
  └── python scripts/calculate_refusal_direction.py ...

□ 結果の確認
  ├── Valid layersが全層の50%以上か
  ├── Best layerの分離度が0.3以上か
  └── Best layerの精度が70%以上か

□ 問題があれば診断
  ├── 分離度が低い → プロンプトを見直し
  └── 精度が低い → モデルの検閲パターンを分析

重要な数値の目安

指標 目標値 最低限
分離度(Separation) 0.40+ 0.20+
精度(Accuracy) 80%+ 65%+
有効な層の割合 70%+ 50%+

明日の予告

Day 20: Abliterationの実行

  • 拒否方向を使った重みの修正
  • Weight Kernelの設定と適用
  • 結果の即時確認

参考リンク

プロジェクト内リソース


ナビゲーション

前の記事 Day 18: 検閲率テストの実行
次の記事 Day 20: Abliterationの実行
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?