网易首页 > 网易号 > 正文 申请入驻

PyTorch中的傅立叶卷积:通过FFT计算大核卷积的数学原理和代码

0
分享至

卷积

卷积在数据分析中无处不在。 几十年来,它们已用于信号和图像处理。 最近,它们已成为现代神经网络的重要组成部分。

在数学上,卷积表示为:

尽管离散卷积在计算应用程序中更为常见,但由于本文使用连续变量证明卷积定理(如下所述)要容易得多,因此在本文的大部分内容中,我将使用连续形式。 之后,我们将返回离散情况,并使用傅立叶变换在PyTorch中实现它。 离散卷积可以看作是连续卷积的近似值,其中连续函数在规则网格上离散化。 因此,我们不会为离散情况重新证明卷积定理。

卷积定理

在数学上,卷积定理可以表示为:

连续傅里叶变换的位置(最大归一化常数):

换句话说,位置空间的卷积等价于频率空间的直接乘法。这个想法是相当不直观的,但证明卷积定理是惊人的容易对于连续的情况。首先把方程的左边写出来。

现在改变积分的顺序,替换变量(x = y + z),并分离两个被积函数。

我们为什么要关心所有这些? 因为快速傅立叶变换的算法复杂度比卷积低。 直接卷积的复杂度为O(n),因为我们将g中的每个元素传递给f中的每个元素。 快速傅立叶变换可以在O(n log n)的时间内计算出来。 当输入数组很大时,它们比卷积要快得多。 在这些情况下,我们可以使用卷积定理来计算频率空间中的卷积,然后执行傅立叶逆变换以返回到位置空间。

当输入较小时(例如3x3卷积内核),直接卷积仍然更快。 在机器学习应用程序中,使用较小的内核大小更为常见,因此PyTorch和Tensorflow之类的深度学习库仅提供直接卷积的实现。 但是,在现实世界中,有很多使用大内核的用例,其中傅立叶卷积更为有效。

PyTorch实现

现在,我将演示如何在PyTorch中实现傅立叶卷积函数。 它应该模仿torch.nn.functional.convNd的功能,并在实现中利用FFT,而无需用户做任何额外的工作。 这样,它应该接受三个张量(信号,内核和可选的偏差),并填充以应用于输入。 从概念上讲,此功能的内部工作原理是:

def fft_conv(
signal: Tensor, kernel: Tensor, bias: Tensor = None, padding: int = 0,
) -> Tensor:
# 1. Pad the input signal & kernel tensors
# 2. Compute FFT for both signal & kernel
# 3. Multiply the transformed Tensors together
# 4. Compute inverse FFT
# 5. Add bias and return

让我们根据上面显示的操作顺序逐步构建FFT卷积。 在此示例中,我将构建一个1D傅立叶卷积,但是将其扩展到2D和3D卷积很简单。 最后我们也会提供github的代码库。在该存储库中,我实现了通用的N维傅立叶卷积方法。

1 填充输入阵列

我们需要确保填充后信号和内核的大小相同。 将初始填充应用于信号,然后调整填充以使内核匹配。

# 1. Pad the input signal & kernel tensors
signal = f.pad(signal, [padding, padding])
kernel_padding = [0, signal.size(-1) - kernel.size(-1)]
padded_kernel = f.pad(kernel, kernel_padding)

注意,我只在一侧填充内核。 我们希望原始内核位于填充数组的左侧,以便它与信号数组的开始对齐。

2 计算傅立叶变换

这非常容易,因为在PyTorch中已经实现了N维FFT。 我们只需使用内置函数,然后沿每个张量的最后一个维度计算FFT。

# 2. Perform fourier convolution
signal_fr = rfftn(signal, dim=-1)
kernel_fr = rfftn(padded_kernel, dim=-1)

3 乘以变换后的张量

这是我们功能中最棘手的部分。 这有两个原因。

(1)PyTorch卷积在多维张量上运行,因此我们的信号和内核张量实际上是三维的。 从PyTorch文档中的该方程式,我们看到矩阵乘法是在前两个维度上执行的(不包括偏差项):

