BP神经网络解决异或问题:从数学原理到Python实现

1次阅读
没有评论

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

image.webp

异或问题的线性不可分性

异或(XOR)运算的真值表如下:

BP 神经网络解决异或问题:从数学原理到 Python 实现

$x_1$ $x_2$ $y$
0 0 0
0 1 1
1 0 1
1 1 0

其决策边界需要满足:

$\begin{cases}
w_1x_1 + w_2x_2 + b \leq 0 & \text{当} y=0 \
w_1x_1 + w_2x_2 + b > 0 & \text{当} y=1
\end{cases}$

通过代数证明可知,不存在直线能完美分割这四个点。几何直观如下图所示(此处可插入二维坐标图示)。

网络结构对比分析

  1. 单层感知机局限
  2. 仅含输入层和输出层
  3. 决策函数为 $f(\mathbf{w}^T\mathbf{x} + b)$
  4. 只能学习线性决策边界

  5. BP 网络优势

  6. 引入隐藏层实现特征变换
  7. 典型结构:2-2-1(输入 - 隐藏 - 输出)
  8. 通过非线性激活函数实现空间扭曲

核心数学推导

Sigmoid 函数及其导数

$\sigma(z) = \frac{1}{1+e^{-z}}$

导数计算过程:

$\begin{aligned}
\sigma'(z) &= \frac{d}{dz}\left(1 + e^{-z}\right)^{-1} \
&= -1 \cdot (1 + e^{-z})^{-2} \cdot (-e^{-z}) \
&= \frac{e^{-z}}{(1 + e^{-z})^2} = \sigma(z)(1 – \sigma(z))
\end{aligned}$

反向传播公式

对于三层网络,定义:
– 隐藏层输出:$\mathbf{h} = \sigma(\mathbf{W}_1 \mathbf{x} + \mathbf{b}_1)$
– 最终输出:$\hat{y} = \sigma(\mathbf{W}_2 \mathbf{h} + \mathbf{b}_2)$

误差反向传播时:

$\begin{aligned}
\delta_2 &= (\hat{y} – y) \odot \sigma'(\mathbf{z}_2) \
\delta_1 &= (\mathbf{W}_2^T \delta_2) \odot \sigma'(\mathbf{z}_1)
\end{aligned}$

Python 实现代码

import numpy as np

class BPNeuralNetwork:
    def __init__(self, input_size, hidden_size):
        # 使用 He 初始化策略
        self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2./input_size)
        self.b1 = np.zeros(hidden_size)
        self.W2 = np.random.randn(hidden_size, 1) * np.sqrt(2./hidden_size)
        self.b2 = np.zeros(1)

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def forward(self, X):
        self.z1 = np.dot(X, self.W1) + self.b1
        self.h = self.sigmoid(self.z1)
        self.z2 = np.dot(self.h, self.W2) + self.b2
        return self.sigmoid(self.z2)

    def backward(self, X, y, lr):
        m = X.shape[0]

        # 输出层误差
        dZ2 = self.y_pred - y
        dW2 = np.dot(self.h.T, dZ2) / m
        db2 = np.sum(dZ2, axis=0) / m

        # 隐藏层误差
        dH = np.dot(dZ2, self.W2.T)
        dZ1 = dH * self.h * (1 - self.h)
        dW1 = np.dot(X.T, dZ1) / m
        db1 = np.sum(dZ1, axis=0) / m

        # 参数更新
        self.W2 -= lr * dW2
        self.b2 -= lr * db2
        self.W1 -= lr * dW1
        self.b1 -= lr * db1

# 训练示例
X = np.array([[0,0], [0,1], [1,0], [1,1]])
y = np.array([[0], [1], [1], [0]])

model = BPNeuralNetwork(input_size=2, hidden_size=2)
for epoch in range(10000):
    y_pred = model.forward(X)
    model.backward(X, y, lr=0.1)

实验分析

  1. 学习率影响
  2. 过大(>0.5):损失震荡发散
  3. 过小(<0.01):收敛速度极慢
  4. 推荐范围:0.05-0.2

  5. 隐藏层规模

  6. 2 个神经元:基本满足需求
  7. 4 个神经元:更快收敛但可能过拟合
  8. 实验记录(建议插入训练曲线对比图)

避坑指南

  1. 梯度消失对策
  2. 改用 ReLU 激活函数
  3. 添加 BatchNorm 层
  4. 残差连接设计

  5. 权重初始化

  6. 输入层:Xavier 初始化
  7. 隐藏层:He 初始化
  8. 输出层:小随机数

  9. 学习率调整

  10. 余弦退火策略
  11. 验证集监控早停
  12. 自适应优化器(Adam)

扩展思考

如何改造当前网络实现三分类任务?考虑以下方向:
– 输出层改用 Softmax
– 损失函数改为交叉熵
– 隐藏层增加 Dropout 正则化

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