使用带隐藏层的神经网络拟合非线性数据

一、整体功能概述

本代码的主要目的是使用一个简单的神经网络来拟合一个非线性数据集。具体步骤包括生成非线性数据集、定义神经网络模型、训练模型以及可视化训练结果。

二、依赖库说明

python

import numpy as np
import matplotlib.pyplot as plt
  • numpy:用于进行数值计算,包括数组操作、矩阵运算等。
  • matplotlib.pyplot:用于绘制数据可视化图表。

三、数据集生成

python

# 1. 生成非线性数据集
np.random.seed(42)
x = np.linspace(-10, 10, 100)
y = x**2 + np.random.normal(0, 10, x.shape)  # 添加噪声,生成非线性数据

  • np.random.seed(42):设置随机数种子,确保每次运行代码时生成的随机数相同,保证结果可复现。
  • np.linspace(-10, 10, 100):生成一个包含 100 个元素的一维数组 x,元素的值在 -10 到 10 之间均匀分布。
  • y = x**2 + np.random.normal(0, 10, x.shape):生成非线性数据,通过对 x 进行平方运算,并添加均值为 0、标准差为 10 的高斯噪声。

四、神经网络模型定义

python

# 2. 定义神经网络(带隐藏层的非线性模型)
class SimpleNN:
    def __init__(self, input_size=1, hidden_size=10, output_size=1):
        # 初始化权重和偏置
        self.w1 = np.random.randn(input_size, hidden_size)  # 输入层到隐藏层的权重
        self.b1 = np.random.randn(hidden_size)              # 隐藏层的偏置
        self.w2 = np.random.randn(hidden_size, output_size)  # 隐藏层到输出层的权重
        self.b2 = np.random.randn(output_size)              # 输出层的偏置

  • __init__ 方法:初始化神经网络的权重和偏置。
    • input_size:输入层的神经元数量,默认为 1。
    • hidden_size:隐藏层的神经元数量,默认为 10。
    • output_size:输出层的神经元数量,默认为 1。
    • self.w1:输入层到隐藏层的权重矩阵。
    • self.b1:隐藏层的偏置向量。
    • self.w2:隐藏层到输出层的权重矩阵。
    • self.b2:输出层的偏置向量。

python

    def relu(self, x):
        return np.maximum(0, x)  # ReLU激活函数
  • relu 方法:实现 ReLU(Rectified Linear Unit)激活函数,将输入小于 0 的值置为 0,大于等于 0 的值保持不变。

python

    def forward(self, x):
        # 前向传播
        self.z1 = np.dot(x, self.w1) + self.b1  # 隐藏层输入
        self.a1 = self.relu(self.z1)            # 隐藏层输出(应用ReLU)
        self.z2 = np.dot(self.a1, self.w2) + self.b2  # 输出层输入
        return self.z2  # 输出层输出(线性)

  • forward 方法:实现神经网络的前向传播过程。
    • self.z1:隐藏层的输入,通过输入 x 与权重矩阵 self.w1 相乘并加上偏置 self.b1 得到。
    • self.a1:隐藏层的输出,对 self.z1 应用 ReLU 激活函数。
    • self.z2:输出层的输入,通过隐藏层输出 self.a1 与权重矩阵 self.w2 相乘并加上偏置 self.b2 得到。
    • 返回输出层的输出 self.z2

python

    def loss(self, y_true, y_pred):
        return np.mean((y_true-y_pred)**2)  # 均方误差

  • loss 方法:计算均方误差(Mean Squared Error, MSE)作为损失函数,用于衡量模型预测值与真实值之间的差异。

python

    def gradient(self, x, y_true, y_pred):
        # 反向传播计算梯度
        m = x.shape[0]
        # 输出层的梯度
        d_z2 = y_pred - y_true
        d_w2 = np.dot(self.a1.T, d_z2) / m
        d_b2 = np.sum(d_z2, axis=0) / m
        # 隐藏层的梯度
        d_a1 = np.dot(d_z2, self.w2.T)
        d_z1 = d_a1 * (self.z1 > 0)  # ReLU的导数
        d_w1 = np.dot(x.T, d_z1) / m
        d_b1 = np.sum(d_z1, axis=0) / m
        return d_w1, d_b1, d_w2, d_b2

  • gradient 方法:实现反向传播算法,计算权重和偏置的梯度。
    • m:样本数量。
    • 输出层的梯度计算:
      • d_z2:输出层的误差。
      • d_w2:隐藏层到输出层的权重梯度。
      • d_b2:输出层的偏置梯度。
    • 隐藏层的梯度计算:
      • d_a1:隐藏层的误差。
      • d_z1:隐藏层输入的误差,通过对 d_a1 应用 ReLU 导数得到。
      • d_w1:输入层到隐藏层的权重梯度。
      • d_b1:隐藏层的偏置梯度。
    • 返回所有梯度。

