神经网络模型的外推性验证

神经网络模型拟合sinx, 训练区间为-4pi~-2pi, 2pi~4pi。在-2pi~2pi, 神经网络模型拟合效果不好。

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
from math import pi

# 设置随机种子确保可重复性
torch.manual_seed(42)
np.random.seed(42)

# 设备设置
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}")

# 1. 生成训练数据(在指定区间采样)
def generate_training_data(n_samples_per_interval=200):
    """在 -4π~-2π 和 2π~4π 区间生成训练数据"""
    # 第一个区间: -4π ~ -2π
    x1 = torch.linspace(-4*pi, -2*pi, n_samples_per_interval).unsqueeze(1)
    y1 = torch.sin(x1)
    
    # 第二个区间: 2π ~ 4π
    x2 = torch.linspace(2*pi, 4*pi, n_samples_per_interval).unsqueeze(1)
    y2 = torch.sin(x2)
    
    # 合并数据
    x_train = torch.cat([x1, x2], dim=0)
    y_train = torch.cat([y1, y2], dim=0)
    
    return x_train.to(device), y_train.to(device)

# 2. 定义神经网络模型
class SinModel(nn.Module):
    def __init__(self, hidden_size=64, num_layers=3):
        super().__init__()
        layers = []
        
        # 输入层
        layers.append(nn.Linear(1, hidden_size))
        layers.append(nn.ReLU())
        
        # 隐藏层
        for _ in range(num_layers - 1):
            layers.append(nn.Linear(hidden_size, hidden_size))
            layers.append(nn.ReLU())
        
        # 输出层
        layers.append(nn.Linear(hidden_size, 1))
        
        self.model = nn.Sequential(*layers)
        
    def forward(self, x):
        return self.model(x)

# 3. 训练函数
def train_model(model, x_train, y_train, num_epochs=5000, lr=0.001):
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=lr)
    
    # 使用更简单的学习率调度器
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=1000, gamma=0.5)
    
    losses = []
    print("开始训练...")
    
    for epoch in range(num_epochs):
        # 前向传播
        outputs = model(x_train)
        loss = criterion(outputs, y_train)
        
        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # 学习率调度
        scheduler.step()
        
        # 记录损失
        losses.append(loss.item())
        
        if (epoch + 1) % 500 == 0:
            current_lr = optimizer.param_groups[0]['lr']
            print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.6f}, LR: {current_lr:.6f}')
    
    return losses

# 4. 生成测试数据(整个区间 -4π ~ 4π)
def generate_test_data(n_samples=1000):
    x_test = torch.linspace(-4*pi, 4*pi, n_samples).unsqueeze(1)
    y_test = torch.sin(x_test)
    return x_test.to(device), y_test.to(device)