我们需要包括此矩阵乘法以及转换后的维度上的直接乘法。

(2)在官方文档中所示,PyTorch实际上实现了互相关方法而不是卷积。 (TensorFlow和其他深度学习库也是如此。)互相关与卷积密切相关,但有一个重要的符号变化:

与卷积相比,这有效地逆转了核函数(g)的方向。我们不是手动翻转核函数,而是通过求傅里叶空间中核函数的复共轭来修正。因为我们不需要创建一个全新的张量,所以这大大加快了存储效率。(本文末尾的附录中包含了如何/为什么这样做的简要演示。)

# 3. Multiply the transformed matrices
def complex_matmul(a: Tensor, b: Tensor) -> Tensor:
"""Multiplies two complex-valued tensors."""
# Scalar matrix multiplication of two tensors, over only the first two dimensions.
# Dimensions 3 and higher will have the same shape after multiplication.
scalar_matmul = partial(torch.einsum, "ab..., cb... -> ac...")
# Compute the real and imaginary parts independently, then manually insert them
# into the output Tensor. This is fairly hacky but necessary for PyTorch 1.7.0,
# because Autograd is not enabled for complex matrix operations yet. Not exactly
# idiomatic PyTorch code, but it should work for all future versions (>= 1.7.0).
real = scalar_matmul(a.real, b.real) - scalar_matmul(a.imag, b.imag)
imag = scalar_matmul(a.imag, b.real) + scalar_matmul(a.real, b.imag)
c = torch.zeros(real.shape, dtype=torch.complex64)
c.real, c.imag = real, imag
return c
# Conjugate the kernel for cross-correlation
kernel_fr.imag *= -1
output_fr = complex_matmul(signal_fr, kernel_fr)

PyTorch 1.7改进了对复数的支持,但是autograd中还不支持对复数值张量的许多操作。现在,我们必须编写自己的complex_matmul方法作为补丁。虽然不是最佳的解决方案,但它目前可以工作。

4 计算逆变换

使用torch.irfftn可以很容易地计算出逆变换。 然后,裁剪出多余的数组填充。

# 4. Compute inverse FFT, and remove extra padded values
output = irfftn(output_fr, dim=-1)
output = output[:, :, :signal.size(-1) - kernel.size(-1) + 1]

5 添加偏置并返回

添加偏置项也非常容易。 请记住,偏置对输出阵列中的每个通道都有一个元素,并进行相应的整形。

# 5. Optionally, add a bias term before returning.
if bias is not None:
output += bias.view(1, -1, 1)

放在一起

为了完整起见,让我们将所有这些代码段编译为一个内聚函数。

def fft_conv_1d(
signal: Tensor, kernel: Tensor, bias: Tensor = None, padding: int = 0,
) -> Tensor:
"""
Args:
signal: (Tensor) Input tensor to be convolved with the kernel.
kernel: (Tensor) Convolution kernel.
bias: (Optional, Tensor) Bias tensor to add to the output.
padding: (int) Number of zero samples to pad the input on the last dimension.
Returns:
(Tensor) Convolved tensor
"""
# 1. Pad the input signal & kernel tensors
signal = f.pad(signal, [padding, padding])
kernel_padding = [0, signal.size(-1) - kernel.size(-1)]
padded_kernel = f.pad(kernel, kernel_padding)
# 2. Perform fourier convolution
signal_fr = rfftn(signal, dim=-1)
kernel_fr = rfftn(padded_kernel, dim=-1)
# 3. Multiply the transformed matrices
kernel_fr.imag *= -1
output_fr = complex_matmul(signal_fr, kernel_fr)
# 4. Compute inverse FFT, and remove extra padded values
output = irfftn(output_fr, dim=-1)
output = output[:, :, :signal.size(-1) - kernel.size(-1) + 1]
# 5. Optionally, add a bias term before returning.
if bias is not None:
output += bias.view(1, -1, 1)
return output

