Сделка с переполнением в exp с использованием numpy
Используя 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
Я понимаю, что операнд не может быть сохранен в выделенном пространстве для float. Но как я могу решить эту проблему?
Ответы
Ответ 1
Вы можете использовать пакет bigfloat, поддерживающий операции с плавающей запятой произвольной точности.
http://packages.python.org/bigfloat/
import bigfloat
bigfloat.exp(5000,bigfloat.precision(100))
# -> BigFloat.exact('2.9676283840236670689662968052896e+2171', precision=100)
Используете ли вы инфраструктуру оптимизации функций? Обычно они используют границы ценности (используя штрафные условия). Попробуй это. Действительно ли релевантные значения действительно экстремальны? В оптимизации это не редкость для минимизации log (f). (приблизительный лог-правдоподобие и т.д.). Вы уверены, что хотите оптимизировать это значение exp, а не log (exp (f)) == f.
Взгляните на мой ответ на этот вопрос: логит и обратные функции логита для экстремальных значений
Btw, если все, что вы делаете, сводит к минимуму powellBadlyScaled (x, y), то минимум находится в x → + inf и y → + inf, поэтому нет необходимости в численных вычислениях.
Ответ 2
Вы можете использовать numpy.seterr
для управления тем, как numpy ведет себя в этом случае: http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html
Вы также можете использовать модуль предупреждений для управления тем, как предупреждения или не представлены: http://docs.python.org/library/warnings.html
Ответ 3
Возможно, вы можете улучшить свой алгоритм, проверив, в каких областях вы получите предупреждения (это, вероятно, вызовет определенные значения для X [0], X [1]) и заменит результат на действительно большое число. Вам нужно посмотреть, как ведет себя ваша функция, я должен проверить, например. exp (-x) + exp (-y) + x * y
Ответ 4
В зависимости от ваших конкретных потребностей может оказаться полезным обрезать входной аргумент 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
Обратите внимание, что powellBadlyScaled2
не выполнял/переполнения, если оригинальный powellBadlyScaled
сделал, но измененная версия дает 1.79769313486e+305
вместо inf
в одном из тестов. Я полагаю, что существует множество приложений, где 1.79769313486e+305
практически inf
, и это было бы хорошо или даже предпочтительнее, потому что 1.79769313486e+305
- это реальное число, а inf
- нет.