2012-03-28 20 views
19

。但是我对这个函数的安全实现有问题。神经网络的softmax激活功能的实现我在神经网络的最后一层使用了激活功能<a href="http://en.wikipedia.org/wiki/Softmax_activation_function" rel="nofollow noreferrer">Softmax</a>

一个天真的实施将是这一个:

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

此功能限制exp的输入。在大多数情况下,这是有效的,但不是所有情况下,我都没有真正设法找出哪些情况下不起作用。当我在上一层有800个隐藏的神经元时,它根本不起作用。

然而,即使这个工作我不知怎的“扭曲”的人工神经网络的结果。你能想出任何其他方式来计算正确的解决方案吗?是否有任何C++库或技巧可用于计算此ANN的确切输出?

编辑:由伊塔马尔·卡茨提供的解决方案是:

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。我想知道为什么没有人将这些实现细节写在教科书中。

+3

“我想知道为什么没有人将这些实现细节写入教科书。” 我一直想知道同样的事情! – pjreddie 2014-01-28 21:03:34

+0

“它确实在数学上是一样的” - 进一步阅读,有人说你的方法是首选的,因为数值稳定性:https://stackoverflow.com/questions/34968722/softmax-function-python – gremwell 2017-06-19 05:57:02

回答

12

首先进入对数刻度,即计算log(y)而不是y。分子的记录是微不足道的。为了计算分母的日志,你可以使用下面的'技巧':http://lingpipe-blog.com/2009/06/25/log-sum-of-exponentials/

+0

一个完美的解决方案。我会在一分钟内添加代码。你能确认吗?非常感谢你。 – alfa 2012-03-28 13:40:24

+0

这似乎不正确;遵循log(y(f))是什么的代数:** log(y(f))= log(exp(y(f))) - log(sum(exp(y(f)))* *并插入所提及的“技巧”结果以记录总和。 – 2012-03-28 14:53:11

+0

LN(y_f)= LN(EXP(A_F)) - LN(求和F 'EXP(A_F '))= AF - LN [求和F' EXP(米)/ EXP(M)* EXP(A_F') ] = A_F - M - LN(求和F 'EXP(-m)* EXP(A_F))= A_F - M - LN [求和F' EXP(a_f'-M)] <=> y_f EXP(A_F-M) /(f'exp(a_f' - m)之和)。在上面列出的代码中,a_f是exp()之前的y_f。错误在哪里? :D – alfa 2012-03-28 18:03:21

7

我知道它已经回答了,但我会在这里发布一步一步的反正。

穿上日志:

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

设M为max_i {字}使用对数和-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)}}) 
+0

谢谢!你的答案有帮助!你提到过“但这没关系,因为这意味着z_i与标准化后softmax输出无关”,你的意思是如果'exp(zi-m)'发生下溢。它不会在结果中增加很多错误? – 2017-02-01 17:25:47

+0

对不起,回复迟了。是的,如果m >> zi然后exp(zi-m)将接近0,则下溢就会将其更改为0,这并不会改变最终结果。 – 2017-06-26 20:13:21