最后,我们将确认这在数值上等于使用torch.nn.functional.conv1d进行直接一维卷积。 我们为所有输入构造随机张量,并测量输出值的相对差异。

import torch
import torch.nn.functional as f
torch.manual_seed(1234)
kernel = torch.randn(2, 3, 1025)
signal = torch.randn(3, 3, 4096)
bias = torch.randn(2)
y0 = f.conv1d(signal, kernel, bias=bias, padding=512)
y1 = fft_conv_1d(signal, kernel, bias=bias, padding=512)
abs_error = torch.abs(y0 - y1)
print(f'\nAbs Error Mean: {abs_error.mean():.3E}')
print(f'Abs Error Std Dev: {abs_error.std():.3E}')
# Abs Error Mean: 1.272E-05
# Abs Error Std Dev: 9.937E-06

每个元素相差约1e-5-相当准确,考虑到我们使用的是32位精度! 我们还可以执行一个快速基准测试来衡量每种方法的速度:

from timeit import timeit
direct_time = timeit(
"f.conv1d(signal, kernel, bias=bias, padding=512)",
globals=locals(),
number=100
) / 100
fourier_time = timeit(
"fft_conv_1d(signal, kernel, bias=bias, padding=512)",
globals=locals(),
number=100
) / 100
print(f"Direct time: {direct_time:.3E} s")
print(f"Fourier time: {fourier_time:.3E} s")
# Direct time: 1.523E-02 s
# Fourier time: 1.149E-03 s

所测得的基准将随着您所使用的机器而发生重大变化。 (我正在使用非常老的Macbook Pro进行测试。)对于1025的内核大小,傅立叶卷积似乎要快10倍以上。

本片文章对傅立叶卷积提供了详尽的介绍。 我认为这是一个很酷的技巧,并且可以在许多实际应用中使用它。 我也很喜欢数学,因此很高兴看到编程和纯数学的这种交汇。 欢迎并鼓励所有评论和建设性批评。

本文的代码 github/fkodom/fft-conv-pytorch

卷积与互相关

在本文前面,我们通过在傅立叶空间中获取内核的复共轭来实现互相关。 这有效地扭转了内核的方向,现在我想证明为什么。 首先,请记住卷积和互相关的公式:

然后,让我们看一下内核的傅里叶变换(g):

取G的复共轭。请注意,内核g(x)是实值,因此不受共轭影响。 然后,更改变量(y = -x)并简化表达式。

因此,我们有效地改变了内核的方向!

作者 Frank Odom

deephub翻译组

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
重磅消息!比亚迪进军踏板车市场 首款大踏板电动车X1即将上市

重磅消息!比亚迪进军踏板车市场 首款大踏板电动车X1即将上市

户外小阿隋
2024-04-24 21:34:25
国运来了挡都挡不住?俄乌战争最起码给中国又争取了五年时间

国运来了挡都挡不住?俄乌战争最起码给中国又争取了五年时间

曾经年少
2024-03-05 11:38:53
郭晶晶游西安被赞似大唐公主穿搭引热议,网友:这双鞋配不上冠军

郭晶晶游西安被赞似大唐公主穿搭引热议,网友:这双鞋配不上冠军

鑫鑫说说
2024-04-24 10:46:04
广州萝岗万达停车费8元/小时!市民吐槽:真贵!

广州萝岗万达停车费8元/小时!市民吐槽:真贵!

普陀动物世界
2024-04-25 01:49:51
华为发布全球首款五合一车控模组

华为发布全球首款五合一车控模组

IT之家
2024-04-24 11:40:32
我终于和他发生了关系

我终于和他发生了关系

佰事可乐
2024-04-24 19:14:37
董云虎被公诉,涉案情节公布

董云虎被公诉,涉案情节公布

鲁中晨报
2024-04-24 10:37:07
杜兰特更衣室暴怒! 要求布克停止观看直播, 太阳内部一盘散沙

杜兰特更衣室暴怒! 要求布克停止观看直播, 太阳内部一盘散沙

创作者_1649389871352
2024-04-24 16:50:23
德国防长称普京不会停下来!爱沙尼亚放豪言,哈萨克斯坦瑟瑟发抖

德国防长称普京不会停下来!爱沙尼亚放豪言,哈萨克斯坦瑟瑟发抖

鹰眼Defence
2024-04-23 18:12:43
美海军陆战队1号价值50亿新直升机被草地打败 不准载拜登进出白宫

美海军陆战队1号价值50亿新直升机被草地打败 不准载拜登进出白宫

鱼莫语
2024-04-24 18:40:08
瘫痪17年汤淼现状:母亲妻子全都改嫁,女儿已7岁,生活已有保障

瘫痪17年汤淼现状:母亲妻子全都改嫁,女儿已7岁,生活已有保障

大咖唠体育
2024-04-24 11:59:48
世锦赛爆冷!瑞恩戴连胜五局逆转四冠王,霍金斯怒锤球桌引热议!

世锦赛爆冷!瑞恩戴连胜五局逆转四冠王,霍金斯怒锤球桌引热议!

世界体坛观察家
2024-04-25 05:39:35
爱奇艺全网独播!女主颜值超高,男主造型杀疯了,一整个期待

爱奇艺全网独播!女主颜值超高,男主造型杀疯了,一整个期待

阿芒娱乐说
2024-04-25 00:00:12
深圳机场第三跑道即将建成,问题不在于扩建,而是要建设第二机场

深圳机场第三跑道即将建成,问题不在于扩建,而是要建设第二机场

作家李楠枫
2024-04-24 21:58:55
笑死!全麻让多少人身败名裂了?医生:就说让你们平常别玩那么花

笑死!全麻让多少人身败名裂了?医生:就说让你们平常别玩那么花

石辰搞笑日常
2024-04-24 16:31:53
叶光富姐姐叶亚丹:看弟弟再次出征 心中多了一份安心

叶光富姐姐叶亚丹:看弟弟再次出征 心中多了一份安心

锦观新闻
2024-04-25 00:09:46
不是好惹的!周鸿祎“换车门”吐槽五菱车太小,五菱反手就是王炸

不是好惹的!周鸿祎“换车门”吐槽五菱车太小,五菱反手就是王炸

户外小阿隋
2024-04-24 19:23:30
2024年4月广东特大暴雨,到底有多大有多异常?为什么这么异常?

2024年4月广东特大暴雨,到底有多大有多异常?为什么这么异常?

中国气象爱好者
2024-04-24 08:41:50
“给我的时间太短了!”前京东副总裁、渐冻人蔡磊病情加重

“给我的时间太短了!”前京东副总裁、渐冻人蔡磊病情加重

齐鲁壹点
2024-04-24 07:54:43
致命9-0!我不该下威少,泰伦卢赛后道歉,而卡椒该把球给哈登啊

致命9-0!我不该下威少,泰伦卢赛后道歉,而卡椒该把球给哈登啊

巴叔GO聊体育
2024-04-24 14:37:41
2024-04-25 07:30:44
deephub
deephub
CV NLP和数据挖掘知识
1321文章数 1412关注度
往期回顾 全部

科技要闻

特斯拉被爆大量毁约应届生 友商"在线抢人"

头条要闻

美总统拜登签署剥离法案 TikTok发声明反对

头条要闻

美总统拜登签署剥离法案 TikTok发声明反对

体育要闻

足智多谋的哈姆,温水里的青蛙

娱乐要闻

方媛带两女儿参加婚礼,当花童超可爱

财经要闻

居民气价确实在涨,多地正普遍发生

汽车要闻

这灯效我能看半小时 奥迪Q6L e-tron有备而来

态度原创

艺术
家居
本地
时尚
公开课

艺术要闻

艺术名画︱爱尔兰画家大卫·科因的刀画作品

家居要闻

光影之间 空间暖意打造生活律动

本地新闻

荒野求生贝爷都得靠边站,真求生还得看留子

六年后全民倒戈,支持魏嬿婉扶正!

公开课

睡前进食会让你发胖吗?

无障碍浏览 进入关怀版