web-dev-qa-db-ja.com

numpyを使用してexpのオーバーフローに対処する

Numpyを使用して、関数のこの定義があります:

def powellBadlyScaled(X):
    f1 = 10**4 * X[0] * X[1] - 1
    f2 = numpy.exp(-numpy.float(X[0])) + numpy.exp(-numpy.float(X[1])) - 1.0001
    return f1 + f2

この関数は、最適化ルーチンで膨大な回数評価されます。多くの場合、例外が発生します。

RuntimeWarning: overflow encountered in exp

オペランドをフロート用に割り当てられたスペースに格納できないことを理解しています。しかし、どうすれば問題を克服できますか?

21
kiriloff

Bigfloatパッケージを使用できます。任意精度の浮動小数点演算をサポートしています。

http://packages.python.org/bigfloat/

import bigfloat
bigfloat.exp(5000,bigfloat.precision(100))
# -> BigFloat.exact('2.9676283840236670689662968052896e+2171', precision=100)

関数最適化フレームワークを使用していますか?通常、それらは値の境界を実装します(ペナルティ条件を使用)。やってみて関連する値は本当に極端ですか?最適化では、log(f)を最小化することは珍しくありません。 (おおよその対数尤度など)。 log(exp(f))== fではなく、そのexp値で最適化してもよろしいですか? ?

この質問に対する私の答えを見てください: 極値のロジットおよび逆ロジット関数

ところで、powellBadlyScaled(x、y)を最小化するだけであれば、最小値はx-> + infおよびy-> + infであるため、数値は不要です。

18
Johan Lundberg

Scipyをお試しください-

scipy.special.expit(x)

3
markroxor

numpy.seterrを使用して、この状況でのnumpyの動作を制御できます。 http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html

警告モジュールを使用して、警告の表示方法または表示方法を制御することもできます。 http://docs.python.org/library/warnings.html

2
Mike McKerns

警告を受け取る領域を確認して(おそらくX [0]、X [1]の特定の値を確認する)、結果を非常に大きな数値に置き換えることで、アルゴリズムを改善できます。あなたはあなたの関数がどのように振る舞うかを見る必要があります。 exp(-x)+ exp(-y)+ x * y

1
ntg

特定のニーズに応じて、exp()への入力引数をトリミングすると便利な場合があります。オーバーフローした場合にinfを実際に取得したい場合、またはとてつもなく膨大な数を取得したい場合は、他の回答がより適切です。

def powellBadlyScaled(X):
    f1 = 10**4 * X[0] * X[1] - 1
    f2 = numpy.exp(-numpy.float(X[0])) + numpy.exp(-numpy.float(X[1])) - 1.0001
    return f1 + f2


def powellBadlyScaled2(X):
    f1 = 10**4 * X[0] * X[1] - 1
    arg1 = -numpy.float(X[0])
    arg2 = -numpy.float(X[1])
    too_big = log(sys.float_info.max / 1000.0)  # The 1000.0 puts a margin in to avoid overflow later
    too_small = log(sys.float_info.min * 1000.0)
    arg1 = max([min([arg1, too_big]), too_small])
    arg2 = max([min([arg2, too_big]), too_small])
    # print('    too_small = {}, too_big = {}'.format(too_small, too_big))  # Uncomment if you're curious
    f2 = numpy.exp(arg1) + numpy.exp(arg2) - 1.0001
    return f1 + f2

print('\nTest against overflow: ------------')
x = [-1e5, 0]
print('powellBadlyScaled({}) = {}'.format(x, powellBadlyScaled(x)))
print('powellBadlyScaled2({}) = {}'.format(x, powellBadlyScaled2(x)))

print('\nTest against underflow: ------------')
x = [0, 1e20]
print('powellBadlyScaled({}) = {}'.format(x, powellBadlyScaled(x)))
print('powellBadlyScaled2({}) = {}'.format(x, powellBadlyScaled2(x)))

結果:

Test against overflow: ------------
*** overflow encountered in exp 
powellBadlyScaled([-100000.0, 0]) = inf
powellBadlyScaled2([-100000.0, 0]) = 1.79769313486e+305

Test against underflow: ------------
*** underflow encountered in exp    
powellBadlyScaled([0, 1e+20]) = -1.0001
powellBadlyScaled2([0, 1e+20]) = -1.0001

元のpowellBadlyScaledがしたときにpowellBadlyScaled2がオーバーフロー/アンダーフローしなかったことに注意してください。ただし、修正版では、テストの1つでinfの代わりに1.79769313486e+305を返します。 1.79769313486e+305が実質的にinfであり、1.79769313486e+305が実数でinfがそうではないため、これで問題ない、または好まれるアプリケーションがたくさんあると思います。

1
EL_DON