跳到主要内容

从神经元到多层感知机

本节定位

深度学习的一切都从人工神经元开始。本节从最简单的感知机出发,认识各种激活函数,再组装成多层感知机(MLP)——这是所有神经网络的基础。

学习目标

  • 理解从生物神经元到人工神经元的映射
  • 掌握感知机模型
  • 掌握常用激活函数:ReLU、Sigmoid、Tanh 等
  • 理解多层感知机(MLP)的结构

历史背景:神经网络这条线最早是怎么长出来的?

这一节最关键的几个历史节点是:

年份节点关键作者它最重要地解决了什么
1943McCulloch-Pitts NeuronMcCulloch, Pitts给出了人工神经元的最早计算抽象
1958PerceptronFrank Rosenblatt提出了最早可训练的单层神经网络分类器之一
1969PerceptronsMinsky, Papert系统揭示了单层感知器对 XOR 等非线性可分问题的局限
1980NeocognitronFukushima提前给出了卷积、局部感受野、层级特征的核心思路

对新人来说,这里最值得先记的是:

感知器不是“落后模型”,而是神经网络历史上第一次非常清楚地让人看到:单层模型能做什么、又做不到什么。

所以你这一节看到的 XOR,不只是一个玩具例子,
而是神经网络历史上非常关键的分水岭问题之一。

为什么 XOR 这个只有 4 个点的小问题会这么出名?

因为它特别“打脸式”地暴露了单层感知器的边界。

表面上看,XOR 非常小:

  • 只有 4 个输入点

但它的意义恰恰在于:

  • 如果一个模型连这么小的非线性模式都搞不定
  • 那就说明它的表达能力不是“差一点”,而是结构上就有边界

所以 XOR 之所以会反复出现在教材里,
不是因为它本身复杂,
而是因为它像一个非常锋利的测试题:

用极小的例子,把“单层不够”这件事讲得无可回避。

为什么感知器会先让人兴奋,后来又让人失望?

因为感知器刚出现时,很多人第一次看见:

  • 机器好像真的可以“学”
  • 而不是所有规则都靠人手写

这件事在当时非常抓人。
它像是在告诉大家:

也许智能不只是编码出来的,也可以训练出来。

但后来 XOR 这类问题又像一盆冷水。

因为它提醒整个领域:

  • 单层模型的表达能力其实很有限
  • “会学”不代表“什么都能学”

所以这段历史特别有故事感的地方在于:

  • 它先点燃了一次巨大期待
  • 然后又迫使大家重新面对模型能力的边界

先建立一张地图

这一节更适合新人的理解顺序不是“背神经网络名词”,而是先看这条线:

所以你真正要先看懂的是:

  • 神经元先做了什么线性计算
  • 激活函数为什么必须存在
  • 一层和多层到底是怎么长出来的

这节和第 5 站最直接的连续性是什么

如果你刚学完第 5 站,可以先把一个神经元理解成:

  • 线性回归 / 逻辑回归那种“加权求和”的升级版

也就是说,神经元并不是凭空出现的新对象,它其实是在第 5 站熟悉的线性模型骨架上,多加了一步:

所以这一节真正新增的核心,其实只有两件事:

  • 激活函数
  • 多层堆叠

一、从生物到人工

核心对应关系:

生物人工
树突(接收信号)输入 x
突触强度权重 w
细胞体(汇总)加权求和 z = Σ(wi·xi) + b
激活/抑制激活函数 f(z)
轴突(输出)输出 a = f(z)

1.1 一个最小“人工神经元”计算例子

新人最容易发虚的地方是:知道公式,但脑子里没有“这一步到底算出了什么”。

先看一个最小例子:

import numpy as np

# 一个样本的 3 个特征
x = np.array([0.8, 0.3, 0.5])

# 一个神经元的 3 个权重
w = np.array([0.2, -0.4, 0.6])
b = 0.1

# 第一步:线性组合
z = np.dot(x, w) + b
print("z =", round(z, 4))

# 第二步:过激活函数
relu_out = max(0, z)
print("ReLU(z) =", round(relu_out, 4))

你可以把这一步理解成:

  • 权重在表达“每个输入有多重要”
  • 偏置在表达“整体阈值往哪边推一点”
  • 激活函数决定“这个神经元到底要不要被激活”

1.1.1 如果先不谈深度学习,可以把神经元想成什么?

一个很适合新人的理解方式是:

  • 把神经元先看成“带一个门”的线性模型

先算:

  • z = x·w + b

