Укороченная версия:
Предположим, у вас есть два тензора, в которых y_hat
содержатся вычисленные оценки для каждого класса (например, из y = W * x + b) и y_true
содержатся метки истинного кодирования в горячем виде.
y_hat = ... # Predicted label, e.g. y = tf.matmul(X, W) + b
y_true = ... # True label, one-hot encoded
Если вы интерпретируете баллы y_hat
как ненормализованные логарифмические вероятности, то они являются логитами .
Кроме того, общая кросс-энтропийная потеря рассчитывается следующим образом:
y_hat_softmax = tf.nn.softmax(y_hat)
total_loss = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), [1]))
по существу эквивалентно полной кросс-энтропийной потере, вычисленной с помощью функции softmax_cross_entropy_with_logits()
:
total_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))
Длинная версия:
В выходном слое вашей нейронной сети вы, вероятно, вычислите массив, который содержит оценки классов для каждого из ваших обучающих экземпляров, например, из вычислений y_hat = W*x + b
. В качестве примера ниже я создал y_hat
массив размером 2 x 3, в котором строки соответствуют обучающим экземплярам, а столбцы - классам. Итак, здесь есть 2 тренировочных экземпляра и 3 класса.
import tensorflow as tf
import numpy as np
sess = tf.Session()
# Create example y_hat.
y_hat = tf.convert_to_tensor(np.array([[0.5, 1.5, 0.1],[2.2, 1.3, 1.7]]))
sess.run(y_hat)
# array([[ 0.5, 1.5, 0.1],
# [ 2.2, 1.3, 1.7]])
Обратите внимание, что значения не нормализованы (то есть строки не суммируют до 1). Чтобы их нормализовать, мы можем применить функцию softmax, которая интерпретирует входные данные как ненормализованные логарифмические вероятности (или логиты ) и выводит нормализованные линейные вероятности.
y_hat_softmax = tf.nn.softmax(y_hat)
sess.run(y_hat_softmax)
# array([[ 0.227863 , 0.61939586, 0.15274114],
# [ 0.49674623, 0.20196195, 0.30129182]])
Важно полностью понимать, что говорит выход softmax. Ниже я показал таблицу, которая более четко представляет результат выше. Можно видеть, что, например, вероятность того, что тренировочный экземпляр 1 будет «Классом 2», составляет 0,619. Вероятности классов для каждого обучающего экземпляра нормированы, поэтому сумма каждой строки равна 1,0.
Pr(Class 1) Pr(Class 2) Pr(Class 3)
,--------------------------------------
Training instance 1 | 0.227863 | 0.61939586 | 0.15274114
Training instance 2 | 0.49674623 | 0.20196195 | 0.30129182
Итак, теперь у нас есть классовые вероятности для каждого обучающего экземпляра, где мы можем взять argmax () каждой строки, чтобы сгенерировать окончательную классификацию. Исходя из вышеизложенного, мы можем сгенерировать, что обучающий экземпляр 1 принадлежит «Классу 2», а обучающий экземпляр 2 принадлежит «Классу 1».
Верны ли эти классификации? Нам нужно сравнить с настоящими ярлыками из учебного набора. Вам потребуется y_true
массив с горячим кодированием , где снова строки - это обучающие экземпляры, а столбцы - это классы. Ниже я создал пример y_true
массива «один горячий», где истинная метка для обучающего экземпляра 1 - «Класс 2», а истинная метка для обучающего экземпляра 2 - «Класс 3».
y_true = tf.convert_to_tensor(np.array([[0.0, 1.0, 0.0],[0.0, 0.0, 1.0]]))
sess.run(y_true)
# array([[ 0., 1., 0.],
# [ 0., 0., 1.]])
Распределение вероятностей y_hat_softmax
близко к распределению вероятностей в y_true
? Мы можем использовать кросс-энтропийную потерю для измерения ошибки.
Мы можем вычислить кросс-энтропийную потерю построчно и посмотреть результаты. Ниже мы видим, что тренировочный экземпляр 1 имеет потерю 0,479, в то время как обучающий экземпляр 2 имеет более высокую потерю 1.200. Этот результат имеет смысл, поскольку в нашем примере выше y_hat_softmax
показано, что наибольшая вероятность для обучающего экземпляра 1 была для «класса 2», который соответствует обучающему экземпляру 1 в y_true
; однако прогноз для обучающего экземпляра 2 показал наибольшую вероятность для «класса 1», который не соответствует истинному классу «класса 3».
loss_per_instance_1 = -tf.reduce_sum(y_true * tf.log(y_hat_softmax), reduction_indices=[1])
sess.run(loss_per_instance_1)
# array([ 0.4790107 , 1.19967598])
То, что мы действительно хотим, это общая потеря по всем тренировочным экземплярам. Таким образом, мы можем вычислить:
total_loss_1 = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), reduction_indices=[1]))
sess.run(total_loss_1)
# 0.83934333897877944
Использование softmax_cross_entropy_with_logits ()
Вместо этого мы можем вычислить общую кросс-энтропийную потерю, используя tf.nn.softmax_cross_entropy_with_logits()
функцию, как показано ниже.
loss_per_instance_2 = tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true)
sess.run(loss_per_instance_2)
# array([ 0.4790107 , 1.19967598])
total_loss_2 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))
sess.run(total_loss_2)
# 0.83934333897877922
Обратите внимание, что total_loss_1
и total_loss_2
дают по существу эквивалентные результаты с некоторыми небольшими различиями в самых последних цифрах. Тем не менее, вы также можете использовать второй подход: он занимает на одну строку кода меньше и накапливает меньше числовых ошибок, потому что softmax выполняется для вас внутри softmax_cross_entropy_with_logits()
.
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(tf.nn.softmax(tf.add(tf.matmul(x,W),b)),y) cost=tf.reduce_mean(cross_entropy)
, Но когда я пользуюсь другим способом,pred=tf.nn.softmax(tf.add(tf.matmul(x,W),b)) cost =tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),reduction_indices=1))
результат стабильнее и лучше.