Algoritma konvolusi cepat & akurat (seperti FFT) untuk rentang dinamis tinggi?

8

Tampaknya konvolusi berbasis FFT menderita resolusi floating-point terbatas karena mengevaluasi segala sesuatu di sekitar akar persatuan, seperti yang dapat Anda lihat di 1014-faktor kesalahan dalam kode Python ini:

from scipy.signal import convolve, fftconvolve
a = [1.0, 1E-15]
b = [1.0, 1E-15]
convolve(a, b)     # [  1.00000000e+00,   2.00000000e-15,   1.00000000e-30]
fftconvolve(a, b)  # [  1.00000000e+00,   2.11022302e-15,   1.10223025e-16]

Adakah algoritma konvolusi cepat yang tidak mengalami masalah ini?
Atau apakah konvolusi langsung (waktu kuadratik) satu-satunya cara untuk mendapatkan solusi yang akurat?

(Apakah angka sekecil itu cukup signifikan untuk tidak terpotong adalah poin saya.)

pengguna541686
sumber
Perhatikan bahwa convolve()panggil saja fftconvolve()sekarang, jika ukuran input besar. Tentukan method='direct'jika Anda ingin mengarahkan.
endolith
@endolith: Poin bagus! Saya baru tahu itu baru-baru ini tetapi lupa tentang itu di sini.
user541686

Jawaban:

5

Penafian: Saya tahu topik ini lebih lama, tetapi jika seseorang mencari "konvolusi cepat rentang dinamis tinggi" atau serupa ini adalah salah satu yang pertama dari hanya beberapa hasil yang layak. Saya ingin berbagi wawasan saya tentang topik ini sehingga mungkin membantu seseorang di masa depan. Saya minta maaf jika saya mungkin menggunakan istilah yang salah dalam jawaban saya, tetapi semua yang saya temukan pada topik ini agak kabur dan menyebabkan kebingungan bahkan di utas ini. Saya harap pembaca akan mengerti juga.

Konvolusi langsung sebagian besar akurat ke presisi mesin untuk setiap titik, yaitu kesalahan relatif biasanya kira-kira atau mendekati 1.e-16 untuk presisi ganda untuk setiap titik hasil. Setiap titik memiliki 16 digit yang benar. Namun kesalahan pembulatan dapat signifikan untuk konvolusi besar yang tidak tipikal, dan secara tegas seseorang harus berhati-hati dengan pembatalan dan menggunakan sesuatu seperti penjumlahan Kahan dan tipe data presisi yang cukup tinggi, tetapi dalam praktiknya kesalahan tersebut hampir selalu optimal.

Kesalahan konvolusi FFT selain dari kesalahan pembulatan adalah kesalahan "relatif global", yang berarti kesalahan di setiap titik tergantung pada presisi mesin dan nilai puncak hasilnya. Misalnya jika nilai puncak dari hasilnya adalah 2.e9, maka kesalahan absolut di setiap titik adalah21091016=2107. Jadi jika nilai dalam hasil seharusnya sangat kecil, katakanlah109, kesalahan relatif pada titik itu bisa sangat besar. Konvolusi FFT pada dasarnya tidak berguna jika Anda memerlukan kesalahan relatif kecil di bagian hasil Anda, misalnya Anda memiliki peluruhan data yang agak eksponensial dan membutuhkan nilai yang akurat di bagian ekor. Menariknya jika konvolusi FFT tidak dibatasi oleh kesalahan itu, ia memiliki kesalahan pembulatan yang jauh lebih kecil dibandingkan dengan konvolusi langsung, karena Anda jelas melakukan penambahan / perkalian yang lebih sedikit. Inilah sebenarnya mengapa orang sering mengklaim konvolusi FFT lebih akurat, dan mereka dalam beberapa hal hampir benar, sehingga mereka bisa sangat bersikeras.

Sayangnya tidak ada perbaikan universal yang mudah untuk mendapatkan konvolusi yang cepat dan akurat, tetapi tergantung pada masalah Anda mungkin ada satu ... Saya telah menemukan dua:

Jika Anda memiliki kernel halus yang dapat didekati dengan baik oleh polinomial di bagian ekor, maka Metode Multipole Cepat kotak hitam dengan interpolasi Chebyshev mungkin menarik bagi Anda. Jika kernel Anda "baik" ini berfungsi dengan sangat sempurna: Anda mendapatkan kompleksitas komputasi linier (!) Dan akurasi presisi mesin. Jika ini cocok dengan masalah Anda, Anda harus menggunakannya. Namun tidak mudah untuk diimplementasikan.

