Algoritmo di convoluzione veloce e preciso (come FFT) per un intervallo dinamico elevato?


8

Sembra che la convoluzione basata su FFT soffra di una risoluzione a virgola mobile limitata a causa della valutazione di tutto intorno alle radici dell'unità, come si può vedere nella 1014-factor error in questo codice Python:

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]

Esistono algoritmi di convoluzione rapida che non soffrono di questo problema?
O la convoluzione diretta (tempo quadratico) è l'unico modo per ottenere una soluzione accurata?

(Il fatto che numeri così piccoli siano abbastanza significativi da non essere tagliati è fuori dal mio punto.)


Si noti che convolve()chiama solo fftconvolve()ora, se le dimensioni di input sono grandi. Specificare method='direct'se si desidera diretto.
endolith,

@endolith: buon punto! L'ho imparato da poco ma me ne sono dimenticato qui.
user541686,

Risposte:


5

Disclaimer: so che questo argomento è più vecchio, ma se si è alla ricerca di "convoluzione veloce elevata gamma dinamica elevata" o simile, questo è uno dei primi di pochi risultati decenti. Voglio condividere le mie intuizioni che ho avuto su questo argomento in modo che possa aiutare qualcuno in futuro. Mi scuso se potrei usare i termini sbagliati nella mia risposta, ma tutto ciò che ho trovato su questo argomento è piuttosto vago e può creare confusione anche in questo thread. Spero che il lettore capirà comunque.

La convoluzione diretta è per lo più accurata alla precisione della macchina per ciascun punto, ovvero l' errore relativo è generalmente approssimativamente o vicino a 1.e-16 per una doppia precisione per ciascun punto del risultato. Ogni punto ha 16 cifre corrette. Gli errori di arrotondamento possono essere significativi per convoluzioni di grandi dimensioni atipiche e, a rigor di termini, si dovrebbe fare attenzione con la cancellazione e utilizzare qualcosa come la somma di Kahan e tipi di dati di precisione sufficientemente elevati, ma in pratica l'errore è quasi sempre ottimale.

L'errore di una convoluzione FFT oltre agli errori di arrotondamento è un errore "relativo globale", il che significa che l'errore in ciascun punto dipende dalla precisione della macchina e dal valore di picco del risultato. Ad esempio, se il valore di picco del risultato è 2.e9, allora l'errore assoluto è in ciascun punto21091016=2107. Quindi, se un valore nel risultato dovrebbe essere molto piccolo, diciamo109, l'errore relativo in quel punto può essere enorme. La convoluzione FFT è sostanzialmente inutile se hai bisogno di piccoli errori relativi nella coda del tuo risultato, ad esempio hai un decadimento in qualche modo esponenziale dei tuoi dati e hai bisogno di valori precisi nella coda. È interessante notare che se la convoluzione FFT non è limitata da quell'errore, ha errori di arrotondamento molto più piccoli rispetto alla convoluzione diretta, poiché ovviamente si fanno meno aggiunte / moltiplicazioni. Questo è il motivo per cui le persone spesso dichiarano che la convoluzione della FFT è più accurata e hanno quasi ragione in un certo senso, quindi possono essere piuttosto irremovibili.

Sfortunatamente non esiste una soluzione universale facile per ottenere convoluzioni veloci e precise, ma a seconda del problema potrebbe essercene uno ... Ne ho trovati due:

Se hai kernel lisci che possono essere approssimati bene da un polinomio nella coda, allora il metodo multipolare veloce in scatola nera con interpolazione di Chebyshev potrebbe essere interessante per te. Se il tuo kernel è "carino", in realtà funziona perfettamente: ottieni complessità computazionale lineare (!) E precisione di precisione della macchina. Se questo si adatta al tuo problema, dovresti usarlo. Tuttavia, non è facile da implementare.

Per alcuni kernel specifici (penso che funzioni convesse, di solito a densità di probabilità) è possibile utilizzare uno "spostamento esponenziale" per ottenere un errore ottimale in alcune parti della coda del risultato. C'è una tesi di dottorato e un github con un'implementazione di Python che lo utilizza sistematicamente, e l'autore lo definisce convoluzione FFT accurata . Nella maggior parte dei casi, ciò non è molto utile, poiché o regredisce alla convoluzione diretta o è possibile utilizzare la convoluzione FFT comunque. Anche se il codice lo fa automaticamente, il che è carino ovviamente.

--------------------MODIFICARE:--------------------

Ho guardato un po 'l' algoritmo Karatsuba (in realtà ho fatto una piccola implementazione), e a me sembra che abbia un comportamento di errore simile alla convoluzione FFT, cioè si ottiene un errore rispetto al valore di picco del risultato. A causa della divisione e della conquista della natura dell'algoritmo, alcuni valori nella coda del risultato hanno effettivamente un errore migliore, ma non vedo un modo semplice e sistematico per dire quali o comunque usare questa osservazione. Peccato, all'inizio pensavo che Karatsuba potesse essere qualcosa di utile tra la convoluzione diretta e la FFT. Ma non vedo casi d'uso comuni in cui Karatsuba dovrebbe essere preferito rispetto ai due comuni algoritmi di convoluzione.

E per aggiungere al cambiamento esponenziale che ho menzionato sopra: ci sono molti casi in cui puoi usarlo per migliorare il risultato di una convoluzione, ma ancora una volta non è una soluzione universale. In realtà lo uso insieme alla convoluzione FFT per ottenere risultati abbastanza buoni (nel caso generale di tutti gli input: nel peggiore stesso errore della normale convoluzione FFT, nella migliore delle ipotesi errore relativo in ogni punto della precisione della macchina). Ma ancora una volta, funziona davvero bene solo per kernel e dati specifici, ma per me sia kernel che dati o in qualche modo esponenziale in decadimento.


