深度神经网络
上一章我们认识了只有一个隐藏层的浅层网络——它已经能拟合很多函数了。可现实中的深度学习模型动辄几十层、上百层,大家为什么非要往"深"里堆?这一章我们就来揭开"深"的秘密:为什么层数多一点,表达能力就能暴涨;为什么小小的 ReLU 激活函数能"拯救"整个深度学习;以及"宽"和"深"到底该怎么选。学完这一章,你会明白"深的就是好的"背后真正的数学原理。
层层递进:深网络到底在"学"什么?
先抛开公式,想想人是怎么认出一张脸的。眼睛不会直接看到"脸",而是先看到横线、竖线、轮廓这些简单特征;然后把这些线条拼成鼻子、眼睛等局部部件;最后才组合出"一张脸"这个复杂概念。深层网络做的就是同一件事:低层学简单特征,高层学复杂特征,一层一层往上"组装"。
深网络就像汽车流水线工厂:第一道工序只负责拧螺丝(简单特征),第二道工序把零件装成发动机(部件),最后总装成整车(复杂概念)。如果让一个工人从头到尾造一辆车,他必须样样精通,工厂效率极低;分层之后,每一层都只需要站在前一层的肩膀上,把已经造好的东西再组合一次。深网络的价值首先在于:它天然匹配"由简单到复杂"的认知结构。
你可能要问:浅层网络不也能拟合任何函数吗?没错,通用逼近定理保证:只要隐藏单元足够多,单层网络也能逼近任意连续函数。但"能"和"划算"是两回事——有些函数需要指数级多的单元才装得下,现实中根本训练不动。深层网络的价值,正是用更少的参数做到同样的事。下面来看它到底怎么做到的。
深度的秘密:折叠与复制
把两个浅层网络串起来——第一个网络的输出 $y$ 变成第二个网络的输入——会发生什么?回顾上一章,一个隐藏层网络做的事是:先对输入 $x$ 做线性变换,再过激活函数 $a[\cdot]$,最后把结果线性组合成输出:
$$h_j = a[\theta_{j0} + \theta_{j1} x], \qquad y = \phi_0 + \sum_j \phi_j h_j$$这个式子的意思是:每个隐藏单元 $h_j$ 都是"一条直线加一个拐点",输出 $y$ 再把它们拼起来。现在把第二个同样的网络接在后面:
$$h'_j = a[\theta'_{j0} + \theta'_{j1} y], \qquad y' = \phi'_0 + \sum_j \phi'_j h'_j$$如果激活函数是 ReLU,每个网络算出的都是分段线性函数。关键来了:假设第一个网络把输入 $x \in [-1, 1]$ 分成了 3 段(3 个线性区域),第二个网络也把 $y$ 分成 3 段。第一个网络会把 3 段不同的输入"折"到同一个输出区间,第二个网络在折好的区间上再切 3 段——结果不是 $3 + 3$,而是 3 × 3 = 9 个线性区域!再串一层就是 27 个。每多一层,区域数就成倍相乘地增长。
这就像折纸剪窗花:一张纸对折一次,剪一刀得到 2 个洞;对折两次剪一刀,得到 4 个洞;对折 $K$ 次,得到 $2^K$ 个洞。深层网络的每一层都是一次"折叠 + 剪切":先折叠输入空间,再用 ReLU"剪"出新拐点。折的次数越多,最后剪出的独立区域就越多——这就是"深"的魔法。
宽度 vs 深度:同样的预算,谁更强?
网络的规模由两个"旋钮"决定,它们叫超参数(训练之前就要定好的量):宽度——每层有多少个隐藏单元;深度——有多少个隐藏层。现在算一笔账:同样是 1 个输入、1 个输出,每层 $D$ 个隐藏单元,浅层(1 层)和深层($K$ 层)各能创造多少个线性区域、要花多少个参数?
$$\text{浅层网络:区域} \le D + 1, \quad \text{参数} = 3D + 1$$ $$\text{深层网络(}K\text{ 层):区域} \le (D+1)^K, \quad \text{参数} = 3D + 1 + (K-1)D(D+1)$$第一个式子是说:一层网络里 $D$ 个隐藏单元顶多切出 $D + 1$ 段,代价是 $3D + 1$ 个参数。第二个式子是说:$K$ 层网络每层都把已有的每一段再切开,区域数变成 $(D+1)^K$,而参数只是随层数线性增加。代入具体数字感受一下:$K = 5$ 层、每层 $D = 10$ 个单元,参数 $3 \times 10 + 1 + 4 \times 10 \times 11 = 471$ 个,却能创造 $11^5 = 161{,}051$ 个线性区域。同样的 471 个参数如果堆在单层(约 157 个单元),最多只有 158 个区域——差了整整一千倍!
不过也要泼盆冷水:区域多不等于一定好。深层网络创造的海量区域之间存在着复杂的依赖和对称性,只有当真实世界的函数恰好有类似的结构(比如确实由简单函数组合而成)时,深度才真正占便宜;而且层数过深会让训练变难。所以"越深越好"是有条件的。
为什么 ReLU 那么重要?
前面反复提到 ReLU,它到底是什么?ReLU(Rectified Linear Unit,线性整流单元)就是一个"把负数清零"的函数:
$$a[z] = \max(0, z) = \begin{cases} z & z > 0 \\ 0 & z \le 0 \end{cases}$$意思是:输入大于 0 就原样输出,小于等于 0 就输出 0。就这么一个简单函数,却是现代深度学习的功臣之一,因为它解决了一个致命问题——梯度消失。
早期网络爱用 sigmoid 激活函数,但它的两端会"饱和":输入很大或很小时,曲线变平,导数趋近于 0。确切地说,sigmoid 的导数最大也只有 0.25:
$$\sigma'(z) = \sigma(z)\,(1 - \sigma(z)) \le 0.25$$这个式子的意思是:sigmoid 的坡度最陡时也只有 0.25。训练时梯度要从输出层一层层往回传(反向传播),每过一层就要乘一次导数。乘 10 次之后 $0.25^{10} \approx 10^{-6}$,梯度被"吃"得几乎为零,前面的层根本学不动,这就是梯度消失(vanishing gradient)。
而 ReLU 的妙处在于:$z > 0$ 时它的导数恒为 1,梯度连乘时不再被层层缩小,深层网络的前面几层也能好好更新;$z \le 0$ 时输出恒为 0,等于把没用的部分"剪掉",还顺带创造了新拐点。正因如此,2012 年 AlexNet 在图像分类上取得划时代突破时,ReLU 与大数据、强算力、随机梯度下降并称四大功臣。一句话概括 ReLU 的三个好处:计算快(一次比较)、缓解梯度消失(导数恒为 1)、配合深度创造海量线性区域(剪切出新拐点)。
动手:用 PyTorch 搭一个深层网络
理论讲完,动手验证一下。下面用 PyTorch 定义一个 3 层全连接网络(2 个隐藏层 + 1 个输出层)。代码里的 nn.Linear 就是权重矩阵 $\boldsymbol{\Omega}$ 和偏置 $\boldsymbol{\beta}$,torch.relu 就是那个"剪切"函数:
import torch
import torch.nn as nn
class DeepNet(nn.Module):
def __init__(self):
super().__init__()
# 宽度 = 每层隐藏单元数,深度 = 隐藏层层数
self.layer1 = nn.Linear(8, 16) # 第一层:8 个输入 → 16 个隐藏单元
self.layer2 = nn.Linear(16, 8) # 第二层:16 → 8
self.layer3 = nn.Linear(8, 1) # 输出层:8 → 1
def forward(self, x):
x = torch.relu(self.layer1(x)) # 线性变换 + ReLU 剪切
x = torch.relu(self.layer2(x))
return self.layer3(x)
net = DeepNet()
x = torch.randn(4, 8) # 一批 4 个样本,每个 8 维
print(net(x).shape) # 输出形状: [4, 1]
把 self.layer2 删掉,这个网络就退化成了浅层网络——试试看同样的数据,深浅两个网络能拟合的函数复杂度差多少。理解了这一章,你就看懂了后面所有现代网络架构(卷积、Transformer)里"层"的角色。