2012-03-04 15 views
17

Korzystanie numpy, mam tę definicję funkcji:Deal z przelewem w exp za pomocą 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 

Funkcja ta jest oceniana ogromną liczbę razy na rutyny optymalizacji. Często powoduje to wyjątek:

RuntimeWarning: overflow encountered in exp 

Rozumiem, że operand nie może być przechowywany w przydzielonym miejscu na float. Ale jak mogę przezwyciężyć ten problem?

+2

Musisz dostosować swój algorytm. Jeśli wartość nie pasuje, nie pasuje. Znajdź inny sposób wyrażenia obliczeń, które się nie przelewają. –

+1

Jedyną rozsądną rzeczą, którą możesz zrobić, to spojrzeć na asymptotyczne zachowanie swojej funkcji. Jeśli jest to rozsądne, to powyżej pewnego progu można zastąpić wyraźne obliczenie wartością asymptotyczną. Jeśli wartość asymptotyczna nie jest sensowna, problem jest najprawdopodobniej związany z wyborem algorytmu, a nie z kodem. – DaveP

+1

DaveP, asymptotyczne zachowanie exp jest exp ... –

Odpowiedz

16

Możesz użyć pakietu bigfloat. Obsługuje arbitralne operacje zmiennoprzecinkowe.

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

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

Używasz ramy optymalizacji funkcji? Zwykle realizują granice wartości (stosując warunki kary). Spróbuj tego. Czy odpowiednie wartości są naprawdę tak ekstremalne? W optymalizacji często zdarza się minimalizować log (f). (przybliżone prawdopodobieństwo logu etcetera itp.). Czy na pewno chcesz zoptymalizować na tej wartości exp i nie logować (exp (f)) == f. ?

Wystarczy popatrzeć na moją odpowiedź na to pytanie: logit and inverse logit functions for extreme values

Btw, jeśli wszystko co robisz jest zminimalizowanie powellBadlyScaled (x, y), to minimalna wynosi x -> + INF oraz y -> + inf, więc nie ma potrzeby numerycznych.

+1

Dokładne, w kontekście optymalizacji, a jeśli chodzi o funkcję Powell Badly Scaled, używam do testowania, nakładam pewne ograniczenia ramek. Skrypt, który pierwszy raz przepuściłem, wziął pod uwagę ograniczenia do zainicjowania (niektóre próbkowania w autoryzowanym pudełku), ale nie w głównej procedurze (nie sprawdzałem już ograniczeń ramek). Biorąc pod uwagę ograniczenia, operand nie przepełnia. I tak spróbuję bigfloat coś później. Dzięki! – octoback

1

Być może możesz ulepszyć swój algorytm, sprawdzając, które obszary otrzymujesz ostrzeżenia (prawdopodobnie będzie to oznaczać pewne wartości dla X [0], X [1]) i zastąpić wynik naprawdę dużą liczbą. Musisz sprawdzić, jak zachowuje się Twoja funkcja. Powinienem sprawdzić np. exp(-x)+exp(-y)+x*y

0

zależności od konkretnych potrzeb , może być użyteczne przycięcie argumentu wejściowego do exp(). Jeśli rzeczywiście chcesz uzyskać inf, jeśli przepełniasz lub chcesz uzyskać absurdalnie duże liczby, wtedy inne odpowiedzi będą bardziej odpowiednie.

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))) 

Wynik:

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 

Uwaga, powellBadlyScaled2 nie powyżej/niedomiar gdy oryginalny powellBadlyScaled zrobił, ale zmodyfikowana wersja daje 1.79769313486e+305 zamiast inf w jednym z testów. Wyobrażam sobie, że istnieje wiele aplikacji, w których 1.79769313486e+305 jest praktycznie inf i byłoby to w porządku, a nawet preferowane, ponieważ 1.79769313486e+305 jest liczbą rzeczywistą, a inf nie.