【问题标题】:Overflow Error in Python's numpy.exp functionPython 的 numpy.exp 函数中的溢出错误
【发布时间】:2017-04-05 05:18:27
【问题描述】:

我想像这样使用numpy.exp

cc = np.array([
    [0.120,0.34,-1234.1]
])

print 1/(1+np.exp(-cc))

但这给了我错误:

/usr/local/lib/python2.7/site-packages/ipykernel/__main__.py:5: RuntimeWarning: overflow encountered in exp

我不明白为什么?我怎样才能解决这个问题?看来问题出在第三个号码(-1234.1)

【问题讨论】:

标签: python numpy scipy


【解决方案1】:

numpy浮点数可以表示的最大值为1.7976931348623157e+308,其对数约为709.782,所以无法表示np.exp(1234.1)

In [1]: import numpy as np

In [2]: np.finfo('d').max
Out[2]: 1.7976931348623157e+308

In [3]: np.log(_)
Out[3]: 709.78271289338397

In [4]: np.exp(709)
Out[4]: 8.2184074615549724e+307

In [5]: np.exp(710)
/usr/local/bin/ipython:1: RuntimeWarning: overflow encountered in exp
  #!/usr/local/bin/python3.5
Out[5]: inf

【讨论】:

  • 顺便说一句,exp(1234.1) 等于 9.17952305220754e+535
【解决方案2】:

正如 fuglede 所说,这里的问题是 np.float64 无法处理像 exp(1234.1) 这样大的数字。尝试改用np.float128

>>> cc = np.array([[0.120,0.34,-1234.1]], dtype=np.float128)
>>> cc
array([[ 0.12,  0.34, -1234.1]], dtype=float128)
>>> 1 / (1 + np.exp(-cc))
array([[ 0.52996405,  0.58419052,  1.0893812e-536]], dtype=float128)

但是请注意,使用扩展精度有一些怪癖。它可能无法在 Windows 上运行;您实际上并没有获得完整的 128 位精度;每当数字通过纯 python 时,您可能会失去精度。您可以阅读更多详情here

对于大多数实际用途,您可以将1 / (1 + <a large number>) 近似为零。也就是说,忽略警告并继续前进。 Numpy 会为您处理近似值(使用 np.float64 时):

>>> 1 / (1 + np.exp(-cc))
/usr/local/bin/ipython3:1: RuntimeWarning: overflow encountered in exp
  #!/usr/local/bin/python3.4
array([[ 0.52996405,  0.58419052,  0.        ]])

如果您想取消警告,您可以使用 scipy.special.expit,正如 WarrenWeckesser 在对该问题的评论中所建议的那样:

>>> from scipy.special import expit
>>> expit(cc)
array([[ 0.52996405,  0.58419052,  0.        ]])

【讨论】:

    【解决方案3】:

    一个可能的解决方案是使用decimal 模块,它可以让您使用任意精度的浮点数。下面是一个使用 numpy 精度为 100 位的浮点数组的示例:

    import numpy as np
    import decimal
    
    # Precision to use
    decimal.getcontext().prec = 100
    
    # Original array
    cc = np.array(
        [0.120,0.34,-1234.1]
    )
    # Fails
    print(1/(1 + np.exp(-cc)))    
    
    # New array with the specified precision
    ccd = np.asarray([decimal.Decimal(el) for el in cc], dtype=object)
    # Works!
    print(1/(1 + np.exp(-ccd)))
    

    【讨论】:

    • 您在使用小数时会失去矢量化运算的优势吗?我猜想既然 numpy 必须把它当作一个对象,你可能会隐含地失去速度?
    • 您确实降低了很多速度。实际上,numpy 将整个计算委托给 Decimal 对象,您的 CPU 无法在这些对象上执行本机操作。对于大型阵列,时间差会很严重。
    【解决方案4】:

    exp(-1234.1) 对于 32 位或 64 位浮点数来说太小了。由于无法表示,因此 numpy 会产生正确的警告。

    使用IEEE 754 32bit floating-point数字,它可以表示的最小正数是2^(-149),大约是1e-45。

    如果你使用IEEE 754 64 bit floating-point 数字,最小的正数是2^(-1074),大约是 1e-327。

    在任何一种情况下,它都不能表示像 exp(-1234.1) 这样小的数字,大约是 1e-535。

    您应该使用 scipy 中的 expit 函数来计算 sigmoid 函数。这会给你更好的精度。

    出于实际目的,exp(-1234.1) 是一个非常小的数字。如果在您的用例中舍入为零有意义,则 numpy 会通过将其舍入为零来产生良性结果。

    【讨论】:

      【解决方案5】:

      如果不关心精度,可以使用numpy.clip

      float64:

      cc = np.clip(cc, -709.78, 709.78)
      

      float32:

      cc = np.clip(cc, -88.72, 88.72)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-04-13
        • 2019-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多