逻辑/sigmoid函数实现数值精度
Posted
技术标签:
【中文标题】逻辑/sigmoid函数实现数值精度【英文标题】:logistic / sigmoid function implementation numerical precision 【发布时间】:2016-09-01 15:44:54 【问题描述】:在scipy.special.expit
中,逻辑函数的实现如下:
if x < 0
a = exp(x)
a / (1 + a)
else
1 / (1 + exp(-x))
但是,我已经看到了其他语言/框架的实现
1 / (1 + exp(-x))
我想知道 scipy 版本实际上带来了多少好处。
对于非常小的x
,结果接近于0。即使exp(-x)
溢出到Inf
,它仍然有效。
【问题讨论】:
似乎给出的答案都没有真正解决这个问题。1 / (1 + exp(-x))
准确与否?
【参考方案1】:
这实际上只是为了稳定 - 输入数量级非常大的值可能会返回意想不到的结果。
如果expit
与1 / (1 + exp(-x))
一样实现,那么将-710
的值放入函数将返回nan
,而-709
将给出接近于零的值。这是因为exp(710)
太大而不能成为双倍。
代码中的分支只是意味着避免了这种情况。
另请参阅 Stack Overflow 上的 this question and answer。
【讨论】:
我不确定我是否遵循您的观点...稳定性被认为是this GitHub thread 中代码的动机。 我想说的是,1 / (1 + exp(-710))
不返回 NaN
,它在 IEEE 标准中返回 0 作为 1 / Inf = 0
。在 numpy 1 / (1 + np.exp(-710)) == 0
中也是如此
但问题肯定出在expit(x)
上,其中x
是一个较大的负值。比如expit(-710)
需要计算正数710的指数,即exp(+710)
。我认为源代码中的exp
指的是您系统的C 数学库exp
(不是ufunc np.exp
),如果检测到溢出,它将引发错误,就像这里的情况一样.【参考方案2】:
似乎使用起来会更有效率:
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
【讨论】:
没有必要,因为exp(710)
计算为无穷大,1 / (1 + inf) = 0
计算为浮点数【参考方案3】:
另一种方法是
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))
,但对于负值更稳定
【讨论】:
以上是关于逻辑/sigmoid函数实现数值精度的主要内容,如果未能解决你的问题,请参考以下文章