再决定:

  • 这个结果要原样通过、压到 0~1、还是小于 0 直接截掉

这个“门”就是激活函数。
所以神经元并不是神秘新物种,而是在“线性打分”外面再加一层非线性选择。


二、感知机——最简单的人工神经元

2.1 模型

感知机是一个做二分类的简单模型:

z = w1·x1 + w2·x2 + ... + wn·xn + b

输出 = 1 如果 z > 0,否则 = 0

import numpy as np
import matplotlib.pyplot as plt

class Perceptron:
"""最简单的感知机"""
def __init__(self, n_features, lr=0.1):
self.w = np.zeros(n_features)
self.b = 0
self.lr = lr

def predict(self, x):
z = np.dot(x, self.w) + self.b
return 1 if z > 0 else 0

def train(self, X, y, epochs=20):
for epoch in range(epochs):
errors = 0
for xi, yi in zip(X, y):
pred = self.predict(xi)
error = yi - pred
if error != 0:
self.w += self.lr * error * xi
self.b += self.lr * error
errors += 1
if errors == 0:
print(f"第 {epoch+1} 轮收敛!")
break

# AND 门
X = np.array([[0,0], [0,1], [1,0], [1,1]])
y = np.array([0, 0, 0, 1])

p = Perceptron(2)
p.train(X, y)
print(f"权重: {p.w}, 偏置: {p.b}")
for xi, yi in zip(X, y):
print(f" 输入 {xi} → 预测 {p.predict(xi)}, 真实 {yi}")

2.2 感知机的局限

感知机只能解决线性可分问题。XOR 问题就无法解决——这正是多层网络出现的原因。

# XOR 问题——感知机无法解决
X_xor = np.array([[0,0], [0,1], [1,0], [1,1]])
y_xor = np.array([0, 1, 1, 0])

p_xor = Perceptron(2)
p_xor.train(X_xor, y_xor, epochs=100)

print("\nXOR 预测结果:")
for xi, yi in zip(X_xor, y_xor):
print(f" 输入 {xi} → 预测 {p_xor.predict(xi)}, 真实 {yi}")

2.3 感知机这一段最该带走什么?

不是“感知机还值得不用”,而是它帮你看清了一个特别关键的事实:

只有线性打分时,模型表达能力会很快遇到边界。

这正是后面为什么需要:

  • 激活函数
  • 多层网络

所以感知机最重要的教学价值,是帮你第一次看到“为什么单层不够”。


三、激活函数

3.1 为什么需要激活函数?

如果没有激活函数,多层网络就退化为一个线性模型——无论叠多少层,效果等同于单层。激活函数引入非线性,让网络能拟合任意复杂的函数。

3.1.1 这句话为什么这么重要?

因为它解释了“深度”为什么不是单纯堆层数。

如果每一层都只是线性变换,那很多层合起来本质上还是一个更大的线性变换。
真正让多层网络变得有意义的,不是层数本身,而是:

  • 每层之间插入了非线性

所以可以先记一个最重要的判断:

  • 没有非线性,深度网络就学不出复杂形状

3.2 常用激活函数

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 200)

# 各种激活函数
activations = {
'Sigmoid': (1 / (1 + np.exp(-x)), 'σ(x) = 1/(1+e⁻ˣ)'),
'Tanh': (np.tanh(x), 'tanh(x)'),
'ReLU': (np.maximum(0, x), 'max(0, x)'),
'Leaky ReLU': (np.where(x > 0, x, 0.01 * x), 'max(0.01x, x)'),
}

fig, axes = plt.subplots(2, 2, figsize=(12, 8))
colors = ['#e74c3c', '#3498db', '#2ecc71', '#9b59b6']

for ax, (name, (y, formula)), color in zip(axes.ravel(), activations.items(), colors):
ax.plot(x, y, linewidth=2, color=color)
ax.axhline(0, color='gray', linewidth=0.5)
ax.axvline(0, color='gray', linewidth=0.5)
ax.set_title(f'{name}: {formula}', fontsize=12)
ax.set_xlim(-5, 5)
ax.grid(True, alpha=0.3)

plt.suptitle('常用激活函数', fontsize=14)
plt.tight_layout()
plt.show()

3.3 对比与选择

激活函数输出范围优点缺点使用场景
ReLU[0, +∞)计算快、缓解梯度消失神经元"死亡"隐藏层首选
Sigmoid(0, 1)输出概率解释梯度消失、非零中心二分类输出层
Tanh(-1, 1)零中心梯度消失RNN(较少用)
Leaky ReLU(-∞, +∞)避免神经元死亡多一个超参数ReLU 改进
GELU约 (-0.17, +∞)平滑、效果好计算稍慢Transformer
Swish约 (-0.28, +∞)平滑、自门控计算稍慢新架构
ReLU 的"神经元死亡"

