// PART 03 · ARTICLE 11 / 15
MEDICAL-AI-ALGO UNSUPERVISED L2

異常検知

— Isolation Forest と One-Class SVM

9 min read· L2· 2026.05.21 update· by Editor

異常検知は、正常データの分布を学び、そこから外れた症例や測定値を見つける方法です。リハビリテーション研究では、歩行センサーの外れ値、FIM 入力ミス、機器エラー、稀な運動パターンの探索に使えます。ただし、異常スコアが高いことは、病変や臨床的悪化を直接意味するわけではありません。本記事では、Isolation Forest、One-Class SVM、Local Outlier Factor を、研究で安全に使うための視点から整理します。

// CONTEXT

異常検知は、分類モデルとは問題設定が違います。少数ラベルを当てる分類ではなく、正常らしさからの外れ方を見る方法です。したがって、AUC だけで評価する前に、異常の定義・閾値・レビュー体制を決めます。データリーケージの考え方は、09·02 データリーケージとは何か14·03 データリーケージデモ を合わせて確認します。閾値の決め方は 14·02 交差検証デモ も参考になります。One-Class SVM の背景は 03·06 SVM、Isolation Forest の木系アンサンブル発想は 03·09 ランダムフォレスト につながります。

// 01 · LEARN OUTCOMESこの記事で学ぶこと

  • 異常検知と不均衡分類の違いを説明できる。
  • Isolation Forest、One-Class SVM、LOF の向き不向きを区別できる。
  • 異常スコアを臨床的な異常と誤読しないための注意点を理解できる。
  • 正常データだけで fit し、閾値を訓練データ内で決める実装の流れを理解できる。

// 02 · CONCLUSIONまず結論

異常検知の価値は、見落としやすい候補を前に出す点にあります。たとえば、FIM の範囲外入力や、歩行センサーの装着ミスは、早く見つけられるほど解析の品質が上がります。一方で、臨床的に重要な異常は、モデルのスコアだけでは決まりません。最終的には、原データ・動画・カルテ記載・測定条件を見直します。

もう一つ重要なのは、異常検知を予後予測と混同しないことです。歩行加速度から「通常とは違う」波形を見つけることと、将来の転倒を予測することは別の課題です。後者には、転倒ラベル・追跡期間・評価指標・外部検証が必要です。

// 03 · FIGURE図で理解する異常検知

// ISOLATION FOREST · ランダム分割で孤立しやすさを測る [ A ] データ空間とランダム分割 青=正常クラスタ / 赤=外れ値候補 外れ値 → 正常クラスタ内: 何度も分割しないと孤立しない [ B ] 経路長 → 異常スコア 短い経路 = 孤立しやすい = 異常 経路長 h(x) → 頻度 ↓ 異常 閾値 s(x, n) = 2^(−E[h(x)] / c(n)) · スコア大 → 異常
図1: Isolation Forest の考え方。正常群の中にある点は何度も分割しないと孤立しません。一方、離れた点は少ない分割で孤立します。経路長 h(x) の短さを異常スコア s(x, n) に変換します。

図1では、孤立しやすさが重要です。Isolation Forest は、データ空間をランダムに切り分けます。周囲から離れている点は、少ない分割で単独になります。その経路長が短いほど、異常度が高いと判断します[1]

// ONE-CLASS SVM · 正常領域を境界で囲み、外側を異常とする [ A ] 線形 / 球形の境界 単純な正常領域 正常領域 異常 [ B ] RBF カーネルでの境界 非線形・曲がった正常領域 サブ群 A サブ群 B γ で境界の細かさ調整 · 大→過適合 / 小→ゆるい囲み
図2: One-Class SVM の考え方。正常データが多く存在する領域を囲み、その外側に出た点を異常候補として扱います。RBF カーネルを使うと、丸い領域だけでなく曲がった正常領域も表現できます。

