web-dev-qa-db-ja.com

RuntimeWarningでのnumpy除算:double_scalarsで無効な値が見つかりました

次のスクリプトを書きました。

import numpy

d = numpy.array([[1089, 1093]])
e = numpy.array([[1000, 4443]])
answer = numpy.exp(-3 * d)
answer1 = numpy.exp(-3 * e)
res = answer.sum()/answer1.sum()
print res

しかし、私はこの結果を得て、エラーが発生しました:

nan
C:\Users\Desktop\test.py:16: RuntimeWarning: invalid value encountered in double_scalars
  res = answer.sum()/answer1.sum()

入力要素が小さすぎてpythonがゼロになったようですが、実際には除算の結果があります。

この種の問題を解決するには?

57
Heinz

解決できません。単にanswer1.sum()==0で、ゼロによる除算を実行できません。

これは、answer1が2つの非常に大きな負の数の指数であり、結果がゼロに丸められるために発生します。

この場合、ゼロ除算のためnanが返されます。

問題を解決するために、次のことができます。

  • mpmath のような高精度数学用のライブラリを探してください。しかし、それは面白くありません。
  • より大きな武器の代替として、以下で詳述するように、いくつかの数学操作を行います。
  • 目的にぴったり合ったscipy/numpy関数を探してください! @Warren Weckesserの回答をご覧ください。

ここでは、この問題に役立つ数学操作の方法を説明します。分子については次のとおりです。

exp(-x)+exp(-y) = exp(log(exp(-x)+exp(-y)))
                = exp(log(exp(-x)*[1+exp(-y+x)]))
                = exp(log(exp(-x) + log(1+exp(-y+x)))
                = exp(-x + log(1+exp(-y+x)))

上記のx=3* 1089およびy=3* 1093。さて、この指数関数の引数は

-x + log(1+exp(-y+x)) = -x + 6.1441934777474324e-06

分母についても同様に進めることができますが、log(1+exp(-z+k))はすでに0に丸められているので、分母の指数関数の引数は単に-z=-3000に丸められます。その後、結果が

exp(-x + log(1+exp(-y+x)))/exp(-z) = exp(-x+z+log(1+exp(-y+x)) 
                                   = exp(-266.99999385580668)

これは、2つの先行する用語(つまり、分子の最初の数1089と分母の最初の数1000)のみを保持する場合に得られる結果にすでに非常に近いものです。

exp(3*(1089-1000))=exp(-267)

そのために、Wolfram alphaの解からどれだけ近いか見てみましょう( link ):

Log[(exp[-3*1089]+exp[-3*1093])/([exp[-3*1000]+exp[-3*4443])] -> -266.999993855806522267194565420933791813296828742310997510523

この数と上記の指数の差は+1.7053025658242404e-13であるため、分母で行った近似は問題ありませんでした。

最終結果は

'exp(-266.99999385580668) = 1.1050349147204485e-116

Wolfram alphaから( link

1.105034914720621496.. × 10^-116 # Wolfram alpha.

また、ここでもnumpyを使用しても安全です。

60
gg349

np.logaddexp を使用できます(これは@ gg349の答えのアイデアを実装しています)。

In [33]: d = np.array([[1089, 1093]])

In [34]: e = np.array([[1000, 4443]])

In [35]: log_res = np.logaddexp(-3*d[0,0], -3*d[0,1]) - np.logaddexp(-3*e[0,0], -3*e[0,1])

In [36]: log_res
Out[36]: -266.99999385580668

In [37]: res = exp(log_res)

In [38]: res
Out[38]: 1.1050349147204485e-116

または、 scipy.misc.logsumexp を使用できます。

In [52]: from scipy.misc import logsumexp

In [53]: res = np.exp(logsumexp(-3*d) - logsumexp(-3*e))

In [54]: res
Out[54]: 1.1050349147204485e-116
13