クレジットカードの不正利用、製造ラインの不良品、サーバーの異常な通信、センサーの故障の兆候——これらに共通するのは、「大量の正常の中に、ごくわずかに紛れ込んだ異常を見つけたい」という課題です。これが 異常検知(anomaly detection) です。正常を予測する普通の機械学習とは事情が違います。なにしろ、見つけたい異常は稀で、多様で、しばしば事前にラベルが無い。「正常とは何か」を学び、そこから外れたものを炙り出す、という独特の発想が要ります。
本記事は異常検知シリーズの導入として、全体像を一気に押さえます。具体的には、(1) そもそも異常とは何か(3つのタイプ)、(2) どんな学習設定があるか、(3) どんな手法群があるか(シリーズの地図)、(4) そして見落とされがちだが決定的に重要な評価指標——不均衡データでなぜ正解率(accuracy)が役に立たず、ROC-AUCとPR-AUCが必要なのか。最後にPythonで代表的な3手法を実際に動かし、評価まで通します。

図の通り、異常検知は「多数の正常(青)」の中から「少数の外れ(赤)」を見つける問題です。一見simpleですが、「外れ」をどう定義し、どう測り、どう評価するかに奥行きがあります。順に見ていきましょう。
異常の3タイプ
ひとくちに「異常」と言っても、性質の異なる3タイプがあります。これを区別することが、適切な手法選びの第一歩です。

- 点異常(point anomaly):単独で見て明らかに外れた値。最も基本的で、外れ値検知の典型。例:通常数千円の決済の中に突然現れる数百万円の決済。
- 文脈異常(contextual anomaly):値そのものは正常範囲でも、文脈の中では異常なもの。例:気温30度は夏なら正常だが冬なら異常。時系列や季節性のあるデータで重要。
- 集団異常(collective anomaly):個々の点は正常でも、連続した区間やまとまりとして異常なもの。例:心電図で個々の波形は正常でも、あるパターンが続くと不整脈。
同じデータでも、どのタイプの異常を狙うかで手法が変わります。点異常なら静的な外れ値検知、文脈・集団異常なら時系列を考慮した手法(本シリーズ後半で扱います)が要ります。
3つの学習設定
異常検知は、手元にどんなラベルがあるかで3つの設定に分かれます。

- 教師あり(supervised):正常・異常の両方にラベルがある。普通の分類問題に見えるが、異常が極端に稀でラベルを集めにくく、未知タイプの異常に弱い。
- 半教師あり(semi-supervised):正常データだけで学習する。正常の「型」を覚え、そこから外れたものを異常とする。実務で最も現実的な設定(正常データは大量に取れることが多い)。
- 教師なし(unsupervised):ラベルが一切ない。データの大多数を正常とみなし、少数派・低密度の点を異常とする。
本シリーズで扱う多くの手法(One-Class SVM、Isolation Forest、オートエンコーダなど)は、半教師あり〜教師なしの設定で使われます。「異常のラベルに頼らず、正常の構造から異常を見抜く」のが異常検知の王道だからです。
手法の地図
異常検知の手法は、「正常をどうモデル化するか」で大きく4系統に分かれます。本シリーズで扱う地図を先に示します。

- 統計的:分布を仮定し、確率的に外れた値を検出(zスコア、3σ、Grubbs検定)。
- 距離・密度ベース:正常データへの近さ・周囲の密度で測る(マハラノビス距離、GMM、LOF)。
- 境界ベース:正常データを囲む境界を学び、外側を異常とする(One-Class SVM、Isolation Forest)。
- 再構成ベース:正常データを圧縮・復元するモデルを学び、うまく復元できない(再構成誤差が大きい)ものを異常とする(オートエンコーダ、VAE)。
これらすべてに共通する基本戦略が、次の一枚に集約されます。

正常の領域(または分布・密度)をモデル化し、そこから外れたものを異常とする——これが異常検知の根本原理です。手法の違いは「正常領域をどう表現するか」の違いに過ぎません。距離で測るか、密度で測るか、境界で囲むか、復元できるかで測るか。この視点を持つと、多様な手法が一本の軸で見通せます。
なぜ評価が難しいのか — 不均衡の罠
手法と同じくらい重要なのが評価です。そして異常検知の評価には、初学者が必ず嵌まる罠があります。

異常検知のデータは、図のように極端に不均衡です(例:正常96%、異常4%)。このとき、正解率(accuracy)はまったく当てになりません。なぜなら「すべて正常と答える」だけのモデルでも、正解率96%を達成できるからです。異常を1つも見つけられないのに高得点——これが不均衡の罠です。
だから異常検知では、正解率ではなく、ROC-AUC と PR-AUC を使います。どちらも、異常スコアにしきい値を1つ決め打ちするのではなく、あらゆるしきい値にわたる性能を1つの数値にまとめたものです。

ROC曲線は、しきい値を変えながら「真陽性率(異常を異常と当てた割合)」対「偽陽性率(正常を誤って異常とした割合)」を描いた曲線で、その下の面積が ROC-AUC(1に近いほど良い、0.5でランダム)です。直感的には「ランダムに選んだ異常の方が、ランダムに選んだ正常よりも高い異常スコアを持つ確率」を表します。

ただし、極端な不均衡ではROC-AUCは楽観的に見えすぎることがあります。そこで併用するのが PR曲線(適合率 対 再現率)とその下の面積 PR-AUC(平均適合率, AP)です。PR-AUCのベースラインは「異常の割合」そのもの(この例なら0.04)なので、不均衡が厳しいほど低い値が基準になり、手法の実力をシビアに映します。不均衡が強いときはPR-AUCを重視する、と覚えておきましょう。

