BP神经网络拟合sin曲线:从数学原理到PyTorch实战

1次阅读
没有评论

共计 2136 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

为什么需要拟合 sin 函数

在工程领域中,sin 函数的拟合具有广泛的实际意义。比如在信号处理中,我们需要对采集到的周期性信号进行建模和预测;在控制系统中,许多被控对象的动态特性可以用 sin 函数来描述。传统方法如多项式回归虽然简单,但在拟合周期性函数时往往效果不佳。

BP 神经网络拟合 sin 曲线:从数学原理到 PyTorch 实战

多项式回归 vs 神经网络

我们先来看一个简单的对比实验。假设我们用 5 阶多项式来拟合 sin 函数:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)

# 多项式拟合
coeff = np.polyfit(x, y, 5)
poly_y = np.polyval(coeff, x)

plt.plot(x, y, label='True sin')
plt.plot(x, poly_y, label='5th order poly')
plt.legend()
plt.show()

从结果可以看到,多项式在区间内能勉强拟合,但在边界处会出现明显的过拟合现象。这就是我们需要神经网络的原因。

BP 神经网络设计

网络结构选择

对于 sin 函数这种简单的非线性映射,一个包含 1 - 2 个隐藏层的网络就足够了。经过实验,我们发现以下结构表现良好:

  • 输入层:1 个神经元(x 值)
  • 隐藏层 1:10 个神经元(使用 tanh 激活)
  • 隐藏层 2:10 个神经元(使用 tanh 激活)
  • 输出层:1 个神经元(线性输出)

激活函数对比

我们测试了三种常见激活函数:

  1. Sigmoid:在深层网络中容易出现梯度消失
  2. ReLU:对于周期性函数拟合效果不佳
  3. Tanh:最适合本任务,因其输出范围与 sin 函数匹配

PyTorch 实现

下面是完整的 PyTorch 实现代码,包含关键注释:

import torch
import torch.nn as nn
import torch.optim as optim

# 生成训练数据
x = torch.linspace(-np.pi, np.pi, 100).reshape(-1, 1)
y = torch.sin(x)

# 定义网络结构
class SinNet(nn.Module):
    def __init__(self):
        super(SinNet, self).__init__()
        self.fc1 = nn.Linear(1, 10)
        self.fc2 = nn.Linear(10, 10)
        self.fc3 = nn.Linear(10, 1)
        self.dropout = nn.Dropout(0.1)  # 轻微 dropout 防止过拟合

    def forward(self, x):
        x = torch.tanh(self.fc1(x))
        x = self.dropout(x)
        x = torch.tanh(self.fc2(x))
        x = self.fc3(x)
        return x

# 训练参数
model = SinNet()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# 训练循环
losses = []
for epoch in range(1000):
    optimizer.zero_grad()
    outputs = model(x)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()
    losses.append(loss.item())

    if epoch % 100 == 0:
        print(f'Epoch {epoch}, Loss: {loss.item():.4f}')

性能分析

训练过程监控

通过绘制 loss 曲线,我们可以观察训练过程:

plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('MSE Loss')
plt.title('Training Loss Curve')
plt.show()

通常在前 200 个 epoch 内 loss 会快速下降,之后趋于平缓。

学习率影响

我们对比了不同学习率的效果:

  • lr=0.1:容易震荡发散
  • lr=0.01:稳定收敛(推荐)
  • lr=0.001:收敛速度过慢

避坑指南

梯度消失问题

当使用 sigmoid 激活时,深层网络可能出现梯度消失。解决方案:

  1. 使用 tanh 或 ReLU 激活
  2. 加入残差连接
  3. 使用批归一化

数据标准化

对于周期性函数,建议将输入数据归一化到 [-π, π] 区间,这与 sin 函数的自然周期匹配。

早停法实现

为防止过拟合,可以实现简单的早停机制:

best_loss = float('inf')
patience = 20
counter = 0

for epoch in range(1000):
    # ... 训练代码...

    if loss.item() < best_loss:
        best_loss = loss.item()
        counter = 0
    else:
        counter += 1
        if counter >= patience:
            print('Early stopping')
            break

总结与思考

通过这个实验,我们掌握了用 BP 神经网络拟合非线性函数的基本方法。这种方法可以轻松扩展到其他周期函数的拟合,只需调整网络结构和训练参数。

留给读者的思考题:如果要拟合 f(x)=sin(x)+0.3cos(2x)这样的复合周期函数,网络结构应该如何调整?欢迎在评论区分享你的实验结

正文完
 0
评论(没有评论)