# 5. 主程序
def main():
    # 生成训练数据
    x_train, y_train = generate_training_data(n_samples_per_interval=200)
    
    # 创建模型
    model = SinModel(hidden_size=128, num_layers=4).to(device)
    print(f"模型参数量: {sum(p.numel() for p in model.parameters() if p.requires_grad)}")
    
    # 训练模型
    losses = train_model(model, x_train, y_train, num_epochs=5000, lr=0.001)
    
    # 生成测试数据
    x_test, y_test_true = generate_test_data(1000)
    
    # 使用模型进行预测
    with torch.no_grad():
        y_test_pred = model(x_test)
        y_train_pred = model(x_train)
    
    # 将数据移到CPU用于绘图
    x_train_cpu = x_train.cpu().numpy()
    y_train_cpu = y_train.cpu().numpy()
    x_test_cpu = x_test.cpu().numpy()
    y_test_true_cpu = y_test_true.cpu().numpy()
    y_test_pred_cpu = y_test_pred.cpu().numpy()
    
    # 6. 绘制结果
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    
    # 子图1: 训练损失曲线
    axes[0, 0].plot(losses, label='Training Loss', color='blue', linewidth=2)
    axes[0, 0].set_xlabel('Epoch')
    axes[0, 0].set_ylabel('Loss (MSE)')
    axes[0, 0].set_title('Training Loss Curve')
    axes[0, 0].set_yscale('log')
    axes[0, 0].grid(True, alpha=0.3)
    axes[0, 0].legend()
    
    # 子图2: 完整拟合结果
    axes[0, 1].plot(x_test_cpu, y_test_true_cpu, 
                    label='True: y=sin(x)', color='blue', linewidth=3, alpha=0.7)
    axes[0, 1].plot(x_test_cpu, y_test_pred_cpu, 
                    label='Predicted', color='red', linewidth=2, linestyle='--')
    axes[0, 1].scatter(x_train_cpu, y_train_cpu, 
                      label='Training Points', color='green', s=20, alpha=0.6, edgecolors='black')
    axes[0, 1].set_xlabel('x')
    axes[0, 1].set_ylabel('y')
    axes[0, 1].set_title('Neural Network Fitting of y=sin(x)')
    axes[0, 1].grid(True, alpha=0.3)
    axes[0, 1].legend()
    
    # 用竖线标记采样区间
    axes[0, 1].axvline(x=-4*pi, color='gray', linestyle=':', alpha=0.5)
    axes[0, 1].axvline(x=-2*pi, color='gray', linestyle=':', alpha=0.5)
    axes[0, 1].axvline(x=2*pi, color='gray', linestyle=':', alpha=0.5)
    axes[0, 1].axvline(x=4*pi, color='gray', linestyle=':', alpha=0.5)
    
    # 子图3: 预测误差
    error = np.abs(y_test_pred_cpu - y_test_true_cpu)
    axes[1, 0].plot(x_test_cpu, error, color='purple', linewidth=2)
    axes[1, 0].set_xlabel('x')
    axes[1, 0].set_ylabel('Absolute Error')
    axes[1, 0].set_title('Prediction Error (|Predicted - True|)')
    axes[1, 0].grid(True, alpha=0.3)
    
    # 标记训练区间
    axes[1, 0].axvspan(-4*pi, -2*pi, alpha=0.1, color='green', label='Training Interval')
    axes[1, 0].axvspan(2*pi, 4*pi, alpha=0.1, color='green')
    axes[1, 0].legend()
    
    # 子图4: 放大显示训练区间的拟合效果
    axes[1, 1].plot(x_test_cpu, y_test_true_cpu, 
                    label='True: y=sin(x)', color='blue', linewidth=2, alpha=0.7)
    axes[1, 1].plot(x_test_cpu, y_test_pred_cpu, 
                    label='Predicted', color='red', linewidth=1.5, linestyle='--')
    axes[1, 1].scatter(x_train_cpu, y_train_cpu, 
                      label='Training Points', color='green', s=30, alpha=0.8, edgecolors='black')
    axes[1, 1].set_xlabel('x')
    axes[1, 1].set_ylabel('y')
    axes[1, 1].set_title('Zoomed View: Training Intervals')
    axes[1, 1].set_xlim([-4.5*pi, 4.5*pi])
    axes[1, 1].grid(True, alpha=0.3)
    axes[1, 1].legend()
    
    plt.tight_layout()
    plt.show()
    
    # 7. 打印训练和测试误差
    with torch.no_grad():
        # 训练误差
        train_pred = model(x_train)
        train_mse = torch.mean((train_pred - y_train)**2).item()
        
        # 测试误差(在整个区间)
        test_pred = model(x_test)
        test_mse = torch.mean((test_pred - y_test_true)**2).item()
        
        # 在训练区间的误差
        train_interval_indices = ((x_test <= -2*pi) & (x_test >= -4*pi)) | ((x_test >= 2*pi) & (x_test <= 4*pi))
        train_interval_pred = test_pred[train_interval_indices]
        train_interval_true = y_test_true[train_interval_indices]
        train_interval_mse = torch.mean((train_interval_pred - train_interval_true)**2).item()
        
        # 在未训练区间的误差
        non_train_interval_pred = test_pred[~train_interval_indices]
        non_train_interval_true = y_test_true[~train_interval_indices]
        non_train_interval_mse = torch.mean((non_train_interval_pred - non_train_interval_true)**2).item()
    
    print("\n" + "="*50)
    print("模型性能评估:")
    print(f"训练集MSE: {train_mse:.6f}")
    print(f"整个区间测试MSE: {test_mse:.6f}")
    print(f"训练区间MSE: {train_interval_mse:.6f}")
    print(f"未训练区间MSE: {non_train_interval_mse:.6f}")
    print("="*50)
    
    # 8. 绘制简单的对比图
    plt.figure(figsize=(12, 6))
    
    plt.subplot(1, 2, 1)
    plt.plot(x_test_cpu, y_test_true_cpu, label='True sin(x)', linewidth=3, alpha=0.7)
    plt.plot(x_test_cpu, y_test_pred_cpu, label='NN Prediction', linewidth=2, linestyle='--')
    plt.scatter(x_train_cpu, y_train_cpu, label='Training Points', 
               color='red', s=20, alpha=0.6, zorder=5)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('Neural Network Fitting of sin(x)')
    plt.legend()
    plt.grid(True, alpha=0.3)
    
    plt.subplot(1, 2, 2)
    plt.plot(x_test_cpu, y_test_pred_cpu - y_test_true_cpu, color='red', linewidth=2)
    plt.fill_between(x_test_cpu.flatten(), 0, y_test_pred_cpu.flatten() - y_test_true_cpu.flatten(), 
                     alpha=0.3, color='red')
    plt.axhline(y=0, color='black', linestyle='-', linewidth=1)
    plt.xlabel('x')
    plt.ylabel('Prediction Error')
    plt.title('Prediction Error')
    plt.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    main()

