STEP 20:異常検知

🔍 STEP 20: 異常検知

Isolation Forest、LOF、One-Class SVMで異常データを検出する

📋 このステップで学ぶこと

  • 異常検知とは何か、なぜ必要か
  • Isolation Forest(孤立森)
  • Local Outlier Factor(LOF)
  • One-Class SVM
  • 異常検知手法の使い分け
  • 実践:不正取引検知

演習問題: 5問

🎯 1. 異常検知とは

📊 異常検知(Anomaly Detection)

「通常」のパターンから大きく外れたデータを検出する手法です。

外れ値検知(Outlier Detection)とも呼ばれます。

異常検知の活用例

【異常検知の活用例】 💳 不正取引検知 → 通常の購買パターンから外れた取引を検出 🏭 製造業の品質管理 → 正常な製品と異なる不良品を検出 🖥️ システム監視 → サーバーの異常動作やサイバー攻撃を検出 🏥 医療診断 → 通常と異なる検査値を検出

異常検知のアプローチ

💡 教師なし学習としての異常検知

異常データは数が少なく、ラベルがないことが多い。
そのため、「正常」のパターンを学習して、そこから外れたものを異常とするアプローチが一般的。

アプローチ 考え方 代表的な手法
統計的手法 データの分布から外れた点を異常とする Zスコア、IQR法
密度ベース 周囲の密度が低い点を異常とする LOF、DBSCAN
木ベース 孤立しやすい点を異常とする Isolation Forest
境界ベース 正常データの境界外を異常とする One-Class SVM

🌲 2. Isolation Forest

Isolation Forestの仕組み

🔍 Isolation Forest(孤立森)

「異常なデータは孤立しやすい」という考えに基づく手法。

・ランダムに特徴量と分割点を選んでデータを分割
異常データ:少ない分割回数で孤立する
正常データ:多くの分割が必要

# Isolation Forestの基本実装 import numpy as np import matplotlib.pyplot as plt from sklearn.ensemble import IsolationForest # サンプルデータの生成 np.random.seed(42) # 正常データ(2つのクラスタ) X_normal = np.vstack([ np.random.randn(100, 2) + [2, 2], np.random.randn(100, 2) + [-2, -2] ]) # 異常データ(散らばった点) X_anomaly = np.random.uniform(-6, 6, (20, 2)) X = np.vstack([X_normal, X_anomaly]) print(f”データサイズ: {X.shape}”) print(f”正常データ: {len(X_normal)}件”) print(f”異常データ: {len(X_anomaly)}件”)
データサイズ: (220, 2) 正常データ: 200件 異常データ: 20件
# Isolation Forestの適用 # contamination: 異常データの割合(わかっている場合) iso_forest = IsolationForest( n_estimators=100, # 木の数 contamination=0.1, # 異常データの割合 random_state=42 ) # 予測(1=正常、-1=異常) y_pred = iso_forest.fit_predict(X) # 結果の確認 n_normal_pred = sum(y_pred == 1) n_anomaly_pred = sum(y_pred == -1) print(f”正常と予測: {n_normal_pred}件”) print(f”異常と予測: {n_anomaly_pred}件”) # 異常スコア(低いほど異常) scores = iso_forest.decision_function(X) print(f”\n異常スコアの範囲: {scores.min():.3f} 〜 {scores.max():.3f}”)
正常と予測: 198件 異常と予測: 22件 異常スコアの範囲: -0.321 〜 0.184
# 可視化 plt.figure(figsize=(10, 4)) # 予測結果 plt.subplot(1, 2, 1) colors = [‘blue’ if p == 1 else ‘red’ for p in y_pred] plt.scatter(X[:, 0], X[:, 1], c=colors, s=30, alpha=0.7) plt.title(‘Isolation Forest(青=正常、赤=異常)’) plt.xlabel(‘Feature 1’) plt.ylabel(‘Feature 2′) # 異常スコア plt.subplot(1, 2, 2) scatter = plt.scatter(X[:, 0], X[:, 1], c=scores, cmap=’coolwarm’, s=30, alpha=0.7) plt.colorbar(scatter, label=’異常スコア’) plt.title(‘異常スコア(低い=異常)’) plt.xlabel(‘Feature 1’) plt.ylabel(‘Feature 2’) plt.tight_layout() plt.show()
✅ Isolation Forestの利点
  • 高速:線形の計算量で大規模データにも対応
  • 高次元に強い:特徴量が多くても性能が落ちにくい
  • パラメータが少ない:contamination程度

