📋 このステップで学ぶこと
- 異常検知とは何か、なぜ必要か
- 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で様々な手法を試せます。
artnasekai
#artnasekai #学習メモ