Mise en œuvre d'une fonction d'activation softmax pour les réseaux neuronaux
j'utilise un Softmax fonction d'activation dans la dernière couche d'un réseau neuronal. Mais j'ai des problèmes avec une mise en œuvre sûre de cette fonction.
Une implémentation naïve serait celui-ci:
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();
cela ne fonctionne pas très bien pour > 100 noeuds cachés car ils seront NaN
dans de nombreux cas (si y(f) > 709, exp(y(f)) retournera inf). Je suis venu avec cette version:
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();
où safeExp
est défini
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);
}
Cette fonction limite l'entrée de l'exp. Dans la plupart des cas, cela fonctionne, mais pas dans tous les cas et je n'ai pas vraiment réussi à savoir dans quels cas il ne fonctionne pas. Quand j'ai 800 neurones cachés dans la couche précédente, il ne fonctionne pas du tout.
cependant, même si cela a fonctionné, j'ai quelque peu "déformé" le résultat de L'ANN. Pouvez-vous penser à d'autres façons de calculer la bonne solution? Existe-il des bibliothèques C++ ou astuces que je peux utiliser pour calculer la la sortie exacte de cette ANN?
edit: la solution fournie par Itamar Katz est:
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();
et c'est mathématiquement la même chose. Dans la pratique, Cependant, certaines petites valeurs deviennent 0 en raison de la précision de la virgule flottante. Je me demande pourquoi personne n'écrit jamais ces détails dans les manuels.
2 réponses
D'abord aller à l'échelle logarithmique, I. e calculer log(y)
au lieu de y
. Le journal du numérateur est trivial. Afin de calculer le journal du dénominateur, vous pouvez utiliser le "truc": http://lingpipe-blog.com/2009/06/25/log-sum-of-exponentials/
je sais que c'est déjà répondu mais je vais poster ici une étape par étape de toute façon.
mettre sur le log:
zj = wj . x + bj
oj = exp(zj)/sum_i{ exp(zi) }
log oj = zj - log sum_i{ exp(zi) }
soit m le max_i { zi } utiliser le journal de la somme-exp truc:
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)}}
le terme exp (zi-m) peut souffrir de sous-flux si m est beaucoup plus grand que les autres z_i, mais ce n'est pas grave puisque cela signifie que z_i n'est pas pertinent sur la sortie softmax après normalisation. résultat final:
oj = exp (zj - m - log{sum_i{exp(zi-m)}})