感知机:神经网络的原子 本节摘要:感知机(Perceptron)是神经网络的原子——把它劈开,里面是权重、偏置和一个决策。它是你能想到的最简单的「学习机器」:接收若干输入,各自乘上一个权重,求和后加上偏置,过一道激活函数给出 0 或 1 的判断,然后根据对错调整数值,如此反复。每一个曾经被造出来的神经网络,都是这一思想的层层堆叠。理解感知机,就是理解代码里「学习」二字的真正含义——不断调整数字,直到输出贴合现实。本节将从零实现感知机类,在逻辑门上训练它,亲眼看到它学不会异或(XOR),再用三层感知机的手工连线破解异或,最后用 sigmoid 与反向传播让网络自动学会异或,为下一节的反向传播埋下伏笔。
本节摘要:感知机(Perceptron)是神经网络的原子——把它劈开,里面是权重、偏置和一个决策。它是你能想到的最简单的「学习机器」:接收若干输入,各自乘上一个权重,求和后加上偏置,过一道激活函数给出 0 或 1 的判断,然后根据对错调整数值,如此反复。每一个曾经被造出来的神经网络,都是这一思想的层层堆叠。理解感知机,就是理解代码里「学习」二字的真正含义——不断调整数字,直到输出贴合现实。本节将从零实现感知机类,在逻辑门上训练它,亲眼看到它学不会异或(XOR),再用三层感知机的手工连线破解异或,最后用 sigmoid 与反向传播让网络自动学会异或,为下一节的反向传播埋下伏笔。
阅读完本节,你应当能够:
你已经认识向量和点积,知道矩阵能把输入变换成输出。但机器究竟是如何学会该用哪种变换的?
感知机给出了最朴素的答案。它是结构最简单的学习机器:接收若干输入,各自乘权重,求和,加偏置,过一道激活函数做出二值决策,然后调整——仅此而已。从此往后的每一层「学习」,都是在把这件事反复堆叠。理解感知机,就等于理解代码里的「学习」究竟在做什么:不停调整数字,直到输出贴合现实。
感知机接收 n 个输入,把每个输入乘以一个权重,求和,加上偏置,再把结果送进激活函数。
阶跃函数(step)毫不留情:若加权求和加偏置大于等于 0 就输出 1,否则输出 0。
step(z) = 1 若 z >= 0 0 若 z < 0
这就是一个线性分类器。权重和偏置共同决定了一条直线(高维下是超平面),把输入空间切成两半。
两个输入时,感知机在二维平面上画一条线:
x2 ┤ │ 类 1 / │ (0) / │ / │ / w1·x1 + w2·x2 + b = 0 │ / │ / 类 2 │ / (1) ┼─────────/──────────── x1
线的一侧全输出 0,另一侧全输出 1。训练做的事,就是把这条线挪到能正确分开两类的地方。
感知机的学习规则非常朴素:
对每个训练样本 (x, y_true): y_pred = predict(x) error = y_true - y_pred 对每个权重: w_i = w_i + learning_rate * error * x_i bias = bias + learning_rate * error
预测对了,error = 0,什么都不动;该输出 1 却预测了 0,权重就增大;该输出 0 却预测了 1,权重就减小。学习率控制每一步调整的幅度。
麻烦来了。看这三种逻辑门:
AND 门: OR 门: XOR 门: x1 x2 out x1 x2 out x1 x2 out 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 1 1 0 0 1 0 1 1 0 1 1 1 1 1 1 1 1 1 0
AND 和 OR 都是线性可分的——一条线就能把 0 和 1 分开。XOR 不是。没有任何一条直线能把 [0,1]、[1,0] 与 [0,0]、[1,1] 分到两侧。
AND(可分): XOR(不可分): x2 x2 1 ┤ 0 1 1 ┤ 1 0 │ / │ 0 ┤ 0 / 0 0 ┤ 0 1 ┼──/──────── x1 ┼──────────── x1 一条线就够! 没有任何一条线能分开!
这是根本性的限制。单个感知机只能解线性可分问题。Minsky 与 Papert 在 1969 年证明了这一点,几乎让神经网络研究停滞了十年。
解法是:把感知机堆成层。多层感知机可以把两次线性决策组合成一次非线性决策,从而破解异或。
完整代码见原课程 phases/03-deep-learning-core/01-the-perceptron/code/ 相应文件,这里给出关键骨架。
class Perceptron: def __init__(self, n_inputs, learning_rate=0.1): self.weights = [0.0] * n_inputs self.bias = 0.0 self.lr = learning_rate def predict(self, inputs): total = sum(w * x for w, x in zip(self.weights, inputs)) total += self.bias return 1 if total >= 0 else 0 def train(self, training_data, epochs=100): for epoch in range(epochs): errors = 0 for inputs, target in training_data: prediction = self.predict(inputs) error = target - prediction if error != 0: errors += 1 for i in range(len(self.weights)): self.weights[i] += self.lr * error * inputs[i] self.bias += self.lr * error if errors == 0: print(f"第 {epoch + 1} 轮收敛") return print(f"训练 {epochs} 轮后仍未收敛")
把 AND、OR、NOT 的真值表作为训练数据,看感知机能否学会。
把 XOR 真值表喂给单个感知机,跑 1000 轮——它永远不会收敛。这是「单层感知机学不会 XOR」的铁证。
诀窍是恒等式:XOR = (x1 OR x2) AND NOT (x1 AND x2)。把三个感知机组合起来:
只要把 OR 神经元权重设成 [1, 1]、偏置 -0.5;NAND 神经元权重 [-1, -1]、偏置 1.5;AND 神经元权重 [1, 1]、偏置 -1.5,四种输入就全部判对。堆叠感知机,就能造出单个感知机画不出的决策边界。
Step 4 是手工焊死权重——对 XOR 行得通,但真正的任务里你不可能预先知道正确权重。解法:把阶跃函数换成 sigmoid,用反向传播自动学权重(本节先用简化版,完整推导见下一节)。关键差异有二:其一,sigmoid 是光滑的,梯度处处存在;其二,train 方法把误差从输出层反向传回隐藏层,按贡献大小调整每个权重——这就是反向传播的雏形,也是通向第 03 节的桥梁。d_output、hidden_deltas 背后的数学,是对网络图反复套用链式法则,下一节会正经推导。
你刚从零搭出来的全部功能,一行 import 就能搞定:
from sklearn.linear_model import Perceptron as SkPerceptron import numpy as np X = np.array([[0,0],[0,1],[1,0],[1,1]]) y = np.array([0, 0, 0, 1]) clf = SkPerceptron(max_iter=100, tol=1e-3) clf.fit(X, y) print([clf.predict([x])[0] for x in X])
五行的 sklearn,干的活和你那三十行的 Perceptron 类一模一样——核心循环完全相同:加权求和、阶跃函数、出错就更新权重。sklearn 版本多了收敛判定、多种损失函数、稀疏输入支持,但骨头是一样的。
真正的差距在规模。生产级网络相比你的玩具版,改动主要是:
单个感知机只会画直线,把它们堆起来,你就能画出任何形状。
本节产出(位于原课程 outputs/):
skill-perceptron.md:一份技能说明,帮你判断何时该用单层、何时必须用多层架构。Perceptron 类加一个跟踪决策边界(w1*x1 + w2*x2 + b = 0)的功能,打印训练 AND 门时这条线每一轮的位置。下一节,我们把单层感知机扩展成多层网络,讲清前向传播——数据如何从输入流经隐藏层抵达输出,以及如何用矩阵维度追踪每一层的形状。