머신러닝&딥러닝/Tensorflow&keras

Tensorflow - 다중 클래스 학습 평가 이론 및 구현

Like_Me 2020. 6. 28. 13:29
반응형

  Classification을 할 때 평가에 Precision이나 Recall 등이 필요할 때가 있다. Binary는 바로 지원이 되어 사용하면 되지만 Multi class의 경우는 따로 구현이 필요하다. 그에대한 Custom 함수를 김태영님의 블로그를 참고하여 Tensorflow 2.2 version으로 만들어 보았다. 모든 구현은 Label이 One-hot 형태가 아닌 int형 데이터일 때를 가정하였다. One-hot인 경우는 추가적으로 argmax를 해주는 과정이 필요하다.

 

Binary Data

  위와 같은 Binary + Imbalanced Data 가 있을 때 , 모델이 예측치로 모두 파란색 공을 뽑아내면 제대로 학습이 되지 않았음에도 정확도가 80%가 나오게 된다. 따라서 Recall이나 Precision의 평가방법이 필요하게 된다. 

 

노란색공을 양성이라 가정하며 모델이 위의 결과가 노란색공일 것이라고 예측한 것을 의미한다.

Recall 같은 경우는 Imbalanced data에서 위와 같이 양성(노란색 공) 판정을 했을 때 아래의 그림과 같이 $ \frac{검출 양성 수} {전체 양성 수} =  \frac {1} {2}$ 이 된다. 

$(\frac{TP} {TP+FN})$

검출 양성 수 / 전체 양성 수

# 특정 클래스에 대한 재현율(TP/(TP+FN))
def single_class_recall(interesting_class_id):
    def recall(y_true, y_pred):
        class_id_true = tf.cast(y_true, tf.int64)
        class_id_pred = tf.math.argmax(y_pred, axis=-1)
        # 전체 양성수
        recall_mask = tf.cast(
            tf.math.equal(class_id_true, interesting_class_id), "int32"
        )
        # 검출 양성수
        class_recall_tensor = (
            tf.cast(tf.math.equal(class_id_true, class_id_pred), "int32") * recall_mask
        )
        # 검출 양성수/전체 양성수
        class_recall = tf.cast(
            tf.math.reduce_sum(class_recall_tensor), "float32"
        ) / tf.cast(tf.math.maximum(tf.math.reduce_sum(recall_mask), 1), "float32")
        return class_recall

    return recall

 

Precision 은 아래 그림과 같이 $ \frac {실제 양성 수} {양성이라고 판정한 수} = \frac {1} {3} $ 이 된다.

$(\frac{TP} {TP+FP})$

(판정한 것 중)실제 양성 수 / 양성이라고 판정한 수

# 특정 클래스에 대한 정밀도(TP/(TP+FP))
def single_class_precision(interesting_class_id):
    def prec(y_true, y_pred):
        class_id_true = tf.cast(y_true, tf.int64)
        class_id_pred = tf.math.argmax(y_pred, axis=-1)
        # 양성이라고 판정한 수(TP+FP)
        precision_mask = tf.cast(
            tf.math.equal(class_id_pred, interesting_class_id), "int32"
        )
        # 예측값중 실제 양성수(TP)
        class_prec_tensor = (
            tf.cast(tf.math.equal(class_id_true, class_id_pred), "int32")
            * precision_mask
        )
        # 실제 양성수 / 양성이라고 판정한 수
        class_prec = tf.cast(
            tf.math.reduce_sum(class_prec_tensor), "float32"
        ) / tf.cast(tf.math.maximum(tf.math.reduce_sum(precision_mask), 1), "float32")
        return class_prec

    return prec

 

 

 

Recall이나 Precision 뿐만 아니라 F1Score 나 Critical Success Index (CSI) 도 사용할 때가 많은데 F1Score는 단순히 

$ \frac {2*recall*precision} {recall+precision} $ 값이다.

 

CSI는 아래 그림과 같이 $ \frac {검출 양성 수} {전체 양성 수 + 양성이라고 판정한 수} = \frac {1} {4} $가 된다.

$(\frac{TP} {TP+FP+FN})$

검출 양성 수 / (전체 양성 수 + 양성이라고 판정한 수)

 

# critical success index - TP/(TP+FP+FN)
def single_class_csi(interesting_class_id):
    def csi(y_true, y_pred):
        class_id_true = tf.cast(y_true, tf.int64)
        class_id_pred = tf.math.argmax(y_pred, axis=-1)
        # 전체 양성수(TP+FN)
        real_positive = tf.cast(
            tf.math.equal(class_id_true, interesting_class_id), "int32"
        )
        # 양성이라고 판정한 수(TP+FP)
        pred_positive = tf.cast(
            tf.math.equal(class_id_pred, interesting_class_id), "int32"
        )
        # 검출 양성수(TP)
        true_positive = (
            tf.cast(tf.math.equal(class_id_true, class_id_pred), "int32")
            * real_positive
        )
        tp_fp_fn = real_positive + pred_positive - true_positive
        # 검출 양성수/전체 양성수
        class_csi = tf.cast(tf.math.reduce_sum(true_positive), "float32") / tf.cast(
            tf.math.maximum(tf.math.reduce_sum(tp_fp_fn), 1), "float32"
        )
        return class_csi

    return csi

 

 

 

 

반응형