Untuk beberapa kernel tertentu (saya pikir fungsi cembung, biasanya dari kepadatan probabilitas) Anda dapat menggunakan "pergeseran eksponensial" untuk mendapatkan kesalahan optimal di beberapa bagian ekor hasil. Ada tesis PHD dan github dengan implementasi python menggunakan itu secara sistematis, dan penulis menyebutnya konvolusi FFT akurat . Namun dalam kebanyakan kasus ini tidak terlalu berguna, karena ia akan kembali ke konvolusi langsung atau Anda tetap dapat menggunakan konvolusi FFT. Meskipun kode melakukannya secara otomatis, tentu saja itu bagus.

-------------------- EDIT: --------------------

Saya melihat sedikit pada algoritma Karatsuba (saya sebenarnya membuat implementasi kecil), dan bagi saya sepertinya memiliki perilaku kesalahan yang sama seperti konvolusi FFT, yaitu Anda mendapatkan kesalahan relatif terhadap nilai puncak hasil. Karena sifat membagi dan menaklukkan algoritma beberapa nilai di ekor hasil sebenarnya memiliki kesalahan yang lebih baik, tapi saya tidak melihat cara sistematis yang mudah untuk mengetahui mana atau dalam hal apa pun bagaimana menggunakan pengamatan ini. Sayang sekali, pada awalnya saya pikir Karatsuba mungkin sesuatu yang berguna di antara konvolusi langsung dan FFT. Tapi saya tidak melihat kasus penggunaan umum di mana Karatsuba harus lebih disukai daripada dua algoritma konvolusi umum.

Dan untuk menambah pergeseran eksponensial yang saya sebutkan di atas: Ada banyak kasus di mana Anda dapat menggunakannya untuk meningkatkan hasil konvolusi, tetapi sekali lagi ini bukan perbaikan universal. Saya benar-benar menggunakan ini bersama dengan konvolusi FFT untuk mendapatkan hasil yang cukup bagus (dalam kasus umum untuk semua input: pada kesalahan terburuk yang sama seperti konvolusi FFT normal, pada kesalahan relatif terbaik di setiap titik ke presisi mesin). Tetapi sekali lagi, ini hanya benar-benar berfungsi dengan baik untuk kernel dan data tertentu, tetapi bagi saya kernel dan data atau agak eksponensial dalam peluruhan.

oli
sumber
+1 selamat datang & terima kasih banyak untuk memposting ini! :)
user541686
1
Wow! saya belajar sesuatu juga dan itu adalah istilah baru untuk sesuatu yang telah saya lakukan sejak tahun 1993. Algoritma penjumlahan Kahan ini tampaknya persis sama dengan apa yang saya panggil pembentukan noise dengan nol pada fungsi transfer noise-to-output ditempatkan tepat di DC atau nol ditempatkan diz=1 di zpesawat. Randy Yates menyebutnya " hemat fraksi ", yang merupakan nama generik ringkas untuknya. saya ingin tahu siapa mr / ms Kahan dan kapan ini dikreditkan.
robert bristow-johnson
2
Publikasi asli Kahan tampaknya berasal dari tahun 1964.
oli
ini kejutan hari ini. Sebenarnya sebentar lagi @DanBoschen meminta puzzle dsp, mengingat rentang dinamis dari angka floating point, yang sebenarnya adalah tentang konsep yang sama tentang menambahkan angka yang sangat kecil ke angka yang sangat besar ...
Fat32
3

Salah satu kandidat adalah algoritma Karatsuba , yang berjalan diO(Nlog23)O(N1.5849625)waktu. Itu tidak berbasis transformasi. Ada juga beberapa kode dengan penjelasan di Music-DSP Source Code Archive, yang terlihat seperti penemuan independen dari algoritma yang sama.

Menguji implementasi Python dari algoritma Karatsuba (diinstal oleh sudo pip install karatsuba) menggunakan angka-angka dalam pertanyaan Anda menunjukkan bahwa bahkan dengan angka floating point 64-bit kesalahan relatif besar untuk salah satu nilai output:

import numpy as np
from karatsuba import *
k = make_plan(range(2), range(2))
l = [np.float64(1), np.float64(1E-15)]
np.set_printoptions(formatter={'float': lambda x: format(x, '.17E')})
print "Karatsuba:"
print(k(l, l)[0:3])
print "Direct:"
print(np.convolve(l, l)[0:3])

yang mencetak:

