Autoformer¶
import torch
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import warnings
# 忽略警告
warnings.filterwarnings('ignore')
# 设置全局字体为系统中的中文字体
font_path = '/System/Library/Fonts/Supplemental/Songti.ttc' # macOS 中的字体路径
font_prop = fm.FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 设置参数
sample_rate = 100 # 采样率100Hz
duration = 1.0 # 1秒信号
N = int(sample_rate * duration) # 样本点数
t = torch.linspace(0, duration, N) # 时间点
# 创建信号:10Hz和20Hz的正弦波
freq1, freq2 = 10, 20
amplitude1, amplitude2 = 1.0, 0.5
signal = amplitude1 * torch.sin(2 * np.pi * freq1 * t) + amplitude2 * torch.sin(2 * np.pi * freq2 * t)
# 计算FFT
signal_rfft = torch.fft.rfft(signal)
# 获取幅度谱 - 这里乘以2是因为能量在正负频率上分布
# 对于rfft,我们只看到正频率部分
magnitude_spectrum = torch.abs(signal_rfft) * 2 / N # 归一化
# 对于第0个和第N/2个频率分量不需要乘以2
if N % 2 == 0: # 如果N是偶数
magnitude_spectrum[0] /= 2
magnitude_spectrum[-1] /= 2
magnitude_spectrum
tensor([4.2915e-08, 2.3602e-03, 4.8474e-03, 7.6151e-03, 1.0882e-02, 1.5008e-02, 2.0674e-02, 2.9410e-02, 4.5613e-02, 8.9570e-02, 9.8378e-01, 1.0874e-01, 4.9075e-02, 2.9227e-02, 1.8140e-02, 9.8490e-03, 1.9463e-03, 7.6402e-03, 2.2964e-02, 6.0132e-02, 4.4995e-01, 1.3441e-01, 6.7800e-02, 4.8336e-02, 3.8852e-02, 3.3153e-02, 2.9309e-02, 2.6521e-02, 2.4397e-02, 2.2719e-02, 2.1359e-02, 2.0234e-02, 1.9289e-02, 1.8486e-02, 1.7797e-02, 1.7201e-02, 1.6684e-02, 1.6234e-02, 1.5841e-02, 1.5497e-02, 1.5198e-02, 1.4938e-02, 1.4714e-02, 1.4522e-02, 1.4360e-02, 1.4226e-02, 1.4118e-02, 1.4036e-02, 1.3978e-02, 1.3943e-02, 6.9658e-03])
# 获取频率轴
frequencies = torch.fft.rfftfreq(N, 1.0/sample_rate)
frequencies
tensor([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50.])
# 详细打印结果
print(f"采样率: {sample_rate}Hz")
print(f"信号长度: {N}点")
print(f"频率分辨率: {sample_rate/N}Hz")
print("\n前25个频率点及其幅度:")
for i in range(min(25, len(frequencies))):
print(f"频率 {frequencies[i]:.1f}Hz: 幅度 {magnitude_spectrum[i]:.4f}")
采样率: 100Hz 信号长度: 100点 频率分辨率: 1.0Hz 前25个频率点及其幅度: 频率 0.0Hz: 幅度 0.0000 频率 1.0Hz: 幅度 0.0024 频率 2.0Hz: 幅度 0.0048 频率 3.0Hz: 幅度 0.0076 频率 4.0Hz: 幅度 0.0109 频率 5.0Hz: 幅度 0.0150 频率 6.0Hz: 幅度 0.0207 频率 7.0Hz: 幅度 0.0294 频率 8.0Hz: 幅度 0.0456 频率 9.0Hz: 幅度 0.0896 频率 10.0Hz: 幅度 0.9838 频率 11.0Hz: 幅度 0.1087 频率 12.0Hz: 幅度 0.0491 频率 13.0Hz: 幅度 0.0292 频率 14.0Hz: 幅度 0.0181 频率 15.0Hz: 幅度 0.0098 频率 16.0Hz: 幅度 0.0019 频率 17.0Hz: 幅度 0.0076 频率 18.0Hz: 幅度 0.0230 频率 19.0Hz: 幅度 0.0601 频率 20.0Hz: 幅度 0.4500 频率 21.0Hz: 幅度 0.1344 频率 22.0Hz: 幅度 0.0678 频率 23.0Hz: 幅度 0.0483 频率 24.0Hz: 幅度 0.0389
# 可视化结果
plt.figure(figsize=(15, 10))
# 原始信号
plt.subplot(3, 1, 1)
plt.plot(t.numpy(), signal.numpy())
plt.title('原始信号 - 10Hz (幅度1.0) + 20Hz (幅度0.5)的正弦波 1*sin(2π*10*t) + 0.5*sin(2π*20*t)')
plt.xlabel('时间 (秒)')
plt.ylabel('幅度')
plt.grid(True)
# 频谱幅度 - 线性刻度
plt.subplot(3, 1, 2)
plt.plot(frequencies.numpy(), magnitude_spectrum.numpy())
plt.title('频谱幅度 (线性刻度)')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.grid(True)
# 标注10Hz和20Hz的峰值
plt.axvline(x=10, color='r', linestyle='--', alpha=0.7)
plt.axvline(x=20, color='g', linestyle='--', alpha=0.7)
plt.text(10, 0.9, '10Hz', color='r')
plt.text(20, 0.45, '20Hz', color='g')
# 频谱幅度 - 对数刻度,便于观察小幅值
plt.subplot(3, 1, 3)
plt.semilogy(frequencies.numpy(), magnitude_spectrum.numpy())
plt.title('频谱幅度 (对数刻度)')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度 (log)')
plt.grid(True)
plt.axvline(x=10, color='r', linestyle='--', alpha=0.7)
plt.axvline(x=20, color='g', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()
# 理论验证 - 重建信号
# 创建理想频谱
ideal_spectrum = torch.zeros_like(signal_rfft)
ideal_spectrum[10] = amplitude1 * N / 2 * np.exp(-0.5j * np.pi) # 10Hz, 相位为-π/2
ideal_spectrum[20] = amplitude2 * N / 2 * np.exp(-0.5j * np.pi) # 20Hz, 相位为-π/2
# 逆变换重建
reconstructed = torch.fft.irfft(ideal_spectrum, n=N)
# 比较原始信号和重建信号
plt.figure(figsize=(12, 6))
plt.plot(t.numpy(), signal.numpy(), 'b-', label='原始信号')
plt.plot(t.numpy(), reconstructed.numpy(), 'r--', label='从频谱重建的信号')
plt.title('原始信号与重建信号比较')
plt.legend()
plt.grid(True)
plt.show()
# 计算重建误差
error = torch.max(torch.abs(signal - reconstructed))
print(f"\n重建误差(最大绝对差): {error:.10f}")
Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol. Font 'default' does not have a glyph for '\u2212' [U+2212], substituting with a dummy symbol.
重建误差(最大绝对差): 1.0633112192
scipy.fft¶
from scipy.fft import fft
fft([4,3,2,1])
array([10.-0.j, 2.-2.j, 2.-0.j, 2.+2.j])
上面实现了 输入数组为 [4, 3, 2, 1],长度为 4。傅里叶变换的结果也是一个长度为 4 的复数数组。
计算公式为:
$ X_k = \sum_{n=0}^{N-1} x_n \cdot e^{-i \cdot 2\pi \cdot k \cdot n / N} $
其中:
$ X_k $ 是第 ( k ) 个频率分量的傅里叶系数
$ x_n $ 是输入信号的第 ( n ) 个样本
$ N $ 是输入信号的长度(在本例中为 4)
计算每个 $X_k$ :
$ X_0 $ :
$ X_0 = 4 \cdot e^{-i \cdot 2\pi \cdot 0 \cdot 0 / 4} + 3 \cdot e^{-i \cdot 2\pi \cdot 0 \cdot 1 / 4} + 2 \cdot e^{-i \cdot 2\pi \cdot 0 \cdot 2 / 4} + 1 \cdot e^{-i \cdot 2\pi \cdot 0 \cdot 3 / 4} $
$ X_0 = 4 + 3 + 2 + 1 = 10 $
$ X_1 $:
$ X_1 = 4 \cdot e^{-i \cdot 2\pi \cdot 1 \cdot 0 / 4} + 3 \cdot e^{-i \cdot 2\pi \cdot 1 \cdot 1 / 4} + 2 \cdot e^{-i \cdot 2\pi \cdot 1 \cdot 2 / 4} + 1 \cdot e^{-i \cdot 2\pi \cdot 1 \cdot 3 / 4} $
$ X_1 = 4 + 3 \cdot e^{-i \cdot \pi / 2} + 2 \cdot e^{-i \cdot \pi} + 1 \cdot e^{-i \cdot 3\pi / 2} $
$ X_1 = 4 + 3 \cdot (-i) + 2 \cdot (-1) + 1 \cdot i $
$ X_1 = 4 - 2 - 2i $