这个代码实现了以下功能:

  1. 数据生成

    • 在指定的两个区间(-4π ~ -2π 和 2π ~ 4π)生成训练数据

    • 在整个区间(-4π ~ 4π)生成测试数据

  2. 神经网络模型

    • 使用多层感知机(MLP)结构

    • 包含ReLU激活函数

    • 可通过参数调整隐藏层大小和层数

  3. 训练过程

    • 使用Adam优化器

    • 使用学习率调度器(ReduceLROnPlateau)

    • 记录训练损失

  4. 可视化结果

    • 训练损失曲线

    • 真实函数与神经网络预测的对比

    • 预测误差分析

    • 训练区间和未训练区间的对比

    • 用不同颜色标记训练区间

  5. 性能评估

    • 计算训练集误差

    • 计算整个区间测试误差

    • 分别计算训练区间和未训练区间的误差

这个神经网络应该能够很好地拟合训练区间的sin(x)函数,但在未训练区间(-2π ~ 2π)可能会表现出不同的拟合效果,这取决于神经网络的泛化能力。

相关推荐
长乐呀17 小时前
数据集获取与整理
python
清水白石00817 小时前
从脚本到系统:设计一个支持插件、限流、重试与监控的 Python 异步爬虫框架
网络·爬虫·python
deepin_sir17 小时前
02 - 第一个 Python 程序
开发语言·python
徐先生 @_@|||17 小时前
pycharm/IDEA + markdown + 图床(PicList)
ide·python·pycharm·intellij-idea
ZHW_AI课题组17 小时前
基于PCA与HOG特征融合的热轧钢带缺陷检测
人工智能·python·机器学习
MediaTea17 小时前
DL:扩散模型的基本原理与 PyTorch 实现
人工智能·pytorch·python·深度学习·机器学习
programhelp_17 小时前
Ramp OA 四关全过,CodeSignal OOD 完整复盘
linux·前端·python
Chasing__Dreams17 小时前
大模型应用开发--0--知识点
python
清风一徐17 小时前
Python文件处理
开发语言·python