Karatsuba:
[1.0, 1.9984014443252818e-15, 1.0000000000000001e-30]
Direct:
[1.00000000000000000E+00 2.00000000000000016E-15 1.00000000000000008E-30]
Olli Niemitalo
sumber
2
Ada tambahan] di akhir tautan ke algoritme
1 karena ini brilian dan tidak pernah terpikir oleh saya bahwa Karatsuba adalah algoritma konvolusi, tetapi alangkah baiknya jika Anda dapat menjelaskan mengapa ini harus menyelesaikan masalah ini. Saya dapat dengan mudah melihatnya untuk kasus 2x2, tetapi dalam pengaturan rekursif umum saya tidak melihat mengapa harus memperbaiki masalah ini. Tampaknya masuk akal bagi saya bahwa itu mungkin bahkan tidak dapat diperbaiki secara umum, tetapi saya tidak tahu.
user541686
1
@ OlliNiemitalo: Yah cara mudah untuk menjelaskannya adalah saya ingin kesalahan relatif lebih rendah dibandingkan dengan mengarahkan O(n2)lilitan. (Definisi "rendah" yang masuk akal akan bekerja di sini ... kesalahan relatif yang saya dapatkan dengan FFT adalah seperti1014yang tidak rendah dengan definisi apa pun.)
user541686
1
Ganda IEEE hanya memiliki ketepatan 15 hingga 16 angka desimal dalam kasus umum. Jadi 1e-14 adalah kesalahan ukuran yang wajar untuk urutan sejumlah operasi aritmatika (kecuali jika Anda memilih beberapa nilai ajaib).
hotpaw2
1
Jika Anda pernah merancang adder floating point, Anda akan tahu bahwa eksponen ditentukan oleh hasil mantissa selama normalisasi. Anda memilih angka yang menghasilkan mantissa sempit yang tidak mungkin.
hotpaw2
3

Daripada membuang algoritma konvolusi cepat, mengapa tidak menggunakan FFT dengan rentang dinamis yang lebih tinggi?

Jawaban untuk pertanyaan ini menunjukkan bagaimana menggunakan perpustakaan Eigen FFT dengan meningkatkan multiprecision.

Mark Borgerding
sumber
2

Saya percaya bahwa ketepatan algoritma Cordic dapat diperpanjang sejauh yang Anda inginkan, jika demikian gunakan DFT integer dan panjang kata yang sesuai dengan masalah Anda.

Hal yang sama berlaku untuk konvolusi langsung, gunakan bilangan bulat yang sangat panjang.


sumber
1

Konvolusi waktu kuadratik untuk mendapatkan hasil DFT biasanya kurang akurat (dapat menimbulkan derau numerik kuantisasi yang lebih terbatas, karena pelapisan langkah aritmatika yang lebih dalam) daripada algoritme FFT tipikal ketika menggunakan tipe aritmatika dan unit operasi yang sama.

Anda mungkin ingin mencoba tipe data dengan presisi lebih tinggi (presisi quad atau bignum aritmatika).

hotpaw2
sumber
Er, ini adalah menggunakan jenis aritmatika yang sama dan unit operasi bukan? Jelas itu lebih akurat. Saya pikir jenis kebisingan yang Anda bicarakan tidak sama dengan jenis yang saya bicarakan. Akar persatuan memiliki besaran 1 yang berarti mereka tidak bisa mewakili nilai yang sangat kecil. Ini sepertinya tidak sepenuhnya terkait dengan pertanyaan tentang bagaimana noise menyebar melalui sistem.
user541686
Tampaknya hanya lebih akurat dalam contoh Anda karena Anda memilih panjang dan nilai di mana pembulatan terjadi untuk Anda. Coba rentang konvolusi yang jauh lebih lama dengan lebih banyak koefisien tidak nol dengan distribusi yang berisi urutan besar besaran.
hotpaw2
Masalah yang saya coba selesaikan tidak ada hubungannya dengan pembulatan sekalipun. Itu masalah yang berbeda yang tidak saya coba pecahkan. Contoh asli yang saya miliki persis seperti apa yang baru saja Anda katakan, dan mereka bekerja dengan baik dengan konvolusi langsung tetapi dihancurkan oleh FFT.
user541686
Pembulatan (atau metode kuantisasi lainnya) terlibat dalam semua aritmatika presisi-terbatas. Beberapa hasil komputasi berubah ketika dibulatkan, yang lain tidak atau kurang berubah.
hotpaw2
Saya tidak pernah mengklaim sebaliknya. Apa yang saya katakan tadi adalah masalah yang saya coba selesaikan tidak ada hubungannya dengan pembulatan. Ini masalah yang berbeda. Saya tidak peduli untuk menghindari pembulatan, tetapi saya ingin menghindari masalah ini.
user541686