python

    def train(self, x, y, lr=0.01, epochs=1000):
        for epoch in range(epochs):
            y_pred = self.forward(x)
            dw1, db1, dw2, db2 = self.gradient(x, y, y_pred)
            # 更新权重和偏置
            self.w1 -= lr * dw1
            self.b1 -= lr * db1
            self.w2 -= lr * dw2
            self.b2 -= lr * db2
            if (epoch + 1) % 100 == 0:
                print(f'Epoch [{epoch+1}/{epochs}], Loss: {self.loss(y, y_pred):.4f}')

  • train 方法:训练神经网络模型。
    • lr:学习率,控制权重和偏置更新的步长,默认为 0.01。
    • epochs:训练的轮数,默认为 1000。
    • 在每一轮训练中:
      • 调用 forward 方法进行前向传播,得到预测值 y_pred
      • 调用 gradient 方法计算梯度。
      • 根据梯度和学习率更新权重和偏置。
      • 每 100 轮打印一次当前的损失值。

五、模型训练

python

# 3. 训练模型
model = SimpleNN()
model.train(x.reshape(-1, 1), y.reshape(-1, 1), lr=0.001, epochs=5000)

  • 创建 SimpleNN 类的一个实例 model
  • 调用 train 方法进行训练,将输入数据 x 和标签数据 y 转换为二维数组,并设置学习率为 0.001,训练轮数为 5000。

六、结果可视化

python

# 4. 可视化结果
y_pred = model.forward(x.reshape(-1, 1))
plt.scatter(x, y, label='Data points')
plt.plot(x, x**2, color='red', label='y = x^2')
plt.plot(x, y_pred, color='green', label='Predicted')
plt.legend()
plt.show()

  • 调用 forward 方法得到模型的预测值 y_pred
  • 使用 plt.scatter 绘制原始数据点。
  • 使用 plt.plot 绘制真实的非线性函数 y = x^2(红色曲线)和模型的预测结果(绿色曲线)。
  • 使用 plt.legend 显示图例。
  • 使用 plt.show 显示绘制的图表。

七、总结

本代码通过构建一个简单的带隐藏层的神经网络,使用反向传播算法进行训练,成功拟合了一个非线性数据集。通过可视化结果可以直观地观察模型的拟合效果。

完整代码

import numpy as np
import matplotlib.pyplot as plt

# 1. 生成非线性数据集
np.random.seed(42)
x = np.linspace(-10, 10, 100)
y = x**2 + np.random.normal(0, 10, x.shape)  # 添加噪声,生成非线性数据

# 2. 定义神经网络(带隐藏层的非线性模型)
class SimpleNN:
    def __init__(self, input_size=1, hidden_size=10, output_size=1):
        # 初始化权重和偏置
        self.w1 = np.random.randn(input_size, hidden_size)  # 输入层到隐藏层的权重
        self.b1 = np.random.randn(hidden_size)              # 隐藏层的偏置
        self.w2 = np.random.randn(hidden_size, output_size)  # 隐藏层到输出层的权重
        self.b2 = np.random.randn(output_size)              # 输出层的偏置

    def relu(self, x):
        return np.maximum(0, x)  # ReLU激活函数

    def forward(self, x):
        # 前向传播
        self.z1 = np.dot(x, self.w1) + self.b1  # 隐藏层输入
        self.a1 = self.relu(self.z1)            # 隐藏层输出(应用ReLU)
        self.z2 = np.dot(self.a1, self.w2) + self.b2  # 输出层输入
        return self.z2  # 输出层输出(线性)

    def loss(self, y_true, y_pred):
        return np.mean((y_true-y_pred)**2)  # 均方误差

    def gradient(self, x, y_true, y_pred):
        # 反向传播计算梯度
        m = x.shape[0]
        # 输出层的梯度
        d_z2 = y_pred - y_true
        d_w2 = np.dot(self.a1.T, d_z2) / m
        d_b2 = np.sum(d_z2, axis=0) / m
        # 隐藏层的梯度
        d_a1 = np.dot(d_z2, self.w2.T)
        d_z1 = d_a1 * (self.z1 > 0)  # ReLU的导数
        d_w1 = np.dot(x.T, d_z1) / m
        d_b1 = np.sum(d_z1, axis=0) / m
        return d_w1, d_b1, d_w2, d_b2

    def train(self, x, y, lr=0.01, epochs=1000):
        for epoch in range(epochs):
            y_pred = self.forward(x)
            dw1, db1, dw2, db2 = self.gradient(x, y, y_pred)
            # 更新权重和偏置
            self.w1 -= lr * dw1
            self.b1 -= lr * db1
            self.w2 -= lr * dw2
            self.b2 -= lr * db2
            if (epoch + 1) % 100 == 0:
                print(f'Epoch [{epoch+1}/{epochs}], Loss: {self.loss(y, y_pred):.4f}')

# 3. 训练模型
model = SimpleNN()
model.train(x.reshape(-1, 1), y.reshape(-1, 1), lr=0.001, epochs=5000)

# 4. 可视化结果
y_pred = model.forward(x.reshape(-1, 1))
plt.scatter(x, y, label='Data points')
plt.plot(x, x**2, color='red', label='y = x^2')
plt.plot(x, y_pred, color='green', label='Predicted')
plt.legend()
plt.show()