第 1 章 1943—1969:MP 神经元、感知机与第一次寒冬
学习目标
- 理解 McCulloch-Pitts 神经元(1943)的数学模型
- 实现 Rosenblatt 感知机(1958)的学习规则,并用它学会 AND / OR
- 理解 Adaline(1960)与感知机的区别
- 理解 XOR 问题的本质:单层线性模型无法表示非线性可分函数
- 了解 1969 年《感知机》一书引发的第一次 AI 寒冬
1.1 一个阈值,一切的开端
1943 年,神经生理学家 Warren McCulloch 和逻辑学家 Walter Pitts 发表了论文《A Logical Calculus of the Ideas Immanent in Nervous Activity》,提出了第一个神经元的数学模型,后人称为 MP 神经元:
MP 神经元把输入 x = (x₁, x₂, …, xₙ) 与权重 w = (w₁, w₂, …, wₙ) 加权求和,若加权和达到阈值 θ 则输出 1(点火),否则输出 0(静默)。
import torch
def mp(x, w, threshold):
"""MP 神经元:sum(w*x) >= threshold 输出 1,否则输出 0。"""
return 1 if (x * w).sum().item() >= threshold else 0权重 w 和阈值 θ 是手工设定的。比如用权重 [1, 1]、阈值 2 就得到 AND 门;阈值降到 1 就得到 OR 门。也就是说,MP 神经元本身不会学习——它是「逻辑电路」的神经版本。
运行 examples/ch01_mp.py 验证三种基本逻辑门:
AND(阈值为 2):
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
OR(阈值为 1):
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
NOT(x) = 1 - x:
NOT 0 = 1
NOT 1 = 0MP 神经元的贡献不是它的能力,而是它把「大脑如何工作」变成了「数学问题」:神经网络从此可以像电路一样被分析。多年后人们回头看,1943 年就是一切的起点。
1.2 感知机:第一个会学习的神经元
1958 年,心理学家 Frank Rosenblatt 在 Cornell 航空实验室造出了 感知机(Perceptron),并给出了一个学习规则:权重不再手工设定,而是根据预测错误自动调整。
设训练样本为 (x, y),其中 y ∈ {+1, -1} 是真实标签。感知机先计算预测 ŷ = sign(w·x + b),然后按规则更新:
若 ŷ ≠ y: w ← w + η·y·x b ← b + η·y
否则: 不更新其中 η 是学习率。直觉:预测错了,就把权重往「正确方向」推一步;预测对了,什么都不用做。这个规则今天看起来简单,但在当时是革命性的——机器第一次能从数据中学习。
用 PyTorch 实现(见 examples/ch01_perceptron.py):
def perceptron(X, y, max_epochs=100, lr=1.0):
w = torch.zeros(X.shape[1])
b = torch.zeros(1)
for epoch in range(1, max_epochs + 1):
errors = 0
for xi, yi in zip(X, y):
pred = torch.sign((xi * w).sum() + b)
if pred != yi:
w += lr * yi * xi
b += lr * yi
errors += 1
if errors == 0:
return epoch, w, b, errors
return max_epochs, w, b, errors在 AND / OR 上运行:
AND:训练 9 轮,权重 w=[3.0, 2.0],正确 4/4
OR:训练 6 轮,权重 w=[2.0, 2.0],正确 4/4
XOR:100 轮后仍有 4 个错误——单层感知机无法学习 XOR感知机学会 AND 和 OR 各用了不到 10 轮。但轮到 XOR,100 轮之后仍然错 4 个(全错)。这不是学习率的问题,而是数学上不可能。
1.3 XOR 的诅咒:线性可分
把 AND、OR、XOR 画在二维平面上:
- AND 和 OR:存在一条直线,把两类点分开——这类问题叫线性可分。
- XOR:两类点交叉分布,任何一条直线都无法把它们分开——线性不可分。
感知机只能学到一个线性决策边界 w·x + b = 0。对线性不可分的数据,无论怎么更新权重都不可能全对。1969 年,Marvin Minsky 和 Seymour Papert 在《感知机》(Perceptrons)一书中严格证明了这一点,并断言多层网络也没有实用价值。舆论迅速转向,第一次 AI 寒冬到来,神经网络研究经费被大幅削减,持续了近二十年。
教训值得记住:架构的表达能力决定了它能学什么。XOR 需要一个「中间层」——这正是第 2 章多层感知机的起点。
1.4 Adaline:可微的感知机
1960 年,Bernard Widrow 和 Ted Hoff 提出了 Adaline(Adaptive Linear Neuron,自适应线性神经元)。它和感知机的区别在于更新依据:
- 感知机用符号后的输出
sign(w·x+b)与标签比较,错了才更新; - Adaline 用线性输出本身
w·x+b与标签的差,每次更新,把误差逐步压小。
这就是最小二乘意义上的梯度下降——今天的神经网络训练,本质上仍是它的延伸。
运行 examples/ch01_adaline.py:
AND:权重 w=[1.00, 1.00],b=-1.50,正确 4/4
OR:权重 w=[1.00, 1.00],b=-0.50,正确 4/4
XOR:正确 3/4,线性模型无法表示 XOR注意 XOR 虽然偶尔「蒙对」3 个,但这不是学会了——线性模型的表达能力上限就在那里。
1.5 逻辑回归:把感知机变成概率模型
感知机和 Adaline 的符号输出 / 线性输出都不是概率。把线性输出经过 sigmoid 压缩到 (0,1),再用交叉熵损失训练,就得到逻辑回归——一个可微的、输出概率的「感知机后代」。它用梯度下降训练,是深度学习的前奏。
examples/ch01_perceptron.py 的最后一段在两组二维高斯点上训练逻辑回归:
逻辑回归:200 步后准确率 92.3%完整代码
本章用到的完整示例代码:
examples/ch01_mp.py
"""第 1 章示例(1943):McCulloch-Pitts 神经元。
MP 神经元是最早的神经网络数学模型:加权求和,超过阈值就输出 1。
用它可以直接"手搓"出逻辑门。
运行方式:
uv run python examples/ch01_mp.py
"""
import torch
def mp(x, w, threshold):
"""MP 神经元:sum(w*x) >= threshold 输出 1,否则输出 0。"""
return 1 if (x * w).sum().item() >= threshold else 0
w = torch.tensor([1.0, 1.0])
print("AND(阈值为 2):")
for a, b in [(0, 0), (0, 1), (1, 0), (1, 1)]:
print(f" {a} AND {b} = {mp(torch.tensor([float(a), float(b)]), w, 2)}")
print("OR(阈值为 1):")
for a, b in [(0, 0), (0, 1), (1, 0), (1, 1)]:
print(f" {a} OR {b} = {mp(torch.tensor([float(a), float(b)]), w, 1)}")
# NOT:y = 1 - x,等价于权重 -1、阈值 0
print("NOT(x) = 1 - x:")
for x in [0, 1]:
print(f" NOT {x} = {1 - x}")examples/ch01_perceptron.py
"""第 1 章示例(1943—1969):MP 神经元、感知机与逻辑回归。
运行方式:
uv run python examples/ch01_perceptron.py
"""
import torch
from torch import nn
torch.manual_seed(0)
# ---------- 1. McCulloch-Pitts 神经元(1943) ----------
def mp_neuron(x, w, threshold):
"""MP 神经元:加权和达到阈值输出 1,否则输出 0。"""
return torch.tensor(1.0 if (x * w).sum() >= threshold else 0.0)
x1 = torch.tensor([1.0, 1.0])
w_and = torch.tensor([1.0, 1.0])
print("MP 神经元 AND:输入 (1,1),阈值为 2 →", int(mp_neuron(x1, w_and, 2).item()))
print("MP 神经元 AND:输入 (1,0),阈值为 2 →", int(mp_neuron(torch.tensor([1.0, 0.0]), w_and, 2).item()))
# ---------- 2. Rosenblatt 感知机学习规则(1958) ----------
def perceptron(X, y, max_epochs=100, lr=1.0):
w = torch.zeros(X.shape[1])
b = torch.zeros(1)
for epoch in range(1, max_epochs + 1):
errors = 0
for xi, yi in zip(X, y):
pred = torch.sign((xi * w).sum() + b)
if pred != yi:
w += lr * yi * xi
b += lr * yi
errors += 1
if errors == 0:
return epoch, w, b, errors
return max_epochs, w, b, errors
def test_dataset(name, X, y):
epoch, w, b, _ = perceptron(X, y)
correct = (torch.sign(X @ w + b) == y).sum().item()
print(f"{name}:训练 {epoch} 轮,权重 w={w.tolist()},正确 {correct}/{len(y)}")
and_X = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
and_y = torch.tensor([-1.0, -1.0, -1.0, 1.0])
or_y = torch.tensor([-1.0, 1.0, 1.0, 1.0])
xor_y = torch.tensor([-1.0, 1.0, 1.0, -1.0])
test_dataset("AND", and_X, and_y)
test_dataset("OR", and_X, or_y)
_, _, _, errors = perceptron(and_X, xor_y)
print(f"XOR:100 轮后仍有 {errors} 个错误——单层感知机无法学习 XOR")
# ---------- 3. 可微的感知机:逻辑回归 ----------
def make_blobs(n=400, seed=0):
g = torch.Generator().manual_seed(seed)
c0 = torch.randn(n, 2, generator=g) + torch.tensor([-1.5, 0.0])
c1 = torch.randn(n, 2, generator=g) + torch.tensor([1.5, 0.0])
X = torch.cat([c0, c1])
y = torch.cat([torch.zeros(n), torch.ones(n)]).long()
return X, y
X, y = make_blobs()
model = nn.Sequential(nn.Linear(2, 1))
opt = torch.optim.SGD(model.parameters(), lr=1.0)
for step in range(200):
opt.zero_grad()
loss = nn.functional.binary_cross_entropy_with_logits(
model(X).squeeze(-1), y.float()
)
loss.backward()
opt.step()
pred = (torch.sigmoid(model(X).squeeze(-1)) > 0.5).long()
acc = (pred == y).float().mean().item()
print(f"逻辑回归:200 步后准确率 {acc * 100:.1f}%")examples/ch01_adaline.py
"""第 1 章示例(1960):Adaline(自适应线性神经元)。
与感知机不同,Adaline 用"预测值"(而不是符号)和真实标签的差值
做梯度下降,使线性输出逼近目标——这是后来逻辑回归、深度学习的雏形。
运行方式:
uv run python examples/ch01_adaline.py
"""
import torch
torch.manual_seed(0)
X = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
and_y = torch.tensor([-1.0, -1.0, -1.0, 1.0])
or_y = torch.tensor([-1.0, 1.0, 1.0, 1.0])
def adaline(X, y, lr=0.2, epochs=200):
w = torch.randn(X.shape[1]) * 0.5
b = 0.0
for _ in range(epochs):
pred = X @ w + b
w -= lr * ((pred - y) @ X) / len(y)
b -= lr * (pred - y).mean()
return w, b
for name, y in [("AND", and_y), ("OR", or_y)]:
w, b = adaline(X, y)
pred = torch.sign(X @ w + b)
print(f"{name}:权重 w=[{w[0]:.2f}, {w[1]:.2f}],b={b:.2f},正确 {(pred == y).sum().item()}/4")
# XOR 仍不可分:Adaline 同样学不会
xor_y = torch.tensor([-1.0, 1.0, 1.0, -1.0])
w, b = adaline(X, xor_y)
pred = torch.sign(X @ w + b)
print(f"XOR:正确 {(pred == xor_y).sum().item()}/4,线性模型无法表示 XOR")动手实践
- 运行
uv run python examples/ch01_mp.py,把 AND 的阈值改成 1.5,观察输出变化,解释为什么阈值决定了逻辑门类型。 - 在
examples/ch01_perceptron.py中给 AND 数据加一个新样本(1, 0.5, -1)(即 1 AND 0.5 应为假),重新训练,观察收敛轮数变化。 - 修改 Adaline 的学习率(0.01 和 1.0),观察权重是否收敛、是否振荡。
- 用
torch.randn生成两组可分的二维数据(均值不同),分别用感知机与逻辑回归训练,比较两者达到 100% / 95% 准确率所需的轮数。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
mp(x, w, threshold) 里 x * w 用了 Python 列表 | TypeError: can't multiply sequence by non-int | 列表不支持逐元素乘法,要用 torch.tensor 或 NumPy |
感知机更新写 w -= lr * y * x | 越学越差 | 符号反了;预测错误时应加上 η·y·x(向正确方向推) |
把标签设为 0/1 而不是 -1/+1 | 感知机不收敛 | 感知机规则基于 ±1 符号;0/1 会让「错」和「对」的更新不一致 |
| 用感知机解 XOR 然后调参 | 永远 4 个错误 | 线性模型数学上无法表示 XOR,与参数无关 |
torch.sign(0) | 返回 0 | 边界情况;实践中通常用大于 0 判定或微小扰动 |
章末练习
基础
- 用 MP 神经元构造 NAND 门(即 NOT AND),并写出它的权重与阈值。
- 感知机学习规则中,
w += lr * y * x的直觉是什么?为什么只在预测错误时更新? - 画出 XOR 的四个点,说明为什么任何直线都无法分开它们。
提高
- 证明:AND 和 OR 是线性可分的(给出两组满足条件的 w、b)。
- 感知机在 OR 上 6 轮收敛,在 AND 上 9 轮——尝试解释为什么 AND 更难学(提示:AND 只有一个正样本)。
- Adaline 的更新公式
w -= lr·(pred-y)ᵀx/len(y)对应哪个损失函数的梯度?写出来。
挑战
- 实现一个「投票感知机」:训练 10 个感知机(不同随机初始权重),用多数投票做预测,在 XOR 上测试准确率,并解释结果(提示:线性模型的投票仍是线性模型)。
- 用逻辑回归(BCEWithLogitsLoss)在 XOR 数据上训练,记录 loss 曲线,解释 loss 停在哪、为什么。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- MP 神经元的输出取决于?
- A. 随机数
- B. 加权和与阈值的比较
- C. 输入个数
- D. 训练轮数
- 感知机的更新规则在什么情况下更新权重?
- A. 每次都更新
- B. 预测正确时
- C. 预测错误时
- D. 从 不更新
- 感知机无法学习 XOR 的根本原因是?
- A. 学习率太大
- B. 训练数据太少
- C. XOR 线性不可分,而感知机只能学线性边界
- D. 权重初始化不好
- Adaline 与感知机的主要区别是?
- A. Adaline 用线性输出做梯度更新,感知机用符号
- B. Adaline 有隐藏层
- C. 感知机使用 ReLU
- D. 没有区别
- 1969 年《感知机》一书的作者是?
- A. Rosenblatt 和 Widrow
- B. Minsky 和 Papert
- C. LeCun 和 Bengio
- D. McCulloch 和 Pitts
- 逻辑回归输出的是?
- A. ±1 的符号
- B. 0~1 之间的概率
- C. 任意实数
- D. 整数
- 感知机学习率 η 的作用是?
- A. 控制每次更新的步长
- B. 控制输入维度
- C. 控制阈值
- D. 没有作用
- 下列哪个问题可以用单层感知机解决?
- A. XOR
- B. AND
- C. 异或的补集
- D. 任何非线性问题
- 「第一次 AI 寒冬」主要导致?
- A. 神经网络研究经费大幅削减
- B. GPU 停产
- C. Python 无法使用
- D. 感知机被禁用
- MP 神经元的权重是如何确定的?
- A. 梯度下降自动学习
- B. 手工设定
- C. 随机搜索
- D. 遗传算法
