web-dev-qa-db-fra.com

Traiter le débordement dans exp en utilisant numpy

En utilisant numpy, j'ai cette définition d'une fonction: 

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

Cette fonction est évaluée un très grand nombre de fois dans une routine d'optimisation. Il soulève souvent une exception:

RuntimeWarning: overflow encountered in exp

Je comprends que l’opérande ne peut pas être stocké dans l’espace alloué pour un float. Mais comment puis-je surmonter le problème?

21
octoback

Vous pouvez utiliser le package bigfloat. Il supporte les opérations à virgule flottante de précision arbitraire.

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

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

Utilisez-vous un cadre d’optimisation des fonctions? Ils implémentent généralement des limites de valeur (en utilisant des termes de pénalité). Essayez ça. Les valeurs pertinentes sont-elles vraiment extrêmes? En optimisation, il n’est pas rare de minimiser le journal (f). (probabilité approximative du journal, etc., etc.). Êtes-vous sûr de vouloir optimiser cette valeur exp et non pas vous connecter (exp (f)) == f. ?

Regardez ma réponse à cette question: fonctions logit et logit inverse pour les valeurs extrêmes

Btw, si tout ce que vous faites est de minimiser powellBadlyScaled (x, y) alors le minimum est à x -> + inf et y -> + inf, donc pas besoin de chiffres.

18
Johan Lundberg

Essayez Scipy -

scipy.special.expit(x).

2
markroxor

Vous pouvez utiliser numpy.seterr pour contrôler le comportement de numpy dans ce cas: http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html

Vous pouvez également utiliser le module warnings pour contrôler le mode d'affichage des avertissements: http://docs.python.org/library/warnings.html

2
Mike McKerns

Peut-être pourriez-vous améliorer votre algorithme en vérifiant dans quels domaines vous recevez des avertissements (certaines valeurs pour X [0], X [1] seront probablement sous-jacentes) et en remplaçant le résultat par un très grand nombre. Vous devez voir comment votre fonction se comporte, vous devriez par exemple vérifier, par exemple. exp (-x) + exp (-y) + x * y

1
ntg

En fonction de vos besoins spécifiques, il peut être utile de rogner l'argument d'entrée en exp(). Si vous voulez réellement obtenir une inf si elle déborde ou si vous voulez obtenir des nombres absurdement énormes, alors d'autres réponses seront plus appropriées.

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

Résultat:

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

Notez que powellBadlyScaled2 n'a pas dépassé/dépassé lorsque powellBadlyScaled d'origine l'a fait, mais la version modifiée donne 1.79769313486e+305 au lieu de inf dans l'un des tests. J'imagine qu'il y a beaucoup d'applications où 1.79769313486e+305 est pratiquement inf et ce serait très bien, voire préférable car 1.79769313486e+305 est un nombre réel et inf ne l'est pas.

0
EL_DON