神经网络模型拟合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()
这个代码实现了以下功能:
-
数据生成:
-
在指定的两个区间(-4π ~ -2π 和 2π ~ 4π)生成训练数据
-
在整个区间(-4π ~ 4π)生成测试数据
-
-
神经网络模型:
-
使用多层感知机(MLP)结构
-
包含ReLU激活函数
-
可通过参数调整隐藏层大小和层数
-
-
训练过程:
-
使用Adam优化器
-
使用学习率调度器(ReduceLROnPlateau)
-
记录训练损失
-
-
可视化结果:
-
训练损失曲线
-
真实函数与神经网络预测的对比
-
预测误差分析
-
训练区间和未训练区间的对比
-
用不同颜色标记训练区间
-
-
性能评估:
-
计算训练集误差
-
计算整个区间测试误差
-
分别计算训练区间和未训练区间的误差
-
这个神经网络应该能够很好地拟合训练区间的sin(x)函数,但在未训练区间(-2π ~ 2π)可能会表现出不同的拟合效果,这取决于神经网络的泛化能力。
