Versione breve:
Supponiamo di avere due tensori, dove y_hat
contiene punteggi calcolati per ciascuna classe (ad esempio, da y = W * x + b) e y_true
contiene etichette vere con codifica a caldo.
y_hat = ... # Predicted label, e.g. y = tf.matmul(X, W) + b
y_true = ... # True label, one-hot encoded
Se interpretate i punteggi y_hat
come probabilità di log non normalizzate, allora sono logit .
Inoltre, la perdita totale di entropia calcolata in questo modo:
y_hat_softmax = tf.nn.softmax(y_hat)
total_loss = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), [1]))
è sostanzialmente equivalente alla perdita totale di entropia calcolata con la funzione softmax_cross_entropy_with_logits()
:
total_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))
Versione lunga:
Nel livello di output della tua rete neurale, probabilmente calcolerai un array che contiene i punteggi delle classi per ciascuna delle tue istanze di allenamento, ad esempio da un calcolo y_hat = W*x + b
. Per fare da esempio, di seguito ho creato un y_hat
array come 2 x 3, in cui le righe corrispondono alle istanze di training e le colonne corrispondono alle classi. Quindi qui ci sono 2 istanze di allenamento e 3 lezioni.
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]])
Si noti che i valori non sono normalizzati (ovvero le righe non aggiungono fino a 1). Per normalizzarli, possiamo applicare la funzione softmax, che interpreta l'input come probabilità di log non normalizzate (ovvero logit ) e genera probabilità lineari normalizzate.
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]])
È importante comprendere appieno cosa sta dicendo l'output di softmax. Di seguito ho mostrato una tabella che rappresenta più chiaramente l'output sopra. Si può vedere che, ad esempio, la probabilità che l'istanza di addestramento 1 sia "Classe 2" è 0,619. Le probabilità di classe per ciascuna istanza di allenamento sono normalizzate, quindi la somma di ogni riga è 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
Quindi ora abbiamo le probabilità di classe per ogni istanza di addestramento, dove possiamo prendere l'argmax () di ogni riga per generare una classificazione finale. Da sopra, possiamo generare che l'istanza di addestramento 1 appartiene a "Classe 2" e l'istanza di addestramento 2 appartiene a "Classe 1".
Queste classificazioni sono corrette? Dobbiamo misurarci con le vere etichette del set di addestramento. Sarà necessario un y_true
array con codifica a caldo , in cui le righe sono istanze di training e le colonne sono classi. Di seguito ho creato un esempio y_true
one-hot array in cui l'etichetta vera per l'istanza di addestramento 1 è "Classe 2" e l'etichetta vera per l'istanza di addestramento 2 è "Classe 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.]])
La distribuzione di probabilità è y_hat_softmax
vicina alla distribuzione di probabilità in y_true
? Possiamo usare la perdita di entropia incrociata per misurare l'errore.
Siamo in grado di calcolare la perdita di entropia su base riga e vedere i risultati. Di seguito possiamo vedere che l'istanza di addestramento 1 ha una perdita di 0.479, mentre l'istanza di addestramento 2 ha una perdita maggiore di 1.200. Questo risultato ha senso perché nel nostro esempio sopra, ha y_hat_softmax
mostrato che la probabilità più alta dell'istanza di allenamento 1 era per "Classe 2", che corrisponde all'istanza di addestramento 1 in y_true
; tuttavia, la previsione per l'addestramento dell'istanza 2 ha mostrato la massima probabilità per "Classe 1", che non corrisponde alla vera classe "Classe 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])
Ciò che vogliamo veramente è la perdita totale in tutte le istanze di allenamento. Quindi possiamo calcolare:
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
Utilizzo di softmax_cross_entropy_with_logits ()
Possiamo invece calcolare la perdita totale di entropia incrociata usando la tf.nn.softmax_cross_entropy_with_logits()
funzione, come mostrato di seguito.
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
Notare che total_loss_1
e total_loss_2
produrre risultati sostanzialmente equivalenti con alcune piccole differenze nelle cifre finali. Tuttavia, potresti anche usare il secondo approccio: prende una riga di codice in meno e accumula meno errori numerici perché il softmax è fatto per te all'interno di 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)
. Ma quando uso un altro modo,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))
il risultato è stabile e migliore.