BP神经网络解决异或问题:从理论推导到Python实现

1次阅读
没有评论

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

image.webp

问题背景

异或(XOR)问题是神经网络领域的一个经典案例,它简单却极具代表性。异或运算的输出与输入之间是非线性关系,传统单层感知机无法解决这个问题。具体来说,单层感知机只能解决线性可分的问题,而异或问题的输入在二维平面上无法用一条直线分开。这就引出了多层神经网络的需求,特别是引入隐藏层的 BP 神经网络。

BP 神经网络解决异或问题:从理论推导到 Python 实现

技术方案

网络结构设计

为了解决异或问题,我们采用 2 -2- 1 的三层 BP 神经网络结构:

  1. 输入层:2 个神经元,对应异或运算的两个输入。
  2. 隐藏层:2 个神经元,用于捕捉输入的非线性关系。
  3. 输出层:1 个神经元,输出最终的预测结果。

激活函数选择

隐藏层和输出层使用 Sigmoid 激活函数,其数学表达式为:

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

选择 Sigmoid 的原因在于其输出范围在 (0,1) 之间,非常适合二分类问题,并且其导数易于计算,便于反向传播。

数学推导

前向传播

  1. 输入层到隐藏层:

$$
z_1 = w_{11}x_1 + w_{12}x_2 + b_1
$$

$$
z_2 = w_{21}x_1 + w_{22}x_2 + b_2
$$

$$
h_1 = \sigma(z_1)
$$

$$
h_2 = \sigma(z_2)
$$

  1. 隐藏层到输出层:

$$
z_3 = w_{31}h_1 + w_{32}h_2 + b_3
$$

$$
y = \sigma(z_3)
$$

反向传播

  1. 计算输出层误差:

$$
\delta_3 = (y – t) \cdot \sigma'(z_3)
$$

其中,$t$ 为目标输出。

  1. 计算隐藏层误差:

$$
\delta_1 = \delta_3 \cdot w_{31} \cdot \sigma'(z_1)
$$

$$
\delta_2 = \delta_3 \cdot w_{32} \cdot \sigma'(z_2)
$$

  1. 更新权重和偏置:

$$
w_{31} = w_{31} – \eta \cdot \delta_3 \cdot h_1
$$

$$
w_{32} = w_{32} – \eta \cdot \delta_3 \cdot h_2
$$

$$
b_3 = b_3 – \eta \cdot \delta_3
$$

输入层到隐藏层的权重更新类似。

代码实现

以下是使用 NumPy 实现的完整代码:

import numpy as np
import matplotlib.pyplot as plt

# Sigmoid 激活函数及其导数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

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

# 输入和输出
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])

# 初始化权重和偏置
np.random.seed(42)
weights1 = np.random.rand(2, 2)
weights2 = np.random.rand(2, 1)
bias1 = np.random.rand(1, 2)
bias2 = np.random.rand(1, 1)

# 超参数
learning_rate = 0.5
epochs = 10000

# 训练过程
loss_history = []

for epoch in range(epochs):
    # 前向传播
    hidden_layer_input = np.dot(X, weights1) + bias1
    hidden_layer_output = sigmoid(hidden_layer_input)
    output_layer_input = np.dot(hidden_layer_output, weights2) + bias2
    predicted_output = sigmoid(output_layer_input)

    # 计算损失
    loss = np.mean((predicted_output - y) ** 2)
    loss_history.append(loss)

    # 反向传播
    error = predicted_output - y
    d_output = error * sigmoid_derivative(predicted_output)

    error_hidden = np.dot(d_output, weights2.T)
    d_hidden = error_hidden * sigmoid_derivative(hidden_layer_output)

    # 更新权重和偏置
    weights2 -= learning_rate * np.dot(hidden_layer_output.T, d_output)
    weights1 -= learning_rate * np.dot(X.T, d_hidden)
    bias2 -= learning_rate * np.sum(d_output, axis=0, keepdims=True)
    bias1 -= learning_rate * np.sum(d_hidden, axis=0, keepdims=True)

# 可视化训练过程
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss over Epochs')
plt.show()

# 测试
hidden_layer_output = sigmoid(np.dot(X, weights1) + bias1)
predicted_output = sigmoid(np.dot(hidden_layer_output, weights2) + bias2)
print("Predicted Output:")
print(predicted_output)

调参技巧

  1. 学习率:学习率过大可能导致震荡,过小则收敛缓慢。建议从 0.1 开始尝试,逐步调整。
  2. 迭代次数:异或问题通常需要几千到上万次迭代才能收敛,可以通过观察损失函数值来判断是否停止训练。
  3. 权重初始化:使用随机初始化时,建议使用较小的值(如均值为 0,方差为 0.01 的正态分布)。

扩展讨论

BP 神经网络不仅适用于异或问题,还可以解决其他非线性分类问题。例如:

  1. 多分类问题:通过增加输出层神经元数量和使用 Softmax 激活函数。
  2. 回归问题:将输出层的激活函数改为线性函数。
  3. 更复杂的网络结构:增加隐藏层数量和神经元数量,以处理更复杂的非线性关系。

思考题

如何改进网络结构以提高训练效率?可以考虑以下几点:

  1. 增加隐藏层数量或神经元数量,但需注意过拟合风险。
  2. 使用不同的激活函数(如 ReLU)以加速收敛。
  3. 采用更先进的优化算法(如 Adam)替代传统的梯度下降。

通过本文的学习,相信你已经掌握了 BP 神经网络解决异或问题的核心原理和实现方法。希望你能将这些知识应用到更广泛的机器学习问题中!

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