Разобраться с переполнением в 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

Я понимаю, что операнд не может храниться в выделенном пространстве для числа с плавающей запятой. Но как я могу победить проблему?


person kiriloff    schedule 04.03.2012    source источник
comment
Вам нужно будет адаптировать свой алгоритм. Если значение не подходит, оно не подходит. Найдите другой способ выражения вычисления, который не вызывает переполнения.   -  person David Heffernan    schedule 05.03.2012
comment
Единственное разумное, что вы можете сделать, это посмотреть на асимптотическое поведение вашей функции. Если это разумно, то выше некоторого порога вы можете заменить явное вычисление асимптотическим значением. Если асимптотическое значение не имеет смысла, проблема, скорее всего, в вашем выборе алгоритма, а не в коде.   -  person DaveP    schedule 05.03.2012
comment
DaveP, асимптотическое поведение exp равно exp...   -  person Johan Lundberg    schedule 05.03.2012


Ответы (6)


Вы можете использовать пакет 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. ?

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

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

person Johan Lundberg    schedule 04.03.2012
comment
Точнее, в контексте оптимизации и поскольку функция Powell Badly Scaled используется для тестирования, я накладываю некоторые ограничения. Сценарий, который я впервые запустил с переполнением, учитывал ограничения для инициализации (некоторая выборка в авторизованном блоке), но не в основной процедуре (я больше не проверял ограничения блока). Принимая во внимание ограничения, операнд не переполняется. Я все равно попробую bigfloat как-нибудь позже. Спасибо! - person kiriloff; 05.03.2012

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

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

person Mike McKerns    schedule 15.07.2014

Попробуйте scipy's -

scipy.special.expit(x).

person markroxor    schedule 15.05.2018

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

person ntg    schedule 18.06.2014

В зависимости от ваших конкретных потребностей может быть полезно обрезать входной аргумент до 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 — нет.

person EL_DON    schedule 12.01.2018

У меня такая же проблема. Если точность можно в какой-то степени игнорировать, используйте np.round(my_float_array, decimals=<a smaller number>), чтобы обойти это предупреждение времени выполнения.

person Jalal    schedule 01.11.2019