본문 바로가기
Study/ML

4. 성능 평가 (Evaluation) - Classification 평가방법

by 까망우동 2023. 5. 27.
반응형

Index

    기계학습 알고리즘의 성능 평가는 기본적으로 Estimator 의 종류가 회귀인지, 분류인지에 따라 어려종류로 나뉜다. 

    회귀의 경우 오차를 기반으로 MSE, MAE, RMSE, R^ 등으로 간단히 계산 할 수 있다. 

    반면, 분류의 경우 정확도(Accuracy) 외에도 여러가지 지표로 판단이 필요하고, target 의 class 가 Binary or Multi 인지에 따라서도 사용하는 지표가 달라지게된다. 

    본 포스팅에서는 분류(Classification)문제에서의 평가방법에 대해서 다룬다. 

     

    1. 정확도 (Accuracy) 

    가장 기본적인 분류문제의 평가지표. "얼마나 정답을 맞췄는지" 로 직관적이지만, 불균형한 분포의 데이터셋에서는 결과가 외곡될 수 있다. 예를들어 100만개 중에 3개정도 불량이 나오는 공정에서 Accuracy 를 지표로 모델을 학습하면 3개를 맞추려고 하기보단 무조건 '정상' 이라고 예측할 것이다. 

    정확도는 사이킷런의 metrics 모듈에 accuracy_score 를 통해 산출한다. 

    from sklearn.metrics import accuracy_score

     

    2. 오차행렬 (Confusion-Matrix) - 정확도, 정밀도, 재현율, F1 Score

    오차행렬은 예측값과 실제값의 True / False 를 기준으로 4사분면으로 평가하는 방식이다. 사이킷런 metrics 모듈의 confusion_matrix 클래스를 통해 만들 수 있다. 아래 평가지표는 항상 헷갈리지만 잘 이해하고 있어야한다. 

    * Precision (정밀도) 

    정밀도는 Positive라고 예측했을때 실제 Positive인 확률이다. 정밀도와 반대되는 개념(1-정밀도)은 α 즉, Type-1 Error 이다. 다시말해 정밀도를 높이려면 FP = False Alarm 을 줄여야 한다. FP 를 줄이려면 Positive 를 소극적으로 예측해야한다 .

    * Recall (재현율) = Sensitivity (민감도) 

    재현율은 정밀도와는 Trade-off 의 관계로, 실제값이 Positive 일때 예측을 Positive로 할 확률이다. 재현율과 반대되는 개념 (1-재현율)은 β 즉, Type-2 Error 이다. 다시말해 재현율을 높이려면 FN = False Negative 를 줄여야한다. FN 을 줄이려면 Positive를 적극적으로 예측해서 Positive 인데 Negative 라고 하는상황을 줄여야한다. 

     

    * 정밀도 vs 재현율 관계 

    정밀도는 Positive 라고 예측했을때 그것이 잘 맞을 확률. 재현율은 실제 Positive 일때 그것이 맞았을 확률... 

    난 이렇게 읽으면 항상 헷갈린다.... 그래서 나처럼 가설검정의 개념이 편한사람은,,

    정밀도 Precision = α 에러를 줄이는 방향 // 재현율 Recall = β 에러를 줄이는 방향으로 이해하는걸 추천한다. 

    알다시피 α 와 β 는 서로 trade-off 관계에 있기 때문에 정밀도, 재현율도 마찬가지다. 

    예시로 이해해보면, 

    ex1) 암 진단 모델은? α 에러가 크면 추가 검진비용, 걱정거리 등의 문제만 있지만 β가 커버리면 생명이 위험한다. 재현율이 높은 모델을 설계해야한다. 

    ex2) 스팸 메일 판단모델은? α 에러가 크면 읽어야 하는 메일을 못읽을 수가 있다. 반면 β가 크면 스팸함으로 가야될게 안가서 성가신 정도다. 따라서 정밀도가 높은 모델을 설계해야한다. 

     

    * F1 Score 

    F1 Score 는 정밀도와 재현율의 가중평균과 같은 의미로, 1에 가까울수록 균형이 잘잡혔다고 할 수 있다. 

     

     

    3. 임계값(Threshold) 

    • 지금까지 평가 지표는 P/N 의 결과값 기반으로 이루어졌다. 그런데 그 이전에, P/N 의 판단은 사실 특정한 임계값(Threshold)를 기준으로  판단된다. 
    • 즉 predict 라는 API 는, 사실 predict_proba() 라는 예측 레이블(P/N) 각각의 확률을 반환해주는 API 에서 파생되었고 predict_proba 의 확률값을 가지고 P/N 을 판단하는 기준이 Threshold 로 디폴트는 50% 이지만 조정할 수 있다. 
    pred = lr_clf.predict(x_test)
    pred_proba = lr_clf.predict_proba(x_test)

    # 예측확률과 결과값 한눈에 보기
    pred_proba_result = np.concatenate([np.round(pred_proba,2),pred.reshape(-1,1)],axis=1)
    print(pred_proba_result[:3])

    결과값

    [[0.53 0.47 0. ] [0.87 0.13 0. ] [0.88 0.12 0. ]]

     

    • 임계값을 바꿔가며 평가하는 코드는 아래와 같다. 사이킷런의 Binarizer 매서드는 입력한 threshold 를 기준으로 1,0 으로 변환시켜주기 떄문에 pred_proba 결과값을 원하는 threshold 로 조정해가며 pred 결과를 만들 수 있다. 
    thresholds = [0.4,0.45,0.5,0.55,0.6]

    def get_eval_by_threshold(y_test,pred_proba_c1,thresholds):
        for custom_threshold in thresholds:
            bz = Binarizer(threshold=custom_threshold).fit(pred_proba_c1)
            custom_predict = bz.transform(pred_proba_c1)
            print('임계값:',custom_threshold)
            get_clf_eval(y_test,custom_predict)  # 정밀도, 정확도, 오차행렬 등 지표 반환하는 사용자 함수

    pred_proba_c1 = pred_proba[:,1].reshape(-1,1)
           
    get_eval_by_threshold(y_test,pred_proba_c1,thresholds)

     

    * precision_recall_cruve 

    아래와 같이 precision_recall_cruve 클래스를 사용하면, threshold 변화에 따른 precision, recall 결과값을 자동으로 반환해준다. 아래 예시 코드에서는 165가지로 threshold 를 변경해가며 계산한다. 

    pred_proba_class1 = lr_clf.predict_proba(x_test)[:,1]

    # 절밀도와 재현율을 반환한다. (각 threshold 에 대해서)
    precisions, recalls, thresholds = precision_recall_curve(y_test,pred_proba_class1)
    print('threshold 의 단위(갯수)확인:', thresholds.shape)

    위의 결과값 (3개 변수) 을 가지고 아래와 같이 임계값에 따른 Precision, Recall curve 를 시각화 해볼수도 있다. 

    아래 그래프에서 볼 수 있듯이, 임계값을 낮춘다는건 Positive 라고 할 확률이 커지는거고, 그 결과 Recall 이 올라간다. 

    3. ROC Curve 와 AUC 스코어 

    • ROC 커브는 x축이 FPR : 실제 Negative 인데 Positive 라고 잘못예측할 확률, y축이 TPR(재현율) 실제 Positive 인데 Positive라고 잘 예측할 확률.
    • 즉, Negative 일때도 잘맞추면서 (x가 작을때) Positive 일때도 잘맞추는 (y가 클때) 커브가 나오는 커브가 좋은 커브이고, 이때의 커브 밑 면적을 AUC (Area Under Curve) 라고 하며, AUC 가 1에 가까울 수록 좋은 모델이다. 
    • x 축, 즉 FPR 을 조정하는 방법은 임계값을 낮출수록 Positive 라고 하기쉽기 때문에 FPR도 올라간다. 

    • ROC Curve 그리기, AUC 면적 계산하기 코드 
    rom sklearn.metrics import roc_curve

    #roc_curve : 실제클래스값과, Positive 의 확률을 입력파라미터로,, fpr/tpr/임계값 3가지를 반환
    fprs, tprs, thresholds = roc_curve(y_test,pred_proba_class1)
    #thresholds[0] 값은 max(예측획률)+1 로 임의 설정됨
    thr_index = np.arange(1,thresholds.shape[0],5)
    print('5-step으로 뽑은 샘플 임계값:',np.round(thresholds[thr_index],2))
    print('샘플 임계값별 FPR:',np.round(fprs[thr_index],2))
    print('샘플 임계값별 TPR:',np.round(tprs[thr_index],2))


    # ROC 커브 그리기
    def roc_curve_plot(y_test,pred_proba_c1):
        fprs, tprs, thresholds = roc_curve(y_test,pred_proba_c1)
        plt.figure(figsize=(6,3))
        plt.plot(fprs,tprs, label='ROC')
        plt.plot([0,1],[0,1],'k--',label='Random')
        start,end = plt.xlim()
        plt.xticks(np.round(np.arange(start,end,0.1),2))
        plt.xlim(0,1);plt.ylim(0,1)
        plt.xlabel('FPR');plt.ylabel('TPR')
        plt.legend()
       
    roc_curve_plot(y_test,pred_proba_class1)
    # AUC 계산하기
    from sklearn.metrics import roc_auc_score
    roc_score = roc_auc_score(y_test,pred_proba_class1)
    print('AUC값:',roc_score)

     

    반응형

    댓글