Хобрук: Ваш путь к мастерству в программировании

tf.keras.losses.CategoricalCrossentropy дает значения, отличные от простой реализации

Кто-нибудь знает, почему необработанная реализация функции категориальной кроссэнтропии так отличается от функции API tf.keras?

import tensorflow as tf
import math
tf.enable_eager_execution()

y_true =np.array( [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
y_pred = np.array([[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]])

ce = tf.keras.losses.CategoricalCrossentropy()
res = ce(y_true, y_pred).numpy()
print("use api:")
print(res)

print()
print("implementation:")
step1 = -y_true * np.log(y_pred )
step2 = np.sum(step1, axis=1)

print("step1.shape:", step1.shape)
print(step1)
print("sum step1:", np.sum(step1, ))
print("mean step1", np.mean(step1))

print()
print("step2.shape:", step2.shape)
print(step2)
print("sum step2:", np.sum(step2, ))
print("mean step2", np.mean(step2))

Выше дано:

use api:
0.3239681124687195

implementation:
step1.shape: (3, 3)
[[0.10536052 0.         0.        ]
 [0.         0.11653382 0.        ]
 [0.         0.         0.0618754 ]]
sum step1: 0.2837697356318653
mean step1 0.031529970625762814

step2.shape: (3,)
[0.10536052 0.11653382 0.0618754 ]
sum step2: 0.2837697356318653
mean step2 0.09458991187728844

Если сейчас с другими y_true и y_pred:

y_true = np.array([[0, 1]])
y_pred = np.array([[0.99999999999, 0.00000000001]])

Это дает:

use api:
16.11809539794922

implementation:
step1.shape: (1, 2)
[[-0.         25.32843602]]
sum step1: 25.328436022934504
mean step1 12.664218011467252

step2.shape: (1,)
[25.32843602]
sum step2: 25.328436022934504
mean step2 25.328436022934504


Ответы:


1

Разница в этих значениях: [.5, .89, .6], так как его сумма не равна 1. Я думаю, вы ошиблись и вместо этого имели в виду это: [.05, .89, .06].

Если вы предоставите значения с суммой, равной 1, то результаты обеих формул будут одинаковыми:

import tensorflow as tf
import numpy as np

y_true = np.array( [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
y_pred = np.array([[.9, .05, .05], [.05, .89, .06], [.05, .01, .94]])

print(tf.keras.losses.categorical_crossentropy(y_true, y_pred).numpy())
print(np.sum(-y_true * np.log(y_pred), axis=1))

#output
#[0.10536052 0.11653382 0.0618754 ]
#[0.10536052 0.11653382 0.0618754 ]

Однако давайте рассмотрим, как рассчитывается тензор y_pred, если он не масштабируется (сумма значений не равна 1)? Если вы посмотрите на исходный код категориальной кросс-энтропии здесь вы увидите, что он масштабируется на y_pred, так что класс probas каждого образца суммируется до 1:

if not from_logits:
    # scale preds so that the class probas of each sample sum to 1
    output /= tf.reduce_sum(output,
                            reduction_indices=len(output.get_shape()) - 1,
                            keep_dims=True)

так как мы передали pred, сумма probas которого не равна 1, давайте посмотрим, как эта операция изменит наш тензор [.5, .89, .6]:

output =  tf.constant([.5, .89, .6])
output /= tf.reduce_sum(output,
                            axis=len(output.get_shape()) - 1,
                            keepdims=True)
print(output.numpy())

# array([0.2512563 , 0.44723618, 0.30150756], dtype=float32)

Таким образом, он должен быть равен, если мы заменим приведенный выше вывод операции (масштабированный y_pred) и передадим его вашей собственной реализованной категориальной кросс-энтропии с немасштабированным y_pred, переходящим в реализацию тензорного потока:

y_true =np.array( [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])

#unscaled y_pred
y_pred = np.array([[.9, .05, .05], [.5, .89, .6], [.05, .01, .94]])  
print(tf.keras.losses.categorical_crossentropy(y_true, y_pred).numpy())

#scaled y_pred (categorical_crossentropy scales above tensor to this internally)
y_pred = np.array([[.9, .05, .05], [0.2512563 , 0.44723618, 0.30150756], [.05, .01, .94]])  
print(np.sum(-y_true * np.log(y_pred), axis=1))

Выход:

[0.10536052 0.80466845 0.0618754 ]
[0.10536052 0.80466846 0.0618754 ]

Теперь давайте изучим результаты вашего второго примера. Почему ваш второй пример показывает другой вывод? Если вы снова проверите исходный код, вы увидите эту строку:

output = tf.clip_by_value(output, epsilon, 1. - epsilon)

который обрезает значения ниже порогового значения. Ваш ввод [0.99999999999, 0.00000000001] будет преобразован в [0.9999999, 0.0000001] в этой строке, поэтому он даст вам другой результат:

y_true = np.array([[0, 1]])
y_pred = np.array([[0.99999999999, 0.00000000001]])

print(tf.keras.losses.categorical_crossentropy(y_true, y_pred).numpy())
print(np.sum(-y_true * np.log(y_pred), axis=1))

#now let's first clip the values less than epsilon, then compare loss
epsilon=1e-7
y_pred = tf.clip_by_value(y_pred, epsilon, 1. - epsilon)
print(tf.keras.losses.categorical_crossentropy(y_true, y_pred).numpy())
print(np.sum(-y_true * np.log(y_pred), axis=1))

Выход:

#results without clipping values
[16.11809565]
[25.32843602]

#results after clipping values if there is a value less than epsilon (1e-7)
[16.11809565]
[16.11809565]
18.07.2021
  • У тебя очень острый взгляд и твердое понимание этого. 22.07.2021
  • Новые материалы

    Не зря же это называют интеллектом
    Стек — C#, Oracle Опыт — 4 года Работа — Разведывательный корпус Мне пора служить Может быть, я немного приукрашиваю себя, но там, где я живу, есть обязательная военная служба на 3..

    LeetCode Проблема 41. Первый пропущенный положительный результат
    LeetCode Проблема 41. Первый пропущенный положительный результат Учитывая несортированный массив целых чисел, найдите наименьшее пропущенное положительное целое число. Пример 1: Input:..

    Расистский и сексистский робот, обученный в Интернете
    Его ИИ основан на предвзятых данных, которые создают предрассудки. Он словно переходит из одного эпизода в другой из серии Черное зеркало , а вместо этого представляет собой хронику..

    Управление состоянием в микрофронтендах
    Стратегии бесперебойного сотрудничества Микро-фронтенды — это быстро растущая тенденция в сфере фронтенда, гарантирующая, что удовольствие не ограничивается исключительно бэкэнд-системами..

    Декларативное и функциональное программирование в стиле LINQ с использованием JavaScript с использованием каррирования и генератора ...
    LINQ - одна из лучших функций C #, которая обеспечивает элегантный способ написания кода декларативного и функционального стиля, который легко читать и понимать. Благодаря таким функциям ES6,..

    Структуры данных в C ++ - Часть 1
    Реализация общих структур данных в C ++ C ++ - это расширение языка программирования C, которое поддерживает создание классов, поэтому оно известно как C с классами . Он используется для..

    Как я опубликовал свое первое приложение в App Store в 13 лет
    Как все началось Все началось три года назад летом после моего четвертого класса в начальной школе. Для меня, четвертого класса, лето кажется бесконечным, пока оно не закончится, и мой отец..