BP神经网络实战:从零构建鸢尾花分类预测模型

1次阅读
没有评论

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

image.webp

背景痛点:为什么需要 BP 神经网络

鸢尾花分类是经典的机器学习入门案例,传统方法如决策树或 SVM 虽然简单,但存在明显局限:

BP 神经网络实战:从零构建鸢尾花分类预测模型

  • 特征交互能力弱:花瓣 / 花萼的长宽组合特征需要人工设计
  • 非线性边界处理差:当类别边界呈现复杂曲线时准确率骤降
  • 泛化能力有限:对噪声数据和特征尺度变化敏感

而 BP 神经网络通过多层非线性变换能自动学习特征组合,但实现时会遇到:

  1. 梯度消失:深层网络在反向传播时梯度指数级衰减
  2. 参数敏感:学习率过大导致震荡,过小则收敛缓慢
  3. 过拟合:在 150 条的小数据集上尤为明显

技术实现:三层网络拆解

网络结构设计

采用 4 -6- 3 的三层结构:

  • 输入层:4 个节点对应花萼长宽 + 花瓣长宽
  • 隐藏层:6 个节点(经实验验证的平衡点)
  • 输出层:3 个节点使用 Softmax 表示三类鸢尾花概率

激活函数选择

隐藏层使用 Sigmoid 而非 ReLU 的原因:

  1. 输出范围 (0,1) 适合概率场景
  2. 求导简单:$\sigma'(x) = \sigma(x)(1-\sigma(x))$
  3. 避免 ReLU 的 ” 死亡神经元 ” 问题

核心公式推导

关键步骤的数学表达:

  1. 前向传播:
    $$h_j = \sigma(\sum_{i} w_{ij}x_i + b_j)$$
  2. 交叉熵损失:
    $$L = -\sum_{k} y_k \log(p_k)$$
  3. 权重更新:
    $$\Delta w_{ij} = -\eta \frac{\partial L}{\partial w_{ij}}$$

代码实现:从零搭建

import numpy as np

class BPNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        # Xavier 初始化防止梯度消失
        self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(1/input_size)
        self.b1 = np.zeros(hidden_size)
        self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(1/hidden_size)
        self.b2 = np.zeros(output_size)

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-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
        # Softmax 输出
        exp_scores = np.exp(self.z2)
        self.probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
        return self.probs

    def backward(self, X, y, learning_rate):
        # 反向传播计算梯度
        delta3 = self.probs
        delta3[range(len(X)), y] -= 1
        dW2 = np.dot(self.a1.T, delta3)
        db2 = np.sum(delta3, axis=0)
        delta2 = np.dot(delta3, self.W2.T) * (self.a1 * (1 - self.a1))
        dW1 = np.dot(X.T, delta2)
        db1 = np.sum(delta2, axis=0)

        # 参数更新(含 L2 正则化)reg_lambda = 0.01
        self.W2 -= learning_rate * (dW2 + reg_lambda * self.W2)
        self.b2 -= learning_rate * db2
        self.W1 -= learning_rate * (dW1 + reg_lambda * self.W1)
        self.b1 -= learning_rate * db1

性能优化实战技巧

学习率对比实验

通过损失曲线可视化发现:

  1. η=0.1:快速收敛但后期震荡
  2. η=0.01:稳定下降(最终选择)
  3. η=0.001:收敛过慢需更多迭代

正则化实现

在反向传播代码中可见,L2 正则项通过 reg_lambda * self.W 实现,能有效控制权重幅度。

避坑指南

  • 权重初始化:全零初始化会导致对称更新问题,推荐 Xavier/Glorot 初始化
  • 批量归一化:在隐藏层后添加(a1 - np.mean(a1))/np.std(a1)
  • 早停法:当验证集准确率连续 3 轮不提升时终止训练

延伸思考

  1. 部署为 API:使用 Flask 封装预测接口,输入特征返回 JSON 格式的类别概率
  2. PyTorch 重构:比较与手动实现的性能差异,特别注意:
  3. 自动求导功能
  4. GPU 加速效果
  5. 内置优化器 (Adam/SGD) 的表现

通过这个项目,你会发现神经网络的『黑箱』其实可以被逐步拆解。建议下一步尝试:

  • 增加隐藏层观察深度网络的影响
  • 用 PCA 降维可视化决策边界
  • 在 sklearn 其他数据集上测试泛化能力
正文完
 0
评论(没有评论)