Реализация функции активации softmax для нейронных сетей - PullRequest
19 голосов
/ 28 марта 2012

Я использую функцию активации Softmax в последнем слое нейронной сети. Но у меня есть проблемы с безопасной реализацией этой функции.

Наивная реализация будет такой:

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();

Это не очень хорошо работает для> 100 скрытых узлов, потому что y во многих случаях будет NaN (если y (f)> 709, exp (y (f)) вернет inf). Я придумал эту версию:

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();

, где safeExp определяется как

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);
}

Эта функция ограничивает ввод эксп. В большинстве случаев это работает, но не во всех случаях, и мне не удалось выяснить, в каких случаях это не работает. Когда у меня есть 800 скрытых нейронов в предыдущем слое, он вообще не работает.

Однако, даже если это сработало, я каким-то образом "исказил" результат ANN. Можете ли вы придумать какой-нибудь другой способ рассчитать правильное решение? Существуют ли какие-либо библиотеки или приемы C ++, которые я могу использовать для вычисления точного результата этого ANN?

edit: Решение, предоставленное 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();

И это действительно математически то же самое. Однако на практике некоторые небольшие значения становятся 0 из-за точности с плавающей запятой. Интересно, почему никто никогда не записывает эти детали реализации в учебники.

Ответы [ 2 ]

12 голосов
/ 28 марта 2012

Сначала перейдите к логарифмической шкале, т.е. рассчитайте log(y) вместо y.Журнал числителя тривиален.Чтобы рассчитать журнал знаменателя, вы можете использовать следующий трюк: http://lingpipe -blog.com / 2009/06/25 / log-sum-of-exponentials /

8 голосов
/ 23 марта 2015

Я знаю, что он уже ответил, но я все равно опубликую здесь шаг за шагом.

поставить на лог:

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

Пусть m будет max_i {zi}, используя трюк 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)}}

термин exp (zi-m) может страдать от недостаточного значения, если m намного больше, чем другие z_i, но это нормально, поскольку это означает, что z_i не имеет значения для выхода softmax после нормализации. Окончательные результаты:

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