logistic / sigmoid 函数实现数值精度
logistic / sigmoid function implementation numerical precision
在scipy.special.expit
中,逻辑函数实现如下:
if x < 0
a = exp(x)
a / (1 + a)
else
1 / (1 + exp(-x))
但是,我在其他 languages/frameworks 中看到了实现
1 / (1 + exp(-x))
我想知道 scipy 版本实际带来了多少好处。
对于非常小的x
,结果接近于0。即使exp(-x)
溢出到Inf
也有效。
这实际上只是为了稳定性 - 否则输入非常大的值可能会 return 出乎意料的结果。
如果 expit
就像 1 / (1 + exp(-x))
一样实现,那么将 -710
的值放入函数中将 return nan
,而 -709
会像预期的那样给出接近于零的值。这是因为 exp(710)
太大而不能成为双数。
代码中的分支只是意味着避免了这种情况。
另请参阅 Stack Overflow 上的 this question and answer。
似乎使用它会更有效率:
if x < -709
sigmoid = 0.0
else
sigmoid = 1.0 / (1.0 + exp(-x))
除非您需要精度为 10^-309 的数字(见下文),否则这似乎太过分了!
>>> 1 / (1 + math.exp(709.78))
5.5777796105262746e-309
另一种方法是
python
np.where(x > 0, 1. / (1. + np.exp(-x)), np.exp(x) / (np.exp(x) + np.exp(0)))
因为 np.exp(x) / (np.exp(x) + np.exp(0))
等同于 1. / (1. + np.exp(-x))
但对于负值更稳定
在scipy.special.expit
中,逻辑函数实现如下:
if x < 0
a = exp(x)
a / (1 + a)
else
1 / (1 + exp(-x))
但是,我在其他 languages/frameworks 中看到了实现
1 / (1 + exp(-x))
我想知道 scipy 版本实际带来了多少好处。
对于非常小的x
,结果接近于0。即使exp(-x)
溢出到Inf
也有效。
这实际上只是为了稳定性 - 否则输入非常大的值可能会 return 出乎意料的结果。
如果 expit
就像 1 / (1 + exp(-x))
一样实现,那么将 -710
的值放入函数中将 return nan
,而 -709
会像预期的那样给出接近于零的值。这是因为 exp(710)
太大而不能成为双数。
代码中的分支只是意味着避免了这种情况。
另请参阅 Stack Overflow 上的 this question and answer。
似乎使用它会更有效率:
if x < -709
sigmoid = 0.0
else
sigmoid = 1.0 / (1.0 + exp(-x))
除非您需要精度为 10^-309 的数字(见下文),否则这似乎太过分了!
>>> 1 / (1 + math.exp(709.78))
5.5777796105262746e-309
另一种方法是
python
np.where(x > 0, 1. / (1. + np.exp(-x)), np.exp(x) / (np.exp(x) + np.exp(0)))
因为 np.exp(x) / (np.exp(x) + np.exp(0))
等同于 1. / (1. + np.exp(-x))
但对于负值更稳定