Сделка с переполнением в 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. Но как мне преодолеть эту проблему?

5 ответов


вы можете использовать пакет bigfloat. Он поддерживает произвольные операции с плавающей запятой точности.

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

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

вы используете фреймворк оптимизации функций? Обычно они реализуют границы значений (используя штрафные термины). Попробовать это. Соответствующие значения действительно экстрим? В оптимизации не редкость минимизировать log (f). (приблизительное правдоподобие журнала etc etc). Вы уверены, что хотите оптимизировать это значение exp, а не log(exp (f)) == f. ?

взгляните на мой ответ на этот вопрос: logit и обратные функции logit для экстремальных значений

кстати, если все,что вы делаете, это минимизировать powellBadlyScaled(x, y), то минимум находится в x -> + inf и y -> + inf, поэтому нет необходимости в цифрах.


можно использовать numpy.seterr для управления включает в себя ведет себя в этой ситуации: http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html

вы также можете использовать модуль предупреждения для управления тем, как предупреждения представлены или не представлены:http://docs.python.org/library/warnings.html


возможно, вы можете улучшить свой алгоритм, проверив, для каких областей вы получаете предупреждения (он,вероятно, будет ниже определенных значений для X[ 0], X[ 1]) и заменив результат действительно большим числом. Вам нужно увидеть, как ведет себя ваша функция, я вещь, которую вы должны проверить, например exp(-x)+exp (- y)+x*y


в зависимости от ваших конкретных потребностей может быть полезно обрезать входной аргумент до 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 didn't over / underflow, когда оригинал powellBadlyScaled сделал, но модифицированная версия дает 1.79769313486e+305 вместо inf в одном из тестов. Я думаю, их много. приложений, где 1.79769313486e+305 практически inf и это было бы хорошо, или даже предпочтительнее, потому что 1.79769313486e+305 - действительное число и inf нет.


попробовать составляющей по -

scipy.special.expit(x).