📊 3. Local Outlier Factor(LOF)

LOFの仕組み

🔍 LOF(Local Outlier Factor)

「周囲と比べて密度が低い点を異常とする」密度ベースの手法。

・各点の「局所的な密度」を計算
LOF ≈ 1:周囲と同じ密度(正常)
LOF >> 1:周囲より密度が低い(異常)

# LOFの実装 from sklearn.neighbors import LocalOutlierFactor # LOFの適用 lof = LocalOutlierFactor( n_neighbors=20, # 近傍点の数 contamination=0.1 # 異常データの割合 ) # 予測(1=正常、-1=異常) y_pred_lof = lof.fit_predict(X) # 結果の確認 n_normal_lof = sum(y_pred_lof == 1) n_anomaly_lof = sum(y_pred_lof == -1) print(f”正常と予測: {n_normal_lof}件”) print(f”異常と予測: {n_anomaly_lof}件”) # LOFスコア(負の値、絶対値が大きいほど異常) lof_scores = lof.negative_outlier_factor_ print(f”\nLOFスコアの範囲: {lof_scores.min():.3f} 〜 {lof_scores.max():.3f}”)
正常と予測: 198件 異常と予測: 22件 LOFスコアの範囲: -4.125 〜 -0.982
⚠️ LOFの注意点
  • 新しいデータに適用できない:fit_predictのみ(novelty=Falseの場合)
  • n_neighborsの調整が必要:データに応じて適切な値を選ぶ
  • 計算コストが高い:大規模データには時間がかかる
# novelty=Trueで新しいデータにも適用可能 lof_novelty = LocalOutlierFactor( n_neighbors=20, contamination=0.1, novelty=True # 新規データへの適用を有効化 ) # 正常データのみで学習 lof_novelty.fit(X_normal) # 新しいデータを予測 X_new = np.array([[0, 0], [10, 10]]) # テストデータ y_new = lof_novelty.predict(X_new) print(f”新しいデータの予測: {y_new}”) # [1, -1]

🎯 4. One-Class SVM

One-Class SVMの仕組み

🔍 One-Class SVM

「正常データを囲む境界を学習する」境界ベースの手法。

・正常データを高次元空間にマッピング
・原点からなるべく離れた超平面を見つける
境界の内側:正常
境界の外側:異常

# One-Class SVMの実装 from sklearn.svm import OneClassSVM # One-Class SVMの適用 oc_svm = OneClassSVM( kernel=’rbf’, # カーネル関数 gamma=’scale’, # カーネル係数 nu=0.1 # 異常データの割合の上限 ) # 学習と予測 oc_svm.fit(X) y_pred_svm = oc_svm.predict(X) # 結果の確認 n_normal_svm = sum(y_pred_svm == 1) n_anomaly_svm = sum(y_pred_svm == -1) print(f”正常と予測: {n_normal_svm}件”) print(f”異常と予測: {n_anomaly_svm}件”) # 決定関数(正=正常、負=異常) svm_scores = oc_svm.decision_function(X) print(f”\n決定関数の範囲: {svm_scores.min():.3f} 〜 {svm_scores.max():.3f}”)
正常と予測: 198件 異常と予測: 22件 決定関数の範囲: -0.412 〜 0.285
✅ One-Class SVMの特徴
  • 非線形な境界を学習できる(カーネルトリック)
  • 新しいデータに適用可能(predict()が使える)
  • 正常データのみで学習可能

⚖️ 5. 手法の比較

手法 考え方 長所 短所
Isolation Forest 孤立しやすさ 高速、高次元OK 局所的な異常に弱い
LOF 局所密度の比較 局所的な異常を検出 計算コスト高、高次元に弱い
One-Class SVM 境界の学習 非線形境界、新規データOK 大規模データに不向き
💡 手法選択のガイドライン