当输入始终为负时,ReLU 输出永远为 0,梯度也为 0,参数不再更新。Leaky ReLU 通过给负数一个小斜率(0.01)来缓解。

3.4 初学阶段怎么选激活函数最不容易乱?

一个足够稳的记法是:

  • 隐藏层先默认用 ReLU
  • 二分类输出层常见 Sigmoid
  • 多分类输出层常见 Softmax
  • Transformer 里经常会碰到 GELU

先把这四条记住,已经足够支撑你进入大部分后续章节。

3.5 第一次看激活函数图,最值得先盯哪几件事?

不要一上来纠结每条曲线的精确公式。先只看这三件事:

  1. 输出范围是什么
  2. 小于 0 的部分会怎么处理
  3. 这条曲线是不是平滑、是不是容易让梯度太小

这三件事会直接决定:

  • 输出能不能解释成概率
  • 梯度会不会消失
  • 训练时会不会比较稳

四、多层感知机(MLP)

4.1 结构

把多个神经元按层排列,前一层的输出作为下一层的输入:

4.1.1 多层到底强在哪里?

一个更适合新人的理解方式是:

  • 第一层先学一些比较基础的小模式
  • 后一层再把这些模式重新组合
  • 层数越往后,表示就越抽象

哪怕在最简单的 MLP 里,你也可以把它先粗略理解成:

  • 前面层在学“中间表示”
  • 最后一层在用这些表示做输出判断

这正是“自动学表示”开始发生的地方。

4.2 用 NumPy 实现 MLP 解决 XOR

np.random.seed(42)

# XOR 数据
X = np.array([[0,0], [0,1], [1,0], [1,1]])
y = np.array([[0], [1], [1], [0]])

# 网络: 2 → 4 → 1
W1 = np.random.randn(2, 4) * 0.5
b1 = np.zeros((1, 4))
W2 = np.random.randn(4, 1) * 0.5
b2 = np.zeros((1, 1))

def sigmoid(z):
return 1 / (1 + np.exp(-z))

def sigmoid_deriv(a):
return a * (1 - a)

lr = 1.0
losses = []

for epoch in range(5000):
# 前向传播
z1 = X @ W1 + b1
a1 = sigmoid(z1)
z2 = a1 @ W2 + b2
a2 = sigmoid(z2)

# 损失
loss = np.mean((y - a2) ** 2)
losses.append(loss)

# 反向传播
dz2 = (a2 - y) * sigmoid_deriv(a2)
dW2 = a1.T @ dz2 / 4
db2 = np.mean(dz2, axis=0, keepdims=True)

dz1 = (dz2 @ W2.T) * sigmoid_deriv(a1)
dW1 = X.T @ dz1 / 4
db1 = np.mean(dz1, axis=0, keepdims=True)

# 更新
W2 -= lr * dW2
b2 -= lr * db2
W1 -= lr * dW1
b1 -= lr * db1

print(f"最终损失: {losses[-1]:.6f}")
print("XOR 预测:")
for xi, yi, pred in zip(X, y, a2):
print(f" {xi}{pred[0]:.4f}, 真实 {yi[0]}")

plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('MLP 解决 XOR')
plt.grid(True, alpha=0.3)
plt.show()

小结

概念要点
人工神经元加权求和 + 激活函数
感知机最简单的神经元,只能线性分类
激活函数引入非线性;隐藏层用 ReLU
MLP多层堆叠,可拟合任意函数

这节最该带走什么

如果只带走一句话,我希望你记住:

神经网络的起点不是“很多层”,而是“在线性计算后加上非线性,再把这种结构不断堆起来”。

所以这一节真正要稳住的是:

  • 神经元先算线性,再过激活
  • 感知机的边界会逼出多层网络
  • 激活函数决定网络有没有真正的非线性表达能力
  • MLP 是后面很多复杂结构的最小原型

动手练习

练习 1:实现 OR 门感知机

修改 AND 门的训练数据为 OR 门(0|0→0, 0|1→1, 1|0→1, 1|1→1),训练感知机并画出决策边界。

练习 2:MLP 分类月牙数据

sklearn.datasets.make_moons 生成月牙数据,手写 NumPy MLP(2→8→1),训练后画出决策边界。