2013-01-11 22 views
9

जोड़ते समय पायथन/numpy में फ्लोट सटीक टूटना मुझे वास्तव में कम संख्या में numpy के साथ उपयोग की वजह से कुछ समस्याएं हैं। इस तथ्य के लिए संख्यात्मक एकीकरण के साथ मेरी निरंतर समस्याओं का पता लगाने में मुझे कई सप्ताह लग गए, जब मैं एक समारोह में फ्लोट जोड़ता हूं तो फ्लोट 64 परिशुद्धता गुम हो जाती है। एक योग के बजाय उत्पाद के साथ गणितीय रूप से समान गणना करना ठीक मूल्यों की ओर जाता है।संख्या

from matplotlib.pyplot import * 
from numpy import vectorize, arange 
import math 

def func_product(x): 
    return math.exp(-x)/(1+math.exp(x)) 

def func_sum(x): 
    return math.exp(-x)-1/(1+math.exp(x)) 

#mathematically, both functions are the same 

vecfunc_sum = vectorize(func_sum) 
vecfunc_product = vectorize(func_product) 

x = arange(0.,300.,1.) 
y_sum = vecfunc_sum(x) 
y_product = vecfunc_product(x) 

plot(x,y_sum, 'k.-', label='sum') 
plot(x,y_product,'r--',label='product') 

yscale('symlog', linthreshy=1E-256) 
legend(loc='lower right') 
show() 

enter image description here

आप देख सकते हैं, अभिव्यक्त किया मानों काफी कम शून्य चारों ओर बिखरे हुए हैं या बिल्कुल शून्य है, जबकि इस प्रकार हैं:

यहाँ एक कोड नमूना और परिणामों की एक साजिश है गुणात्मक मान ठीक हैं ...

कृपया, कोई मदद/समझा सकता है? आपका बहुत बहुत धन्यवाद!

+2

तो 'np.float64' पर्याप्त नहीं है, वहाँ है' एनपी .float128'। क्या यह एक ही समस्या है? – eumiro

+0

हाँ, float96 के लिए एक ही समस्या ... –

+0

सटीक समस्या से बचने के लिए लॉग डोमेन में इन रकम क्यों नहीं करते? http://lingpipe-blog.com/2012/02/16/howprevent-overflow-underflow-logistic-regression/ – jeff7

उत्तर

5

फ़्लोटिंग पॉइंट परिशुद्धता राउंडऑफ त्रुटि के कारण अतिरिक्त/घटाव के लिए बहुत संवेदनशील है। आखिरकार, 1+exp(x) इतना बड़ा हो जाता है कि एक्स (एक्स) में 1 जोड़ना एक्स (x) के समान चीज़ देता है। डबल परिशुद्धता कहीं exp(x) == 1e16 के आसपास है कि में:

>>> (1e16 + 1) == (1e16) 
True 
>>> (1e15 + 1) == (1e15) 
False 

ध्यान दें कि math.log(1e16) लगभग 37 है - जहां चीजें अपने भूखंड पर पागल हो जो मोटे तौर पर है।

आप एक ही समस्या है सकते हैं, लेकिन अलग-अलग पैमानों पर:

>>> (1e-16 + 1.) == (1.) 
True 
>>> (1e-15 + 1.) == (1.) 
False 

अपने शासन में अंक की एक विशाल बहुमत के लिए, अपने func_product वास्तव में गणना कर रहा है:

exp(-x)/exp(x) == exp(-2*x) 

यही वजह है कि है आपके ग्राफ में -2 की अच्छी ढलान है।

अन्य चरम करने के लिए इसे ले रहा है, तो आप अन्य संस्करण (कम से कम लगभग) गणना कर रहा है कर रहे हैं:

exp(-x) - 1./exp(x) 

जो लगभग है

exp(-x) - exp(-x) 
2

समस्या यह है कि आपके func_sumnumerically unstable क्योंकि है इसमें दो बहुत करीबी मूल्यों के बीच एक घटाव शामिल है।

func_sum(200) की गणना में, उदाहरण के लिए, math.exp(-200) और 1/(1+math.exp(200)) एक ही मूल्य,, क्योंकि math.exp(200) करने के लिए 1 जोड़ने कोई प्रभाव नहीं है, क्योंकि यह 64-बिट चल बिन्दु की शुद्धता के बाहर है:

math.exp(200).hex() 
0x1.73f60ea79f5b9p+288 

(math.exp(200) + 1).hex() 
0x1.73f60ea79f5b9p+288 

(1/(math.exp(200) + 1)).hex() 
0x1.6061812054cfap-289 

math.exp(-200).hex() 
0x1.6061812054cfap-289 

यह बताता है कि क्यों func_sum(200) शून्य देता है, लेकिन एक्स अक्ष से जुड़े बिंदुओं के बारे में क्या? ये फ्लोटिंग प्वाइंट अपर्याप्तता के कारण भी होते हैं; यह कभी-कभी होता है कि math.exp(-x)1/math.exp(x) के बराबर नहीं है; आदर्श, math.exp(x)e^x पर निकटतम फ़्लोटिंग-पॉइंट मान है, और 1/math.exp(x)math.exp(x) द्वारा गणना की गई फ्लोटिंग-पॉइंट संख्या के पारस्परिक रूप से निकटतम फ़्लोटिंग-पॉइंट मान है, जो आवश्यक नहीं है e^-x।दरअसल, math.exp(-100) और 1/(1+math.exp(100)) बहुत करीब और वास्तव में केवल अंतिम इकाई में मतभेद हैं:

math.exp(-100).hex() 
0x1.a8c1f14e2af5dp-145 

(1/math.exp(100)).hex() 
0x1.a8c1f14e2af5cp-145 

(1/(1+math.exp(100))).hex() 
0x1.a8c1f14e2af5cp-145 

func_sum(100).hex() 
0x1.0000000000000p-197 

तो क्या आप वास्तव में गणना की है अंतर है, यदि कोई हो, है math.exp(-x) और 1/math.exp(x) के बीच। आप फ़ंक्शन की रेखा का पता लगाने के लिए देख सकते हैं कि यह func_sum के सकारात्मक मानों से गुज़रता है (याद रखें कि 52 64-बिट फ़्लोटिंग पॉइंट में महत्व का आकार है)।

4

यह catastrophic cancellation का एक उदाहरण है। पहले बिंदु पर

आइए नज़र जहां गणना धराशायी हो जाता है, जब x = 36.0

In [42]: np.exp(-x) 
Out[42]: 2.3195228302435691e-16 

In [43]: - 1/(1+np.exp(x)) 
Out[43]: -2.3195228302435691e-16 

In [44]: np.exp(-x) - 1/(1+np.exp(x)) 
Out[44]: 0.0 

गणना func_product का उपयोग कर लगभग बराबर संख्या घटाना नहीं है, तो यह भयावह रद्द बचा जाता है।


वैसे, अगर आप math.expnp.exp को बदलने के लिए, आप np.vectorize से छुटकारा पा सकते हैं (जो धीमी है):

def func_product(x): 
    return np.exp(-x)/(1+np.exp(x)) 

def func_sum(x): 
    return np.exp(-x)-1/(1+np.exp(x)) 

y_sum = func_sum_sum(x) 
y_product = func_product_product(x)