from sklearn.tree import DecisionTreeClassifier
이상 탐지(Anomaly Detection)의 정의
- 주어진 데이터의 정상 여부를 판별하는 문제
- 신용 카드 사기, 대출 사기 탐지 등이 대표적인 예시
이상 탐지 문제의 가장 큰 특징은 정상 데이터와 이상 데이터의 비율의 불균형입니다.
암 진단으로 예시를 들었을 때, 암이 아닌 환자가 99명이고 암인 환자가 1명이면 모든 데이터를 암이 아닌 환자라고 예측하면 정확도가 99프로로 엄청 높게 나옵니다.
데이터 불균형(Class Imbalance)의 해결 방법(샘플링)
Under Sampling
큰 그룹의 데이터를 덜 뽑아서 데이터의 비율을 맞추는 방법입니다.
Over Sampling
작은 그룹의 데이터를 더 뽑아서 데이터의 비율을 맞추는 방법입니다.
Under Sampling
Random Under Sampling
말 그대로 무작위로 삭제하는 방법입니다. 그렇게 때문에 빠르고 쉽지만, 정보의 손실이 있을 수도 있습니다.
Near Miss Under Sampling
적은 그룹 근처에 있는 큰 그룹의 데이터를 선택하는 방법입니다.
- Near-Miss ver.1
- 가장 가까운 3개의 작은 그룹의 데이터의 평균 거리
- Near-Miss ver.2
- 가장 먼 3개의 작은 그룹의 데이터의 평균 거리
- Near-Miss ver.3
- 가장 가까운 작은 그룹의 데이터와의 거리
Over Sampling
Simple Over Sampling
작은 그룹에서 데이터를 더 많이 추출하는 방법
장점
빠르게 적용이 가능합니다.
단점
- 데이터의 새로운 정보를 제공하지 않습니다.
- 과대 적합의 위험이 큽니다.
SMOTE(synthetic minority oversampling technique)
합성 데이터를 생성해서 데이터의 비율을 맞추는 방법입니다.
- Minority Group에서 임의의 데이터를 선택합니다.
- KNN으로 선택된 데이터와 Minority Group 중 가까운 이웃을 찾습니다.
- 선택된 데이터와 가까운 이웃 사이의 거리를 측정합니다.
- 0~1 사이의 임의의 수를 곲하여 데이터를 생성합니다.
- 데이터의 비율이 같아질 때 까지 1~4를 반복합니다.
Step 1) Minority Group에서 임의의 데이터를 선택합니다.
Step 2) KNN으로 선택된 데이터와 작은 그룹 중 가까운 이웃을 찾습니다.
Step 3) 선택된 데이터와 가까운 이웃 사이의 거리를 측정합니다.
Step 4) 0~1 사이의 임의의 수를 곱하여 데이터를 생성합니다.
임의의 수 = 0.5
Step 5) 데이터의 비율이 같아질 때까지 1~4를 반복합니다.
데이터 불균형(Class Imbalance)의 해결 방법(샘플링)
정보가 손실되지는 않지만, 원래 데이터를 OverSampling 하였기 때문에 새로운 데이터를 잘 예측하지 못합니다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_moons
data, label = make_moons(n_samples=300, shuffle=True, noise=0.5, random_state=2021)
plt.scatter(data[:, 0], data[:, 1], c=label)
from imblearn.datasets import make_imbalance
from collections import Counter
from sklearn.model_selection import train_test_split
def ratio_func(y, multiplier, minority_class):
target_stats = Counter(y)
return {minority_class: int(multiplier * target_stats[minority_class])}
data, label = make_imbalance(
data,
label,
sampling_strategy=ratio_func,
**{"multiplier": 0.1, "minority_class": 1,}
)
train_x, test_x, train_y, test_y = train_test_split(data, label, test_size=0.3, random_state=2021, stratify=label)
plt.scatter(train_x[:, 0], train_x[:, 1], c=train_y)
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score, f1_score
tree = DecisionTreeClassifier()
tree.fit(train_x, train_y)
train_pred = tree.predict(train_x)
test_pred = tree.predict(test_x)
tree_train_acc = accuracy_score(train_pred, train_y)
tree_test_acc = accuracy_score(test_pred, test_y)
train_f1_score = f1_score(train_pred, train_y)
test_f1_score = f1_score(test_pred, test_y)
print("Tree train accuracy is {:.4f}".format(tree_train_acc))
print("Tree test accuracy is {:.4f}".format(tree_test_acc))
print("Tree train F1-Score is {:.4f}".format(train_f1_score))
print("Tree test F1-Score is {:.4f}".format(test_f1_score))
Tree train accuracy is 1.0000
Tree test accuracy is 0.9200
Tree train F1-Score is 1.0000
Tree test F1-Score is 0.5000
from imblearn.under_sampling import RandomUnderSampler, NearMiss
under_dict = {}
# Random
rus = RandomUnderSampler(random_state=2021)
rus_data, rus_label = rus.fit_resample(train_x, train_y)
under_dict['rus'] = {'data': rus_data, 'label': rus_label}
#Near
for i in range(1, 4):
near_miss = NearMiss(version=i)
near_data, near_label = near_miss.fit_resample(train_x, train_y)
under_dict[f"near_{i}"] = {
"data": near_data, "label": near_label
}
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
for idx, (name, sample) in enumerate(under_dict.items()):
ax = axs[idx//2, idx%2]
d, l = sample['data'], sample['label']
ax.scatter(d[:, 0], d[:, 1], c=l)
ax.set_title(name)
from sklearn.tree import DecisionTreeClassifier
under_model = {}
for name, sample in under_dict.items():
under_tree = DecisionTreeClassifier()
under_tree.fit(sample['data'], sample['label'])
under_model[name] = under_tree
under_pred = {}
for name, under_tree in under_model.items():
under_test_pred = under_tree.predict(test_x)
under_pred[name] = under_test_pred
for name, pred in under_pred.items():
acc = accuracy_score(test_y, pred)
print(f"{name} Sampling test accuracy i {acc:.4f}")
print()
for name, pred in under_pred.items():
f1 = f1_score(test_y, pred)
print(f"{name} Sampling test F1-Score is {f1:.4f}")
rus Sampling test accuracy i 0.8400
near_1 Sampling test accuracy i 0.2600
near_2 Sampling test accuracy i 0.3400
near_3 Sampling test accuracy i 0.6000
rus Sampling test F1-Score is 0.5000
near_1 Sampling test F1-Score is 0.1778
near_2 Sampling test F1-Score is 0.1951
near_3 Sampling test F1-Score is 0.2857
#Over Sampling
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=2021)
smote_data, smote_label = smote.fit_resample(train_x, train_y)
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
axs[0].scatter(train_x[:, 0], train_x[:, 1], c=train_y)
axs[0].set_title('raw data')
axs[1].scatter(smote_data[:, 0], smote_data[:, 1], c=smote_label)
axs[1].set_title('smote data')
smote_tree = DecisionTreeClassifier()
smote_tree.fit(smote_data, smote_label)
smote_test_pred = smote_tree.predict(test_x)
smote_acc = accuracy_score(test_y, smote_test_pred)
smote_f1 = f1_score(test_y, smote_test_pred)
print(f"SMOTE test accuracy is {smote_acc:.4f}")
print(f"SMOTE test F1-SCORE is {smote_f1:.4f}")
SMOTE test accuracy is 0.9200
SMOTE test F1-SCORE is 0.6667
데이터 불균형(Class Imbalance)의 해결 방법(모델)
Out-of-Distribution
- 모델은 정상 범위의 데이터(In-Distribution)만을 학습
- 정상 범위에서 먼 데이터(Out-of-Distribution)를 이상으로 판단하는 이상 탐지 모델
모델은 정상 범위(In-Distribution)의 데이터만을 학습
정상 범위에서 먼 데이터(Out-of-Distribution)를 탐지
Isolation Forest
- Regression Decision Tree를 기반으로 한 Random Forest
- 정상 데이터는 더 많은 분할을 비정상 데이터는 덜 분할한다는 개념
- 랜덤으로 데이터를 split 하여 모든 관측치를 고립시키며 구현됩니다.
각 관측치를 고립(=분리)시키는 것은 이상치가 정상 데이터보다 쉽습니다.
학습 방법
- 정상 데이터는 tree의 terminal node와 근접하며, 경로 길이가 큽니다.
- 이상치는 tree의 root node와 근접하며, 경로의 길이가 작습니다.
OCSVM
- One-Class SVM
- 데이터를 Mapping 한 뒤 정상 데이터를 원점에서 멀어지게 하는 방법
- Feature Space에서 원점에서 가까운 데이터는 비정상 데이터
- Feature Space에서 원점에서 먼 데이터는 정상 데이터
PCA
- 차원 축소를 이용하는 방법
- 차원 축소 전 원래 데이터의 위치와 비교하여 거리가 먼 데이터를 비정상으로 판단
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from imblearn.datasets import make_imbalance
from collections import Counter
from sklearn.model_selection import train_test_split
from sklearn.ensemble import IsolationForest
from sklearn.svm import OneClassSVM
from sklearn.decomposition import PCA
data, label = make_moons(n_samples=300, shuffle=True, noise=0.5, random_state=2021)
def ratio_func(y, multiplier, minority_class):
target_stats = Counter(y)
return {minority_class: int(multiplier * target_stats[minority_class])}
data, label = make_imbalance(
data,
label,
sampling_strategy=ratio_func,
**{"multiplier": 0.1, "minority_class": 1,}
)
train_x, test_x, train_y, test_y = train_test_split(data, label, test_size=0.3, stratify=label)
isol_forest = IsolationForest()
isol_forest.fit(train_x, train_y)
isol_test_pred = isol_forest.predict(test_x)
isol_test_acc = accuracy_score(test_y, isol_test_pred == -1)
isol_test_f1 = f1_score(test_y, isol_test_pred == -1)
ocsvm = OneClassSVM()
ocsvm.fit(train_x, train_y)
ocsvm_test_pred = ocsvm.predict(test_x)
ocsvm_test_acc = accuracy_score(test_y, ocsvm_test_pred == -1)
ocsvm_test_f1 = f1_score(test_y, ocsvm_test_pred == -1)
pca = PCA(n_components=1)
pca.fit(train_x)
test_latent = pca.transform(test_x)
test_recon = pca.inverse_transform(test_latent)
recon_diff = (test_x - test_recon) ** 2
pca_pred = recon_diff.mean(1)
plt.scatter(test_x[:, 0], test_x[:, 1], c=test_y)
plt.plot(test_recon[:, 0], test_recon[:, 1])
from sklearn.metrics import roc_curve, roc_auc_score, roc_curve
print('isol', isol_test_acc)
print('isol', isol_test_f1)
print('ocsvm ', ocsvm_test_acc)
print('ocsvm ', ocsvm_test_f1)
print('pca roc_auc :', roc_auc_score(test_y, pca_pred))
isol 0.68
isol 0.3333333333333333
ocsvm 0.54
ocsvm 0.25806451612903225
pca roc_auc : 0.8577777777777778
'머신러닝' 카테고리의 다른 글
[머신러닝] 추천 시스템 (0) | 2022.05.12 |
---|---|
[머신러닝] 차원축소 (0) | 2022.05.10 |
[머신러닝] 군집화 (0) | 2022.05.06 |
[머신러닝] SVM (0) | 2022.05.06 |
[머신러닝] K-Nearest Neighbors (0) | 2022.05.06 |
댓글