図2では、正常領域の外側を見る発想です。One-Class SVM は、正常データの支持領域を学習します[2]。RBF カーネルを使うと、丸い領域だけでなく、曲がった正常領域も表現できます。ただし距離に基づくため、尺度の違う特徴量をそのまま入れると不安定です。

// 04 · CLINICALリハ研究での使いどころ

// SCENARIO 1 · 歩行加速度信号の異常パターン

通常歩行 N=500 の加速度特徴量から、歩行速度・左右対称性・立脚時間・周波数特徴量を抽出します。その分布から外れる波形を、異常候補として抽出します。この時点では転倒前兆とは断定しません。装着位置のずれ・杖の使用・疲労・方向転換・測定ミスも同じように外れ値になります。

// SCENARIO 2 · 頭部 MRI の正常群からの候補抽出

正常コントロール群の形状特徴量や深層特徴量を用い、外れた症例を候補として抽出します。深層特徴量を使う異常検知では、表現学習の偏りにも注意します[5]。ここで得られるのは、脳腫瘍・萎縮・撮影条件差・体動アーチファクトなどが混ざった候補です。異常スコアの上位例を専門家が確認し、研究対象に含めるか、除外するかを検討します。

// SCENARIO 3 · 電子カルテのバイタル時系列

回復期病棟で、血圧・脈拍・SpO2・体温の時系列から外れ値を検出します。急な変化は臨床的悪化を示すことがあります。しかし、測定姿勢・機器エラー・入力時刻のずれでも外れます。異常検知は、カルテ確認を効率化する補助線として扱います。

// SCENARIO 4 · FIM スコアの自動 QA

FIM 各項目は 1〜7 点、合計点は一定範囲に収まります。範囲外の値はルールで検出できます。さらに、合計点と項目点の組み合わせが不自然な症例を、異常検知でフラグ化できます。解析前のデータ品質管理として有用です。

// 05 · THEORY仕組みを少し詳しく見る

1. 異常検知の問題設定

異常検知では、正常データの分布を学びます。その後、新しい症例がその分布からどの程度外れるかを点数化します。教師あり分類のように、全例に正常と異常の正解ラベルがあるとは限りません。

Training data:
  X_normal = samples assumed to be normal

At inference:
  anomaly_score(x) = degree of deviation from normal pattern
  flag(x) = anomaly_score(x) > threshold

医療データでは、正常データにもノイズが混ざります。そのため「正常群」として集めたデータが、完全に均一とは限りません。ここに、異常検知の難しさがあります[4]

2. Isolation Forest

Isolation Forest は、異常を「孤立しやすい点」と考えます。ランダムな特徴量とランダムな閾値でデータを分割し、各症例が単独になるまでの経路長を見ます。経路長が短いほど、周囲から離れていると判断します。発想は ランダムフォレスト と同じ Bagging 系ですが、目的は孤立度の測定です。

c(n) = 2H(n - 1) - 2(n - 1) / n

s(x, n) = 2 ^ ( - E[h(x)] / c(n) )

h(x): path length for sample x
c(n): average path length normalization
s(x): anomaly score

n_estimators は木の数、contamination は異常候補の割合の仮定です。たとえば 0.05 は、約 5% を異常候補として扱う設定です。これは臨床的な有病率そのものではなく、フラグ数を決める実務上の仮定です。

3. One-Class SVM

One-Class SVM は、正常データが存在する領域を学びます。通常の SVM が 2 群を分けるのに対し、One-Class SVM は正常群と外側を分けます。nu は、正常データのうち外れ値として許容する割合に関係します。

minimize:   (1/2)||w||^2 + (1/(nu*n)) Σ xi_i - rho
subject to: w·phi(x_i) >= rho - xi_i
            xi_i >= 0

RBF カーネルでは、gamma が正常領域の細かさに影響します。gamma が大きすぎると、正常データの周囲だけを細かく囲みます。その結果、新しい正常例まで異常扱いしやすくなります。

4. Local Outlier Factor

