BP神经网络实战:从零实现鸢尾花分类模型

1次阅读
没有评论

共计 2514 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

背景介绍

BP 神经网络是一种通过误差反向传播算法训练的多层前馈网络。其核心是通过链式法则计算损失函数对每一层参数的梯度,然后使用梯度下降法更新权重。鸢尾花数据集(Iris)是机器学习领域的经典数据集,包含 150 个样本,每个样本有 4 个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)和 3 个类别标签(Setosa、Versicolor、Virginica)。

BP 神经网络实战:从零实现鸢尾花分类模型

技术实现

数据预处理

  1. 标准化:将特征数据缩放到均值为 0,方差为 1 的分布,公式为:
    $$x’ = \frac{x – \mu}{\sigma}$$
  2. 训练集 / 测试集划分:通常按 7:3 或 8:2 比例分割,确保数据分布一致

网络结构设计

  1. 输入层节点数:4(对应 4 个特征)
  2. 隐藏层节点数:经验公式 $\sqrt{输入节点数 \times 输出节点数}$,通常取 5 -10
  3. 输出层节点数:3(对应 3 个类别),使用 Softmax 输出概率分布

激活函数与损失函数

  1. 隐藏层激活函数:Sigmoid 函数
    $$\sigma(z) = \frac{1}{1+e^{-z}}$$
  2. 损失函数:交叉熵损失
    $$L = -\sum y_i \log(p_i)$$

完整代码示例

import numpy as np
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 数据加载与预处理
iris = load_iris()
X, y = iris.data, iris.target
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 将标签转为 one-hot 编码
def to_onehot(y):
    onehot = np.zeros((len(y), 3))
    for i in range(len(y)):
        onehot[i, y[i]] = 1
    return onehot

y_onehot = to_onehot(y)
X_train, X_test, y_train, y_test = train_test_split(X, y_onehot, test_size=0.3)

# 网络参数初始化
input_size = 4
hidden_size = 6
output_size = 3
learning_rate = 0.1
epochs = 1000

# 初始化权重(Xavier 初始化)W1 = np.random.randn(input_size, hidden_size) * np.sqrt(1/input_size)
W2 = np.random.randn(hidden_size, output_size) * np.sqrt(1/hidden_size)

# 定义激活函数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    return x * (1 - x)

# 训练过程
loss_history = []

for epoch in range(epochs):
    # 前向传播
    hidden_input = np.dot(X_train, W1)
    hidden_output = sigmoid(hidden_input)
    output_input = np.dot(hidden_output, W2)
    output_output = sigmoid(output_input)

    # 计算损失
    loss = -np.sum(y_train * np.log(output_output)) / len(X_train)
    loss_history.append(loss)

    # 反向传播
    output_error = output_output - y_train
    output_delta = output_error * sigmoid_derivative(output_output)

    hidden_error = np.dot(output_delta, W2.T)
    hidden_delta = hidden_error * sigmoid_derivative(hidden_output)

    # 更新权重
    W2 -= learning_rate * np.dot(hidden_output.T, output_delta)
    W1 -= learning_rate * np.dot(X_train.T, hidden_delta)

    # 早停法
    if len(loss_history) > 10 and abs(loss_history[-10] - loss) < 1e-6:
        break

# 测试集评估
def predict(X):
    hidden = sigmoid(np.dot(X, W1))
    output = sigmoid(np.dot(hidden, W2))
    return np.argmax(output, axis=1)

y_pred = predict(X_test)
y_true = np.argmax(y_test, axis=1)
accuracy = np.mean(y_pred == y_true)
print(f"测试集准确率: {accuracy:.2%}")

模型训练

  1. 学习率设置:初始值通常取 0.1,可通过学习率衰减策略优化
  2. 迭代次数:结合早停法,当连续 10 次迭代损失变化小于阈值时停止
  3. 可视化:绘制损失曲线观察收敛情况

避坑指南

  1. 梯度消失问题:使用 ReLU 激活函数替代 Sigmoid;采用批标准化
  2. 过拟合:添加 L2 正则化;使用 Dropout 技术
  3. 参数初始化:Xavier/Glorot 初始化,避免初始权重过大或过小

性能评估

  1. 准确率:评估整体分类正确率
  2. 混淆矩阵:分析各类别的分类情况
  3. 与 sklearn 对比:MLPClassifier 实现更高效,但自定义实现更透明

延伸思考

  1. 如何通过增加网络深度提升性能?
  2. 尝试不同的激活函数(如 ReLU)会有什么影响?
  3. 批处理(Batch)训练相比全样本训练有哪些优势?
  4. 如何设计更复杂的网络结构(如残差连接)来提升小数据集表现?

通过本文的实现,读者可以掌握 BP 神经网络的核心原理和实现方法,为进一步探索深度学习奠定基础。实际应用中还需要考虑更多优化技巧,如学习率调度、正则化策略等,这些都可以在掌握基本原理后逐步扩展。

正文完
 0
评论(没有评论)