phân chia numpy với RuntimeWarning: gặp phải giá trị không hợp lệ trong double_scalars


79

Tôi đã viết kịch bản sau:

import numpy

d = numpy.array([[1089, 1093]])
e = numpy.array([[1000, 4443]])
answer = numpy.exp(-3 * d)
answer1 = numpy.exp(-3 * e)
res = answer.sum()/answer1.sum()
print res

Nhưng tôi nhận được kết quả này và đã xảy ra lỗi:

nan
C:\Users\Desktop\test.py:16: RuntimeWarning: invalid value encountered in double_scalars
  res = answer.sum()/answer1.sum()

Có vẻ như phần tử đầu vào quá nhỏ nên python đã biến chúng thành số không, nhưng thực sự phép chia có kết quả.

Làm thế nào để giải quyết loại vấn đề này?

Câu trả lời:


83

Bạn không thể giải quyết nó. Đơn giản answer1.sum()==0, và bạn không thể thực hiện phép chia cho số không.

Điều này xảy ra vì answer1là cấp số nhân của 2 số âm, rất lớn, do đó kết quả được làm tròn thành 0.

nan được trả về trong trường hợp này vì phép chia cho số không.

Bây giờ để giải quyết vấn đề của bạn, bạn có thể:

  • đi tìm thư viện dành cho toán học có độ chính xác cao, như mpmath . Nhưng điều đó kém vui hơn.
  • như một sự thay thế cho một vũ khí lớn hơn, hãy thực hiện một số thao tác toán học, như chi tiết bên dưới.
  • sử dụng một scipy/numpychức năng phù hợp để thực hiện chính xác những gì bạn muốn! Kiểm tra câu trả lời của @Warren Weckesser.

Ở đây tôi giải thích cách thực hiện một số thao tác toán học hữu ích cho vấn đề này. Chúng tôi có điều đó cho tử số:

exp(-x)+exp(-y) = exp(log(exp(-x)+exp(-y)))
                = exp(log(exp(-x)*[1+exp(-y+x)]))
                = exp(log(exp(-x) + log(1+exp(-y+x)))
                = exp(-x + log(1+exp(-y+x)))

ở trên x=3* 1089y=3* 1093. Bây giờ, đối số của cấp số nhân này là

-x + log(1+exp(-y+x)) = -x + 6.1441934777474324e-06

Đối với mẫu số, bạn có thể tiến hành tương tự nhưng lấy giá trị đó log(1+exp(-z+k))đã được làm tròn thành 0, để đối số của hàm mũ ở mẫu số được làm tròn thành -z=-3000. Sau đó, bạn có kết quả của bạn là

exp(-x + log(1+exp(-y+x)))/exp(-z) = exp(-x+z+log(1+exp(-y+x)) 
                                   = exp(-266.99999385580668)

vốn đã rất gần với kết quả mà bạn sẽ nhận được nếu chỉ giữ lại 2 số hạng đứng đầu (tức là số đầu tiên 1089ở tử số và số đầu tiên 1000ở mẫu số):

exp(3*(1089-1000))=exp(-267)

Vì lợi ích của nó, chúng ta hãy xem chúng ta đang ở gần như thế nào với giải pháp của Wolfram alpha ( liên kết ):

Log[(exp[-3*1089]+exp[-3*1093])/([exp[-3*1000]+exp[-3*4443])] -> -266.999993855806522267194565420933791813296828742310997510523

Sự khác biệt giữa số này và số mũ ở trên là +1.7053025658242404e-13, vì vậy xấp xỉ chúng tôi thực hiện ở mẫu số là ổn.

Kết quả cuối cùng là

'exp(-266.99999385580668) = 1.1050349147204485e-116

Từ wolfram alpha là ( liên kết )

1.105034914720621496.. × 10^-116 # Wolfram alpha.

và một lần nữa, nó cũng an toàn để sử dụng numpy ở đây.


Nhưng trong trường hợp này tôi cần lấy giá trị của phép chia cho 2 giá trị rất nhỏ.
Heinz

@Heinz Tôi nghĩ bạn muốn nói đến trường hợp một số nhỏ bị chia cho một số nhỏ. Trong trường hợp đó, thay đổi thuật toán của bạn để tăng tỷ lệ cả hai con số sẽ tốt hơn nhiều so với việc tìm kiếm sự xoắn cơ học. Ví dụ: lấy logarit của các phương trình phân tích mà mã của bạn đang cố gắng mô phỏng. Có nhiều vấn đề với sự ổn định của tính toán khi có số lượng nhỏ. Tốt hơn là bạn nên tránh có bất kỳ thứ nào trong số chúng nếu có thể.
Mai

20

Bạn có thể sử dụng np.logaddexp(thực hiện ý tưởng trong câu trả lời của @ gg349):

In [33]: d = np.array([[1089, 1093]])

In [34]: e = np.array([[1000, 4443]])

In [35]: log_res = np.logaddexp(-3*d[0,0], -3*d[0,1]) - np.logaddexp(-3*e[0,0], -3*e[0,1])

In [36]: log_res
Out[36]: -266.99999385580668

In [37]: res = exp(log_res)

In [38]: res
Out[38]: 1.1050349147204485e-116

Hoặc bạn có thể sử dụng scipy.special.logsumexp:

In [52]: from scipy.special import logsumexp

In [53]: res = np.exp(logsumexp(-3*d) - logsumexp(-3*e))

In [54]: res
Out[54]: 1.1050349147204485e-116
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.