LOF は、近傍密度を比べる方法です[3]。周囲の点に比べて、自分の周辺だけ密度が低いと、局所的な外れ値と判断します。全体から離れた点だけでなく、密な群の中にある小さな外れも拾えることがあります。

LOF idea:
  compare local density of x
  with local density of its neighbors

LOF(x) > 1:
  x is more isolated than its neighbors

n_neighbors は、局所密度を見る近傍数です。小さすぎるとノイズに敏感です。大きすぎると局所的な異常をならしてしまいます。歩行パターンのように複数の正常サブタイプがある場合、LOF が合うことがあります。

5. 不均衡分類との違い

不均衡分類では、少数でも異常ラベルがあります。たとえば転倒あり / なしのラベルがある場合、分類問題として扱えます。一方、異常検知では、異常ラベルがない、または定義が曖昧な場面が中心です。

Imbalanced classification:
  y = 0 or 1 is available
  goal = predict y

Anomaly detection:
  y may be unavailable
  goal = flag unusual samples for review

この違いを曖昧にすると、評価方法も報告方法も崩れます。ラベルがあるなら、AUC・感度・特異度・陽性的中率を確認できます。ラベルがないなら、スコア分布・上位例レビュー・再現性確認を中心にします。

// 06 · IMPLEMENTATION · PYTHONPython 実装例

以下は教育用の仮想データ example_rehab_dataset.csv を使う例です。個人情報を含む実データを扱う場合は、倫理審査・施設ルール・利用規約・匿名化手順を確認します。ここでは正常データだけで fit し、訓練正常データ内で閾値を決めます。全データで標準化や閾値決定をしてから分割する方法は、リーケージ になります。

PYTHON
# Educational example only.
# Confirm IRB approval, data governance, anonymization,
# institutional rules, and dataset terms before real use.

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
from sklearn.svm import OneClassSVM

RANDOM_STATE = 42

# example_rehab_dataset.csv is an educational dataset.
df = pd.read_csv("example_rehab_dataset.csv")

numeric_features = [
    "age", "fim_motor_adm", "fim_cognition_adm",
    "gait_speed", "tug_sec", "bbs", "sias_total"
]
categorical_features = ["facility_type"]
features = numeric_features + categorical_features
label_col = "qa_label" if "qa_label" in df.columns else None

normal_df = df[df["assumed_normal"] == 1].copy()
candidate_df = df.copy()

numeric_pipe = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler())
])

categorical_pipe = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore"))
])

preprocess = ColumnTransformer(transformers=[
    ("num", numeric_pipe, numeric_features),
    ("cat", categorical_pipe, categorical_features),
])

normal_train, normal_valid = train_test_split(
    normal_df, test_size=0.25, random_state=RANDOM_STATE
)

X_train = normal_train[features]
X_valid = normal_valid[features]
X_all   = candidate_df[features]

models = {
    "isolation_forest": IsolationForest(
        n_estimators=200, contamination=0.05, random_state=RANDOM_STATE
    ),
    "one_class_svm": OneClassSVM(
        kernel="rbf", nu=0.05, gamma="scale"
    ),
    "lof": LocalOutlierFactor(
        n_neighbors=20, contamination=0.05, novelty=True
    ),
}

results = {}
for name, estimator in models.items():
    pipe = Pipeline(steps=[
        ("preprocess", preprocess),
        ("model", estimator)
    ])
    pipe.fit(X_train)

    valid_normal_score = pipe.decision_function(X_valid)
    all_normal_score = pipe.decision_function(X_all)

    threshold = np.quantile(valid_normal_score, 0.05)
    anomaly_score = -all_normal_score
    anomaly_flag = (all_normal_score < threshold).astype(int)

    out = candidate_df.copy()
    out[f"{name}_score"] = anomaly_score
    out[f"{name}_flag"]  = anomaly_flag

    if label_col is not None:
        auc = roc_auc_score(out[label_col], anomaly_score)
    else:
        auc = np.nan

    results[name] = {
        "pipeline": pipe,
        "threshold": threshold,
        "n_flagged": int(anomaly_flag.sum()),
        "auc_if_label_available": auc,
        "output": out[[f"{name}_score", f"{name}_flag"]]
    }

