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

1次阅读
没有评论

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

image.webp

引言

异或(XOR)问题是神经网络学习中的经典案例,它简单却足够说明神经网络的强大能力。本文将带你从零开始,理解为什么单层感知机无法解决异或问题,以及如何用 BP 神经网络解决它。我们会用 Python 一步步实现一个 2 -2- 1 结构的 BP 网络,并详细解释每个环节的原理。

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

1. 异或问题为什么难以解决

异或运算的真值表如下:

A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

从数学上看,异或问题就是寻找一个函数 $f$ 使得:

$$
\begin{cases}
f(0,0) = 0 \
f(0,1) = 1 \
f(1,0) = 1 \
f(1,1) = 0
\end{cases}
$$

单层感知机本质上是一个线性分类器,只能解决线性可分的问题。而异或问题是典型的非线性问题,在二维平面上,我们无法用一条直线将 (0,1) 和(1,0)与 (0,0) 和(1,1)分开。这就是为什么单层感知机无法解决异或问题。

2. BP 神经网络简介

BP(Back Propagation)神经网络是一种多层前馈神经网络,通过反向传播算法来训练网络。它通常包含:

  1. 输入层:接收外部输入
  2. 隐含层:进行非线性变换
  3. 输出层:输出最终结果

对于异或问题,我们采用 2 -2- 1 结构:
– 输入层:2 个神经元(对应两个输入)
– 隐含层:2 个神经元
– 输出层:1 个神经元

3. Python 实现

下面是用 Python 实现 BP 神经网络解决异或问题的完整代码:

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]])

# 网络参数
input_neurons = 2
hidden_neurons = 2
output_neurons = 1

# 权重初始化
np.random.seed(42)
weights_input_hidden = np.random.uniform(size=(input_neurons,hidden_neurons))
weights_hidden_output = np.random.uniform(size=(hidden_neurons,output_neurons))

# 训练参数
learning_rate = 0.5
epochs = 10000
loss_history = []

# 训练过程
for epoch in range(epochs):
    # 前向传播
    hidden_layer_input = np.dot(X, weights_input_hidden)
    hidden_layer_output = sigmoid(hidden_layer_input)

    output_layer_input = np.dot(hidden_layer_output, weights_hidden_output)
    predicted_output = sigmoid(output_layer_input)

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

    # 反向传播
    d_predicted_output = error * sigmoid_derivative(predicted_output)

    error_hidden_layer = d_predicted_output.dot(weights_hidden_output.T)
    d_hidden_layer = error_hidden_layer * sigmoid_derivative(hidden_layer_output)

    # 更新权重
    weights_hidden_output += hidden_layer_output.T.dot(d_predicted_output) * learning_rate
    weights_input_hidden += X.T.dot(d_hidden_layer) * learning_rate

# 绘制损失曲线
plt.plot(loss_history)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

# 测试
print("Final predictions:")
for i in range(len(X)):
    hidden_layer_input = np.dot(X[i], weights_input_hidden)
    hidden_layer_output = sigmoid(hidden_layer_input)
    output_layer_input = np.dot(hidden_layer_output, weights_hidden_output)
    predicted_output = sigmoid(output_layer_input)
    print(f"Input: {X[i]} Output: {predicted_output}")

4. 关键参数说明

  1. 学习率(learning_rate): 控制权重更新的步长。太大会导致震荡,太小会使训练缓慢。0.1-0.5 是常用范围。

  2. 迭代次数(epochs): 训练轮数。需要足够大才能收敛,但太大可能导致过拟合。

  3. 权重初始化 : 使用随机初始化打破对称性,通常范围在[-1,1] 或[0,1]之间。

5. 避坑指南

  1. 梯度消失问题: 当网络层数较多时,梯度可能在反向传播过程中变得极小。解决方法包括使用 ReLU 等激活函数,或采用残差连接。

  2. 学习率设置技巧: 可以尝试学习率衰减策略,随着训练进行逐渐减小学习率。

  3. 权重初始化方法: Xavier 初始化或 He 初始化往往比纯随机初始化效果更好。

6. 扩展思考

  1. 如何修改网络结构来处理三输入异或问题?
  2. 除了 Sigmoid,还可以尝试哪些激活函数?
  3. 如何将这个网络扩展到解决其他逻辑运算问题?

结语

通过这个简单的例子,我们看到了 BP 神经网络强大的非线性建模能力。虽然现代深度学习框架已经帮我们封装好了这些底层细节,但理解这些基本原理对于调参和解决问题仍然至关重要。希望这篇文章能帮助你入门神经网络的世界!

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