跳到主要内容

多层感知机

隐藏层先从输入中算出一些中间特征,再交给输出层。在下面的小网络里,两个神经元分别计算两个输入的差。关掉非线性激活,就能看到为什么光增加层数还不够。

亲手试试

XOR 为什么需要隐藏层?

XOR(异或):两个输入只有一个是 1 时,输出才是 1。切换输入,再关掉 ReLU,看看同一组权重为什么不管用了。

固定权重的小网络:两个输入、两个隐藏单元、一个输出[0,1] → [0,1] → 1输入ReLU输出+1−1−1+1+1+10x₁1x₂0h₁1h₂1ŷ连线上的数是固定权重;虚线表示负权重

[0, 1] → h = [0, 1] → 输出 = 1

ReLU 保留正的差值,把负数变成 0。两个隐藏值相加,就能判断输入是否不同。

这里的权重是手工选好的,没有训练。它说明这样的表示确实存在,不保证训练一定能找到它。
对照全部四种输入
x₁x₂目标输出
0000
0111
1011
1100
展开代码 · NumPy / JAX

上面的交互用 JavaScript 运行。下面的 Python 代码使用相同数据和公式,从默认值开始,不跟随当前滑块。JAX 的 jax.grad 可以自动计算损失的梯度。 JAX 自动微分文档

在新的本地项目中,把示例保存为 demo.py。CPU 环境: uv init ai-labcd ai-labuv add numpy jaxuv run demo.py

import jax.numpy as jnp
# NumPy: replace the import above with: import numpy as jnp

