【问题标题】:How to implement Pytorch 1D crosscorrelation for long signals in fourier domain?如何为傅里叶域中的长信号实现 Pytorch 1D 互相关?
【发布时间】:2019-11-04 19:07:25
【问题描述】:

我有一系列长度为 n = 36,000 的信号,我需要对其执行互相关。目前,我在 numpy 中的 cpu 实现有点慢。我听说 Pytorch 可以大大加快张量运算,并提供了一种在 GPU 上并行执行计算的方法。我想探索这个选项,但我不太确定如何使用框架来完成。

由于这些信号的长度,我更愿意在频域中执行互相关运算。

通常使用 numpy 我会执行如下操作:

import numpy as np

signal_length=36000

# make the signals
signal_1 = np.random.uniform(-1,1, signal_length)
signal_2 = np.random.uniform(-1,1, signal_length)

# output target length of crosscorrelation
x_cor_sig_length = signal_length*2 - 1

# get optimized array length for fft computation
fast_length = np.fftpack.next_fast_len(x_cor_sig_length)

# move data into the frequency domain. axis=-1 to perform 
# along last dimension
fft_1 = np.fft.rfft(src_data, fast_length, axis=-1)
fft_2 = np.fft.rfft(src_data, fast_length, axis=-1)

# take the complex conjugate of one of the spectrums. Which one you choose depends on domain specific conventions
fft_1 = np.conj(fft_1)


fft_multiplied = fft_1 * fft_2

# back to time domain. 
prelim_correlation = np.fft.irfft(result, x_corr_sig_length, axis=-1)

# shift the signal to make it look like a proper crosscorrelation,
# and transform the output to be purely real

final_result = np.real(np.fft.fftshift(prelim_correlation),axes=-1)).astype(np.float64)

查看 Pytorch 文档,似乎没有 numpy.conj() 的等价物。我也不确定是否/如何在 irfft 操作后实施 fftshift。

那么您将如何使用傅里叶方法在 Pytorch 中编写一维互相关?

