Machine Learning/Metrics

confusion_matrix

언킴 2021. 6. 20. 00:02
반응형

1. confusion_matrix (오분류표)

TN : 원래 값이 0인 값을 0으로 제대로 측정한 값 True Negative

FP : 원래 값은 0이지만 1로 잘못 예측한 값 False Positive

FN : 원래 값은 1이지만 0으로 잘못 예측한 값 False Negative

TP : 원래 값이 1인 값을 1로 제대로 측정한 값 True Positive

 

 

python 에서 confusion matrix 를 사용하기 위해 sklearn을 사용하였다. 

from sklearn.metrics import confusion_matrix

 

confusion matrix 는 Accuracy, Precision, Recall(Sensitivity), Specificity, f1-score 등이 활용될 수 있다.

Accuracy = ( TN + TP ) / ( TN + FN + TP + FP ) : 전체 중 제대로 예측한 값

모델의 성능을 평가할 때 많이 쓰이는 값으로 모델이 얼마나 정확한지 나타내는 지표다.

 

Precision = TP / ( FP + TP ) : 1로 측정된 것 중에 실제로 1인 값

Recall = TP / ( TP + FN ) : 실제 1인 값들 중 1로 예측된 값

f1-score = 2 * Precision * Recall / ( Precision + Recall ) : 정밀도와 민감도의 조화평균

 

 

마냥 Accuracy가 높다고 좋은 모델이라고 할 수 없다. Accuracy가 0.8인 모델을 가지고 있는데 다른 모델은 전부 1로만 예측해버리는 모델이 있다고 가정해보자. 

# 모두 1로 예측해버리는 FakeClassifier
class MyFakeClassifier(BaseEstimator):
    def fit(self, X, y):
        pass
    def predict(self, X):
        return np.zeros( (len(X), 1), dtype = 'bool')
digits = load_digits()

y = (digits.target == 7).astype('int')
X_train, X_test, y_train, y_test = train_test_split(digits.data, y, random_state = 42)
X_train.shape, X_test.shape, y_train.shape, y_test.shape

# ((1347, 64), (450, 64), (1347,), (450,))

 

clf = MyFakeClassifier()
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

print(pd.Series(y_test).value_counts(), accuracy_score(y_test, y_pred), confusion_matrix(y_test, y_pred), sep = '\n')

# 0    409
# 1     41

# accuracy : 0.91

# confusion matrix
 [409   0]    TN  FP
 [ 41   0]    FN  TP

그럴경우 데이터가 100개중 91개가 1이라고 한다면 전부 1로 예측해버리는 모델의 Accuracy값은 0.91로 직접 튜닝을 통해 만든 Accuracy 보다 11% 더 높게 된다. 이럴 경우 Precision과 Recall 을 활용하여 모델을 평가하여야 한다.

 

precision = TP / ( FP + TP ) = 0 / ( 0 + 0 )

recall = TP / ( FN + TP ) = 0 / ( 41 + 0 ) 

 

accuracy 값은 높지만 Precision과 Recall 값은 정의할 수 없다. 

이러한 문제들 때문에 Precision과 Recall 값의 중요성을 알 수 있다.

 

2.  Precision과 Recall 

기본적인 sigmoid function 을 사용하면 Thresholds 가 0.5 이다. 0.5 기준으로 0과 1을 분리하는 이진분류를 하게 되는데 이에 다음과 같은 문제가 발생할 수 있다. 병원에서 암세포 관련 검사를 받은 환자 중 암세포가 있다고 예측된 사람들 중 정상이 있다고 하는 것과 실제 암세포가 있는사람들 중 정상이라고 판단되는 것을 보면 전자의 경우 재검사 비용을 지불하고 큰 문제가 발생하지 않지만, 후자의 경우에는 암세포를 가진 사람은 암세포가 있다는 것을 모르기 때문에 큰 문제가 발생할 수 있다. 

 

이런 경우에는 recall 값이 중요하게 된다. 하지만 recall 값을 올리기 위해 무분별하게 Thresholds 값을 낮추게 된다면 Precision 값이 낮아지고 Accuracy 값도 낮아지게 된다. 그러한 문제를 해결하기 위해 recall 과 Precision 의 조화평균인 f1 - score 값이 나오게 된다. f1 - score 값을 적절하게 측정하여 모델을 평가해야 된다.

 

예시를 보여주기 위하여 타이타닉 데이터를 활용하였다.

# 현재는 labeling 목적이 아니라 confusion matrix에 관해서만 다루고 있기 때문에 전처리 과정을 생략하고 
# 변수값을 drop 하였습니다.
titanic.drop(['Name','Cabin', 'Ticket','Embarked'], axis = 1, inplace = True)
titanic.fillna(0, inplace = True)

#label encoding 
le = LabelEncoder()
titanic['Sex'] = le.fit_transform(titanic['Sex'])



feature_cols = titanic.columns.difference(['Survived'])
y = titanic['Survived']
x = titanic[feature_cols]
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state= 42)

x_train.shape, x_test.shape, y_train.shape, y_test.shape

# ((668, 7), (223, 7), (668,), (223,))


logit = LogisticRegression()
logit.fit(x_train, y_train)
y_pred = logit.predict(x_test)



def get_score(y_test, y_pred):
    confusion = confusion_matrix(y_test, y_pred)
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred)
    recall = recall_score(y_test, y_pred)
    print('confusion matrix')
    print(confusion)
    print('acc : {}, precision : {}, recall : {}'.format(accuracy, precision, recall))

get_score(y_test, y_pred)


# confusion matrix
# [[111  23]
# [ 28  61]]
# acc : 0.7713, precision : 0.7262, recall : 0.6854

 

 

 

precisions, recalls, thresholds = precision_recall_curve(y_pred, success)
# precision_recall_curve 는 precision, recall, threshold 값을 반환

# import matplotlib.pyplot as plt

plt.figure(figsize = (8,5))
plt.plot(thresholds, precisions[0:thresholds.shape[0]], linestyle = '--', label = 'precision' )
plt.plot(thresholds, recalls[0:thresholds.shape[0]], label = 'recall')
plt.xticks(np.arange(0,1.1,0.1))
plt.xlabel('Thresholds') ; plt.ylabel('Precision and Recall')
plt.legend()
plt.show()

 

fprs, tprs, thresholds = roc_curve(y_test, success)

plt.plot(fprs, tprs, label = 'ROC')
plt.plot([0,1], [0,1], 'k--', label = 'Random')
plt.xlabel('FPR') ; plt.ylabel('TPR')
plt.legend()
plt.show()

 

AUC값이 클수록 모델의 성능이 좋아지고 0.5에 가까울수록 random하게 됩니다. ( 동전뒤집기 )

Precision 과 recall 값을 잘 보고 적절한 Threshold 값을 찾아야한다.