神经网络拟合高频信号实验

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq
import warnings
warnings.filterwarnings('ignore')

# 设置随机种子
torch.manual_seed(42)
np.random.seed(42)

# 生成高频信号
def generate_high_frequency_signal(n_samples=1000, freq=20):
    """生成高频正弦信号"""
    x = torch.linspace(0, 2*np.pi, n_samples).reshape(-1, 1)
    y = torch.sin(freq * x)  # 高频成分
    y += 0.5 * torch.sin(3 * freq * x)  # 更高频成分
    y += 0.3 * torch.sin(5 * freq * x)  # 超高频成分
    y += 0.1 * torch.randn_like(y)  # 加噪声
    
    return x, y

# 定义神经网络模型
class SimpleNN(nn.Module):
    def __init__(self, hidden_size=256):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(1, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, 1)
        )
    
    def forward(self, x):
        return self.net(x)

# 定义带傅里叶特征的神经网络
class FourierFeatureNN(nn.Module):
    def __init__(self, hidden_size=256, fourier_features=20):
        super().__init__()
        self.fourier_features = fourier_features
        
        # 随机初始化傅里叶特征映射的频率
        self.freqs = nn.Parameter(torch.randn(fourier_features) * 10.0)
        
        # 傅里叶特征映射后的网络
        self.net = nn.Sequential(
            nn.Linear(2 * fourier_features, hidden_size),  # 2* 因为包括sin和cos
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, 1)
        )
    
    def forward(self, x):
        # 傅里叶特征映射
        fourier_encoded = []
        for freq in self.freqs:
            fourier_encoded.append(torch.sin(freq * x))
            fourier_encoded.append(torch.cos(freq * x))
        
        fourier_features = torch.cat(fourier_encoded, dim=1)
        return self.net(fourier_features)

def train_and_evaluate(model, x_train, y_train, x_test, y_test, epochs=5000, lr=0.001):
    """训练和评估模型"""
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=lr)
    
    train_losses = []
    test_losses = []
    
    for epoch in range(epochs):
        # 训练
        model.train()
        optimizer.zero_grad()
        y_pred = model(x_train)
        loss = criterion(y_pred, y_train)
        loss.backward()
        optimizer.step()
        
        # 评估
        model.eval()
        with torch.no_grad():
            y_test_pred = model(x_test)
            test_loss = criterion(y_test_pred, y_test)
        
        if epoch % 500 == 0:
            train_losses.append(loss.item())
            test_losses.append(test_loss.item())
            
            if epoch % 1000 == 0:
                print(f'Epoch {epoch:4d}: Train Loss = {loss.item():.6f}, Test Loss = {test_loss.item():.6f}')
    
    return train_losses, test_losses, y_pred.detach()

