Коротка версія:
Припустимо, у вас є два тензори, де 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, яка інтерпретує вхід як ненормалізовані ймовірності журналу (aka logits ) та виводить нормалізовані лінійні ймовірності.
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))
результат стабільний і кращий.