【问题讨论】:

    标签: python-3.x gpu fft pytorch cross-correlation


    【解决方案1】:

    需要考虑的一些事情。

    Python 解释器非常慢,这些向量化库所做的就是将工作负载转移到本机实现。为了有所作为,您需要能够在一条 python 指令中执行许多操作。在 GPU 上评估事物遵循相同的原则,虽然 GPU 具有更强的计算能力,但将数据复制到 GPU 或从 GPU 复制数据的速度较慢。

    我修改了您的示例以同时处理多个信号。

    import numpy as np
    def numpy_xcorr(BATCH=1, signal_length=36000):
        # make the signals
        signal_1 = np.random.uniform(-1,1, (BATCH, signal_length))
        signal_2 = np.random.uniform(-1,1, (BATCH, signal_length))
    
        # output target length of crosscorrelation
        x_cor_sig_length = signal_length*2 - 1
    
        # get optimized array length for fft computation
        fast_length = next_fast_len(x_cor_sig_length)
    
        # move data into the frequency domain. axis=-1 to perform 
        # along last dimension
        fft_1 = np.fft.rfft(signal_1, fast_length, axis=-1)
        fft_2 = np.fft.rfft(signal_2 + 0.1 * signal_1, fast_length, axis=-1)
    
        # take the complex conjugate of one of the spectrums. 
        fft_1 = np.conj(fft_1)
    
    
        fft_multiplied = fft_1 * fft_2
    
        # back to time domain. 
        prelim_correlation = np.fft.irfft(fft_multiplied, fast_length, axis=-1)
    
        # shift the signal to make it look like a proper crosscorrelation,
        # and transform the output to be purely real
    
        final_result = np.fft.fftshift(np.real(prelim_correlation), axes=-1)
        return final_result, np.sum(final_result)
    

    从 torch 1.7 开始,我们有了 torch.fft 模块,该模块提供了类似于 numpy.fft 的接口,缺少 fftshift 但可以使用 torch.roll 获得相同的结果。还有一点就是numpy默认使用64位精度,torch会使用32位精度。

    快速长度在于选择平滑数字(那些被分解为小的素数,我想你熟悉这个主题)。

    def next_fast_len(n, factors=[2, 3, 5, 7]):
        '''
          Returns the minimum integer not smaller than n that can
          be written as a product (possibly with repettitions) of
          the given factors.
        '''
        best = float('inf')
        stack = [1]
        while len(stack):
            a = stack.pop()
            if a >= n:
                if a < best:
                    best = a;
                    if best == n:
                        break; # no reason to keep searching
            else:
                for p in factors:
                    b = a * p;
                    if b < best:
                        stack.append(b)
        return best;
    

    然后火炬实现去

    import torch;
    import torch.fft
    def torch_xcorr(BATCH=1, signal_length=36000, device='cpu', factors=[2,3,5], dtype=torch.float):
        signal_length=36000
        # torch.rand is random in the range (0, 1)
        signal_1 = 1 - 2*torch.rand((BATCH, signal_length), device=device, dtype=dtype)
        signal_2 = 1 - 2*torch.rand((BATCH, signal_length), device=device, dtype=dtype)
    
        # just make the cross correlation more interesting
        signal_2 += 0.1 * signal_1;
    
        # output target length of crosscorrelation
        x_cor_sig_length = signal_length*2 - 1
    
        # get optimized array length for fft computation
        fast_length = next_fast_len(x_cor_sig_length, [2, 3])
    
        # the last signal_ndim axes (1,2 or 3) will be transformed
        fft_1 = torch.fft.rfft(signal_1, fast_length, dim=-1)
        fft_2 = torch.fft.rfft(signal_2, fast_length, dim=-1)
    
        # take the complex conjugate of one of the spectrums. Which one you choose depends on domain specific conventions
    
        fft_multiplied = torch.conj(fft_1) * fft_2
    
        # back to time domain. 
        prelim_correlation = torch.fft.irfft(fft_multiplied, dim=-1)
    
        # shift the signal to make it look like a proper crosscorrelation,
        # and transform the output to be purely real
    
        final_result = torch.roll(prelim_correlation, (fast_length//2,))
        return final_result, torch.sum(final_result);
    
    

    这里是测试结果的代码

    import time
    funcs = {'numpy-f64': lambda b: numpy_xcorr(b, factors=[2,3,5], dtype=np.float64), 
             'numpy-f32': lambda b: numpy_xcorr(b, factors=[2,3,5], dtype=np.float32), 
             'torch-cpu-f64': lambda b: torch_xcorr(b, device='cpu', factors=[2,3,5], dtype=torch.float64), 
             'torch-cpu': lambda b: torch_xcorr(b, device='cpu', factors=[2,3,5], dtype=torch.float32), 
             'torch-gpu-f64': lambda b: torch_xcorr(b, device='cuda', factors=[2,3,5], dtype=torch.float64),
             'torch-gpu': lambda b: torch_xcorr(b, device='cuda', factors=[2,3,5], dtype=torch.float32),
             }
    times ={}
    for batch in [1, 10, 100]:
        times[batch] = {}
        for l, f in funcs.items():
            t0 = time.time()
            t1, t2 = f(batch)
            tf = time.time()
            del t1
            del t2
            times[batch][l] = 1000 * (tf - t0) / batch;
    

    我得到了以下结果

    令我惊讶的是当数字不是那么平滑时的结果,例如使用 17-smooth 长度,torch 实现要好得多,以至于我在这里使用了对数刻度(批量大小为 100 时,torch gpu 比批量大小为 1 的 numpy 快 10000 倍)。

    请记住,这些函数通常在 GPU 上生成数据,我们希望将最终结果复制到 CPU,如果我们考虑将最终结果复制到 CPU 所花费的时间,我观察到的时间比互相关高出 10 倍计算(随机数据生成 + 三个 FFT)。

    【讨论】:

      猜你喜欢
      • 2016-04-29
      • 2021-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多