Интуиция за категориальной перекрестной энтропией

Я пытаюсь сделать категориальную функцию потери перекрестной энтропии, чтобы лучше понять интуицию, стоящую за ней. Пока моя реализация выглядит так:

# Observations
y_true = np.array([[0, 1, 0], [0, 0, 1]])
y_pred = np.array([[0.05, 0.95, 0.05], [0.1, 0.8, 0.1]])

# Loss calculations
def categorical_loss():
  loss1 = -(0.0 * np.log(0.05) + 1.0 * np.log(0.95) + 0 * np.log(0.05))
  loss2 = -(0.0 * np.log(0.1) + 0.0 * np.log(0.8) + 1.0 * np.log(0.1))
  loss = (loss1 + loss2) / 2 # divided by 2 because y_true and y_pred have 2 observations and 3 classes
  return loss

# Show loss
print(categorical_loss()) # 1.176939193690798

Однако я не понимаю, как должна вести себя функция, чтобы возвращать правильное значение, когда:

  • хотя бы одно число из y_pred равно 0 или 1, потому что тогда функция log возвращает -inf или 0 и как в этом случае должна выглядеть реализация кода
  • по крайней мере одно число из y_true равно 0, потому что умножение на 0 всегда возвращает 0 и значение np.log(0.95) тогда будет отброшено, и как должна выглядеть реализация кода в этом случае

person mikolajs    schedule 17.01.2021    source источник


Ответы (1)


Что касается y_pred, равного 0 или 1, покопайтесь в исходном коде бэкэнда Keras для обоих binary_crossentropy и categorical_crossentropy, получаем:

def binary_crossentropy(target, output, from_logits=False):
    if not from_logits:
        output = np.clip(output, 1e-7, 1 - 1e-7)
        output = np.log(output / (1 - output))
    return (target * -np.log(sigmoid(output)) +
            (1 - target) * -np.log(1 - sigmoid(output)))


def categorical_crossentropy(target, output, from_logits=False):
    if from_logits:
        output = softmax(output)
    else:
        output /= output.sum(axis=-1, keepdims=True)
    output = np.clip(output, 1e-7, 1 - 1e-7)
    return np.sum(target * -np.log(output), axis=-1, keepdims=False)

откуда вы можете ясно видеть, что в обеих функциях есть операция отсечения output (т. е. предсказания), чтобы избежать бесконечностей от логарифмов:

output = np.clip(output, 1e-7, 1 - 1e-7)

Итак, здесь y_pred никогда не будет точно равно 0 или 1 в базовых вычислениях. Обработка аналогична в других фреймворках.

Относительно того, что y_true равно 0, нет никаких проблем - соответствующие термины устанавливаются равными 0, как и должно быть согласно математическому определению.

person desertnaut    schedule 17.01.2021