异或门问题的难点在于其非线性可分性。
1. 为什么单层感知机无法处理 XOR?
如果我们尝试用一个只有输入层和输出层的单层感知机来解决 XOR 问题,会遇到困难。
单层感知机只能学习线性可分的决策边界。这意味着它只能用一条直线(或超平面)将不同类别的样本分开。
- 将 XOR 的数据点画在坐标系上:(0,0) 和 (1,1) 是 0 类,(0,1) 和 (1,0) 是 1 类。
- 无法画出一条直线将 0 类点与 1 类点完全分开。
这就是著名的“Minsky 和 Papert 悖论”(1969年),一度导致了神经网络研究的低谷。
2. 多层感知机(MLP)的解决方案
解决 XOR 问题的关键是引入隐藏层(Hidden Layer)和非线性激活函数(Non-linear Activation Function)。
一个具有至少一个隐藏层的神经网络可以学习非线性可分的决策边界。
解决方案示例:双层神经网络结构
一个经典的解决 XOR 的神经网络通常包含以下结构:
- 输入层:2 个神经元(对应 A 和 B)。
- 隐藏层:2 个神经元。
- 输出层:1 个神经元(对应 Y)。
- 激活函数:通常使用 Sigmoid 或 ReLU 等非线性函数。
3. 工作原理(通过构建新特征)
隐藏层的作用是将原始输入数据转换到一个新的、线性可分的特征空间。
这个双层网络可以看作是分别实现了两个更简单的线性操作(例如 AND, OR, NOT 的组合),然后将它们组合起来:
- 隐藏层的第一个神经元可以检测 (A OR B) 是否为真。
- 隐藏层的第二个神经元可以检测 (A AND B) 是否为真。
- 输出层将这两个信息结合起来(例如,(A OR B) AND (NOT (A AND B))),这正是 XOR 的逻辑。
通过隐藏层的非线性转换,原始数据点在新的特征空间中变得线性可分了,从而使整个网络能够解决 XOR 问题。
Torvalds大佬曾言“Talk is cheap, show me the code!”,好了,到了实战环节,
以下是使用 Python 的
scikit-learn 库实现解决 XOR 问题的神经网络示例代码。虽然 scikit-learn 的 MLPClassifier 隐藏了训练循环细节,但它完美地展示了多层网络结构如何工作。示例代码:使用 scikit-learn
这个例子使用了 MLPClassifier,它是一个标准的、具有反向传播训练算法的多层感知机实现。
import numpy as np from sklearn.neural_network import MLPClassifier from sklearn.metrics import accuracy_score# 1. 定义 XOR 门的输入数据 (X) 和目标标签 (y) # X: [输入A, 输入B] # y: [输出] X = np.array([[0, 0],[0, 1],[1, 0],[1, 1] ])y = np.array([0, 1, 1, 0])# 2. 初始化神经网络模型 # hidden_layer_sizes=(2,) 表示一个包含 2 个神经元的隐藏层 # activation='logistic' 使用 Sigmoid 激活函数 # solver='lbfgs' 是一个优化算法,用于小数据集时比 Adam 更有效 # random_state=1 用于重现结果 model = MLPClassifier(hidden_layer_sizes=(2,), activation='logistic', solver='lbfgs', random_state=1,max_iter=1000)# 3. 训练模型 # 模型通过学习隐藏层的权重和偏置来创建非线性决策边界 model.fit(X, y)# 4. 进行预测 predictions = model.predict(X)# 5. 评估模型性能 print("真实标签:", y) print("模型预测:", predictions) accuracy = accuracy_score(y, predictions) print(f"准确率: {accuracy * 100}%")# 6. 打印模型学习到的参数(可选) # print("\n权重(输入层 -> 隐藏层):") # print(model.coefs_[0]) # print("\n权重(隐藏层 -> 输出层):") # print(model.coefs_[1])
代码解释
hidden_layer_sizes=(2,):这是解决问题的关键。我们增加了一个包含两个神经元的隐藏层。这使得网络能够学习非线性的组合特征。activation='logistic':即 Sigmoid 激活函数。这个非线性函数允许网络进行复杂的映射。model.fit(X, y):训练过程通过反向传播和梯度下降来调整所有权重和偏置,直到网络学会了如何正确地将输入映射到 XOR 输出。
运行此代码,模型仅实现 50% 的准确率。天塌了,是不是犹如晴天霹雳,老师教的不对吗,抑或机器走神了?尽信书,不如无书。关键时刻,且听我为大家分析。
4. 为什么会出现 50% 的准确率?
虽然上述提供的代码在理论上和实践中通常可以解决 XOR 问题,但在特定运行中出现低准确率,可能有以下几个原因:
- 随机性影响: 神经网络的训练是一个随机过程。尽管设置了
random_state=1试图保持一致性,但在某些运行或不同的库版本中,模型可能陷入了局部最优解(Local Minima),而没有找到全局最优解。 - 迭代次数不足: 默认的
max_iter可能不够。虽然示例代码设置了max_iter=1000,但在某些随机初始化下,优化器可能需要更多的时间来收敛。 - 超参数选择: 优化器
solver='lbfgs'对初始权重比较敏感。有时 Adam 或 SGD 配合合适的学习率衰减可能表现更稳定。
5. 如何解决并提高准确率?
您可以尝试调整代码中的超参数来提高模型的学习能力:
- 增加隐藏层神经元数量: 尝试
hidden_layer_sizes=(4,)或hidden_layer_sizes=(4, 2)。 - 更改激活函数: 尝试更现代的
activation='tanh'(双曲正切)或activation='relu'(ReLU 函数)。 - 增加最大迭代次数: 尝试
max_iter=2000或max_iter=5000。 - 更改优化器: 尝试使用
solver='adam',它通常更鲁棒,但可能需要您调整学习率learning_rate_init。
最简单的修改建议:
将代码中的模型初始化行修改为使用
tanh 激活函数,通常对 XOR 问题更有效:model = MLPClassifier(hidden_layer_sizes=(2,), activation='tanh', # 更改为 'tanh'solver='lbfgs', random_state=1,max_iter=1000)
至此,你是否明白“多层感知机(Multilayer Perceptron, MLP)相较于单层感知机具有巨大优势。”这句话的内在原理了 吗?
参考资料:
1. 多层感知机MLP