一、整体功能概述
本代码的主要目的是使用一个简单的神经网络来拟合一个非线性数据集。具体步骤包括生成非线性数据集、定义神经网络模型、训练模型以及可视化训练结果。
二、依赖库说明
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()