이상 탐지 모델 선택: Isolation Forest·One-Class SVM·Autoencoder
Isolation Forest, One-Class SVM, Autoencoder 기반 이상 탐지의 특성, 임계값 설정, 운영 환경별 선택 기준을 정리합니다.
2026-08-14 · 최초 발행 2024-04-29
정상 범위 밖의 신호를 어떻게 판별할 것인가
이상 탐지는 정상 패턴과 다른 관측치를 자동으로 식별하는 기법이다. 결제 사기, 설비 고장 예지, 보안 침해 탐지처럼 이상 레이블이 부족하거나 아예 없는 환경에서 특히 활용된다.
학습은 대체로 비지도 또는 준지도 방식으로 이뤄진다. 정상 데이터의 분포나 표현을 먼저 학습한 뒤, 그 기준에서 멀어진 입력을 이상으로 본다. 대표적인 접근은 다음과 같다.
- Isolation Forest는 무작위 특성과 분할값으로 트리를 만들고, 샘플이 얼마나 빨리 고립되는지로 이상도를 계산한다.
- One-Class SVM은 커널 공간에서 정상 영역을 둘러싸는 초곡면을 학습하고 경계 바깥의 점을 이상으로 판정한다.
- Autoencoder는 정상 입력을 복원하도록 학습한 신경망의 재구성 오차를 이상 점수로 쓴다.
운영 흐름은 데이터 수집, 정규화·윈저라이징, 모델 스코어링, 임계값 적용, 알림 또는 리뷰, 모니터링과 드리프트 재학습으로 이어진다. 결측이나 스케일 불일치가 있으면 대체 규칙과 정규화를 다시 적용해야 하며, 학습 단계에서는 이상 비율을 과도하게 추정하지 않도록 주의한다. 데이터 스키마 버전, 스케일러와 피처 맵, 재현 가능한 시드도 함께 고정한다.
고립도·경계·재구성 오차의 차이
Isolation Forest는 고립되기 쉬운 점을 찾는다
Isolation Forest는 특성과 분할값을 무작위로 골라 트리를 성장시킨다. 샘플의 평균 경로 길이가 짧을수록 쉽게 고립된 것이므로 이상일 확률이 높다고 본다.
근사 복잡도는 O(n log n)이며 선형 확장성이 있어 고차원 데이터에서도 견고한 편이다. 다만 군집 경계가 복잡한 경우에는 미세한 경계를 표현하는 데 한계가 있다. 운영 시에는 contamination, n_estimators, max_samples를 조정하고, score_samples의 부호와 스케일을 해석할 때 유의해야 한다.
One-Class SVM은 정상 영역의 경계를 만든다
One-Class SVM은 커널 공간에서 원점으로부터의 마진을 최대화해 정상 영역을 추정한다. nu는 이상 비율 상한과 서포트 벡터 비율로 작동한다.
복잡한 비선형 경계를 표현하는 능력은 좋지만, O(n^2~n^3)의 메모리·시간 복잡도와 커널 행렬 의존성 때문에 대용량 학습에서는 제약이 생긴다. 표준화는 필수이며, RBF 커널을 사용할 때 nu와 gamma의 민감도가 높으므로 소규모 검증 세트와 그리드 탐색이 필요하다.
Autoencoder는 정상 데이터를 잘 복원하도록 학습한다
Autoencoder 기반 탐지는 정상 패턴의 재구성에 맞춘 표현 학습 방식이다. 입력과 복원 결과의 차이인 MSE 또는 MAE를 이상 점수로 사용한다.
이미지, 시계열, 로그 임베딩처럼 고차원 입력에서 강점을 보인다. 대신 데이터와 아키텍처에 대한 의존성이 크며, 충분한 정상 데이터, 정규화, 규제가 필요하다. 드롭아웃·가중치 감쇠·얼리스톱으로 과적합을 제어하고, 검증 세트의 상위 분위수 또는 비용 함수로 임계값을 정한다.
모델별 운영 특성 비교
| 지표 | Isolation Forest | One-Class SVM | Autoencoder |
|---|---|---|---|
| 성능(비선형/고차원) | 중상, 희소·잡음 강건 | 상, 경계 정밀 | 최상, 복잡 패턴 학습 |
| 확장성 | 높음(O(n log n)) | 낮음(커널 행렬 의존) | 중상(GPU 병렬로 개선) |
| 일관성 | 분포 이동에 보통 민감 | 파라미터 민감도 큼 | 데이터/학습 조건 민감 |
| 안정성(임계값 변화) | 보통, contamination 영향 | 민감, nu·gamma 영향 큼 | 보통, 검증 기반 튜닝 필요 |
| 운영 편의 | 간단, 의존성 적음 | 파라미터 튜닝 난이도 높음 | 파이프라인/배포 복잡 |
데이터 형태와 제약에 맞춘 적용
제조·설비 예지보전에서 센서 다변량 시계열을 다룬다면 Autoencoder(Seq2Seq/Temporal CNN)를 우선 고려하고 드리프트 모니터링을 함께 둔다. 공정 특징을 요약한 지표를 상시 감시하는 용도라면 Isolation Forest가 경량 선택지가 된다.
결제 사기와 트랜잭션에서는 온라인 실시간 경로에 Isolation Forest를 두고, Autoencoder를 오프라인 재학습 백테스트에 활용할 수 있다. 희소한 레이블을 보강해야 한다면 One-Class SVM으로 고위험 경계를 탐색하고 룰 기반 탐지와 혼합한다.
네트워크와 보안 데이터에서는 플로우 피처에 Isolation Forest와 시간대별 분위수를 적용한 적응형 임계값을 사용할 수 있다. 로그 임베딩은 Autoencoder 또는 Transformer AE의 재구성 오차로 탐지한다. AIOps의 메트릭 이상은 STL 분해 뒤 잔차를 IF/OC-SVM에 입력해 주기성을 제거하면 오탐을 줄일 수 있다. IoT·에지처럼 리소스가 제한된 환경에서는 소형 IF 모델을 배포하고 서버 측 AE를 세컨더리 검증에 쓴다.
정량적으로는 경보대비정탐(Precision) 1030%p 개선, 오탐 알림 2050% 감소 사례를 기대할 수 있다. 장애·사기 탐지 리드타임은 3070% 단축되고 운영 인력의 검토 시간은 2040% 절감될 수 있다. 비정상 패턴을 일찍 포착하고 원인 분석을 앞당기며, 감사 추적과 스코어 로깅을 통해 규정 준수와 재현 가능성도 강화한다.
임계값보다 먼저 고정할 운영 기준
정규화에는 StandardScaler 또는 RobustScaler를 한 번 적합한 뒤 동일한 파이프라인을 재사용한다. 이상 비율이 불확실하면 contamination과 nu를 0.5~2%처럼 보수적으로 설정하고 검증 세트에서 보정한다. 누락값과 이상값은 윈저라이징, IQR 기반 클리핑, 시간 정렬, 중복 제거로 다룬다.
라벨이 없는 경우에는 정상 검증 세트의 상위 q 분위수, 예를 들어 0.98을 임계값으로 둘 수 있다. 제한적인 라벨이 있으면 PR-AUC 최적화와 비용 민감 ROC를 사용해 튜닝한다. 오탐 비용이 미탐 비용보다 큰 조건도 이때 반영한다. PSI와 K-S로 분포 변화를 감지하고, 임계값과 모델 재학습을 자동화한다.
피처·스케일러·모델의 버전을 고정하고 Feature Store 도입을 고려한다. PII는 마스킹하며 최소 필요 피처 원칙을 지킨다. 학습 데이터 검역으로 적대적 데이터 혼입도 막아야 한다. 추론 서비스는 저지연 경로인 IF/OC-SVM과 심층 경로인 AE를 이중화하고, 서킷 브레이커와 폴백 스코어를 적용한다.
동일한 전처리 위에서 모델을 비교하는 코드
전제조건
- Python 3.10+, numpy 1.24+, scikit-learn 1.3+, tensorflow 2.12+ (Autoencoder 예시)
- 재현성: random_state=42 고정
- 데이터: 2D 합성 데이터(정상 다변량 가우시안 + 소량 이상)
데이터 생성 및 공통 전처리
# pip install numpy scikit-learn tensorflow
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
rng = np.random.default_rng(42)
# 정상 데이터 10,000개
X_normal = rng.normal(0, 1, size=(10000, 2))
# 이상 데이터 200개(희소, 큰 값)
X_anom = rng.uniform(low=6, high=10, size=(200, 2))
X = np.vstack([X_normal, X_anom])
y = np.hstack([np.zeros(len(X_normal)), np.ones(len(X_anom))]) # 0=정상, 1=이상
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=42)
# 정상 데이터만으로 학습(준지도)
X_train_normal = X_train[y_train == 0].copy()
scaler = StandardScaler().fit(X_train_normal)
X_tr = scaler.transform(X_train_normal)
X_te = scaler.transform(X_test)
Isolation Forest
from sklearn.ensemble import IsolationForest
from sklearn.metrics import average_precision_score, precision_recall_curve
# contamination은 기대 이상 비율 상한 추정치
if_model = IsolationForest(
n_estimators=200, max_samples='auto',
contamination=0.02, random_state=42, n_jobs=-1
).fit(X_tr)
# score_samples는 값이 낮을수록 이상 → 부호 반전하여 "높을수록 이상" 스코어로 사용
if_scores = -if_model.score_samples(X_te)
# 임계값: 정상 검증 분위수(여기서는 테스트의 y를 모르고 설정한다고 가정하면, X_tr 상 98 분위수가 바람직)
q = 0.98
thr = np.quantile(-if_model.score_samples(X_tr), q)
y_pred = (if_scores >= thr).astype(int)
# 라벨이 있을 때 평가
ap = average_precision_score(y_test, if_scores)
print(f"IsolationForest AP: {ap:.3f}, threshold@{q}: {thr:.3f}")
One-Class SVM
from sklearn.svm import OneClassSVM
from sklearn.metrics import average_precision_score
# nu는 이상 비율 상한 추정(보수적으로 0.01~0.05)
ocsvm = OneClassSVM(kernel='rbf', gamma='scale', nu=0.02).fit(X_tr)
# decision_function: 양수=정상, 음수=이상 → 부호 반전
svm_scores = -ocsvm.decision_function(X_te).ravel()
thr = np.quantile(-ocsvm.decision_function(X_tr).ravel(), 0.98)
y_pred = (svm_scores >= thr).astype(int)
ap = average_precision_score(y_test, svm_scores)
print(f"OC-SVM AP: {ap:.3f}, threshold: {thr:.3f}")
Autoencoder(Keras)
import tensorflow as tf
from tensorflow import keras
from sklearn.metrics import average_precision_score
tf.keras.utils.set_random_seed(42)
input_dim = X_tr.shape[1]
ae = keras.Sequential([
keras.layers.Input(shape=(input_dim,)),
keras.layers.Dense(16, activation='relu'),
keras.layers.Dense(8, activation='relu'),
keras.layers.Dense(16, activation='relu'),
keras.layers.Dense(input_dim, activation='linear'),
])
ae.compile(optimizer=keras.optimizers.Adam(1e-3), loss='mse')
early = keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
# 정상 데이터 내에서 검증 분리
X_tr_tr, X_tr_val = train_test_split(X_tr, test_size=0.1, random_state=42)
ae.fit(X_tr_tr, X_tr_tr, validation_data=(X_tr_val, X_tr_val), epochs=100, batch_size=256, callbacks=[early], verbose=0)
# 재구성 오차
recon = ae.predict(X_te, verbose=0)
ae_err = np.mean((recon - X_te)**2, axis=1)
thr = np.quantile(np.mean((ae.predict(X_tr_val, verbose=0) - X_tr_val)**2, axis=1), 0.98)
y_pred = (ae_err >= thr).astype(int)
ap = average_precision_score(y_test, ae_err)
print(f"Autoencoder AP: {ap:.3f}, threshold: {thr:.6f}")
학습에는 정상 데이터만 사용한다. 이상 혼입 가능성이 있으면 LOF/IF로 사전 필터링한 뒤 재학습한다. 스케일러, 피처 순서, 결측 처리 로직은 모델과 함께 아카이빙해 재현성을 확보한다. 운영 환경의 분포 변화에는 시간대별 분위수를 활용한 적응형 임계값 전략을 병행한다.