+1 benvenuto e grazie mille per aver pubblicato questo! :)
user541686

1
Wow! ho imparato anche qualcosa e questo è un nuovo termine per qualcosa che faccio dal 1993. Questo algoritmo di somma Kahan sembra essere esattamente lo stesso di quello che avevo chiamato noise shaping con uno zero nella funzione di trasferimento da rumore a uscita posto a destra o lo zero è posto az=1 sul zaereo. Randy Yates lo ha chiamato " risparmio di frazione ", che è un nome generico conciso per questo. mi chiedo chi sia il signor / ms Kahan e quando questo è accreditato.
robert bristow-johnson,

2
La pubblicazione originale di Kahan sembra essere del 1964.
oli

è la sorpresa di oggi. In realtà un po 'di tempo @DanBoschen aveva chiesto un puzzle dsp, considerando la gamma dinamica di numeri in virgola mobile, che in realtà riguardava lo stesso concetto di aggiungere numeri molto piccoli a numeri molto grandi ...
Fat32

3

Un candidato è l' algoritmo Karatsuba , che viene eseguitoO(Nlog23)O(N1.5849625)tempo. Non è basato sulla trasformazione. C'è anche del codice con una spiegazione nell'archivio del codice sorgente di Music-DSP, che sembra una scoperta indipendente di un algoritmo simile.

Testare un'implementazione Python dell'algoritmo Karatsuba (installato da sudo pip install karatsuba) usando i numeri nella tua domanda mostra che anche con numeri in virgola mobile a 64 bit l'errore relativo è grande per uno dei valori di 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])

che stampa:

Karatsuba:
[1.0, 1.9984014443252818e-15, 1.0000000000000001e-30]
Direct:
[1.00000000000000000E+00 2.00000000000000016E-15 1.00000000000000008E-30]

2
C'è un extra] alla fine del link all'algoritmo Karatsuba

+1 perché è geniale e non mi era mai venuto in mente che Karatsuba fosse un algoritmo di convoluzione, ma sarebbe bello se tu potessi spiegare perché dovrebbe risolvere questo problema. Posso facilmente vederlo per il caso 2x2, ma nell'impostazione ricorsiva generale non vedo perché dovrebbe risolvere questo problema. Mi sembra plausibile che potrebbe non essere nemmeno risolvibile in generale, ma non lo so.
user541686,

1
@OlliNiemitalo: Beh, il modo più semplice per spiegarlo è che voglio che l'errore relativo sia basso rispetto a diretto O(n2)convoluzione. (Qualsiasi definizione ragionevole di "basso" funzionerebbe qui ... l'errore relativo che sto ottenendo con FFT è come1014che non è basso da nessuna definizione.)
user541686

1
I doppi IEEE hanno solo una precisione di 15-16 cifre decimali nel caso generale. Quindi 1e-14 è un errore di dimensioni ragionevoli per una sequenza di un certo numero di operazioni aritmetiche (a meno che tu non scelga alcuni valori magici).
hotpaw2,

1
Se hai mai progettato un sommatore a virgola mobile, saprai che l'esponente è determinato dal risultato della mantissa durante la normalizzazione. Hai scelto numeri che producono un'improbabile mantissa stretta.
hotpaw2

3

Invece di eliminare l'algoritmo di convoluzione rapida, perché non utilizzare un FFT con un intervallo dinamico più elevato?

Una risposta a questa domanda mostra come utilizzare la libreria EFT FFT con boost multiprecision.


2

Credo che la precisione dell'algoritmo Cordic possa essere estesa quanto vuoi, in tal caso usa un DFT intero e una lunghezza della parola adatta al tuo problema.

Lo stesso sarebbe vero con la convoluzione diretta, utilizzare numeri interi molto lunghi.


1

La convoluzione quadratica del tempo per ottenere un risultato DFT è in genere meno accurata (può comportare un rumore numerico di quantizzazione più finito, a causa di una più profonda stratificazione dei passi aritmetici) rispetto all'algoritmo FFT tipico quando si utilizzano gli stessi tipi aritmetici e unità operative.

Potresti voler provare tipi di dati di maggiore precisione (quad precisione o aritmetica del bignum).


Ehm, questo sta usando gli stessi tipi aritmetici e le unità operative non è vero? Chiaramente è più preciso. Penso che il tipo di rumore di cui stai parlando non sia lo stesso di quello di cui sto parlando. Le radici dell'unità hanno una grandezza di 1, il che significa che semplicemente non possono rappresentare valori molto piccoli. Ciò non sembra totalmente correlato alla domanda su come il rumore si propaga attraverso il sistema.
user541686,

Sembra solo più preciso nel tuo esempio perché hai scelto una lunghezza e dei valori in cui l'arrotondamento ha funzionato a tuo favore. Prova una serie di convoluzioni molto più lunghe con molti più coefficienti diversi da zero con una distribuzione contenente un ampio ordine di grandezza.
hotpaw2,

Il problema che sto cercando di risolvere non ha nulla a che fare con l'arrotondamento. È un problema diverso che non sto cercando di risolvere. Gli esempi originali che ho avuto erano esattamente come quelli che hai appena detto, e hanno funzionato perfettamente con la convoluzione diretta ma sono stati distrutti da FFT.
user541686,

L'arrotondamento (o altri metodi di quantizzazione) è coinvolto in tutta l'aritmetica a precisione finita. Alcuni risultati computazionali cambiano quando arrotondati, altri no o cambiano di meno.
hotpaw2,

Non ho mai preteso diversamente. Quello che ti ho appena detto è che il problema che sto cercando di risolvere non ha nulla a che fare con l'arrotondamento. È un problema diverso. Non mi interessa evitare gli arrotondamenti, ma mi interessa evitare questo problema.
user541686,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.