gpt4 book ai didi

c++ - 使用 FFT (FFTW) 计算两个函数的卷积

转载 作者:塔克拉玛干 更新时间:2023-11-03 08:27:53 26 4
gpt4 key购买 nike

我正在尝试使用 FFT 加速神经模拟器的计算。

等式是:

(1)\sum(j=1 到 N) (w(i - j) * s_NMDA[j])

其中 s_NMDA 是长度为 N 的 vector ,w 定义为:

(2) w(j) = tanh[1/(2 * sigma * p)] * exp(-abs(j)/(sigma * p)]

其中 sigma 和 p 是常数。

(有没有更好的方法在 stackoverflow 上渲染方程式?)

必须对 N 个神经元进行计算。由于 (1) 仅取决于绝对距离 abs(i - j),因此应该可以使用 FFT(卷积定理)进行计算。

我已尝试使用 FFTW 实现此功能,但结果与预期结果不符。我以前从未使用过 FFTW,现在我不确定我的实现是否不正确,我对卷积定理的假设是否错误。

void f_I_NMDA_FFT(
const double **states, // states[i][6] == s_NMDA[i]
const unsigned int numNeurons)
{
fftw_complex *distances, *sNMDAs, *convolution;
fftw_complex *distances_f, *sNMDAs_f, *convolution_f;
fftw_plan p, pinv;
const double scale = 1./numNeurons;

distances = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * numNeurons);
sNMDAs = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * numNeurons);
convolution = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * numNeurons);
distances_f = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * numNeurons);
sNMDAs_f = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * numNeurons);
convolution_f = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * numNeurons);

// fill input array for distances
for (unsigned int i = 0; i < numNeurons; ++i)
{
distances[i][0] = w(i);
distances[i][1] = 0;
}

// fill input array for sNMDAs
for (unsigned int i = 0; i < numNeurons; ++i)
{
sNMDAs[i][0] = states[i][6];
sNMDAs[i][1] = 0;
}

p = fftw_plan_dft_1d(numNeurons,
distances,
distances_f,
FFTW_FORWARD,
FFTW_ESTIMATE);
fftw_execute(p);

p = fftw_plan_dft_1d(numNeurons,
sNMDAs,
sNMDAs_f,
FFTW_FORWARD,
FFTW_ESTIMATE);
fftw_execute(p);

// convolution in frequency domain
for(unsigned int i = 0; i < numNeurons; ++i)
{
convolution_f[i][0] = (distances_f[i][0] * sNMDAs_f[i][0]
- distances_f[i][1] * sNMDAs_f[i][1]) * scale;
convolution_f[i][1] = (distances_f[i][0] * sNMDAs_f[i][1]
- distances_f[i][1] * sNMDAs_f[i][0]) * scale;
}

pinv = fftw_plan_dft_1d(numNeurons,
convolution_f,
convolution,
FFTW_FORWARD,
FFTW_ESTIMATE);
fftw_execute(pinv);

// compute and compare with expected result
for (unsigned int i = 0; i < numNeurons; ++i)
{
double expected = 0;

for (int j = 0; j < numNeurons; ++j)
{
expected += w(i - j) * states[j][6];
}
printf("i=%d, FFT: r%f, i%f : Expected: %f\n", i, convolution[i][0], convolution[i][1], expected);
}

fftw_destroy_plan(p);
fftw_destroy_plan(pinv);

fftw_free(distances), fftw_free(sNMDAs), fftw_free(convolution);
fftw_free(distances_f), fftw_free(sNMDAs_f), fftw_free(convolution_f);

这是 20 个神经元的示例输出:

i=0, FFT: r0.042309, i0.000000 : Expected: 0.041504
i=1, FFT: r0.042389, i0.000000 : Expected: 0.042639
i=2, FFT: r0.042466, i0.000000 : Expected: 0.043633
i=3, FFT: r0.042543, i0.000000 : Expected: 0.044487
i=4, FFT: r0.041940, i0.000000 : Expected: 0.045203
i=5, FFT: r0.041334, i0.000000 : Expected: 0.045963
i=6, FFT: r0.041405, i0.000000 : Expected: 0.046585
i=7, FFT: r0.041472, i0.000000 : Expected: 0.047070
i=8, FFT: r0.041537, i0.000000 : Expected: 0.047419
i=9, FFT: r0.041600, i0.000000 : Expected: 0.047631
i=10, FFT: r0.041660, i0.000000 : Expected: 0.047708
i=11, FFT: r0.041717, i0.000000 : Expected: 0.047649
i=12, FFT: r0.041773, i0.000000 : Expected: 0.047454
i=13, FFT: r0.041826, i0.000000 : Expected: 0.047123
i=14, FFT: r0.041877, i0.000000 : Expected: 0.046656
i=15, FFT: r0.041926, i0.000000 : Expected: 0.046052
i=16, FFT: r0.041294, i0.000000 : Expected: 0.045310
i=17, FFT: r0.042059, i0.000000 : Expected: 0.044430
i=18, FFT: r0.042144, i0.000000 : Expected: 0.043412
i=19, FFT: r0.042228, i0.000000 : Expected: 0.042253

结果看起来几乎是正确的,但是误差随着神经元数量的增加而增加。此外,对于非常低或非常高的位置 (i),结果似乎更准确。这里发生了什么?

更新:根据 Oli Charlesworth 的建议,我在 Octave 中实现了算法,看看它是一个实现问题还是数学问题:

input = [0.186775; 0.186775; 0.186775; 0.186775; 0.186775; 0; 0.186775; 0.186775; 0.186775; 0.186775];

function ret = _w(i)
ret = tanh(1 / (2* 1 * 32)) * exp(-abs(i) / (1 * 32));
end

for i = linspace(1, 10, 10)
expected = 0;
for j = linspace(1, 10, 10)
expected += _w(i-j) * input(j);
end
expected
end

distances = _w(transpose(linspace(0, 9, 10)));

input_f = fft(input);
distances_f = fft(distances);

convolution_f = input_f .* distances_f;

convolution = ifft(convolution_f)

结果:

expected =  0.022959
expected = 0.023506
expected = 0.023893
expected = 0.024121
expected = 0.024190
expected = 0.024100
expected = 0.024034
expected = 0.023808
expected = 0.023424
expected = 0.022880
convolution =

0.022959
0.023036
0.023111
0.023183
0.023253
0.022537
0.022627
0.022714
0.022798
0.022880

结果非常相似。所以,我对卷积定理/FFT的理解一定有问题。

最佳答案

要通过 FFT 对 2 个信号进行卷积,您通常需要这样做:

  1. 根据需要向每个信号添加尽可能多的零,使其长度成为原始信号的累积长度 - 1(即卷积结果的长度)。
  2. 如果您的 FFT 库要求输入长度为 2 的幂,请向每个信号添加尽可能多的零以满足该要求。
  3. 计算信号 1 的 DFT(通过 FFT)。
  4. 计算信号 2 的 DFT(通过 FFT)。
  5. 将两个 DFT 按元素相乘。顺便说一下,它应该是一个复杂的乘法。
  6. 计算相乘 DFT 的逆 DFT(通过 FFT)。这就是您的卷积结果。

在您的代码中,我在所有 3 个 FFT 中都看到了 FFTW_FORWARD。我猜如果那不是问题,那是问题的一部分。最后的FFT应该是“后向”,而不是“前向”。

此外,我认为您需要在第二个表达式中使用“+”,而不是“-”:

convolution_f[i][0] = (distances_f[i][0] * sNMDAs_f[i][0]
- distances_f[i][1] * sNMDAs_f[i][1]) * scale;

convolution_f[i][1] = (distances_f[i][0] * sNMDAs_f[i][1]
- distances_f[i][1] * sNMDAs_f[i][0]) * scale;

关于c++ - 使用 FFT (FFTW) 计算两个函数的卷积,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11178080/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com