x = jnp.array([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
w1 = jnp.array([[1., -1.], [-1., 1.]])
w2 = jnp.array([1., 1.])
h = jnp.maximum(x @ w1, 0.)
print(h @ w2) # [0., 1., 1., 0.]
print((x @ w1) @ w2) # [0., 0., 0., 0.]: no ReLU

MLP 的核心结构是交替堆叠仿射变换(Affine Map)和逐元素非线性激活(Elementwise Nonlinearity)。对于输入 xRd\mathbf{x}\in\mathbb{R}^{d},隐藏层宽度 hh 和输出宽度 kk,含一个隐藏层的结构可表示为:

z1=W1x+b1,h=ϕ(z1),o=W2h+b2,\mathbf{z}_1=W_1\mathbf{x}+\mathbf{b}_1, \quad \mathbf{h}=\phi(\mathbf{z}_1), \quad \mathbf{o}=W_2\mathbf{h}+\mathbf{b}_2,

其中 W1Rh×dW_1\in\mathbb{R}^{h\times d}W2Rk×hW_2\in\mathbb{R}^{k\times h}。注意,如果去掉激活函数 ϕ\phi,连续的仿射变换会坍缩为单个仿射变换。这意味着,仅靠增加层数(深度)而不引入非线性,无法扩展函数类的表达能力。

输出头、损失函数与假设

问题类型输出形式常用损失函数关键检查点
实值回归线性值MSE, MAE 或基于似然的损失尾部分布、尺度、异方差性
二分类单个 logitBinary Cross-Entropy (BCE)阈值、类别成本、概率校准
互斥多分类kk 个 logitsSoftmax Cross-Entropy类别是否真正互斥
多标签分类kk 个独立 logits独立 BCE标签间依赖关系及阈值

隐藏层的激活函数与输出层的语义承担不同职责。不要先选定库中的损失函数,再强行套用业务问题;应根据任务特性反向推导输出头设计。

反向传播的计算本质

设单样本损失为 LL,定义 δ2=L/o\boldsymbol{\delta}_2=\partial L/\partial\mathbf{o}。根据链式法则,梯度计算如下:

LW2=δ2h,δ1=(W2δ2)ϕ(z1),\frac{\partial L}{\partial W_2}=\boldsymbol{\delta}_2\mathbf{h}^{\top}, \qquad \boldsymbol{\delta}_1=(W_2^{\top}\boldsymbol{\delta}_2)\odot\phi'(\mathbf{z}_1), LW1=δ1x.\frac{\partial L}{\partial W_1}=\boldsymbol{\delta}_1\mathbf{x}^{\top}.

自动微分(Autodiff)消除了手写导数的需求,但并未消除对张量形状、数值稳定性及目标函数假设的依赖。梯度为零可能意味着达到最优解,但也可能源于 Sigmoid/Tanh 饱和、ReLU 死亡、错误的 Mask 或计算图断开。

手算一次标量反向传播

x=2x=2w1=1w_1=1b1=1b_1=-1w2=3w_2=3b2=0b_2=0,目标 y=1y=1,损失 L=(oy)2/2L=(o-y)^2/2。前向计算得到 z1=1z_1=1h=ReLU(1)=1h=\operatorname{ReLU}(1)=1o=3o=3L=2L=2。反向计算得到 δ2=2\delta_2=2L/w2=2\partial L/\partial w_2=2δ1=6\delta_1=6L/w1=12\partial L/\partial w_1=12。两层偏置的梯度分别为 6622。所有导数都必须使用同一次前向传播的权重;若先更新 w2w_2 再算 δ1\delta_1,求导的就不再是原来的计算。

反向传播负责算梯度,优化器负责更新参数。批次损失若取平均,梯度也应平均;下一次独立更新前要清除已累积的梯度。推理只需前向计算。若网络包含 dropout,训练时会随机屏蔽激活(inverted dropout 会在训练时补偿数值尺度),评估时关闭这种随机性。仅含仿射层和 ReLU 的 MLP 没有这种随模式变化的层。

实例:用两个 ReLU 表示 XOR

对于 x1,x2{0,1}x_1,x_2\in\{0,1\},定义:

h1=ReLU(x1x2),h2=ReLU(x2x1),h_1=\operatorname{ReLU}(x_1-x_2), \qquad h_2=\operatorname{ReLU}(x_2-x_1), f(x1,x2)=h1+h2.f(x_1,x_2)=h_1+h_2.

当输入相等时((0,0)(0,0)(1,1)(1,1)),输出为 0;当输入不等时((1,0)(1,0)(0,1)(0,1)),输出为 1。单个线性边界无法分离 XOR 问题,而隐藏层首先构建了“输入是否不同”的中间表示。

此例仅证明该表示在数学上存在,并不保证 SGD 能从任意初始化中找到它,也不保证其在噪声数据下的泛化能力。

现代非线性激活

  • ReLU:计算廉价,正半轴梯度不饱和,但负半轴单元可能永久失活。它仍是有效的基线,并非在所有架构中都过时。
  • GELUSiLU/Swish:平滑的逐元素函数,常见于现代 Transformer 及部分 ConvNet。
  • GEGLU/SwiGLU:使用两条投影分支,改变了宽度、参数量和 Kernel 结构,并非简单的即插即用激活函数。
  • Sigmoid/Tanh:尽管在大绝对值区域存在饱和问题,但在门控机制和有界状态表示中依然有用。

详见 激活函数与门控前馈网络。公平的性能对比必须控制参数量、FLOPs、训练预算及实现 Kernel。

容量不等于可学习性

Cybenko 定理指出:使用连续 sigmoid 型激活函数的有限个单元之和,可以在紧致立方体上一致逼近任意连续函数,达到任意给定的正误差容限。所需的有限宽度可随目标函数与容限增长,并没有一个固定宽度适用于所有目标。它还不保证

  • 训练数据足够;
  • 存在参数或样本高效的表示;
  • 优化算法能成功找到该表示;
  • 输出概率经过校准;
  • 因果有效性或分布外(OOD)泛化能力。

“万能逼近器”(Universal Approximator)是一个理论性质,而非模型选择的依据。

归纳偏置与反例

MLP 将输入视为扁平向量。平移不变性、序列顺序、图结构或置换不变性不会自动出现。CNN、RNN、注意力机制和图网络通过编码更强的参数共享或计算模式,通常能改变样本效率。

反之,在处理稳定的表格特征且数据量有限时,线性模型、树模型或小型 MLP 可能优于复杂的“时尚”架构。必须在相同的数据切分和预算下进行对比。

最小验收标准

  1. 断言 Batch、特征和输出的张量形状。
  2. 在极小样本上过拟合,以验证实现的可学习性。
  3. 与常数、线性或树模型基线进行对比。
  4. 仅在训练折内拟合预处理步骤。
  5. 报告多个随机种子、失败案例及资源消耗,而非仅展示最佳运行结果。
  6. 使用 分布偏移下的评估 测试部署假设。

本文聚焦于传统监督梯度训练,不将 MLP 视为生物模型,也不涵盖所有激活函数和优化器的细节。

探索关联打开关联网络