LESSON

Isolation Forest

「不正検知には大きく2つのアプローチがある。教師あり学習と、異常検知だ。」

田中VPoEがホワイトボードに図を描く。

「まずはIsolation Forestから始めよう。ラベルなしでも使える強力な異常検知アルゴリズムだ。新しい不正パターンにも対応できる可能性がある。」

異常検知アプローチの利点

教師あり学習はラベル付きデータが必要だが、異常検知は「正常」のパターンを学習し、そこから外れるものを異常とみなす。

教師あり学習:
  「不正とはこういうものだ」を学習
  → 既知の不正パターンに強い
  → 未知のパターンに弱い

異常検知:
  「正常とはこういうものだ」を学習
  → 正常から外れれば未知の不正も検知可能
  → 偽陽性が多くなりやすい

Isolation Forestの原理

基本アイデア

「異常なデータは、ランダムな分割で素早く孤立(Isolate)できる」

正常データ: 密集しているため、何度も分割しないと孤立できない
異常データ: 外れた位置にあるため、少ない分割で孤立できる

→ 孤立に必要な分割回数(パスの長さ)が短い = 異常

アルゴリズム

Isolation Forest アルゴリズム:

1. サブサンプリング: データからランダムにサンプルを抽出
2. Isolation Tree の構築:
   a. ランダムに特徴量を1つ選択
   b. その特徴量の最小値〜最大値の間でランダムに分割点を選択
   c. データを左右に分割
   d. 各サンプルが孤立するか、最大深さに達するまで繰り返し
3. 複数のIsolation Treeでアンサンブル
4. 異常スコア = 各Treeでの平均パス長の逆数

可視化イメージ

通常のデータ点(密集):        異常なデータ点(孤立):

    ●●●●                        ○
    ●●●●
    ●●●●                      ←分割1回で孤立!
    ●●●●

    何回も分割が必要

scikit-learnでの実装

from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
import numpy as np

# データ準備
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Isolation Forest
iso_forest = IsolationForest(
    n_estimators=200,        # ツリーの数
    max_samples='auto',      # サブサンプルサイズ(auto=256)
    contamination=0.002,     # 異常の想定割合(不正率)
    max_features=1.0,        # 使用する特徴量の割合
    random_state=42,
    n_jobs=-1
)

# 学習(正常データのみで学習するのが理想的)
iso_forest.fit(X_scaled)

# 予測: -1=異常, 1=正常
predictions = iso_forest.predict(X_scaled)

# 異常スコア(低いほど異常)
anomaly_scores = iso_forest.decision_function(X_scaled)
# score_samples() はより生のスコアを返す
raw_scores = iso_forest.score_samples(X_scaled)

ハイパーパラメータ

パラメータ説明推奨値注意点
n_estimatorsツリーの数100〜300多いほど安定するが計算コスト増
max_samplesサブサンプルサイズ256 or ‘auto’大きすぎると正常パターンに過適合
contamination異常の想定割合実際の不正率に近い値閾値に影響する重要パラメータ
max_features使用特徴量の割合0.5〜1.0低いと多様性が増す

contaminationの調整

# contamination は閾値を決めるパラメータ
# 実際の不正率がわかっている場合はそれを設定

# 方法1: 既知の不正率を使用
iso_forest_1 = IsolationForest(contamination=0.0017)

# 方法2: 閾値を自分で設定(推奨)
iso_forest_2 = IsolationForest(contamination='auto')
iso_forest_2.fit(X_train_scaled)

# スコアを取得して独自の閾値を設定
scores = iso_forest_2.decision_function(X_test_scaled)

# パーセンタイルで閾値設定
threshold = np.percentile(scores, 0.5)  # 下位0.5%を異常とする
predictions = (scores < threshold).astype(int)

閾値設定の戦略

スコア分布の分析

import matplotlib.pyplot as plt

# 正常と不正のスコア分布
scores_normal = anomaly_scores[y == 0]
scores_fraud = anomaly_scores[y == 1]

plt.figure(figsize=(10, 5))
plt.hist(scores_normal, bins=100, alpha=0.7, label='正常', density=True)
plt.hist(scores_fraud, bins=50, alpha=0.7, label='不正', density=True, color='red')
plt.xlabel('Anomaly Score')
plt.ylabel('Density')
plt.title('Isolation Forest: 異常スコアの分布')
plt.legend()
plt.show()

# 不正取引のスコアが低い(異常側)に分布していることを確認

コストベース閾値最適化

# 閾値を変えてビジネスコストを最小化
thresholds = np.linspace(scores.min(), scores.max(), 200)
best_cost = float('inf')
best_threshold = None

for t in thresholds:
    y_pred = (scores < t).astype(int)
    fn = ((y_test == 1) & (y_pred == 0)).sum()
    fp = ((y_test == 0) & (y_pred == 1)).sum()
    cost = fn * 50000 + fp * 500

    if cost < best_cost:
        best_cost = cost
        best_threshold = t

print(f"最適閾値: {best_threshold:.4f}")
print(f"最小コスト: {best_cost:,.0f}円")

Isolation Forestの長所と短所

長所短所
ラベルなしで使える教師あり学習より精度が劣る傾向
線形時間で学習(高速)特徴量の重要度がわかりにくい
未知の不正パターンに対応可能閾値設定が難しい
次元の呪いに比較的強い局所的な異常を見逃す場合がある

まとめ

項目ポイント
原理異常データはランダム分割で素早く孤立できる
利点ラベル不要、高速、未知パターン対応
重要パラメータcontamination、n_estimators、max_samples
閾値設定コストベースで最適化するのが実用的

チェックリスト

  • Isolation Forestの原理を説明できる
  • 「パスの長さが短い = 異常」の意味を理解した
  • contamination パラメータの役割を説明できる
  • コストベースの閾値最適化を実装できる

次のステップへ

異常検知アプローチを理解したところで、次は教師あり学習のアプローチで不正検知モデルを構築しよう。

推定読了時間: 30分