본문 바로가기
머신러닝/비지도학습

[머신러닝(비지도)] #3 - Anomaly Detection (이상 탐지)

by 황오독 2024. 10. 22.

## 들어가기전

공장 불량품 예시

 

Anomaly Detection은 지도학습인 Classification과 같은 문제유형으로,

모델을 만들고, 정상(normal)과 비정상(abnormal)을 평가한다.

하지만 대체로 비지도학습에 가까운데,

그 이유는 비정상 (Abnormal) 데이터가 너무 부족하기 때문이다.

또한 그나마 있는 데이터도 Class를 대표하기 어렵다.

=> 따라서, 정상(Normal) 데이터를 가지고 어디까지가 정상인지 추정한다.

Classfication과 Anomaly Detection 차이

 

1. 이상탐지의 몇 가지 Challenge

 (1) Label을 어떻게 확보할 것인가?

   - 보통, Normal Data는 자동으로 수집하고, Abnoraml Data는 수동관리하는 경우가 많음

   - 비지도 학습이지만, 모델을 평가하려면 Label이 필요함.

 (2) 낮은 성능 문제

   - 정상모델과 비정상모델의 비율차이로, 정확도(Accuracy)는 높지만 재현율(recall)이 낮음.

   - 어떤 지표를 사용해야하는지, 어떤 기준으로 잘라서 나눠야(0,1) 하는지  적절한 지표와 기준을 사용해야 함.

 

2. 평가지표 : f1-Score (정밀도와 재현율의 조화 평균)

  => Recall과 Precision을 균형 있게 볼 수 있는 F1-Score가 좀 더 나은 지표

  => 비즈니스 관점으로 평가하는 것이 중요함.

 

3. Anomaly Detection 해결 방법들

- Abnormal이 매우 부족한 경우 : Anomaly Detection => Isolation Forest / One Class SVM

- Abnormal이 적당히(?) 부족한 경우 : Classification => Class Imbalance 문제 해결

 

4. Isolation Forest 이해

 (1) 절차

1 Train Set로 부터, "일부" 데이터 샘플링 - 256개 정도면 충분하다고 알려져 있음
2 Isolation Tree 만들기 - 랜덤하게 feature 선정하고 랜덤하게 split 기준을 삼아 Tree 생성
   (data가 leaf node에 혼자 남을 때까지)
- 정상 데이터일수록 Isolation 시키려면 많은 Split 필요 => Depth 길어짐
- 비정상 데이터일수록 => Depth 짧아짐
- 랜덤 Tree를 늘려가면서 고립시키기 위한 Depth 측정
3 Scoring으로 정상/비정상 구분
- 1에 가까울수록 Abnormal
- xi depth가 전체 평균과 비슷하면 0.5
- xi depth가 전체 평균보다 작으면 1
- xi depth가 전체 평균보다 크면 0
=> 랜덤 트리 여러개...

 

 (2) hyper parameter

   ① n_estimators

     - 하나의 데이터를 고립시키기 위해 생성하는 tree의 개수

     - Default : 100

   ② max_samples

     - Default : 256

   ③ contamination

     - Score 계산 후, 이상치로 간주할 비율 (0.5보다 작아야 함)

 

 (3) 모델링 절차

   ① 방법1

     - Contamination 조정 : 학습데이터에서 Abnormal 비율로 부터 시작해서 조정하며 성능 최적화

     - Validation : 예측 및 성능 평가

   ② 방법2

     - Score 계산 및 최적의 cut-off 찾기

       : x_val로 모델 생성 후 스코어 산출, cut-off 조금씩 조절하면서 F1 최대화 하는 cut-off 찾기

   ③ 튜닝

     - Hypter-parameter 튜닝

       : n_estimator, max_depth 조절하면서 튜닝 (for loop 사용)

# 가장 적합한 contamination 찾기
result = []
conts = np.linspace(0.01, 0.5, 100)
for c in conts:
    model = IsolationForest(contamination = c, random_state = 20)
    model.fit(x_train)
    pred = model.predict(x_val)
    pred = np.where(pred == 1, 0, 1)
    result.append(f1_score(y_val, pred))

# 가장 큰값의 결과
print(round(max(result),3), round(conts[np.argmax(result)], 3) )
    
# 해당 contamination 값 입력 후 학습
model = IsolationForest(contamination=0.03, random_state = 20)

model.fit(x_train)

# 예측
pred = model.predict(x_val)

# 결과를 1,0으로 변환(np.where)
pred = np.where(pred == 1, 0, 1)

# 분류 결과를 평가
print(confusion_matrix(y_val, pred))
print(classification_report(y_val, pred))