summary = pd.DataFrame([
    {
        "model": name,
        "threshold": item["threshold"],
        "n_flagged": item["n_flagged"],
        "auc_if_label_available": item["auc_if_label_available"]
    }
    for name, item in results.items()
])
print(summary)

# Review flagged cases manually.
# Check raw values, videos, device logs, and clinical notes.
# A high anomaly score is not a diagnosis.

この実装では、assumed_normal == 1 の症例だけで学習します。その後、正常検証データのスコア分布から閾値を決めます。既知のエラーラベルや異常ラベルがある場合だけ、AUC を参考にします。ラベルがない場合は、AUC を出さず、上位フラグ例のレビューを主な確認にします。

Isolation Forest 自体は木系なので、標準化が必須ではありません。しかし、One-Class SVM と LOF は距離や密度に依存します。複数モデルを比較する教育例では、共通の前処理として標準化を入れています。本番の解析では、モデルごとに前処理の妥当性を確認します。

// 07 · MYTHSよくある誤解

誤解 1: 異常検知は教師ラベル不要なので簡単
ラベルがない分、異常の定義と閾値設定が難しくなります。フラグ化した症例を、誰がどの基準で確認するかも設計に含めます。
誤解 2: Isolation Forest だけで十分
表形式の QA には使いやすい一方、局所密度の異常には LOF が合うことがあります。境界の外側を見たい場合は、One-Class SVM も候補です。
誤解 3: 異常スコアが高い症例は病変
異常スコアはデータ分布からの外れ方です。撮影条件・装着ミス・入力エラー・まれな正常変動も同じように拾います。
誤解 4: 不均衡分類と異常検知は同じ
少数でも正解ラベルがあるなら、不均衡分類として評価できます。ラベルがない、または異常の定義が未確定なら、異常検知として扱います。

// 08 · WRITING論文 Methods に書くこと

異常検知の論文では、アルゴリズム名だけでは不十分です。何を正常とみなしたか・どのデータで fit したか・どの基準で閾値を決めたかを書きます。とくに医療データでは、異常スコアの上位例をどう確認したかが重要です。

// METHODS CHECK
  • 正常学習データの定義: 正常コントロール、通常歩行、範囲内 FIM などを明記。
  • 除外基準: 測定ミス、欠測過多、機器エラー、重複記録をどう扱ったかを書く。
  • 前処理: 欠測補完、標準化、カテゴリ変数処理を、学習 fold 内で行ったことを書く。
  • モデル設定: contaminationnugamman_neighborsn_estimators を記載。
  • 閾値設定: 何パーセンタイル、何件レビュー、または外部ラベルで決めたかを明記。
  • 評価: ラベルありなら AUC や感度を、ラベルなしならスコア分布と専門家レビューを示す。
  • 再現性: 乱数 seed、ソフトウェア、scikit-learn のバージョンを書く。
  • 限界: 異常スコアは診断ではなく、臨床的意味は追加確認が必要と述べる。

査読では、正常データに異常例が混じっていないかを問われます。また、閾値を全データのスコア分布で決めていないかも確認されます。これは リーケージ の一種です。テストデータや候補データの情報を使って閾値を調整すると、性能を高く見積もります。

予測モデルとして異常スコアを使う場合は、TRIPOD+AI の報告項目を意識します[6]。研究の質やバイアスの確認には、PROBAST+AI の観点も参考になります[7]。ただし、探索的な QA 用途と、臨床予測モデルとしての用途は分けて書きます。

