【深度学习】实验一 BP算法实现分类预测
实验一 BP算法实现分类预测
一、实验学时: 2学时
二、实验目的
- 掌握BP算法在前馈神经网络中的实现与应用;
- 理解数据预处理(独热编码与归一化)在模型训练中的作用;
- 熟悉神经网络在二分类问题中的应用方法;
三、实验内容
利用BP算法实现西瓜数据集的分类预测。
四、主要实验步骤及结果
使用BP算法训练一个单隐藏层网络,编程实现在西瓜数据集(melon.csv)上预测“是否为好瓜”的应用,需要给出源代码以及运行过程和结果的截图。
1. 数据预处理,将西瓜特征转换为数值,将好瓜标记为“1”,坏瓜标记为“0”,对“密度”和“含糖量”进行归一化,关键代码如下:
def preprocess_data(file_path):
# 读取数据
data = pd.read_csv(file_path)
print("melon.csv的数据:")
print(data)
# 初始化类别特征编码器字典
label_encoders = {}
# 需要编码的类别特征列
categorical_cols = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
# 对每个类别特征进行标签编码
for col in categorical_cols:
le = LabelEncoder()
data[col] = le.fit_transform(data[col]) # 拟合并转换数据
label_encoders[col] = le # 保存编码器用于后续预测
# 将标签列("好瓜")转换为数值 (是->1, 否->0)
data['好瓜'] = data['好瓜'].map({'是': 1, '否': 0})
# 提取特征矩阵 (X) 和标签向量 (y)
x = data[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']].values
y = data['好瓜'].values.reshape(-1, 1) # 转换为列向量
# 对数值特征"密度"和"含糖率"进行归一化
scaler = MinMaxScaler()
# 只对最后两列(数值特征)进行归一化
x[:, -2:] = scaler.fit_transform(x[:, -2:])
return x, y, label_encoders, scaler
读取melon.csv中的数据,并输出,检查是否成功读取,运行结果如图4-1所示:
图4-1 读取数据
2. 构建模型,设置输入层节点数为8(节点数等于特征数),隐藏层节点数1,输出层节点数为1,调用BP网络类,初始化权重,使用小随机值(np.random.randn() * 0.1)防止梯度爆炸,初始化偏置为0,关键代码如下:
# 初始化
def __init__(self, input_size, hidden_size, output_size):
# 初始化输入层到隐藏层的权重矩阵 (小型随机值)
self.W1 = np.random.randn(input_size, hidden_size) * 0.1
# 初始化隐藏层偏置 (初始为0)
self.b1 = np.zeros((1, hidden_size))
# 初始化隐藏层到输出层的权重矩阵 (小型随机值)
self.W2 = np.random.randn(hidden_size, output_size) * 0.1
# 初始化输出层偏置 (初始为0)
self.b2 = np.zeros((1, output_size))
def sigmoid(self, x): # Sigmoid激活函数
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(self, x): # Sigmoid函数的导数
return x * (1 - x)
3. 模型训练,使用Sigmoid激活函数计算更新权重和偏置,设置训练的学习率为0.1,训练10000次,前向传播计算输出值,反向传播更新权重和偏置,每1000次输出一次损失值,关键代码如下所示:
def forward(self, x):
# 计算隐藏层输入 (线性组合)
self.z1 = np.dot(x, self.W1) + self.b1
# 计算隐藏层输出 (应用激活函数)
self.a1 = self.sigmoid(self.z1)
# 计算输出层输入 (线性组合)
self.z2 = np.dot(self.a1, self.W2) + self.b2
# 计算输出层输出 (应用激活函数)
self.a2 = self.sigmoid(self.z2)
return self.a2
def backward(self, x, y, output, learning_rate):
m = x.shape[0] # 样本数量
# 计算输出层误差 (实际输出与预测输出的差)
output_error = output - y
# 计算输出层delta (误差乘以激活函数的导数)
output_delta = output_error * self.sigmoid_derivative(output)
# 计算隐藏层误差 (反向传播输出层delta)
hidden_error = np.dot(output_delta, self.W2.T)
# 计算隐藏层delta (误差乘以激活函数的导数)
hidden_delta = hidden_error * self.sigmoid_derivative(self.a1)
# 更新隐藏层到输出层的权重和偏置
self.W2 -= learning_rate * np.dot(self.a1.T, output_delta) / m
self.b2 -= learning_rate * np.sum(output_delta, axis=0, keepdims=True) / m
# 更新输入层到隐藏层的权重和偏置
self.W1 -= learning_rate * np.dot(x.T, hidden_delta) / m
self.b1 -= learning_rate * np.sum(hidden_delta, axis=0) / m
def train(self, x, y, epochs=10000, learning_rate=0.1, verbose=True):
for i in range(epochs):
# 前向传播
output = self.forward(x)
# 反向传播
self.backward(x, y, output, learning_rate)
# 每1000轮打印一次损失
if verbose and i % 1000 == 0:
loss = np.mean(np.square(y - output)) # 均方误差
print(f"Epoch {i}, Loss: {loss:.4f}")
def predict(self, x):
# 预测结果 (0或1)
output = self.forward(x)
return (output > 0.5).astype(int) # 大于0.5为1,否则为0
训练过程如图4-2所示:

图4-2 训练结果
4.预测结果,计算测试集预测准确率及混淆矩阵,关键代码如下:
def predict_single_melon(model, melon_features, label_encoders, scaler):
# 将特征字典转换为DataFrame以便处理
melon_df = pd.DataFrame([melon_features])
categorical_cols = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
for col in categorical_cols:
melon_df[col] = label_encoders[col].transform(melon_df[col])
# 提取特征并归一化数值特征
x = melon_df[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']].values
x[:, -2:] = scaler.transform(x[:, -2:]) # 只归一化最后两列(数值特征)
prediction = model.predict(x) # 获取预测类别 (0或1)
probability = model.forward(x)[0][0] # 获取预测概率
return prediction[0][0], probability
运行结果如图4-3所示:

图4-3 预测结果
五、实验小结(包括问题和解决办法、心得体会、意见与建议等)
1. 问题和解决办法:
问题1:数据集中的数据较少,训练的误差较大,测试集准确率较低
解决方法:添加更多的数据进行训练,隐藏层节点数设置为1,模型拟合能力不足,测试集准确率较低。
问题2:模型训练过程中损失值下降缓慢,收敛速度较慢。
解决方法:在训练过程中,发现损失值下降速度较慢,模型收敛需要较长时间,这可能是由于学习率设置不当、模型初始化不佳或优化算法选择不合适等原因造成的。
2. 心得体会:通过本次实验,我深刻理解了BP算法的基本原理和实现方法,掌握了神经网络模型在分类预测中的应用技巧。实验过程中,我认识到数据预处理对模型训练的重要性,以及模型结构和参数设置对预测性能的影响。同时,我也意识到在实际应用中,需要根据具体问题选择合适的模型结构和参数设置,以达到最佳的预测效果。本次实验不仅加深了我对深度学习的理解,也锻炼了我的实践能力和问题解决能力。我相信,这些经验和技能将对我未来的学习和研究产生积极的影响。
源代码:
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split
class BPNeuralNetwork:
# 初始化
def __init__(self, input_size, hidden_size, output_size):
self.z2 = None
self.a1 = None
self.z1 = None
self.a2 = None
# 初始化输入层到隐藏层的权重矩阵 (小型随机值)
self.W1 = np.random.randn(input_size, hidden_size) * 0.1
# 初始化隐藏层偏置 (初始为0)
self.b1 = np.zeros((1, hidden_size))
# 初始化隐藏层到输出层的权重矩阵 (小型随机值)
self.W2 = np.random.randn(hidden_size, output_size) * 0.1
# 初始化输出层偏置 (初始为0)
self.b2 = np.zeros((1, output_size))
def sigmoid(self, x): # Sigmoid激活函数
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(self, x): # Sigmoid函数的导数
return x * (1 - x)
def forward(self, x):
# 计算隐藏层输入 (线性组合)
self.z1 = np.dot(x, self.W1) + self.b1
# 计算隐藏层输出 (应用激活函数)
self.a1 = self.sigmoid(self.z1)
# 计算输出层输入 (线性组合)
self.z2 = np.dot(self.a1, self.W2) + self.b2
# 计算输出层输出 (应用激活函数)
self.a2 = self.sigmoid(self.z2)
return self.a2
def backward(self, x, y, output, learning_rate):
m = x.shape[0] # 样本数量
# 计算输出层误差 (实际输出与预测输出的差)
output_error = output - y
# 计算输出层delta (误差乘以激活函数的导数)
output_delta = output_error * self.sigmoid_derivative(output)
# 计算隐藏层误差 (反向传播输出层delta)
hidden_error = np.dot(output_delta, self.W2.T)
# 计算隐藏层delta (误差乘以激活函数的导数)
hidden_delta = hidden_error * self.sigmoid_derivative(self.a1)
# 更新隐藏层到输出层的权重和偏置
self.W2 -= learning_rate * np.dot(self.a1.T, output_delta) / m
self.b2 -= learning_rate * np.sum(output_delta, axis=0, keepdims=True) / m
# 更新输入层到隐藏层的权重和偏置
self.W1 -= learning_rate * np.dot(x.T, hidden_delta) / m
self.b1 -= learning_rate * np.sum(hidden_delta, axis=0) / m
def train(self, x, y, epochs=10000, learning_rate=0.1, verbose=True):
for i in range(epochs):
# 前向传播
output = self.forward(x)
# 反向传播
self.backward(x, y, output, learning_rate)
# 每1000轮打印一次损失
if verbose and i % 1000 == 0:
loss = np.mean(np.square(y - output)) # 均方误差
print(f"Epoch {i}, Loss: {loss:.4f}")
def predict(self, x):
# 预测结果 (0或1)
output = self.forward(x)
return (output > 0.5).astype(int) # 大于0.5为1,否则为0
def preprocess_data(file_path):
# 读取数据
data = pd.read_csv(file_path)
print("melon.csv的数据:")
print(data)
# 初始化类别特征编码器字典
label_encoders = {}
# 需要编码的类别特征列
categorical_cols = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
# 对每个类别特征进行标签编码
for col in categorical_cols:
le = LabelEncoder()
data[col] = le.fit_transform(data[col]) # 拟合并转换数据
label_encoders[col] = le # 保存编码器用于后续预测
# 将标签列("好瓜")转换为数值 (是->1, 否->0)
data['好瓜'] = data['好瓜'].map({'是': 1, '否': 0})
# 提取特征矩阵 (X) 和标签向量 (y)
x = data[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']].values
y = data['好瓜'].values.reshape(-1, 1) # 转换为列向量
# 对数值特征"密度"和"含糖率"进行归一化
scaler = MinMaxScaler()
# 只对最后两列(数值特征)进行归一化
x[:, -2:] = scaler.fit_transform(x[:, -2:])
return x, y, label_encoders, scaler
def predict_single_melon(model, melon_features, label_encoders, scaler):
# 将特征字典转换为DataFrame以便处理
melon_df = pd.DataFrame([melon_features])
# 对类别特征进行编码 (使用之前保存的编码器)
categorical_cols = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
for col in categorical_cols:
melon_df[col] = label_encoders[col].transform(melon_df[col])
# 提取特征并归一化数值特征
x = melon_df[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']].values
x[:, -2:] = scaler.transform(x[:, -2:]) # 只归一化最后两列(数值特征)
# 进行预测
prediction = model.predict(x) # 获取预测类别 (0或1)
probability = model.forward(x)[0][0] # 获取预测概率
return prediction[0][0], probability
def main():
# 加载和预处理数据集
data_file = 'melon.csv'
print(f"正在加载并预处理数据: ", data_file)
x, y, label_encoders, scaler = preprocess_data(data_file)
# 划分训练集和测试集 (80%训练, 20%测试)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
# 创建神经网络
input_size = x_train.shape[1] # 输入层节点数 = 特征数
print("输入层:", input_size)
hidden_size = 1 # 隐藏层节点数
output_size = 1 # 输出层节点数
nn = BPNeuralNetwork(input_size, hidden_size, output_size)
# 训练神经网络
print("\n开始训练神经网络...")
nn.train(x_train, y_train, epochs=10000, learning_rate=0.1)
# 在测试集上评估模型
test_predictions = nn.predict(x_test)
test_accuracy = np.mean(test_predictions == y_test)
print(f"\n测试集准确率: {test_accuracy * 100:.2f}%")
# 需要预测的西瓜特征
test1 = {
'色泽': '青绿',
'根蒂': '蜷缩',
'敲声': '浊响',
'纹理': '清晰',
'脐部': '凹陷',
'触感': '硬滑',
'密度': 0.697,
'含糖率': 0.460
}
# 青绿 稍蜷 浊响 稍糊 凹陷 硬滑 0.639 0.161 0
test2 = {
'色泽': '青绿',
'根蒂': '稍蜷',
'敲声': '浊响',
'纹理': '稍糊',
'脐部': '凹陷',
'触感': '硬滑',
'密度': 0.639,
'含糖率': 0.161
}
# 进行预测并输出结果
print("\n预测单个西瓜:")
print("西瓜特征:", test2)
prediction, probability = predict_single_melon(nn, test2, label_encoders, scaler)
result = "好瓜" if prediction == 1 else "坏瓜"
print(f"预测结果: {result} (概率: {probability:.4f})")
if __name__ == "__main__":
# 运行主函数
main()
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)