☰
PyTorch torch.fft 频谱变换完全指南:从 FFT 到 Hermitian 变换的函数族、参数与实战
2026/10/9 21:44:01 网站建设 项目流程

PyTorch torch.fft 频谱变换完全指南:从 FFT 到 Hermitian 变换的函数族、参数与实战

【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch

本指南以 PyTorch 仓库中的 docs/source/fft.md 频谱变换 API 参考页为核心,系统梳理torch.fft模块下 18 个离散傅里叶变换(DFT/FFT)函数与 4 个频率辅助函数:从 1D/ND 正逆变换、实数信号的紧凑单侧表示(rfft 系列)、Hermitian 对称输入的 hfft 系列,到归一化模式、频率轴生成与频移重排。读完本文,你将掌握每一类变换的适用场景、全部关键参数(n/s/dim/norm/out)的语义与默认值、奇偶长度信号的往返(round-trip)陷阱,以及如何结合仓库源码 torch/fft/init.py 与测试 test/test_spectral_ops.py 验证结果的正确性。

torch.fft 模块总览:一张图看清 18 + 4 个接口

torch.fft是 PyTorch 的离散傅里叶变换命名空间,官方 API 参考页(即本指南依据的 docs/source/fft.md)将其划分为两大组:

  • 快速傅里叶变换(Fast Fourier Transforms):fft、ifft、fft2、ifft2、fftn、ifftn、rfft、irfft、rfft2、irfft2、rfftn、irfftn、hfft、ihfft、hfft2、ihfft2、hfftn、ihfftn,共 18 个。
  • 辅助函数(Helper Functions):fftfreq、rfftfreq、fftshift、ifftshift,共 4 个。

这些函数在源码中通过torch._C._add_docstr将 Python 命名空间绑定到 C++ 层的torch._C._fft内建函数上(见 torch/fft/init.py),并完整声明在__all__中:

__all__ = [ "fft", "ifft", "fft2", "ifft2", "fftn", "ifftn", "rfft", "irfft", "rfft2", "irfft2", "rfftn", "irfftn", "hfft", "ihfft", "hfft2", "ihfft2", "hfftn", "ihfftn", "fftfreq", "rfftfreq", "fftshift", "ifftshift", "Tensor", ]

命名规律非常清晰,掌握后可举一反三:

后缀含义输入域输出域
无后缀 /fft一维变换任意(通常复数)完整频谱
2二维变换(默认只变换最后两维)任意完整频谱
nN 维变换(默认变换所有维)任意完整频谱
r前缀real,实数输入实数(时域)单侧 Hermitian 频谱
i前缀inverse,逆变换频域时域
h前缀Hermitian,Hermitian 对称输入单侧 Hermitian(时域)实数(频域)
ih前缀inverse Hermitian实数(频域)单侧 Hermitian(时域)

其中rfft/irfft系列与hfft/ihfft系列互为镜像关系:前者输入实数、输出具备 Hermitian 对称性的频谱;后者输入 Hermitian 对称信号、输出实数。下面逐一展开。

三大核心概念:Hermitian 对称性、norm 归一化与逻辑变换长度

在进入函数细节之前,先掌握贯穿全部 18 个变换函数的三个基础概念,它们决定了输出形状、数值语义与往返精度。

1. 实信号的 Hermitian 对称性

任何实数信号的傅里叶变换都满足 Hermitian 性质:

  • 一维:X[i] = conj(X[-i])
  • 二维:X[i, j] = conj(X[-i, -j])
  • N 维:X[i_1, ..., i_n] = conj(X[-i_1, ..., -i_n])

这意味着实信号完整频谱的负频率一半是冗余的(见 torch/fft/init.py 对fft的说明)。fft/fft2/fftn始终返回包含正负频率的完整输出;而rfft系列利用该性质只返回奈奎斯特频率以下的正频率部分,输出更紧凑、计算更省。

2. 三种归一化模式:forward / backward / ortho

所有变换函数都接受norm参数,取值为"forward"、"backward"、"ortho"三者之一,默认是"backward"。其含义在正、逆变换方向上互为对偶:

norm 模式正向变换(fft/rfft/hfft)逆变换(ifft/irfft/ihfft)
"forward"除以1/n不归一化
"backward"(默认)不归一化除以1/n
"ortho"除以1/sqrt(n)除以1/sqrt(n)

其中n是逻辑变换长度:一维变换中即信号长度n;多维变换中为n = prod(s),即各变换维尺寸的乘积。关键性质是:正逆变换使用相同的norm模式时,两者之间的总归一化恰为1/n,从而保证ifft(fft(x))是精确的逆运算(见 torch/fft/init.py)。"ortho"模式使变换成为正交(酉)变换,正逆对称,常用于数值稳定性要求高的场景。

3. 信号长度 n / s:补零、截断与往返的关键

  • 一维函数用n(int):若给定,变换前输入会被零填充或截断到该长度。
  • 多维函数用s(tuple):每个变换维dim[i]会被零填充或截断到s[i];其中指定-1表示该维不做填充。默认s = [input.size(d) for d in dim]。

n/s对irfft、irfft2/irfftn、hfft系列尤其关键:由于单侧 Hermitian 输入的形状无法区分原始信号是奇长度还是偶长度,默认会假设为偶数长度,奇长度信号若不显式传n/s将无法正确往返。源码中的建议是始终显式传入信号长度(见 torch/fft/init.py)。

一维变换:fft 与 ifft

fft计算输入的一维离散傅里叶变换,ifft是其逆:

torch.fft.fft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor torch.fft.ifft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor
  • input:输入张量;
  • n:信号长度,给定则先补零或截断;
  • dim:执行一维 FFT 的维度,默认-1(最后一维);
  • norm:归一化模式,默认"backward";
  • out:可选输出张量(关键字参数)。

官方示例展示了最基本的用法(来自 torch/fft/init.py):

>>> t = torch.arange(4) >>> t tensor([0, 1, 2, 3]) >>> torch.fft.fft(t) tensor([ 6.+0.j, -2.+2.j, -2.+0.j, -2.-2.j]) >>> t = torch.tensor([0.+1.j, 2.+3.j, 4.+5.j, 6.+7.j]) >>> torch.fft.fft(t) tensor([12.+16.j, -8.+0.j, -4.-4.j, 0.-8.j])

逆变换示例(torch/fft/init.py):

>>> t = torch.tensor([ 6.+0.j, -2.+2.j, -2.+0.j, -2.-2.j]) >>> torch.fft.ifft(t) tensor([0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j])

注意fft的结果中,T[1] == T[-1].conj()、T[2] == T[-2].conj()的对称性一目了然,这正是下文rfft压缩表示的依据。

多维变换:fft2 / ifft2 与 fftn / ifftn

fft2/ifft2等价于fftn/ifftn只变换最后两维的版本;fftn/ifftn默认变换所有维度,是通用入口:

torch.fft.fft2(input, s=None, dim=(-2, -1), norm=None, *, out=None) -> Tensor torch.fft.fftn(input, s=None, dim=None, norm=None, *, out=None) -> Tensor
  • s:各变换维的信号尺寸;dim[i]中的-1表示该维不填充;
  • dim:要变换的维度;fft2默认最后两维,fftn默认所有维度(若给了s则默认最后len(s)维);
  • 多维时逻辑长度n = prod(s),归一化语义与一维完全一致。

多维 DFT 是可分离的,因此fft2等价于沿两个维度依次做两次一维fft。官方示例用torch.testing.assert_close验证了这一等价性(torch/fft/init.py):

>>> x = torch.rand(10, 10, dtype=torch.complex64) >>> fft2 = torch.fft.fft2(x) >>> two_ffts = torch.fft.fft(torch.fft.fft(x, dim=0), dim=1) >>> torch.testing.assert_close(fft2, two_ffts, check_stride=False)

ifft2、ifftn与正向版本同构,只是默认归一化方向相反(默认"backward"即除以1/n)。

实数输入的紧凑表示:rfft / irfft 系列

对于实数输入,完整频谱的负频率冗余,因此rfft系列只返回正频率部分(含零频与奈奎斯特项),输出长度约为输入的一半,既省内存又省计算。

一维:rfft / irfft

torch.fft.rfft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor torch.fft.irfft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor

对比示例(torch/fft/init.py):

>>> t = torch.arange(4) >>> torch.fft.rfft(t) tensor([ 6.+0.j, -2.+2.j, -2.+0.j]) >>> torch.fft.fft(t) # 完整输出作对比 tensor([ 6.+0.j, -2.+2.j, -2.+0.j, -2.-2.j])

可见对称元素T[-1] == T[1].conj()被省略;奈奎斯特频率处T[-2] == T[2]是自身对称对,因此必须为实数。

irfft有两条重要约束(见 torch/fft/init.py):

  1. 部分频率必须为实值才能满足 Hermitian 性质,其虚部会被忽略——例如零频项的虚部永远无法体现在实数输出中。
  2. n默认取偶数值n = 2 * (input.size(dim) - 1);若原始信号是奇长度,不传n就无法正确往返。

奇偶长度陷阱的官方示例(torch/fft/init.py):

>>> t = torch.linspace(0, 1, 5) # 奇长度:5 >>> T = torch.fft.rfft(t) >>> T tensor([ 2.5000+0.0000j, -0.6250+0.8602j, -0.6250+0.2031j]) >>> torch.fft.irfft(T) # 不传 n:默认按偶长度 4 恢复,结果错误 tensor([0.1562, 0.3511, 0.7812, 1.2114]) >>> roundtrip = torch.fft.irfft(T, t.numel()) # 显式传 n=5,正确往返 >>> torch.testing.assert_close(roundtrip, t, check_stride=False)

多维:rfft2 / irfft2、rfftn / irfftn

rfft2/rfftn只压缩最后一个变换维的负频率,其余维输出完整。例如对10 x 10实数张量,rfft2输出形状为[10, 6]:

>>> t = torch.rand(10, 10) >>> rfft2 = torch.fft.rfft2(t) >>> rfft2.size() torch.Size([10, 6]) >>> fft2 = torch.fft.fft2(t) >>> torch.testing.assert_close(fft2[..., :6], rfft2, check_stride=False)

rfft2与两次一维变换等价(torch/fft/init.py):

>>> two_ffts = torch.fft.fft(torch.fft.rfft(t, dim=1), dim=0) >>> torch.testing.assert_close(rfft2, two_ffts, check_stride=False)

irfft2/irfftn的s默认在最后维取偶数值s[-1] = 2 * (input.size(dim[-1]) - 1),并要求dim的最后一个是半 Hermitian 压缩维。官方示例同样演示了奇长度信号的往返修复(torch/fft/init.py):

>>> t = torch.rand(10, 9) # 最后一维为奇长度 9 >>> T = torch.fft.rfft2(t) >>> torch.fft.irfft2(T).size() # 默认输出 [10, 8],错误 torch.Size([10, 8]) >>> roundtrip = torch.fft.irfft2(T, t.size()) # 显式传 s 后正确 >>> torch.testing.assert_close(roundtrip, t, check_stride=False)

Hermitian 对称输入:hfft / ihfft 系列

hfft/ihfft系列与rfft/irfft系列完全镜像:rfft期望时域实数、得到频域 Hermitian 对称;hfft期望时域 Hermitian 对称(单侧表示)、得到频域实数(见 torch/fft/init.py)。

一维:hfft / ihfft

torch.fft.hfft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor torch.fft.ihfft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor

官方示例完整展示了从实数频域信号到 Hermitian 时域、再用单侧输入恢复的闭环(torch/fft/init.py):

>>> t = torch.linspace(0, 1, 5) >>> T = torch.fft.ifft(t) # 实数信号的 IFFT 产生 Hermitian 对称输出 >>> T tensor([ 0.5000-0.0000j, -0.1250-0.1720j, -0.1250-0.0406j, -0.1250+0.0406j, -0.1250+0.1720j]) # T[1] == T[-1].conj() 且 T[2] == T[-2].conj(),负频率冗余 >>> torch.fft.hfft(T[:3], n=5) # 只用正频率部分即可恢复实数频域信号 tensor([0.0000, 0.2500, 0.5000, 0.7500, 1.0000]) >>> torch.fft.hfft(T[:3]) # 不传 n 时按偶长度恢复,同样会出错 tensor([0.1250, 0.2809, 0.6250, 0.9691])

ihfft是hfft的逆,输入实数、输出单侧 Hermitian(torch/fft/init.py):

>>> t = torch.arange(5) >>> torch.fft.ihfft(t) tensor([ 2.0000-0.0000j, -0.5000-0.6882j, -0.5000-0.1625j]) >>> torch.fft.ifft(t) # 完整输出作对比 tensor([ 2.0000-0.0000j, -0.5000-0.6882j, -0.5000-0.1625j, -0.5000+0.1625j, -0.5000+0.6882j])

多维:hfft2 / ihfft2、hfftn / ihfftn

多维版本与rfft2/rfftn的规则完全对称:hfft2/hfftn在最后一个变换维接收单侧 Hermitian 输入,输出实数;s默认最后维取偶数值s[-1] = 2 * (input.size(dim[-1]) - 1)。官方示例(torch/fft/init.py):

>>> T = torch.rand(10, 9) # 实数频域信号,奇长度 >>> t = torch.fft.ihfft2(T) # 生成 Hermitian 时域信号 >>> torch.fft.hfft2(t).size() # 默认输出 [10, 10],错误 torch.Size([10, 10]) >>> roundtrip = torch.fft.hfft2(t, T.size()) # 显式传 s 后正确往返 >>> torch.allclose(roundtrip, T) True

辅助函数:频率轴与频谱重排

fftfreq:生成 FFT 样本频率

torch.fft.fftfreq(n, d=1.0, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> Tensor

约定fft先返回正频率、再按逆序返回负频率,因此频率序列为(torch/fft/init.py):

f = [0, 1, ..., (n - 1) // 2, -(n // 2), ..., -1] / (d * n)
  • n:FFT 长度;
  • d:采样间隔(长度单位),默认 1.0;除以实际采样间隔即可换算为物理频率单位;
  • 偶长度时奈奎斯特频率f[n/2]遵循 NumPy 惯例取负值(torch/fft/init.py)。
>>> torch.fft.fftfreq(5) tensor([ 0.0000, 0.2000, 0.4000, -0.4000, -0.2000]) >>> torch.fft.fftfreq(4) # 偶长度:奈奎斯特频率 f[2] = -0.5 tensor([ 0.0000, 0.2500, -0.5000, -0.2500])

rfftfreq:生成 rfft 样本频率

torch.fft.rfftfreq(n, d=1.0, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) -> Tensor

与fftfreq的唯一区别:rfft只返回正频率,因此频率序列为f = torch.arange((n + 1) // 2) / (d * n),且偶长度下的奈奎斯特频率取正值(与fftfreq符号相反,torch/fft/init.py):

>>> torch.fft.rfftfreq(5) tensor([0.0000, 0.2000, 0.4000]) >>> torch.fft.rfftfreq(4) tensor([0.0000, 0.2500, 0.5000]) # 对比 fftfreq(4) 中为 -0.5

fftshift / ifftshift:零频居中与反操作

torch.fft.fftshift(input, dim=None) -> Tensor torch.fft.ifftshift(input, dim=None) -> Tensor

fftshift对 N 维 FFT 数据做周期性移位,把原点(0, ..., 0)(零频)移到每个所选维度的input.shape[dim] // 2处,使频率按从负到正升序排列;dim默认所有维度,也可指定部分维度。ifftshift是其逆运算,二者组合构成恒等变换(torch/fft/init.py):

>>> f = torch.fft.fftfreq(5) >>> f tensor([ 0.0000, 0.2000, 0.4000, -0.4000, -0.2000]) >>> shifted = torch.fft.fftshift(f) >>> torch.fft.ifftshift(shifted) tensor([ 0.0000, 0.2000, 0.4000, -0.4000, -0.2000])

一个非常实用的场景是中心化网格数据:若空间数据定义在中心化网格[-(N//2), (N-1)//2]上,而标准 FFT 期望非中心化网格[0, N),则可用ifftshift前移、fftshift后移完成闭环(torch/fft/init.py):

>>> x_centered = torch.arange(-5, 5) >>> x_uncentered = torch.fft.ifftshift(x_centered) # 空间域去中心化 >>> fft_uncentered = torch.fft.fft(x_uncentered) >>> fft_centered = torch.fft.fftshift(fft_uncentered) # 频域中心化 >>> x_centered_2 = torch.fft.fftshift(torch.fft.ifft(torch.fft.ifftshift(fft_centered))) >>> torch.testing.assert_close(x_centered.to(torch.complex64), x_centered_2, check_stride=False)

参数速查:18 个变换函数的签名总表

函数签名默认变换维关键差异
fft/ifft(input, n=None, dim=-1, norm=None, *, out=None)最后一维一维完整频谱
fft2/ifft2(input, s=None, dim=(-2, -1), norm=None, *, out=None)最后两维二维完整频谱
fftn/ifftn(input, s=None, dim=None, norm=None, *, out=None)所有维N 维完整频谱
rfft/irfft(input, n=None, dim=-1, norm=None, *, out=None)最后一维实数输入 / 单侧输出
rfft2/irfft2(input, s=None, dim=(-2, -1), norm=None, *, out=None)最后两维仅压缩最后维
rfftn/irfftn(input, s=None, dim=None, norm=None, *, out=None)所有维仅压缩最后维
hfft/ihfft(input, n=None, dim=-1, norm=None, *, out=None)最后一维Hermitian 输入 / 实数输出
hfft2/ihfft2(input, s=None, dim=(-2, -1), norm=None, *, out=None)最后两维单侧输入,最后维压缩
hfftn/ihfftn(input, s=None, dim=None, norm=None, *, out=None)所有维单侧输入,最后维压缩

共享参数语义小结:

  • input:输入张量;
  • n(一维)/s(多维):变换前的补零或截断长度/尺寸,s中-1表示该维不填充;irfft/irfftn/hfft/hfftn系列默认按偶数长度推断;
  • dim:变换维度;逆变换系列要求最后一个变换维为压缩维;
  • norm:"forward"/"backward"(默认)/"ortho";
  • out:可选输出张量(仅关键字);
  • fftfreq/rfftfreq额外支持dtype、layout、device、requires_grad等工厂参数,语义与常规张量工厂函数一致(见 torch/_torch_docs.py 中factory_common_args的定义)。

源码实现:Python 命名空间如何连接到 C++ FFT 内核

torch.fft的全部函数并非在 Python 中实现算法,而是通过_add_docstr把文档字符串与 C++ 层torch._C._fft中的内建函数绑定(见 torch/fft/init.py):

import torch from torch._C import _add_docstr, _fft # type: ignore[attr-defined] fft = _add_docstr( _fft.fft_fft, r"""fft(input, n=None, dim=-1, norm=None, *, out=None) -> Tensor ...""" )

也就是说,torch.fft.fft实际调用的是 C++ 层的torch._C._fft.fft_fft。源码注释明确指出:这里不仅为频谱算子附加文档字符串,更重要的是把 torch.fft Python 命名空间连接到 torch._C._fft 内建函数(见 torch/fft/init.py)。这意味着这些算子直接受益于 PyTorch 底层的自动微分、设备分派(CPU/CUDA/MPS 等)与内存管理机制。各函数的完整 docstring 均以_add_docstr形式内嵌在 torch/fft/init.py 中,是 docs/source/fft.md 生成 API 文档的权威来源。

精度与平台限制:CUDA 上的 half / chalf 支持

所有变换函数在文档中都声明了相同的精度限制(见 torch/fft/init.py 等处的 Note):

  • CUDA 上支持torch.half(float16)与torch.chalf(complex32),要求 GPU 架构为SM53 及以上;
  • 但半精度下每个变换维的信号长度必须是 2 的幂;
  • 对irfft、irfftn、hfft、hfftn系列,默认参数下要求压缩维尺寸形如(2^n + 1)(因为默认输出按偶数长度2 * (size - 1)推断)。

这些限制是使用半精度加速时需要提前核对的关键前提,与文档中给出的默认长度公式保持一致。

结果验证与测试

仓库中的测试入口为 test/test_spectral_ops.py,是验证上述全部语义的权威参考。日常使用中,官方文档推荐的三种验证手段:

  1. 往返测试:用torch.testing.assert_close(roundtrip, x, check_stride=False)验证ifft(fft(x))是否精确还原——注意 FFT 结果不保证与输入步长(stride)一致,需关闭check_stride;
  2. 可分离性测试:用assert_close(fft2, fft(fft(x, dim=0), dim=1), check_stride=False)验证多维变换与一维变换组合的等价性;
  3. 对称性对比:用assert_close(fft2[..., :6], rfft2, check_stride=False)验证rfft系列输出正是完整频谱的正频率部分。

将这三类断言融入自己的代码,可以快速发现奇偶长度n/s传参错误、归一化模式不一致等高频踩坑点。

实践要点总结

  • 选型:输入为实数且只关心幅度谱,优先rfft系列(省一半内存与计算);需要做频域滤波后还原,成对使用rfft/irfft并始终显式传递原始信号长度n或形状s,奇偶长度才能正确往返;
  • 可视化频谱:用fftfreq/rfftfreq生成频率轴,用fftshift/ifftshift将零频居中,二者是频域分析和滤波器设计的标准组合;
  • 归一化:默认"backward"下逆变换自带1/n;追求正逆对称时统一改用"ortho";
  • 半精度:CUDA 上启用torch.half/torch.chalf前,确认架构不低于 SM53 且各变换维长度为 2 的幂;
  • 权威参考:每个函数完整的行为说明、示例与参数表格可直接查阅 docs/source/fft.md 的 API 参考页,实现细节以 torch/fft/init.py 的 docstring 为准。

【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询