Skip to content

第 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(静默)。

python
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 = 0

MP 神经元的贡献不是它的能力,而是它把「大脑如何工作」变成了「数学问题」:神经网络从此可以像电路一样被分析。多年后人们回头看,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):

python
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 ​

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 ​

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 ​

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

动手实践 ​

  1. 运行 uv run python examples/ch01_mp.py,把 AND 的阈值改成 1.5,观察输出变化,解释为什么阈值决定了逻辑门类型。
  2. 在 examples/ch01_perceptron.py 中给 AND 数据加一个新样本 (1, 0.5, -1)(即 1 AND 0.5 应为假),重新训练,观察收敛轮数变化。
  3. 修改 Adaline 的学习率(0.01 和 1.0),观察权重是否收敛、是否振荡。
  4. 用 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 判定或微小扰动

章末练习 ​

基础

  1. 用 MP 神经元构造 NAND 门(即 NOT AND),并写出它的权重与阈值。
  2. 感知机学习规则中,w += lr * y * x 的直觉是什么?为什么只在预测错误时更新?
  3. 画出 XOR 的四个点,说明为什么任何直线都无法分开它们。

提高

  1. 证明:AND 和 OR 是线性可分的(给出两组满足条件的 w、b)。
  2. 感知机在 OR 上 6 轮收敛,在 AND 上 9 轮——尝试解释为什么 AND 更难学(提示:AND 只有一个正样本)。
  3. Adaline 的更新公式 w -= lr·(pred-y)ᵀx/len(y) 对应哪个损失函数的梯度?写出来。

挑战

  1. 实现一个「投票感知机」:训练 10 个感知机(不同随机初始权重),用多数投票做预测,在 XOR 上测试准确率,并解释结果(提示:线性模型的投票仍是线性模型)。
  2. 用逻辑回归(BCEWithLogitsLoss)在 XOR 数据上训练,记录 loss 曲线,解释 loss 停在哪、为什么。

章末自测 ​

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. MP 神经元的输出取决于?
    • A. 随机数
    • B. 加权和与阈值的比较
    • C. 输入个数
    • D. 训练轮数
  2. 感知机的更新规则在什么情况下更新权重?
    • A. 每次都更新
    • B. 预测正确时
    • C. 预测错误时
    • D. 从 不更新
  3. 感知机无法学习 XOR 的根本原因是?
    • A. 学习率太大
    • B. 训练数据太少
    • C. XOR 线性不可分,而感知机只能学线性边界
    • D. 权重初始化不好
  4. Adaline 与感知机的主要区别是?
    • A. Adaline 用线性输出做梯度更新,感知机用符号
    • B. Adaline 有隐藏层
    • C. 感知机使用 ReLU
    • D. 没有区别
  5. 1969 年《感知机》一书的作者是?
    • A. Rosenblatt 和 Widrow
    • B. Minsky 和 Papert
    • C. LeCun 和 Bengio
    • D. McCulloch 和 Pitts
  6. 逻辑回归输出的是?
    • A. ±1 的符号
    • B. 0~1 之间的概率
    • C. 任意实数
    • D. 整数
  7. 感知机学习率 η 的作用是?
    • A. 控制每次更新的步长
    • B. 控制输入维度
    • C. 控制阈值
    • D. 没有作用
  8. 下列哪个问题可以用单层感知机解决?
    • A. XOR
    • B. AND
    • C. 异或的补集
    • D. 任何非线性问题
  9. 「第一次 AI 寒冬」主要导致?
    • A. 神经网络研究经费大幅削减
    • B. GPU 停产
    • C. Python 无法使用
    • D. 感知机被禁用
  10. MP 神经元的权重是如何确定的?
    • A. 梯度下降自动学习
    • B. 手工设定
    • C. 随机搜索
    • D. 遗传算法