共计 2395 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点
鸢尾花数据集是机器学习领域的经典案例,包含 150 个样本,每个样本有 4 个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)和 3 个分类标签(山鸢尾、变色鸢尾、维吉尼亚鸢尾)。这个数据集非常适合初学者练习分类任务,因为:

- 数据量适中,训练速度快
- 特征维度低,便于可视化理解
- 分类边界非线性,能体现神经网络的威力
初学者在实现 BP 神经网络时,常会遇到以下问题:
- 梯度消失 :深层网络在反向传播时梯度逐渐变小,导致训练停滞
- 过拟合 :模型在训练集表现很好,但测试集效果差
- 参数难调 :学习率、隐藏层节点数等超参数选择困难
技术实现
网络结构设计
- 输入层 :4 个节点(对应 4 个特征)
- 隐藏层 :经验法则是取输入节点数和输出节点数的平均值,这里选择 5 个节点
- 输出层 :3 个节点(对应 3 个分类),使用独热编码表示
激活函数选择
使用 Sigmoid 函数,因为它:
- 输出范围 (0,1),适合概率预测
- 导数容易计算:σ'(x) = σ(x)(1-σ(x))
- 数学表达式:σ(x) = 1/(1+e^(-x))
反向传播过程
- 计算输出层误差:δ^L = (y – a^L) ⊙ σ'(z^L)
- 反向传播误差:δ^l = (W^l)^T δ^{l+1} ⊙ σ'(z^l)
- 权重更新:ΔW = η * δ^{l+1} (a^l)^T
代码示例
import numpy as np
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
# 数据加载和预处理
iris = load_iris()
X = iris.data
y = iris.target
# 标准化特征
scaler = StandardScaler()
X = scaler.fit_transform(X)
# 将标签转为独热编码
def to_one_hot(y):
one_hot = np.zeros((len(y), 3))
one_hot[np.arange(len(y)), y] = 1
return one_hot
y_one_hot = to_one_hot(y)
# 定义神经网络结构
input_size = 4
hidden_size = 5
output_size = 3
# 初始化权重
W1 = np.random.randn(input_size, hidden_size) * 0.01
W2 = np.random.randn(hidden_size, output_size) * 0.01
# 定义 sigmoid 激活函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x):
return x * (1 - x)
# 训练参数
learning_rate = 0.1
epochs = 1000
# 存储损失值
losses = []
# 训练循环
for epoch in range(epochs):
# 前向传播
hidden_layer = sigmoid(np.dot(X, W1))
output_layer = sigmoid(np.dot(hidden_layer, W2))
# 计算损失(均方误差)loss = np.mean(0.5 * (y_one_hot - output_layer) ** 2)
losses.append(loss)
# 反向传播
output_error = y_one_hot - output_layer
output_delta = output_error * sigmoid_derivative(output_layer)
hidden_error = output_delta.dot(W2.T)
hidden_delta = hidden_error * sigmoid_derivative(hidden_layer)
# 更新权重
W2 += learning_rate * hidden_layer.T.dot(output_delta)
W1 += learning_rate * X.T.dot(hidden_delta)
优化实践
学习率对比
- 学习率 =0.1:收敛快但可能震荡
- 学习率 =0.01:收敛稳定但速度慢
- 学习率 =0.001:可能无法在合理步数内收敛
L2 正则化实现
在损失函数中加入权重惩罚项:
l2_lambda = 0.01
loss = np.mean(0.5 * (y_one_hot - output_layer) ** 2) +
l2_lambda * (np.sum(W1**2) + np.sum(W2**2))
模型评估
from sklearn.metrics import accuracy_score, confusion_matrix
# 预测函数
def predict(X):
hidden_layer = sigmoid(np.dot(X, W1))
output_layer = sigmoid(np.dot(hidden_layer, W2))
return np.argmax(output_layer, axis=1)
# 计算准确率
y_pred = predict(X)
accuracy = accuracy_score(y, y_pred)
print(f"Accuracy: {accuracy:.2f}")
# 混淆矩阵
cm = confusion_matrix(y, y_pred)
print("Confusion Matrix:")
print(cm)
避坑指南
- 特征缩放 :务必对输入特征做标准化 / 归一化,否则不同尺度的特征会影响收敛
- 隐藏层节点数 :可以从输入输出节点数的几何平均值开始尝试
- 训练终止条件 :可以设置早停机制,当验证集损失连续几轮不下降时停止
延伸思考
- 激活函数改进 :尝试 ReLU 函数,可能解决梯度消失问题
- 可视化权重 :用热力图展示隐藏层权重,理解网络学习到的特征
- 批归一化 :在隐藏层后加入 BN 层,加速训练过程
通过这个简单的实现,我们完成了鸢尾花分类任务。虽然代码不到 100 行,但涵盖了 BP 神经网络的核心概念。建议读者可以尝试调整网络结构、更换激活函数、添加正则化等改进措施,观察模型表现的变化。
正文完
