一、实验目的
理解BP神经网络的结构和原理,掌握反向传播算法对神经元的训练过程,了解反向传播公式。通过构建BP网络模式识别实例,熟悉BP网络的原理及结构
二、实验内容
基于提供的数据集,训练1个BP神经网络模型:
1. 模型参数:
- layer num(网络层数):4
- 网络每层节点数、激活函数、训练函数、学习率等: 自定义
2. 输出:
- 模型的准确率
3. 要求:
-选择合适的模型参数
-给出神经网络训练成功后的误差变化曲线图
-设置不同的学习率,如0.01、0.1、0.5、1。给出各学习率下的误差变化曲线图,分析学习率变化对训练结果的影响。
三、实验原理
BP算法的基本原理
BP算法是一种监督学习算法,利用梯度下降法(Gradient Descent)来优化神经网络的权重。其工作原理分为两个阶段:前向传播(Forward Propagation)*和*反向传播(Backward Propagation)。
1. 前向传播阶段:
在前向传播阶段,输入信号通过网络的各层逐层传递,直到输出层生成网络的输出。具体过程如下:
- 输入层接收外部输入数据,将输入值传递给第一隐藏层。
- 隐藏层对接收到的信号进行加权求和,并通过激活函数(如Sigmoid、ReLU、Tanh等)进行处理,传递到下一层。
- 输出层通过激活函数生成最终输出。
通过这个过程,神经网络得到一个预测值(网络输出)。
2. 反向传播阶段:
在反向传播阶段,神经网络通过计算输出误差,并将误差反向传播回各层,逐步调整各个层的权重。具体步骤如下:
- 计算误差:首先,计算网络的预测输出与真实标签之间的误差。常用的误差度量方法是均方误差(MSE)或交叉熵损失函数
- 计算输出层的误差:在输出层,误差被定义为目标输出与实际输出的差异,然后用来计算输出层的梯度。
- 反向传播误差:接着,误差从输出层反向传播到前一层,通过链式法则计算每一层的误差。
- 更新权重和偏置:一旦得到误差,利用梯度下降法来更新各层的权重和偏置,使得误差逐渐减小。
- 梯度的计算:梯度通过链式法则逐层计算得到,梯度的大小决定了权重更新的幅度。
3. 反向传播算法的总结:
- 前向传播:从输入层到输出层,计算每一层的输出。
- 反向传播:从输出层到输入层,计算每一层的误差并更新权重。
- 梯度下降:通过梯度下降法最小化误差函数,更新网络的参数(权重和偏置)。
BP算法的关键概念:
1. 误差反向传播(Backpropagation):指的是将输出误差反向传播到前一层,计算每一层的误差,然后根据这些误差更新权重。
2. 链式法则:在反向传播过程中,使用链式法则将误差逐层传播,计算每一层权重的梯度。
3. 梯度下降法:用来优化神经网络权重的算法,沿着梯度方向调整权重,使得误差最小化。常用的有批量梯度下降、随机梯度下降和小批量梯度下降。
BP算法的优缺点:
优点:
- 可适应复杂问题:BP算法能够训练多层神经网络,适应复杂的非线性问题。
- 全局优化:通过梯度下降法,BP算法可以有效地寻找最优解,尤其是在多层网络中,能够捕捉到更复杂的模式。
缺点:
- 容易陷入局部最优解:BP算法使用梯度下降法优化误差函数,容易受到初始权重和学习率的影响,可能陷入局部最优解。
- 梯度消失问题:在深层网络中,误差通过链式法则逐层反向传播时,梯度可能会变得非常小,导致前几层的权重几乎不更新,这就是所谓的“梯度消失”问题。
- 计算复杂度高:对于大规模神经网络,BP算法需要大量的计算资源,尤其是在训练深度神经网络时。
四、数据集:wdbc_data.mat(乳腺癌威斯康星州数据集)
- 1-30:属性特征
- 31:类别
- 实验训练集/测试集:4/1
- 数据集介绍:https://archive-beta.ics.uci.edu/dataset/17/breast+cancer+wisconsin+diagnostic
五、实验步骤
基于提供的数据集,训练1个BP神经网络模型:
1. 模型参数:
- layer num(网络层数):4
- 网络每层节点数、激活函数、训练函数、学习率等: 自定义
2. 输出:
- 模型的准确率
3. 要求:
-选择合适的模型参数
-给出神经网络训练成功后的误差变化曲线图
-设置不同的学习率,如0.01、0.1、0.5、1。给出各学习率下的误差变化曲线图,分析学习率变化对训练结果的影响。
六、分析与讨论
神经网络结构和反向传播算法的理解
在本实验中,使用的是一个4层的BP神经网络(即输入层、两个隐藏层和输出层)。BP(反向传播)神经网络是通过不断调整权重和偏置,使得网络的输出接近真实标签,从而实现模式识别的目标。在反向传播的过程中,误差由输出层传回到输入层,通过计算梯度来更新网络的参数,从而降低损失函数值。
对于乳腺癌数据集来说,我们使用的是监督学习方法,目标是通过网络学习样本中的特征与标签之间的映射关系,最终实现对新样本的分类。
网络层数:4层网络:输入层、两个隐藏层、输出层。网络的层数和每一层的节点数决定了网络的复杂度。在这里,选择了4层结构,以便通过较复杂的网络结构来捕捉乳腺癌数据中的潜在模式。
激活函数选择:常用的激活函数有 Sigmoid、ReLU、Tanh 等。在这个实验中可以选择 Sigmoid 或 ReLU 激活函数。Sigmoid 函数有平滑的导数,适合用于二分类问题(如乳腺癌的良性或恶性分类),而 ReLU 在大规模训练时通常收敛速度较快,但可能会面临“死亡神经元”的问题。
七、源代码
# 导入所需的库
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
def norm(x):
"""
对输入数据进行归一化处理。
将数据的每一列分别进行最小-最大归一化,使得每一列数据都在[0,1]范围内。
参数:
x: 待归一化的数据矩阵。
返回:
归一化后的数据矩阵。
"""
# 转置数据矩阵,方便按列处理
x = x.T
new_list = []
for i in range(x.shape[0]):
arr = x[i]
Min = np.min(arr) # 获取当前列的最小值
Max = np.max(arr) # 获取当前列的最大值
# 对当前列进行最小-最大归一化
new_list.append((arr - Min) / (Max - Min))
# 将归一化后的列表转为numpy数组并转置回原始形状
new_list = np.asarray(new_list).T
return new_list
def get_model(hidden_layer_sizes, learning_rate):
"""
创建并返回一个MLPClassifier模型。
该模型使用Adam算法进行梯度下降,内部使用relu激活函数。
隐藏层有两层,神经元个数分别为64、32。
返回:
初始化的MLPClassifier模型。
"""
model = MLPClassifier(
solver='adam', # 使用Adam算法实现梯度下降
activation='relu', # 网络内部使用relu激活函数
max_iter=1000, # 最大迭代次数,控制模型的迭代速度
alpha=1e-3, # 正则化项参数
hidden_layer_sizes=hidden_layer_sizes, # 隐藏层神经元个数
learning_rate_init=learning_rate # 学习率
)
return model
def get_my_data():
"""
加载并预处理数据。
从文件中加载WDBC数据,将数据转换为numpy数组,并进行归一化处理。
将诊断结果从字符型转换为0和1表示的数组。
返回:
处理后的数据矩阵和目标标签数组。
"""
# 使用pandas从文件加载WDBC数据
my_cancer = pd.read_csv("breast+cancer+wisconsin+diagnostic/wdbc.data")
# 转化为numpy数据矩阵
my_cancer = my_cancer.to_numpy()
# 除去无关的身份证ID数据内容(第一列)
cancer_data = my_cancer[:, 2::]
# 提取数据中的诊断结果,作为target(第二列)
ori_cancer_target = my_cancer[:, 1]
cancer_target = [] # 设定诊断结果的01矩阵
# M,B英文字符转化为0,1
for i in range(len(ori_cancer_target)):
if ori_cancer_target[i] == 'M':
cancer_target.append(0)
else:
cancer_target.append(1)
# 将数组数据转化为numpy矩阵
cancer_target = np.asarray(cancer_target)
# 对数据进行归一化处理
cancer_data = norm(cancer_data)
return cancer_data, cancer_target
def main():
"""
主函数。
创建模型,加载数据,训练模型,并使用模型进行预测。
最后,可视化真实标签和预测标签的数据分布,并打印预测准确率。
"""
# 加载并预处理数据
cancer_data, cancer_target = get_my_data()
# 将数据分为训练集和测试集(80%训练集,20%测试集)
train_data, test_data = train_test_split(cancer_data, test_size=0.2, random_state=42)
train_goal, test_goal = train_test_split(cancer_target, test_size=0.2, random_state=42)
errors = {} # 用于记录不同学习率下的误差变化
while True:
try:
# 提示用户输入学习率
lr_input = input("请输入学习率(0到1之间,输入'q'退出): ")
if lr_input.lower() == 'q': # 如果用户输入'q',退出循环
break
lr = float(lr_input) # 将输入转换为浮点数
if lr < 0 or lr > 1: # 检查学习率是否在合理范围内
print("学习率必须在0到1之间,请重新输入。")
continue
# 创建并初始化BP神经网络模型
bp_network = get_model(hidden_layer_sizes=(64, 32), learning_rate=lr)
# 训练模型
bp_network.fit(train_data, train_goal)
# 使用模型进行预测
predict_test_labels = bp_network.predict(test_data)
# 计算并打印预测准确率
score = bp_network.score(test_data, test_goal)
print(f"学习率: {lr}, 预测准确率: {score:.4f}")
# 记录误差变化曲线
errors[lr] = bp_network.loss_curve_
# 绘制误差变化曲线
plt.figure(figsize=(12, 8)) # 创建一个新的图形对象,设置大小
plt.plot(bp_network.loss_curve_, label=f'Learning Rate: {lr}') # 绘制误差曲线
plt.xlabel('Epochs') # 设置x轴标签
plt.ylabel('Error') # 设置y轴标签
plt.title(f'Error vs Epochs for Learning Rate: {lr}') # 设置图表标题
plt.legend() # 显示图例
plt.show() # 显示图表
except ValueError:
print("输入无效,请输入一个有效的数字或'q'退出。")
main() # 调用主函数