2012-03-28 35 views
19

Sto utilizzando una funzione di attivazione Softmax nell'ultimo livello di una rete neurale. Ma ho problemi con un'implementazione sicura di questa funzione.Implementazione di una funzione di attivazione softmax per reti neurali

Un'implementazione ingenuo sarebbe questo:

Vector y = mlp(x); // output of the neural network without softmax activation function 
for(int f = 0; f < y.rows(); f++) 
    y(f) = exp(y(f)); 
y /= y.sum(); 

Questo non funziona molto bene per> 100 nodi nascosti perché la y sarà NaN in molti casi (se y (f)> 709, exp (y (f)) restituirà inf). Sono venuto con questa versione:

Vector y = mlp(x); // output of the neural network without softmax activation function 
for(int f = 0; f < y.rows(); f++) 
    y(f) = safeExp(y(f), y.rows()); 
y /= y.sum(); 

dove safeExp è definito come

double safeExp(double x, int div) 
{ 
    static const double maxX = std::log(std::numeric_limits<double>::max()); 
    const double max = maxX/(double) div; 
    if(x > max) 
    x = max; 
    return std::exp(x); 
} 

Questa funzione limita l'ingresso di exp. Nella maggior parte dei casi questo funziona, ma non in tutti i casi e non sono riuscito a scoprire in quali casi non funziona. Quando ho 800 neuroni nascosti nel livello precedente non funziona affatto.

Tuttavia, anche se questo ha funzionato ho in qualche modo "distorto" il risultato della ANN. Riesci a pensare a un altro modo per calcolare la soluzione corretta? Ci sono alcune librerie o trucchi C++ che posso usare per calcolare l'output esatto di questa ANN?

edit: La soluzione fornita da Itamar Katz è:

Vector y = mlp(x); // output of the neural network without softmax activation function 
double ymax = maximal component of y 
for(int f = 0; f < y.rows(); f++) 
    y(f) = exp(y(f) - ymax); 
y /= y.sum(); 

Ed è proprio matematicamente lo stesso. In pratica, tuttavia, alcuni valori piccoli diventano 0 a causa della precisione in virgola mobile. Mi chiedo perché nessuno scriva mai questi dettagli di implementazione nei libri di testo.

+3

"Mi chiedo perché nessuno scriva mai questi dettagli di implementazione nei libri di testo." Mi sono sempre chiesto la stessa cosa! – pjreddie

+0

"È matematicamente lo stesso" - leggendo ulteriormente, qualcuno dice che il metodo è preferito a causa della stabilità numerica: https://stackoverflow.com/questions/34968722/softmax-function-python – gremwell

risposta

12

Prima andare su scala di registro, calcolare log(y) anziché . Il registro del numeratore è banale. Per calcolare il registro del denominatore, è possibile utilizzare il seguente "trucco": http://lingpipe-blog.com/2009/06/25/log-sum-of-exponentials/

+0

Una soluzione perfetta. Aggiungerò il codice in un minuto. Potresti confermarlo per favore? Grazie mille. – alfa

+0

Non sembra corretto; seguire l'algebra di ciò che 'log (y (f))' è: ** log (y (f)) = log (exp (y (f))) - log (sum (exp (y (f))) * * e inserisci il risultato del "trucco" menzionato per il registro della somma. –

+0

ln (y_f) = ln (exp (a_f)) - ln (somma su f 'exp (a_f')) = af - ln [somma su f 'exp (m)/exp (m) * exp (a_f') ] = a_f - m - ln (somma su f 'exp (-m) * exp (a_f)) = a_f - m - ln [somma su f' exp (a_f'-m)] <=> y_f exp (a_f-m)/(somma su f 'exp (a_f' - m)). a_f è y_f prima di exp() nel codice sopra elencato. Dov'è l'errore? : D – alfa

7

So che ha già risposto, ma lo pubblicherò comunque passo dopo passo.

messo sul registro:

zj = wj . x + bj 
oj = exp(zj)/sum_i{ exp(zi) } 
log oj = zj - log sum_i{ exp(zi) } 

Sia M il MAX_I {zi} usare il trucco di log-sum-exp:

log oj = zj - log {sum_i { exp(zi + m - m)}} 
    = zj - log {sum_i { exp(m) exp(zi - m) }}, 
    = zj - log {exp(m) sum_i {exp(zi - m)}} 
    = zj - m - log {sum_i { exp(zi - m)}} 

il termine exp (zi-m) possono soffrire underflow se m è molto maggiore di altri z_i, ma va bene poiché ciò significa che z_i è irrilevante sull'output di softmax dopo la normalizzazione. i risultati finali sono:

oj = exp (zj - m - log{sum_i{exp(zi-m)}}) 
+0

Grazie! La tua risposta aiuta! Hai menzionato "ma va bene poiché ciò significa che z_i è irrilevante sull'output di softmax dopo la normalizzazione", intendi se avviene l'underflow di "exp (zi-m)". Non aggiunge molto errore nel risultato? –

+0

Scusate la risposta tardiva. Sì, se m >> zi poi exp (zi-m) sarebbe vicino a 0, il underflow lo cambia a 0, che non cambia gran parte dei risultati finali. –

Problemi correlati