Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

从线性模型到神经网络

前面我们给模型准备过“彩色页数”这样的交互特征。原始页数和模式分开看,不容易表达“彩印每页都慢一点”;把它们相乘,线性模型就能利用这条关系。

如果特征越来越多,可能的组合也越来越多,难道要把每一种组合都手工想出来?

神经网络提供了另一条路:先把输入变成一组新的数,再用这些数预测;而这套变换本身,也从数据中学习。这一章先不急着训练大网络,用四个点看清它为什么有用。

1. 一条直线分不开的四个点

输入是两个开关 x1,x2x_1,x_2,各取 0 或 1。恰好一个开关打开时,标签为 1;两个都关或都开时,标签为 0。这就是异或,常写作 XOR。

x1x_1x2x_2标签
000
011
101
110

把四个点画出来,会发现两类分别占据正方形的两条对角线。无论把一条直线怎样旋转,都不能让同类点完整落在同一边。

还可以从中点理解:两类各自的两个点,中点都是 (0.5,0.5)(0.5,0.5)。线性分数在中点等于两端分数的平均。如果两个正例都在分界线正侧,中点也应在正侧;两个负例却又要求同一个中点在负侧,矛盾了。

这不是学习率没调好,而是当前模型的表达方式不够。

2. 多接一层直线,为什么还不够

把两次仿射变换接起来:

h=W1x+b1 \boldsymbol h=W_1\boldsymbol x+\boldsymbol b_1

s=W2h+b2 s=W_2\boldsymbol h+b_2

将第一式代入第二式:

s=(W2W1)x+(W2b1+b2) s=(W_2W_1)\boldsymbol x +(W_2\boldsymbol b_1+b_2)

它仍然只是一次仿射变换。中间虽然多算了一步,却没有获得新的弯曲能力。

因此,我们在中间加一个非线性函数。最常见的例子之一是 ReLU:

ReLU⁡(z)=max⁡(0,z) \operatorname{ReLU}(z)=\max(0,z)

负数变成零,正数原样通过。这个小小的折角,让许多直线片段组合出更复杂的形状。

图 1:激活函数改变了层与层之间的关系。tanh 的输出在 −1~1 之间,Sigmoid 在 0~1 之间;ReLU 的正半轴没有上界。

激活函数不一定用于输出层。回归通常直接输出数值;二分类可以用 Sigmoid;多分类把输出分数交给 Softmax。隐藏层与输出层承担不同工作,不必使用同一种函数。

3. 两个隐藏单元,就能把 XOR 重新表示

我们手工安排两个隐藏单元:

h1=ReLU⁡(x1+x2)h2=ReLU⁡(x1+x2−1) \begin{aligned} h_1&=\operatorname{ReLU}(x_1+x_2)\\ h_2&=\operatorname{ReLU}(x_1+x_2-1) \end{aligned}

再计算 q=h1−2h2q=h_1-2h_2。四条输入会变成:

输入新表示 (h1,h2)(h_1,h_2)qq
(0,0)(0,0)0
(0,1)(1,0)1
(1,0)(1,0)1
(1,1)(2,1)0

现在只需要判断 q≥0.5q\ge0.5,就能分开两类。原空间中难以用直线分开的点,经过非线性变换后,可以在新空间中用直线分开。

图 2:隐藏表示不必保留输入的一切信息。本例把 (0,1) 与 (1,0) 合并,因为对当前标签而言,它们不需要被区分。

如果希望输出落在 0~1,可以再计算:

p=σ(12(q−0.5)) p=\sigma\bigl(12(q-0.5)\bigr)

12 控制曲线在阈值附近变得多陡。它与前面的权重、偏置都由我们手工设置,不是训练出来的结果。这里证明的是“这种网络可以表达这个规则”,不是“任意训练过程都能找到它”,也没有验证新数据上的概率校准。

4. 隐藏层、宽度与张量形状

一个典型的单隐藏层网络可以写成:

h=ϕ(W1x+b1) \boldsymbol h=\phi(W_1\boldsymbol x+\boldsymbol b_1)

s=W2h+b2 \boldsymbol s=W_2\boldsymbol h+\boldsymbol b_2

ϕ\phi 对每个分量应用激活函数。隐藏层并不神秘,只是输入与最终输出之间的中间计算;它的数值没有直接作为标签提供给训练程序。

若输入有 dd 个特征,隐藏层有 mm 个单元,输出有 kk 个分数,采用列向量记法时:

对象形状
x\boldsymbol xdd
W1W_1、b1\boldsymbol b_1m×dm\times d、mm
h\boldsymbol hmm
W2W_2、b2\boldsymbol b_2k×mk\times m、kk

对于一批按行排列的输入 X∈Rn×dX\in\mathbb R^{n\times d},计算会写成 XW1TXW_1^\mathsf T,结果是 n×mn\times m。偏置加到每一行,通常由框架的广播机制完成。