まず試す:Isolation Forest(高速で汎用的)
局所的な異常を検出したい:LOF
明確な境界を学習したい:One-Class SVM
大規模データ:Isolation Forest

💳 6. 実践:不正取引検知

# 不正取引検知のシミュレーション import numpy as np from sklearn.ensemble import IsolationForest from sklearn.neighbors import LocalOutlierFactor from sklearn.svm import OneClassSVM from sklearn.preprocessing import StandardScaler # データ生成 np.random.seed(42) # 正常な取引(金額小〜中、時間帯は日中) n_normal = 1000 amount_normal = np.random.exponential(50, n_normal) # 金額(平均50) hour_normal = np.random.normal(14, 3, n_normal) # 時間(平均14時) hour_normal = np.clip(hour_normal, 0, 24) # 不正な取引(金額が高い、深夜が多い) n_fraud = 50 amount_fraud = np.random.exponential(200, n_fraud) # 金額(平均200) hour_fraud = np.random.uniform(0, 6, n_fraud) # 時間(深夜) # データ結合 X_transactions = np.column_stack([ np.concatenate([amount_normal, amount_fraud]), np.concatenate([hour_normal, hour_fraud]) ]) y_true = np.array([0]*n_normal + [1]*n_fraud) # 0=正常、1=不正 print(f”取引データ: {X_transactions.shape}”) print(f”正常取引: {n_normal}件”) print(f”不正取引: {n_fraud}件”) # 標準化 scaler = StandardScaler() X_scaled = scaler.fit_transform(X_transactions)
# 各手法で異常検知 # contamination = 不正取引の割合 contamination = n_fraud / (n_normal + n_fraud) # Isolation Forest iso = IsolationForest(contamination=contamination, random_state=42) y_iso = iso.fit_predict(X_scaled) # LOF lof = LocalOutlierFactor(contamination=contamination) y_lof = lof.fit_predict(X_scaled) # One-Class SVM svm = OneClassSVM(nu=contamination) y_svm = svm.fit_predict(X_scaled) # 評価(-1を1に変換して、1=異常と判定) def evaluate(y_true, y_pred): # y_pred: 1=正常、-1=異常 → 0=正常、1=異常に変換 y_pred_binary = (y_pred == -1).astype(int) # 真の不正(y_true=1)のうち、検出できた割合(Recall) recall = sum((y_true == 1) & (y_pred_binary == 1)) / sum(y_true == 1) # 不正と予測したうち、本当に不正だった割合(Precision) if sum(y_pred_binary == 1) > 0: precision = sum((y_true == 1) & (y_pred_binary == 1)) / sum(y_pred_binary == 1) else: precision = 0 return recall, precision # 結果表示 print(“=”*50) print(“不正取引検知の結果”) print(“=”*50) for name, y_pred in [(“Isolation Forest”, y_iso), (“LOF”, y_lof), (“One-Class SVM”, y_svm)]: recall, precision = evaluate(y_true, y_pred) print(f”{name:20s}: Recall={recall:.2f}, Precision={precision:.2f}”)
取引データ: (1050, 2) 正常取引: 1000件 不正取引: 50件 ================================================== 不正取引検知の結果 ================================================== Isolation Forest : Recall=0.84, Precision=0.81 LOF : Recall=0.80, Precision=0.77 One-Class SVM : Recall=0.76, Precision=0.73
✅ 実務でのポイント
  • Recallを重視:不正を見逃さないことが重要
  • 複数の手法を組み合わせる:アンサンブルで精度向上
  • 閾値を調整:ビジネス要件に合わせて調整

📝 練習問題

問題1 やさしい

異常検知の考え方

異常検知の基本的な考え方として正しいものを選んでください。

  • A. 異常データをたくさん集めて学習する
  • B. 正常のパターンを学習し、そこから外れたものを異常とする
  • C. すべてのデータにラベルをつけて分類する
正解:B

異常データは数が少なくラベルがないことが多いため、「正常」のパターンを学習して、そこから外れたものを異常とするアプローチが一般的です。

問題2 ふつう

Isolation Forestの特徴