実運用では最終的にしきい値を1つ決めます。図のように正常・異常のスコア分布を見て、許容できる見逃し・誤報のバランスから決めるのが基本です。AUCで手法を選び、運用しきい値で現場に合わせる、という二段構えになります。
precision@k と現場での見方
ROC-AUC・PR-AUCは「しきい値によらない総合力」を測りますが、現場の関心はしばしばもっと具体的です。「上位 $k$ 件を人手で確認するとして、そのうち何件が本当の異常か」——これを測るのが precision@k(上位 $k$ 件の適合率)です。たとえば検査員が1日に50件しか確認できないなら、precision@50 が実務上の価値を直接表します。異常スコアの高い順に並べ、上位 $k$ 件の中の真の異常の割合を見るだけのシンプルな指標ですが、「限られた確認コストの中でどれだけ異常を拾えるか」という運用の現実に最も近い物差しです。AUCで手法の地力を測り、precision@kで運用効果を見積もる、と使い分けると良いでしょう。
「正常」を定義することの難しさ
異常検知の根本戦略は「正常をモデル化し、外れを異常とする」でした。だとすれば、最大の難所は「正常とは何か」を適切に定めることにあります。ここには実務で必ずぶつかる難しさが3つあります。
第一に、正常データに異常が混入している問題(コンタミネーション)。教師なし設定では「大多数は正常」と仮定しますが、訓練データに異常が紛れていれば、モデルはそれを正常として学んでしまいます。Isolation Forest のように汚染をある程度許容する手法もありますが、混入率が高いと性能は落ちます。
第二に、正常自体が変化する問題(コンセプトドリフト)。機械は経年で振る舞いが変わり、季節や運用条件で正常範囲は移ろいます。一度学んだ正常モデルが、時間とともに現実とずれていく。定期的な再学習やオンライン更新が要ります。
第三に、正常の多様性。正常が単一の塊ではなく、複数のモード(運転状態・動作モードなど)を持つことはよくあります。先の実験でマハラノビス距離が苦戦したのは、まさに正常が2クラスタだったからです。正常が多峰なら、それを表現できる手法(混合ガウス、近傍ベース、再構成ベース)が必要になります。
つまり「正常を定義する」とは、混入・変化・多様性に向き合うことに他なりません。手法選びは、自分のデータの正常がどんな形をしているかの理解から始まります。
Pythonで体験する
代表的な3手法——マハラノビス距離(距離ベース)、Isolation Forest(境界ベース)、One-Class SVM(境界ベース)——を同じデータで動かし、ROC-AUCとPR-AUCで比べます。正常は2つのクラスタ、異常は一様に散らばる外れ点(全体の4%)とします。
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.svm import OneClassSVM
from sklearn.covariance import EmpiricalCovariance
from sklearn.metrics import roc_auc_score, average_precision_score
rng = np.random.default_rng(0)
Xn = np.vstack([rng.normal([0,0], 0.6, (480,2)),
rng.normal([3,3], 0.6, (480,2))]) # 正常:2クラスタ
Xa = rng.uniform(-4, 7, (40,2)) # 異常:一様に散る外れ点(4%)
X = np.vstack([Xn, Xa])
y = np.r_[np.zeros(len(Xn)), np.ones(len(Xa))] # 0=正常, 1=異常
# 各手法の「異常スコア」(大きいほど異常)
cov = EmpiricalCovariance().fit(Xn) # 正常データで共分散を推定
scores = {
"Mahalanobis": cov.mahalanobis(X),
"IsolationForest": -IsolationForest(random_state=0).fit(X).score_samples(X),
"OneClassSVM": -OneClassSVM(gamma="scale", nu=0.05).fit(Xn).decision_function(X),
}
for name, s in scores.items():
print(f"{name}: ROC-AUC={roc_auc_score(y, s):.3f} PR-AUC={average_precision_score(y, s):.3f}")
出力は次の通りです。
Mahalanobis: ROC-AUC=0.894 PR-AUC=0.772
IsolationForest: ROC-AUC=0.941 PR-AUC=0.789
OneClassSVM: ROC-AUC=0.942 PR-AUC=0.791

3手法とも、ランダム(0.5)やPR-AUCのベースライン(0.04)を大きく上回り、異常をしっかり捉えています。とくにIsolation ForestとOne-Class SVMが高く(ROC-AUC 0.941、0.942)、非線形な正常領域も柔軟に囲めることがうかがえます。マハラノビス距離はやや低い(0.894)——これは正常を「1つの楕円(単峰のガウス)」とみなす手法のため、2クラスタという形を表現しきれないからです。手法の前提とデータの形が合っているかが性能を分ける、という異常検知の勘所が、この小さな実験に表れています。各手法の中身は、続く記事で1つずつ掘り下げます。
まとめ
異常検知の全体像を押さえました。
- 異常検知は、多数の正常から少数・多様・しばしば未ラベルの異常を見つける問題。「正常をモデル化し、外れを検出する」が根本戦略。
- 異常には点・文脈・集団の3タイプがあり、狙うタイプで手法が変わる。
- 学習設定は教師あり・半教師あり・教師なし。実務では正常のみで学ぶ半教師ありが現実的。
- 手法は統計的・距離/密度・境界・再構成の4系統(本シリーズの地図)。
- 評価では、不均衡ゆえ正解率は無意味。ROC-AUCと、不均衡で重要なPR-AUCを使う。
- 実測では Isolation Forest・One-Class SVM が ROC-AUC 0.94 と高く、マハラノビス(0.894)は単峰ガウス仮定がデータの2クラスタ形状と合わずやや劣った。
次の記事では、最も基本的な統計的異常検知(zスコア・3σ・Grubbs検定)から、正常の分布を仮定して外れ値を検出する考え方を掘り下げます。