隐藏层有多少单元,叫宽度;串联多少个可学习的层,与网络深度有关。数层数时应说明是否计入输出层,避免同一个网络被叫出不同数字。

输入与相邻层每个单元都连接的结构叫全连接网络。带有隐藏层的前馈全连接网络通常称为多层感知机(MLP);“前馈”表示这次计算按输入到输出的方向进行,没有循环状态。

本例 2→2→1 的可调参数位置共有:

(2×2+2)+(2×1+1)=9 (2\times2+2)+(2\times1+1)=9

即使我们恰好给某些位置设了相同数值,它们在普通全连接结构中仍是独立位置。只有明确共享同一个参数,才应按共享参数计数。

5. 把四个点送进网络

完整脚本只需要 Python 标准库:

python3 src/examples/neural_network.py
"""第 12 章:手工设置一个能表达 XOR 的网络;只用标准库。"""

from math import exp


def relu(value):
    return max(0.0, value)


def sigmoid(value):
    if value >= 0:
        return 1/(1+exp(-value))
    e = exp(value)
    return e/(1+e)


def forward(x1, x2):
    hidden = [relu(x1+x2), relu(x1+x2-1)]
    score = 12*(hidden[0]-2*hidden[1]-0.5)
    return hidden, sigmoid(score)


def main():
    for x1, x2 in [(0, 0), (0, 1), (1, 0), (1, 1)]:
        hidden, probability = forward(x1, x2)
        print(f"输入=({x1},{x2}),隐藏表示={hidden},"
              f"类别1概率={probability:.4f},预测={int(probability >= 0.5)}")
    parameters = (2*2+2)+(2*1+1)
    print(f"2→2→1 全连接网络的可调参数位置:{parameters}")


if __name__ == "__main__":
    main()

运行结果:

输入=(0,0),隐藏表示=[0.0, 0.0],类别1概率=0.0025,预测=0
输入=(0,1),隐藏表示=[1, 0.0],类别1概率=0.9975,预测=1
输入=(1,0),隐藏表示=[1, 0.0],类别1概率=0.9975,预测=1
输入=(1,1),隐藏表示=[2, 1],类别1概率=0.0025,预测=0
2→2→1 全连接网络的可调参数位置:9

如果把两个 ReLU 去掉,试着重新检查四条输入。你会发现多接一层的计算又折回了线性关系。非线性不是装饰,它决定了这里能表达什么。

6. 从手工表示走向表示学习

刚才是我们替模型找到了 h1,h2h_1,h_2。真正训练时,通常随机初始化权重,用任务损失同时调整隐藏层与输出层。输出错了,梯度会告诉各层参数朝哪个方向改变。

这就是表示学习:中间特征并非全部由人预先写死,而是根据训练目标形成。它不保证每个隐藏单元都对应一个容易命名的概念。多个单元可以共同表示一种模式;改变参数后,相同功能也可能换一种编码方式。

更多单元和层可以扩大可表达的函数范围,但“能表达”不等于“能从有限数据中学到”。优化是否成功、数据是否足够、训练分布是否覆盖真实需求,都要分别考虑。

一个大网络甚至可能记住随机标签。训练集上表现很好,只说明它适应了这些样本;第八章关于验证、测试和泄漏的要求,并没有因为模型叫作神经网络而失效。

7. 自测与小实验

  1. XOR 的两个正例为什么不能与负例用一条直线分开?
  2. 两个没有激活函数的仿射层串联,会变成什么?
  3. ReLU 对 −3、0、2 的输出分别是多少?
  4. 本例为什么允许两个不同输入拥有相同隐藏表示?
  5. 输入 4 维、隐藏层 5 个单元、输出 3 个分数的全连接网络,有多少参数?
  6. 网络能表达正确规则,是否说明梯度训练一定能找到它?
  7. 把本例输出分数整体乘得更大,分类标签不变,概率与交叉熵一定也不变吗?
展开参考答案
  1. 两类各自的中点是同一点,而线性分数不可能要求这个中点同时处于分界线两侧。
  2. 仍是一次仿射变换,只是权重与偏置合并了。
  3. 依次为 0、0、2。
  4. 它们对当前分类任务有相同标签,表示只需保留与目标有关的信息;换一个任务时未必还能合并。
  5. 为 (4×5+5)+(5×3+3)=43(4\times5+5)+(5\times3+3)=43。
  6. 不说明。表达能力与优化过程是两个问题。
  7. 不一定。分数大小影响概率的尖锐程度,进而改变交叉熵;更自信也可能让错误代价更大。

选做:在 0~1 之间取一些非整数输入,观察模型的概率。四个角点的标签是否足以决定整个平面上唯一“正确”的分类?这个实验能帮助区分训练约束与模型在未见位置的行为。

下一章反向传播与自动微分会回答最关键的问题:一次预测经过许多层,怎样把最终误差的影响算回每一个参数?更系统的前馈网络介绍,可参阅《动手学深度学习》的多层感知机章节。