Kết quả nhỏ, không thể đoán trước khi chạy mô hình xác định


10

Tôi có một mô hình khá lớn (~ 5000 dòng) được viết bằng C. Đây là một chương trình nối tiếp, không có tạo số ngẫu nhiên ở bất cứ đâu. Nó sử dụng thư viện FFTW cho các chức năng bằng cách sử dụng FFT - Tôi không biết chi tiết về việc triển khai FFTW, nhưng tôi cho rằng các chức năng trong đó cũng có tính xác định (sửa tôi nếu tôi mắc lỗi).

Vấn đề tôi không thể hiểu là tôi nhận được sự khác biệt nhỏ trong kết quả cho các lần chạy giống hệt nhau trên cùng một máy (cùng trình biên dịch, cùng thư viện).

Tôi sử dụng các biến có độ chính xác kép và để đưa ra kết quả trong biến valuechẳng hạn, tôi đưa ra: fprintf(outFID, "%.15e\n", value);hoặc
fwrite(&value, 1, sizeof(double), outFID);

Và tôi sẽ liên tục nhận được sự khác biệt, chẳng hạn như:
2.07843469652206 4 e-16 so với 2.07843469652206 3 e-16

Tôi đã dành nhiều thời gian để cố gắng tìm hiểu tại sao điều này là. Ban đầu tôi nghĩ rằng một trong những chip bộ nhớ của tôi đã bị hỏng và tôi đã đặt hàng và thay thế chúng, nhưng không có kết quả. Sau đó tôi cũng đã thử chạy mã của mình trên máy Linux của đồng nghiệp và tôi nhận được sự khác biệt có cùng bản chất.

Điều gì có thể gây ra điều này? Bây giờ nó chỉ là một vấn đề nhỏ, nhưng tôi tự hỏi liệu nó có phải là "phần nổi của tảng băng chìm" (của một vấn đề nghiêm trọng).

Tôi nghĩ rằng tôi sẽ đăng ở đây thay vì StackOverflow trong trường hợp ai đó làm việc với các mô hình số có thể gặp phải vấn đề này. Nếu bất cứ ai có thể làm sáng tỏ điều này, tôi sẽ có nhiều nghĩa vụ.

Theo dõi các bình luận:
Christian Clason và Vikram: đầu tiên, cảm ơn bạn đã quan tâm đến câu hỏi của tôi. Các bài viết bạn liên kết để đề xuất rằng: 1. lỗi làm tròn giới hạn độ chính xác và 2. mã khác nhau (chẳng hạn như đưa ra các câu lệnh in dường như vô hại) có thể ảnh hưởng đến kết quả lên tới epsilon của máy. Tôi nên làm rõ rằng tôi không so sánh các hiệu ứng fwritefprintfchức năng. Tôi đang sử dụng cái này HOẶC cái kia. Cụ thể, cùng một tệp thực thi được sử dụng cho cả hai lần chạy. Tôi chỉ đơn giản là nêu vấn đề xảy ra cho dù tôi sử dụng fprintfHOẶC fwrite.

Vì vậy, đường dẫn mã (và thực thi) là như nhau, và phần cứng là như nhau. Với tất cả các yếu tố bên ngoài được giữ cố định, sự ngẫu nhiên đến từ đâu, về cơ bản? Tôi nghi ngờ việc lật bit xảy ra do bộ nhớ bị lỗi không giữ lại một chút chính xác, đó là lý do tại sao tôi thay thế chip bộ nhớ, nhưng dường như đó không phải là vấn đề ở đây, tôi đã xác minh và bạn chỉ ra. Chương trình của tôi đưa ra hàng ngàn số chính xác kép này trong một lần chạy và luôn có một số ngẫu nhiên có các bit lật ngẫu nhiên.

