오늘은 이상탐지에 대해 배웠다. 이상탐지 파트도 내용이 엄청 많고 하나하나 다 어려운 개념들이라 정말 힘들었다. 그래도 기초 통계 이론을 정리해둬서 도움이 되는 부분이 많아 빨리 머신러닝 통계 이론도 정리해야 될 것 같다.
| 구분 | 상세 내용 | 공식 |
|---|---|---|
| 표준화 점수 (Z-Score) | • 방식 : 표준화 점수를 기준으로 이상치를 판별 • 장점 : 표준화 점수의 절대값이 3 이상이면 이상치로 간주 • 단점 : 단변량 기준이며, 좌우 대칭하는 정규분포를 가정 | |
| 사분범위 (IQR) | • 방식 : 사분범위를 기준으로 이상치를 판별 • 장점 : 사분위수 기반이라 평균/표준편차 대비 로버스트함 • 단점 : 단변량 기준이며, 변수 간 상관관계를 반영할 수 없음 | |
| 마할라노비스 거리 (MD) | • 방식 : 공분산 구조를 반영한 다변량 거리로 이상치를 판별 • 장점 : 다변량 기준이며, 변수 간 조합으로 이상치를 포착 • 단점 : 공분산 추정에 민감 → 다중공선성, 소표본, 결측값이 많은 데이터에 대해 불안정 | |
| 구분 | 방식 및 특징 | 주요 공식 |
|---|---|---|
| Hotelling's | • 방식 : 주성분 공간에서의 마할라노비스 거리 측정 • 장점 : PCA를 통해 잡음 제거 가능 • 단점 : 정상 데이터 기반의 모델 학습 필요 | |
| Squared Prediction Error (SPE) | • 방식 : 실제값과 PCA 복원값의 차이(잔차) 기반 • 장점 : 설비 고장 등 미세한 변화 포착에 강함 • 단점 : 노이즈와 고장 징후의 구분 필요 |
| 구분 | 상세 내용 |
|---|---|
| Isolation Forest | • 방식 : 무작위 트리를 반복 생성하고, 적은 분할로 고립되는 관측값을 이상치로 판단 • 장점 : 고차원 데이터에 강하고, 비정규성 및 비선형 패턴을 탐지할 수 있음 • 단점 : 데이터의 밀도나 구조가 매우 복잡하면 성능이 불안정해질 수 있음 |
| One-class SVM | • 방식 : 정상 데이터의 경계를 학습해 경계 밖의 관측값을 이상치로 분류 • 장점 : 경계 기반 방법을 사용하므로 매우 미세한 패턴을 포착할 수 있음 • 단점 : 대량 데이터에서는 느리고, 커널 선택에 매우 민감 |
| Autoencoder | • 방식 : 입력 → 잠재공간 → 복원 과정에서 재구성 오차가 큰 샘플을 이상치로 판단 • 장점 : 비선형 패턴, 고차원 시계열 및 센서 데이터에서도 강력한 성능을 보임 • 단점 : 학습 비용이 크며, 정상 데이터만으로 충분히 학습되어야 함 |
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = pd.DataFrame(
data=scaler.fit_transform(X=train_num),
index=train_num.index,
columns=train_num.columns
)
X_scaled.abs().apply(func= lambda x: x.ge(3).sum())
# AirTmp 0
# ProcTmp 0
# RotSpd 164
# Torque 25
# ToolWear 0
# dtype: int64
def iqr_outlier_count(x):
q1, q3 = x.quantile(q=[0.25, 0.75])
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
outlier = x.lt(lower) | x.gt(upper)
return outlier.sum()
X_scaled.apply(func= iqr_outlier_count)
# AirTmp 0
# ProcTmp 0
# RotSpd 418
# Torque 69
# ToolWear 0
# dtype: int64
from scipy.spatial.distance import mahalanobis
mean_vec = train_num.mean()
# AirTmp 300.00493
# ProcTmp 310.00556
# RotSpd 1538.77610
# Torque 39.98691
# ToolWear 107.95100
# dtype: float64
cov_mat = train_num.cov()
cov_inv = np.linalg.inv(cov_mat)
mnd = [mahalanobis(u=x, v=mean_vec, VI=cov_inv) for x in train_num.values]
from scipy import stats
mnd_threshold = stats.chi2.ppf(q=0.99, df=train_num.shape[1])
# 15.08627246938899
np.sqrt(mnd_threshold)
# np.float64(3.884105105347819)
mnd_outlier = (mnd > np.sqrt(mnd_threshold))
mnd_outlier.sum()
# np.int64(194)
df.loc[mnd_outlier, 'Target'].value_counts(normalize=True).sort_index()
# Target
# 0 0.587629
# 1 0.412371
# Name: proportion, dtype: float64
from sklearn.decomposition import PCA
X_normal = X_scaled.loc[df['Target'].eq(0)]
n_components : 0~1 범위의 실수 지정 시 누적 분산 비율에 해당하는 주성분 개수 자동 적용model_pca = PCA(n_components=0.90)
model_pca.fit(X=X_normal)
pca_score = model_pca.transform(X=X_scaled)
model_pca.explained_variance_ratio_.cumsum()
# array([0.39825224, 0.74785604, 0.95356962])
lambdas = model_pca.explained_variance_
ht2 = (pca_score ** 2 / lambdas).sum(axis=1)
m = model_pca.n_components_
# np.int64(3)
n = X_normal.shape[0]
# 9661
f_critical = stats.f.ppf(q=0.99, dfn=m, dfd=n-m)
# np.float64(3.783648204590835)
ht2_threshold = m * (n - 1) / (n - m) * f_critical
# np.float64(11.353295192487305)
ht2_outlier = (ht2 > ht2_threshold)
ht2_outlier.sum()
# np.int64(139)
df.loc[ht2_outlier, 'Target'].value_counts(normalize=True).sort_index()
# Target
# 0 0.741007
# 1 0.258993
# Name: proportion, dtype: float64
내일 머신러닝 파트가 마무리된 후 캐글 경진대회 2회차가 시작된다. 수요일은 깃허브 특강이 있고, 이후에는 머신러닝과 CV 파트가 시작될 예정이다.