Isolation Forestの説明として正しいものを選んでください。

  • A. 周囲の密度を計算して異常を検出する
  • B. 正常データを囲む境界を学習する
  • C. 異常データは少ない分割回数で孤立するという考えに基づく
正解:C

Isolation Forestは「異常なデータは孤立しやすい」という考えに基づきます。ランダムに分割を繰り返し、少ない回数で孤立するデータを異常と判定します。

問題3 ふつう

LOFの特徴

LOFの説明として正しいものを選んでください。

  • A. 大規模データでも高速に計算できる
  • B. 局所的な密度を比較して異常を検出する
  • C. 線形な境界を学習する
正解:B

LOFは各点の「局所的な密度」を計算し、周囲と比べて密度が低い点を異常とします。局所的な異常を検出できますが、計算コストは高いです。

問題4 むずかしい

手法の選択

100万件のデータから異常を検出したい場合、どの手法が最も適切ですか?

  • A. LOF(局所的な異常を検出できるため)
  • B. One-Class SVM(非線形境界を学習できるため)
  • C. Isolation Forest(計算が高速なため)
正解:C(Isolation Forest)

Isolation Forestは線形の計算量で大規模データにも対応できます。LOFやOne-Class SVMは計算コストが高く、100万件には不向きです。

問題5 むずかしい

異常検知の実装

乳がんデータセットでIsolation Forestを使い、contamination=0.05で異常検知を行ってください。

from sklearn.datasets import load_breast_cancer from sklearn.ensemble import IsolationForest from sklearn.preprocessing import StandardScaler # データ読み込み cancer = load_breast_cancer() X = cancer.data # 標準化 X_scaled = StandardScaler().fit_transform(X) # Isolation Forest iso = IsolationForest(contamination=0.05, random_state=42) y_pred = iso.fit_predict(X_scaled) # 結果 n_normal = sum(y_pred == 1) n_anomaly = sum(y_pred == -1) print(f”正常: {n_normal}件”) print(f”異常: {n_anomaly}件”) print(f”異常の割合: {n_anomaly/len(y_pred)*100:.1f}%”)
正常: 541件 異常: 28件 異常の割合: 4.9%

📝 STEP 20 のまとめ

✅ このステップで学んだこと
  • 異常検知:正常から外れたデータを検出する教師なし学習
  • Isolation Forest:孤立しやすさで異常を検出、高速で汎用的
  • LOF:局所密度で異常を検出、局所的な異常に強い
  • One-Class SVM:境界を学習、非線形な境界に対応
  • 使い分け:大規模データ→Isolation Forest、局所異常→LOF
🚀 次のステップへ

次のSTEP 21では、教師なし学習の総合演習を行います。クラスタリング、次元削減、異常検知を組み合わせた実践的なプロジェクト(顧客セグメンテーション)に取り組みましょう!

❓ よくある質問

Q1. contaminationがわからない場合は?
contamination='auto'を指定すると、元の論文に基づいた閾値(0.5)が使われます。または、ドメイン知識から推定するか、複数の値を試して結果を比較しましょう。
Q2. 異常検知の評価はどうする?
ラベルがある場合はPrecision、Recall、F1-score、AUCで評価します。ラベルがない場合は、ドメイン専門家に検出結果を確認してもらうか、シルエットスコアなどの内部指標を使います。
Q3. 複数の手法を組み合わせるには?
アンサンブル手法:
・各手法の異常スコアを平均/最大値を取る
・複数の手法で「異常」と判定されたものだけを異常とする
・PyODライブラリに様々なアンサンブル手法が実装されています
Q4. リアルタイムで異常検知するには?
ストリーミング対応手法:
・Isolation Forestはfit後にpredict可能
・One-Class SVMも同様
・LOFはnovelty=Trueで新規データに適用可能
実務ではモデルを定期的に再学習することも重要です。
Q5. PyODライブラリとは?
PyOD(Python Outlier Detection)は、30以上の異常検知アルゴリズムを提供するライブラリです。pip install pyodでインストールできます。一貫したAPIで様々な手法を試せます。
📝

学習メモ

機械学習入門 - Step 20

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE