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 | 二维变换(默认只变换最后两维) | 任意 | 完整频谱 |
n | N 维变换(默认变换所有维) | 任意 | 完整频谱 |
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) -> Tensorinput:输入张量;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) -> Tensors:各变换维的信号尺寸;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):
- 部分频率必须为实值才能满足 Hermitian 性质,其虚部会被忽略——例如零频项的虚部永远无法体现在实数输出中。
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.5fftshift / ifftshift:零频居中与反操作
torch.fft.fftshift(input, dim=None) -> Tensor torch.fft.ifftshift(input, dim=None) -> Tensorfftshift对 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,是验证上述全部语义的权威参考。日常使用中,官方文档推荐的三种验证手段:
- 往返测试:用
torch.testing.assert_close(roundtrip, x, check_stride=False)验证ifft(fft(x))是否精确还原——注意 FFT 结果不保证与输入步长(stride)一致,需关闭check_stride; - 可分离性测试:用
assert_close(fft2, fft(fft(x, dim=0), dim=1), check_stride=False)验证多维变换与一维变换组合的等价性; - 对称性对比:用
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),仅供参考