def analyze_frequency_spectrum(signal, sampling_rate=1000):
    """分析信号的频率谱"""
    n = len(signal)
    signal_np = signal.numpy().flatten()
    
    # 计算FFT
    yf = fft(signal_np)
    xf = fftfreq(n, 1/sampling_rate)[:n//2]
    
    # 计算幅度谱
    magnitude = 2.0/n * np.abs(yf[:n//2])
    
    return xf, magnitude

def visualize_results(x, y_true, y_pred_nn, y_pred_fourier, freq_spectrum_nn, freq_spectrum_fourier):
    """可视化结果"""
    fig, axes = plt.subplots(2, 4, figsize=(15, 10))
    
    # 原始信号 vs NN预测
    axes[0, 0].plot(x.numpy(), y_true.numpy(), 'b-', alpha=0.6, label='Original Signal')
    axes[0, 0].plot(x.numpy(), y_pred_nn.numpy(), 'r--', alpha=0.8, label='NN Prediction')
    axes[0, 0].set_title('Standard NN: Time Domain')
    axes[0, 0].set_xlabel('Time')
    axes[0, 0].set_ylabel('Amplitude')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    
    # 原始信号 vs Fourier NN预测
    axes[0, 1].plot(x.numpy(), y_true.numpy(), 'b-', alpha=0.6, label='Original Signal')
    axes[0, 1].plot(x.numpy(), y_pred_fourier.numpy(), 'g--', alpha=0.8, label='Fourier NN Prediction')
    axes[0, 1].set_title('Fourier Feature NN: Time Domain')
    axes[0, 1].set_xlabel('Time')
    axes[0, 1].set_ylabel('Amplitude')
    axes[0, 1].legend()
    axes[0, 1].grid(True, alpha=0.3)
    
    # 两个预测的对比
    axes[0, 2].plot(x.numpy(), y_true.numpy(), 'b-', alpha=0.3, label='Original')
    axes[0, 2].plot(x.numpy(), y_pred_nn.numpy(), 'r--', alpha=0.6, label='Standard NN')
    axes[0, 2].plot(x.numpy(), y_pred_fourier.numpy(), 'g--', alpha=0.6, label='Fourier NN')
    axes[0, 2].set_title('Comparison of Predictions')
    axes[0, 2].set_xlabel('Time')
    axes[0, 2].set_ylabel('Amplitude')
    axes[0, 2].legend()
    axes[0, 2].grid(True, alpha=0.3)

    # 误差对比
    error_nn = torch.abs(y_true - y_pred_nn)
    error_fourier = torch.abs(y_true - y_pred_fourier)
    axes[0, 3].plot(x.numpy(), error_nn.numpy(), 'r-', alpha=0.6, label='Standard NN Error')
    axes[0, 3].plot(x.numpy(), error_fourier.numpy(), 'g-', alpha=0.6, label='Fourier NN Error')
    axes[0, 3].set_title('Prediction Errors')
    axes[0, 3].set_xlabel('Time')
    axes[0, 3].set_ylabel('Absolute Error')
    axes[0, 3].legend()
    axes[0, 3].grid(True, alpha=0.3)
    
    # 频率谱对比
    truncated_freq_index = 101
    axes[1, 0].plot(freq_spectrum_nn[0][:truncated_freq_index ], freq_spectrum_nn[1][:truncated_freq_index ], 'r-', label='NN Prediction Spectrum')
    axes[1, 0].set_title('Standard NN: Frequency Spectrum')
    axes[1, 0].set_xlabel('Frequency (Hz)')
    axes[1, 0].set_ylabel('Magnitude')
    axes[1, 0].legend()
    axes[1, 0].grid(True, alpha=0.3)
    
    axes[1, 1].plot(freq_spectrum_fourier[0][:truncated_freq_index ], freq_spectrum_fourier[1][:truncated_freq_index ], 'g-', label='Fourier NN Spectrum')
    axes[1, 1].set_title('Fourier Feature NN: Frequency Spectrum')
    axes[1, 1].set_xlabel('Frequency (Hz)')
    axes[1, 1].set_ylabel('Magnitude')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)
    
    axes[1, 2].plot(freq_spectrum_original[0][:truncated_freq_index], freq_spectrum_original[1][:truncated_freq_index], 'g-', label='Fourier NN Spectrum',
              linewidth=1.5)
    axes[1, 2].set_title('Original Frequency Spectrum')
    axes[1, 2].set_xlabel('Frequency (Hz)')
    axes[1, 2].set_ylabel('Magnitude')
    axes[1, 2].legend()
    axes[1, 2].grid(True, alpha=0.3)

    error_nn = freq_spectrum_original[1] - freq_spectrum_nn[1]
    error_fourier = freq_spectrum_original[1] - freq_spectrum_fourier[1]
    axes[1, 3].plot(freq_spectrum_original[0][:truncated_freq_index], error_nn[:truncated_freq_index], 'r-', alpha=0.6, label='Standard NN  Error', linewidth=1.5)
    axes[1, 3].plot(freq_spectrum_original[0][:truncated_freq_index], error_fourier[:truncated_freq_index], 'g-', alpha=0.6, label='Fourier NN Error', linewidth=1.5)
    axes[1, 3].set_title('Prediction Errors')
    axes[1, 3].set_xlabel('Frequency (Hz)')
    axes[1, 3].set_ylabel('Magnitude Error')
    axes[1, 3].legend()
    axes[1, 3].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()

# 计算频谱偏差
def calculate_spectral_bias(original_spectrum, predicted_spectrum, n_freqs=50):
        """计算频率谱的偏差"""
        bias = []
        for i in range(min(len(original_spectrum[1]), n_freqs)):
            orig_mag = original_spectrum[1][i]
            pred_mag = predicted_spectrum[1][i]
            if orig_mag > 0.01:  # 只考虑显著频率
                relative_error = abs(orig_mag - pred_mag) / orig_mag
                bias.append((original_spectrum[0][i], relative_error))
        return bias

def main():
    print("=" * 60)
    print("神经网络拟合高频信号实验")
    print("=" * 60)
    
    # 生成数据
    print("\n1. 生成高频信号...")
    n_samples = 100000
    train_test_ratio = 0.8
    n_train = int(n_samples * train_test_ratio)
    x, y = generate_high_frequency_signal(n_samples, freq=20)
    x_train, y_train = x[:n_train], y[:n_train]
    x_test, y_test = x[n_train:], y[n_train:]
    
    print(f"信号形状: x={x.shape}, y={y.shape}")
    print(f"信号频率: 包含20Hz, 60Hz, 100Hz的高频成分")
    
    # 训练标准神经网络
    print("\n2. 训练标准神经网络...")
    model_nn = SimpleNN(hidden_size=256)
    train_losses_nn, test_losses_nn, y_pred_nn = train_and_evaluate(
        model_nn, x_train, y_train, x_test, y_test, epochs=5000, lr=0.001
    )
    
    # 训练傅里叶特征神经网络
    print("\n3. 训练傅里叶特征神经网络...")
    model_fourier = FourierFeatureNN(hidden_size=256, fourier_features=20)
    train_losses_fourier, test_losses_fourier, y_pred_fourier = train_and_evaluate(
        model_fourier, x_train, y_train, x_test, y_test, epochs=5000, lr=0.001
    )
    
    # 分析频率谱
    print("\n4. 分析频率谱...")
    freq_spectrum_original = analyze_frequency_spectrum(y, sampling_rate=1000)
    freq_spectrum_nn = analyze_frequency_spectrum(y_pred_nn, sampling_rate=1000)
    freq_spectrum_fourier = analyze_frequency_spectrum(y_pred_fourier, sampling_rate=1000)
    
    # 打印频率分析结果
    print("\n频率分析:")
    print(f"原始信号主要频率: {freq_spectrum_original[0][np.argmax(freq_spectrum_original[1][:50])]:.1f} Hz")
    print(f"标准NN预测主要频率: {freq_spectrum_nn[0][np.argmax(freq_spectrum_nn[1][:50])]:.1f} Hz")
    print(f"傅里叶NN预测主要频率: {freq_spectrum_fourier[0][np.argmax(freq_spectrum_fourier[1][:50])]:.1f} Hz")
    
    # 计算和显示最终误差
    final_error_nn = torch.mean(torch.abs(y_test - model_nn(x_test))).item()
    final_error_fourier = torch.mean(torch.abs(y_test - model_fourier(x_test))).item()
    
    print("\n" + "=" * 60)
    print("实验结果总结:")
    print("=" * 60)
    print(f"标准神经网络最终测试误差: {final_error_nn:.6f}")
    print(f"傅里叶特征神经网络最终测试误差: {final_error_fourier:.6f}")
    print(f"误差改进: {(final_error_nn - final_error_fourier) / final_error_nn * 100:.1f}%")
    
    print("\n关键观察:")
    print("1. 标准神经网络难以拟合高频信号(频谱偏差)")
    print("2. 傅里叶特征变换显著改善了高频信号的拟合能力")
    print("3. 高频信号需要网络学习快速变化的函数,这超出了ReLU网络的频率容量")
    print("4. 傅里叶特征映射将输入投影到高频空间,帮助网络捕捉快速变化")
    
    
    
    # 额外实验:展示频谱偏差
    print("\n" + "=" * 60)
    print("频谱偏差分析:")
    print("=" * 60)
    
    
    
    bias_nn = calculate_spectral_bias(freq_spectrum_original, freq_spectrum_nn)
    bias_fourier = calculate_spectral_bias(freq_spectrum_original, freq_spectrum_fourier)
    
    print("\n频率相对误差(越高表示拟合越差):")
    print("频率(Hz) | 标准NN误差 | 傅里叶NN误差")
    print("-" * 40)
    
    for i in range(min(10, len(bias_nn))):
        freq = bias_nn[i][0]
        nn_error = bias_nn[i][1]
        fourier_error = bias_fourier[i][1] if i < len(bias_fourier) else float('nan')
        print(f"{freq:7.1f} | {nn_error:10.3f} | {fourier_error:10.3f}")
    
    # 可视化
    visualize_results(x, y, y_pred_nn, y_pred_fourier, freq_spectrum_nn, freq_spectrum_fourier)

    return {
        'model_nn': model_nn,
        'model_fourier': model_fourier,
        'errors': (final_error_nn, final_error_fourier),
        'spectral_bias': (bias_nn, bias_fourier)
    }

if __name__ == "__main__":
    # 检查环境
    print(f"Python版本: 3.12.10")
    print(f"PyTorch版本: {torch.__version__}")
    print(f"CUDA可用: {torch.cuda.is_available()}")
    if torch.cuda.is_available():
        print(f"CUDA版本: {torch.version.cuda}")
    
    # 运行实验
    results = main()
相关推荐
逆境不可逃4 小时前
Hello-Agents 第二部分-第八章总结:记忆与检索
人工智能·向量·rag
Fabarta技术团队5 小时前
模数共振・智能就位|枫清科技以企业级 AI Agent,响应国家 “智能体即服务” 战略
人工智能·科技
Terrence Shen5 小时前
Agent面试八股文(系列之三)
人工智能·大模型·agent·rag·智能体·大模型技术
十六年开源服务商5 小时前
2026网站建设方案内容审批避坑指南
大数据·人工智能
DisonTangor5 小时前
【上篇】SenseNova-U1:基于NEO-unify架构统一多模态理解与生成
人工智能·ai作画·开源·aigc
团象科技5 小时前
跨境业务频繁卡顿遇瓶颈?谷歌云AI算力补齐链路短板破局增收
大数据·人工智能·深度学习
AI医影跨模态组学5 小时前
Eur Radiol 哈尔滨医科大学附属肿瘤医院王瑞涛团队:多模态深度学习探究肿瘤与内脏脂肪对结直肠癌隐匿性腹膜转移的影响
人工智能·深度学习·论文·医学影像·影像组学
Keano Reurink5 小时前
AI内容检测:用SERP对比识别搜索引擎眼中的“优质内容“
人工智能·搜索引擎·chatgpt
easy_coder5 小时前
Kubernetes 域名解析问题排查实战:短名为什么有时能解析,有时不行
人工智能·kubernetes·云计算