티스토리 뷰
제조업같은 곳에서는 precision 뿐만 아니라 recall이 높아질 수록, 불량품의 갯수가 줄어든다.
그렇게 불량품의 갯수가 줄어든다는 소리는 검출할 수 있는 데이터 셋이 줄어들고, 특징 추출이 어렵게 된다는 것이다.
Upsampling을 적용할 수는 있으나, 다른 정상적인 데이터가 영향을 받고, accuracy적인 면에서 한계점이 있을 수 있다.
그렇다면 진짜 데이터에서 특징을 추출해서, 가짜 데이터를 분간하는 데 사용할 수는 없을까?
진짜에서 진짜의 특징을 빼면 가짜다
AutoEncoder의 원리는 이렇습니다.
정상 데이터만을 가지고 Training을 진행한다.
그리고 정상 데이터의 중요한 특징만을 추출해서 기억한다.
그렇게 만들 모델에 val data를 넣었을 때, 일정 Threshold를 넘는 것들은 전부 뭘까요? -> 가짜입니다.
이런 원리로 만들어진 게 AutoEncoder입니다.
Autoencoder의 모델
모델에 대해서 설명하기 전에, 코드부터 보겠습니다.
model = Sequential([Dense(32, input_shape = (input_dim,), activation = 'relu'),
Dense(16, activation="relu"),
Dense(8, activation="relu"),
Dense(16, activation="relu"),
Dense(32, activation='relu'),
Dense(input_dim, activation='sigmoid')])
model.summary()
딥러닝을 조금이라도 알고 계신 분들이라면, 코드가 무엇을 뜻하는지 바로 이해했을 것입니다.
input이 32 -> 16-> 8의 차원까지 줄어들었다가, 8->16-> 32를 거쳐 최종적으로 다시 input_dim의 사이즈로 복원됩니다.
32>16>8로 과정을 압축하는 과정(Encoder)라고 부르고, 8>16>32를 거쳐 다시 복원하는 과정을 (Decoder)라고 부릅니다.
그리고 val data를 넣어 output을 만드는데, 이를 Reconstruction(재구성)한다고 합니다.
그렇게 된다면 무슨 일이 일어날까요? 아무것도 일어나지 않을까요?
정보 이론을 공부하신 분들은 아시겠지만, 컴퓨터에서 어떤 걸 압축하는 과정에서는 무조건 정보의 손실이 일어납니다.
그리고 그걸 되돌리게 되더라도 마찬가지입니다.
그렇기에 이렇게 학습이 되면 error를 최소화하기 위해 output으로 나온 정보는 input에서의 아주 강력하고도 중요한 특징만(즉, 결정적인 특징)을 담게 됩니다(그 과정에서 노이즈도 같이 제거 됩니다.)
이렇게 val을 넣어 output이 나왔다고 해 봅시다.
이 모델은 간단히 말하면 진짜 감별사와 같습니다. 진짜의 특징만을 학습했으니까요.
그러면 이 모델을 통과시켰을 때 error가 많은 녀석들은? -> 가짜라는 것이죠.
우리는 이를 통해서 normal data와 abnormal data를 구분할 수 있게 됩니다.
참고로 학습할 때는 Normal Data만 학습해야 합니다. 진짜 감별사니까요.
그러나 검증할 떄(Val, test 둘 다) Normal과 Abnormal을 전부 포함해야 합니다.
하지만 Threshold는 정해줘야 한다
그렇다면 재구성 오차를 어떻게 계산하고, 얼마나 크면 Abnormal로 칭해야 할까요?
def recon_err_plot(x, x_pred, y, threshold=0):
# treshold : 우리가 지정해줘야 함.(어떻게?)
# 재구성 오차 계산
mse = np.mean(np.power(x - x_pred, 2), axis=1)
error_df = pd.DataFrame({'recon_err': mse, 'y': y})
error_df = error_df.reset_index()
# 재구성 오차 그래프
groups = error_df.groupby('y')
fig, ax = plt.subplots()
for name, group in groups:
ax.plot(group.index, group.recon_err, marker='o', ms=3.5, linestyle='',
label= "Abnormal" if name == 1 else "Normal")
ax.hlines(threshold, ax.get_xlim()[0], ax.get_xlim()[1], colors="r", zorder=100, label='Threshold')
ax.legend()
plt.title("Reconstruction error for each data point")
plt.ylabel("Reconstruction error")
plt.xlabel("Data point index")
plt.show()
return error_df
위 코드는 input과 output을 비교하여 그 차이를 mse로 계산하고 plot을 그리는 함수입니다.
일단 Threshold는 임의로 지정해줄 수 있지만, 더 스마트한 방법은 없을까요?
이는 평가 지표에 따라 다릅니다. 그런데 우리는 평가 지표를 구할 수 있습니다.
그러면 평가 지표를 최대화하는 곳을 찾아서 Threshold를 정한다면 best지 않을까요?
def prec_rec_f1_curve(y, score, pos = 1) :
precision, recall, thresholds = precision_recall_curve(y, score, pos_label=1)
f1 = 2 / (1/precision + 1/recall)
plt.figure(figsize = (8, 6))
plt.plot(thresholds, np.delete(precision, -1), label = 'precision')
plt.plot(thresholds, np.delete(recall, -1), label = 'recall')
plt.plot(thresholds, np.delete(f1, -1), label = 'f1')
plt.xlabel('Anomaly Score')
plt.legend()
plt.grid()
plt.show()
return precision, recall, f1, thresholds
평가 지표를 plot에 그리기 위해서 precision, recall, f1 curve를 그립니다.
그리고 그 후에 나온 f1 값에 대해서 max를 구하면 되겠죠.
thres_f1_max = thresholds[np.argmax(f1)]
thres_f1_max
이렇게 되면 thresholds 이상으로 잘라냈을 때, precision과 recall을 구할 수 있을 겁니다.
from sklearn.metrics import confusion_matrix, classification_report
def classification_report2(y, pred, thresholds):
pred_temp = np.where(pred > thresholds , 1, 0)
print('< confusion matrix >\n')
print(confusion_matrix(y, pred_temp))
print('\n' + '='*60 + '\n')
print('< classification_report >\n')
print(classification_report(y, pred_temp))
참고로, 재구성 에러가 큰 데이터는 Abnormal이므로, 재구성 에러 계산 시 값이 큰 instance(index)가 이상탐지에 중요한 변수로 볼 수 있습니다.
그러므로 Train(input)과 Prediction(x_pred- 오타가 아닙니다. autoencoder는 x_val을 넣어 pred는 x가 됩니다)를 계산했을 때, mse가 큰 index가 중요한 데이터일 것입니다.