본문 바로가기
Study/머신러닝

Keras에서 binary_crossentropy와 categorical_crossentropy 차이

by 김카비 2019. 12. 16.

바이너리 classification 문제를 풀려고하면 둘 중 어떤걸 써야하나.. 뭐가 다른가 궁금해서 찾아보았다

 

우선 binary_crossentropy 소스

 

def binary_crossentropy(target, output, from_logits=False):
    """Binary crossentropy between an output tensor and a target tensor.

    # Arguments
        target: A tensor with the same shape as `output`.
        output: A tensor.
        from_logits: Whether `output` is expected to be a logits tensor.
            By default, we consider that `output`
            encodes a probability distribution.

    # Returns
        A tensor.
    """
    # Note: tf.nn.sigmoid_cross_entropy_with_logits
    # expects logits, Keras expects probabilities.
    if not from_logits:
        # transform back to logits
        _epsilon = _to_tensor(epsilon(), output.dtype.base_dtype)
        output = tf.clip_by_value(output, _epsilon, 1 - _epsilon)
        output = tf.log(output / (1 - output))

    return tf.nn.sigmoid_cross_entropy_with_logits(labels=target,
                                                   logits=output)

 

 

코드를 보면 기본적으로 logits=False로 되어있다.

따라서 if not form_logits: 구문을 보면 transform back to logits이라고 되어있다.

다시말해서 inverse sigmoid를 해줌으로서 확률값을 logit값으로 변경해주는 것이다.

 

$$ y = 1/(1+ \exp(-x)) \longleftrightarrow x = \ln(y/(1-y)) $$

더보기

혹시 logit이 뭔지 모른다면 아래링크가 도움이 될 것이다.

https://gentlej90.tistory.com/69

그 이유는 return에 있는 sigmoid_cross_entropy_with_logits 함수 에서

z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x)) 이런 짓을 해주기 때문이다...!!

 

엄청 비효율적이다... 굳이 sigmoid한걸 inverse해서 다시 sigmoid 해준다

아마도 tf backend로 keras를 사용하려하다보니 이렇게 된것 같다

 

어쨌든 binary_crossentropy를 쓰면서 from_logits옵션을 따로 주지 않는 경우에는 ouput layer에 sigmoid를 추가해주어야 한다

만약 from_logits = True 으로 한다면 마지막에 sigmoid activation을 하지 않아야한다.

 

 

 

 

이번에는 categorical_crossentropy를 보자

 

def categorical_crossentropy(target, output, from_logits=False, axis=-1):
    """Categorical crossentropy between an output tensor and a target tensor.

    # Arguments
        target: A tensor of the same shape as `output`.
        output: A tensor resulting from a softmax
            (unless `from_logits` is True, in which
            case `output` is expected to be the logits).
        from_logits: Boolean, whether `output` is the
            result of a softmax, or is a tensor of logits.
        axis: Int specifying the channels axis. `axis=-1`
            corresponds to data format `channels_last`,
            and `axis=1` corresponds to data format
            `channels_first`.

    # Returns
        Output tensor.

    # Raises
        ValueError: if `axis` is neither -1 nor one of
            the axes of `output`.
    """
    output_dimensions = list(range(len(output.get_shape())))
    if axis != -1 and axis not in output_dimensions:
        raise ValueError(
            '{}{}{}'.format(
                'Unexpected channels axis {}. '.format(axis),
                'Expected to be -1 or one of the axes of `output`, ',
                'which has {} dimensions.'.format(len(output.get_shape()))))
    # Note: tf.nn.softmax_cross_entropy_with_logits
    # expects logits, Keras expects probabilities.
    if not from_logits:
        # scale preds so that the class probas of each sample sum to 1
        output /= tf.reduce_sum(output, axis, True)
        # manual computation of crossentropy
        _epsilon = _to_tensor(epsilon(), output.dtype.base_dtype)
        output = tf.clip_by_value(output, _epsilon, 1. - _epsilon)
        return - tf.reduce_sum(target * tf.log(output), axis)
    else:
        return tf.nn.softmax_cross_entropy_with_logits(labels=target,
                                                       logits=output)

 

 

마찬가지로 logit옵션이 기본적으로 False이며 return값은 nagative likelihood이다.

또한 output sum을 1로 scaling 해주긴 하지만, 확률값으로의 변환은 따로 없기 때문에

multiclass classification 문제를 해결하고자 한다면 마지막에 softmax를 해주어야한다.

from_logits=True로 하면 softmax 가 자동으로 적용된다.

 

결론적으로 둘의 차이는 output layer가 sigmoid이냐 softmax이냐 라고 볼 수 있으며

내가 가진 label 값이 0 or 1 이면서 sigmoid_cross_entropy를 loss로 하고 싶으면 binary_crossentropy 를 쓰면 된다

 

내가 가진 label이 [0,1] or [1,0] 의 형태이면서 softmax_cross_entropy를 loss로 하고자 할 때는 categorical_crossentropy를 쓰면 된다

label 이 0 or 1 인데 softmax_cross_entropy를 쓰고싶으면 sparse_categorical_crossentropy를 쓰면 된다

 

binary_crossentropy는 logistic regression 아니면 별로 쓸일이 없을 거 같긴하다.

웬만하면 categorical_crossentropy나 sparse_categorical_crossentropy를 쓸듯

 

 

댓글