Theo dõi nhận xét đầu tiên của Christian Clason: Tại sao giống với 0 trong độ chính xác của máy? Số dương nhỏ nhất cho một đôi là 2,22e-308, vậy không nên bằng 0? Chương trình của tôi tạo ra hàng ngàn giá trị trong phạm vi 10 ^ -16 (từ 1e-15 đến 8e-17) và chúng tôi đã thấy các biến thể có ý nghĩa trong dự án nghiên cứu của chúng tôi, vì vậy tôi hy vọng chúng tôi không nhìn vào vô nghĩa số.21016

Phần tiếp theo # 2 :
Đây là một âm mưu của đầu ra chuỗi thời gian theo mô hình, để hỗ trợ cho các cuộc thảo luận ngoài luồng trong các bình luận. nhập mô tả hình ảnh ở đây


Chào mừng bạn đến với SciComp.SE! Bạn có biết rằng các số dấu phẩy động có độ chính xác hạn chế - cụ thể là ở độ chính xác kép, bằng 0 với độ chính xác của máy không? Vì vậy, sự khác biệt mà bạn báo cáo không thực sự có ý nghĩa và có thể là do sự khác biệt nhỏ trong việc triển khai hai chức năng mà bạn đặt tên dẫn đến mã máy hơi khác nhau. 21016
Christian Clason

Bạn đang hỏi tại sao máy của bạn không chính xác hơn độ chính xác của máy. vi.wikipedia.org/wiki/Machine_epsilon
Vikram

1
Xem inf.ethz.ch/personal/gander/Heisenberg/apers.html để biết ví dụ liên quan về ảnh hưởng tinh tế của các đường dẫn mã đến số học dấu phẩy động. Và, tất nhiên, ece.uwaterloo.ca/~dwharder/NumericalAnalysis/02Numerics/Double/ Kẻ
Christian Clason

1
Cũng có thể là tỷ lệ của vấn đề của bạn sao cho câu trả lời đúng theo thứ tự . Trong mọi trường hợp, bạn nên được đồng nhất với độ chính xác tương đối của các giải pháp của bạn. 1016
Brian Borchers

2
@ boxofchalk1 Chúng chắc chắn không giống như tiếng ồn; như Brian đã nói, nếu tất cả dữ liệu của bạn có độ lớn như vậy, bạn có thể ổn (một lần nữa, đó là về độ chính xác tương đối ). Để đảm bảo, bạn có thể giải quyết vấn đề của mình theo thứ tự hoặc chạy lại mã của bạn với độ chính xác mở rộng (xem fftw.org/doc/Precision.html ). 1
Christian Clason

Câu trả lời:


9

Có những khía cạnh của các hệ thống máy tính hiện đại vốn không mang tính quyết định có thể gây ra những loại khác biệt này. Miễn là sự khác biệt là rất nhỏ so với độ chính xác cần thiết của các giải pháp của bạn, có lẽ không có lý do gì để lo lắng về điều này.

Một ví dụ về những gì có thể đi sai dựa trên kinh nghiệm của riêng tôi. Hãy xem xét vấn đề tính toán tích của hai vectơ x và y.

d=i=1nxiyi

Việc tính toán sản phẩm chấm này yêu cầu tính toán các sản phẩm và sau đó thêm kết quả. Phép nhân dấu phẩy động sẽ tạo ra kết quả chính xác giống nhau mỗi lần. Nếu các bổ sung dấu phẩy động được tính theo cùng một thứ tự mỗi lần thì tổng sẽ giống nhau. Tuy nhiên, vì phép cộng dấu phẩy động không liên kết, bạn có thể nhận được các kết quả khác nhau nếu tích của hai vectơ được tính theo cách các phép cộng được thực hiện theo thứ tự khác nhau. xiyi

Ví dụ: trước tiên bạn có thể tính tích của hai vectơ là

d=((x1y1)+(x2y2))+(x3y3)

và sau đó là

d=(x1y1)+((x2y2)+(x3y3)) .