// 09 · CHECKLIST実装前チェックリスト

  • 01異常検知と不均衡分類のどちらとして扱うかを決めた
  • 02正常データの定義を臨床的に説明できる
  • 03欠測補完と標準化を学習データ内で fit している
  • 04閾値を候補データ全体で後から調整していない
  • 05contaminationnun_neighbors の意味を説明できる
  • 06フラグ例を確認する担当者と手順を決めている
  • 07異常スコアを病名や原因として解釈していない
  • 08ラベルがある場合とない場合で、評価方法を分けている

// 10 · QUIZ理解度チェック

  1. Q1Isolation Forest の基本的な発想として最も近いものはどれですか。
    • 2 群の境界を最大マージンで分ける
    • ランダム分割で孤立しやすい点を見つける
    • 係数の大きさから重要因子を決める
    • すべての症例をクラスタに割り当てる
    SHOW ANSWER
    B. 少ない分割で孤立する点を、異常候補として扱います。
  2. Q2One-Class SVM や LOF で特に重要な前処理はどれですか。
    • 数値特徴量の標準化
    • 目的変数の対数変換
    • 決定木の剪定
    • 画像の回転拡張
    SHOW ANSWER
    A. 距離や密度に依存するため、尺度差の影響を受けます。
  3. Q3異常スコアの正しい読み方はどれですか。
    • 高いほど病変を示す
    • 高いほどモデルが診断した
    • 高いほど分布から外れている可能性がある
    • 高いほど治療効果が低い
    SHOW ANSWER
    C. 臨床的意味は、原データや専門家レビューで確認します。
  4. Q4ラベルありの稀な転倒を予測する課題は、まず何として扱いやすいですか。
    • 不均衡分類
    • PCA
    • 教師なしクラスタリング
    • 外れ値除去だけの問題
    SHOW ANSWER
    A. 転倒あり / なしのラベルがあるなら、分類問題として評価できます。

// 11 · FAQよくある質問

異常検知と不均衡分類はどう違いますか?
少数でも異常ラベルがあるなら、不均衡分類として AUC・感度・特異度・陽性的中率で評価できます。一方、異常検知はラベルがない・あるいは異常の定義が曖昧な場面で、正常データの分布からの外れ方を点数化するアプローチです。
Isolation Forest、One-Class SVM、LOF はどう使い分けますか?
Isolation Forest は軽量で表形式データの QA に向きます。One-Class SVM は正常領域を境界で囲み、非線形な分布にも対応できますが標準化が必須です。LOF は局所密度を比較するため、複数の正常サブタイプがあるデータで局所的な外れ値を拾えます。
異常スコアが高い症例は病変や臨床的悪化を意味しますか?
いいえ。異常スコアはあくまでデータ分布からの外れ方であり、装着ミス・入力エラー・撮影条件差・稀な正常変動も同じように高スコアになります。原データや動画、カルテを専門家が確認するレビュー体制と組み合わせて使います。

// REF参考文献

  1. Liu FT, Ting KM, Zhou ZH. Isolation forest. ICDM 2008:413-422.
  2. Schölkopf B, Platt JC, Shawe-Taylor J, Smola AJ, Williamson RC. Estimating the support of a high-dimensional distribution. Neural Computation 2001;13(7):1443-1471.
  3. Breunig MM, Kriegel HP, Ng RT, Sander J. LOF: Identifying density-based local outliers. SIGMOD 2000:93-104.
  4. Chandola V, Banerjee A, Kumar V. Anomaly detection: A survey. ACM Computing Surveys 2009;41(3):15.
  5. Pang G, Shen C, Cao L, Hengel AV. Deep learning for anomaly detection: A review. ACM Computing Surveys 2021;54(2):38.
  6. Collins GS, Moons KGM, Dhiman P, et al. TRIPOD+AI statement: updated guidance for reporting clinical prediction models that use regression or machine learning methods. BMJ 2024;385:e078378.
  7. Moons KGM, Wolff RF, Riley RD, et al. PROBAST+AI: an updated quality, risk of bias, and applicability assessment tool for prediction models using regression or artificial intelligence methods. BMJ 2025;388:e082505.