Làm thế nào điều này có thể xảy ra? Đây là hai khả năng.

  1. Tính toán đa luồng trên lõi song song. Các máy tính hiện đại thường có 2, 4, 8 hoặc thậm chí nhiều lõi xử lý hơn có thể hoạt động song song. Nếu mã của bạn đang sử dụng các luồng song song để tính toán một sản phẩm chấm trên nhiều bộ xử lý, thì bất kỳ sự nhiễu loạn ngẫu nhiên nào của hệ thống (ví dụ: người dùng di chuyển chuột và một trong các lõi của bộ xử lý phải xử lý chuyển động của chuột trước khi quay lại sản phẩm chấm) dẫn đến một sự thay đổi trong thứ tự bổ sung.

  2. Sắp xếp dữ liệu và hướng dẫn vector. Các bộ xử lý Intel hiện đại có một bộ hướng dẫn đặc biệt có thể hoạt động (ví dụ) cho các số dấu phẩy động tại một thời điểm. Các hướng dẫn vectơ này hoạt động tốt nhất nếu dữ liệu được căn chỉnh trên ranh giới 16 byte. Thông thường, một vòng lặp sản phẩm chấm sẽ chia dữ liệu thành các phần 16 byte (4 lần thả nổi một lần.) Nếu bạn chạy lại mã lần thứ hai, dữ liệu có thể được căn chỉnh khác với các khối bộ nhớ 16 byte để bổ sung thực hiện theo một thứ tự khác nhau, dẫn đến một câu trả lời khác nhau.

Bạn có thể giải quyết điểm 1 bằng cách làm cho mã của bạn chạy dưới dạng một luồng và vô hiệu hóa tất cả xử lý song song. Bạn có thể giải quyết điểm 2 bằng cách yêu cầu cấp phát bộ nhớ để căn chỉnh các khối bộ nhớ (chính xác là bạn sẽ làm điều này bằng cách biên dịch mã bằng một công tắc như -align.) Nếu mã của bạn vẫn cho kết quả khác nhau thì sẽ có những khả năng khác để xem tại.

Tài liệu này của Intel thảo luận về các vấn đề có thể dẫn đến không thể lặp lại kết quả với Thư viện hạt nhân toán học Intel. Một tài liệu khác của Intel thảo luận về các trình chuyển đổi trình biên dịch sẽ sử dụng với trình biên dịch của Intel.


Tôi thấy rằng bạn nghĩ rằng mã của bạn đang chạy một luồng. Mặc dù bạn có thể biết rõ mã của mình, tôi sẽ không ngạc nhiên nếu bạn gọi chương trình con (ví dụ: các thường trình BLAS) chạy đa luồng. Bạn nên kiểm tra để xem chính xác những thư viện bạn đang sử dụng. Bạn cũng có thể sử dụng các công cụ giám sát hệ thống để xem mức độ sử dụng CPU của mình.
Brian Borchers

1
hoặc, như đã nêu, thư viện FFTW ...
Christian Clason

@BrianBorchers, cảm ơn bạn. Ví dụ về tính ngẫu nhiên đến từ bản chất không liên kết của phép cộng dấu phẩy động là khai sáng. Christian Clason đưa ra một vấn đề thứ yếu về việc liệu đầu ra mô hình của tôi có ý nghĩa hay không, với độ lớn của các con số - đó có thể là một vấn đề lớn nếu anh ấy đúng (và tôi hiểu chính xác anh ấy), vì vậy bây giờ tôi đang xem xét điều đó.
boxofchalk1

2

Thư viện FFTW được đề cập có thể chạy ở chế độ không xác định.

Nếu bạn đang sử dụng chế độ FFTW_MEASURE hoặc FFTW_PATIENT, các chương trình sẽ kiểm tra khi chạy, giá trị tham số nào hoạt động nhanh nhất và sau đó sẽ sử dụng các tham số đó trong toàn bộ chương trình. Bởi vì thời gian chạy rõ ràng sẽ dao động một chút, các tham số sẽ khác nhau và kết quả của các biến đổi Fourier sẽ không xác định. Nếu bạn muốn FFTW xác định, hãy sử dụng chế độ FFTW_ESTIMATE.


1

Mặc dù đúng là các thay đổi thứ tự đánh giá thuật ngữ biểu thức rất có thể xảy ra do các kịch bản xử lý đa lõi / đa luồng, đừng quên rằng có thể có (mặc dù đó là một cú đánh dài) một số lỗi thiết kế phần cứng đang hoạt động. Ghi nhớ vấn đề Pentium FDIV? (Xem https://en.wikipedia.org/wiki/Pentium_FDIV_orms ). Cách đây một thời gian, tôi đã làm việc trên phần mềm mô phỏng mạch tương tự dựa trên pc. Một phần của phương pháp của chúng tôi liên quan đến việc phát triển các bộ kiểm thử hồi quy, mà chúng tôi sẽ chạy với các bản dựng phần mềm hàng đêm. Với nhiều mô hình chúng tôi đã phát triển, các phương pháp lặp (ví dụ: Newton-Raphson ( https://en.wikipedia.org/wiki/Newton%27s_method) và Runge-Kutta) đã được sử dụng rộng rãi trong các thuật toán mô phỏng. Với các thiết bị tương tự, thường xảy ra trường hợp các tạo tác bên trong, chẳng hạn như điện áp, dòng điện, v.v., xảy ra có các giá trị số cực kỳ nhỏ. Các giá trị này, như là một phần của quá trình mô phỏng, được thay đổi tăng dần theo thời gian (mô phỏng). Tầm quan trọng của những thay đổi này có thể rất nhỏ và điều chúng ta thường quan sát là các hoạt động tiếp theo của FPU trên các giá trị delta như vậy giáp với ngưỡng "nhiễu" của độ chính xác của FPU (64 bit nổi có mantissa 53 bit, IIRC). Điều đó, cùng với thực tế là chúng tôi thường phải đưa mã đăng nhập "PrintF" vào các mô hình để cho phép gỡ lỗi (ah, ngày tốt của ol!), Trên thực tế hàng ngày được đảm bảo kết quả lẻ tẻ! Vậy thì sao' Tất cả điều này có nghĩa là gì? Bạn phải mong đợi để thấy sự khác biệt trong những trường hợp như vậy, và điều tốt nhất cần làm là xác định và thực hiện một cách để quyết định (cường độ, tần suất, xu hướng, v.v.) khi / làm thế nào để bỏ qua chúng.


Cảm ơn bạn, Jim cho cái nhìn sâu sắc. Bất kỳ ý tưởng về những hiện tượng cơ bản sẽ gây ra "hiện vật nội bộ" như vậy? Tôi nghĩ nhiễu điện từ có thể là một, nhưng sau đó các bit quan trọng cũng sẽ bị ảnh hưởng, phải không?
boxofchalk1

1

Mặc dù làm tròn điểm nổi từ các hoạt động không đồng bộ có thể là vấn đề, tôi nghi ngờ rằng đó là một thứ gì đó tầm thường hơn. Việc sử dụng biến chưa được khởi tạo là thêm tính ngẫu nhiên vào mã xác định khác của bạn. Đây là một vấn đề phổ biến thường bị các nhà phát triển bỏ qua vì khi bạn chạy trong chế độ gỡ lỗi, tất cả các biến được khởi tạo thành 0 khi khai báo. Khi không chạy trong chế độ gỡ lỗi, bộ nhớ được gán cho một biến có bất kỳ giá trị nào mà bộ nhớ có trước khi gán. Bộ nhớ không bị xóa trên nhiệm vụ như là một tối ưu hóa. Nếu điều này xảy ra trong mã của bạn, nó sẽ dễ dàng sửa chữa, ít hơn trong mã thư viện.

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.