多层感知机
为什么直接堆线性层不行?激活函数如何打破线性,配合全连接层堆叠出能拟合 XOR、同心圆、月牙的多层感知机(MLP)。
实验:堆叠两个线性层
上节课我们用 一个线性层 拟合简单的二维数据, 但碰到 XOR 这种弯曲的边界就完全没辙—— 因为单层线性模型只能画出一条 直线 分界。
而深度学习的核心想法,就是 把多层「层叠」起来 , 通过多层组合让网络变深、表达能力变强,从而拟合更复杂的数据。 我们能不能直接把两个线性层堆起来,搞定 XOR 呢?我们正面试一下:
下面这张图就是上述公式对应的网络结构(从左到右:输入 → 隐藏层 → 输出):
每个隐藏神经元 都是输入的加权和加偏置; 每个输出 又是 的加权和加偏置。 看起来是「两步」运算,下面我们把它合并成一步看看。
把第一行代入第二行,展开计算:
令:
于是 —— 两个线性层的组合,等价于一个线性层 。
突破口:引入非线性
既然问题出在「线性的复合还是线性」,那就在层与层之间塞一个 非线性变换 ,打破这种线性传递性:
其中 是某种非线性函数, 它就是 激活函数 (Activation Function)。
那么,一个合格的激活函数应该具备哪些性质?我们逐条来看:
- 非线性(必需) : 这是激活函数 唯一硬性 的要求。 若激活函数本身是线性的,多层网络仍然会塌缩成单层, 上一节的「两个线性层等价于一个线性层」会再次发生—— 网络失去深度带来的表达力。
- 处处可导(强烈推荐) : 神经网络靠 梯度 来更新参数(下一课就会讲反向传播)。 激活函数最好处处可导,反向传播才能顺畅地把梯度传回每一层。 ReLU 在 0 点不可导,但在工程上可以约定取一个次梯度,影响很小。
- 计算便宜 : 深度网络在每一层、每个神经元都要执行一次激活函数。 如果函数本身计算昂贵(比如要算指数、除法),整体训练和推理都会被拖慢。 ReLU 之所以流行,一部分原因就是它便宜到极致——只是一次比较。
- 梯度不易消失 : 如果在某些区间内导数趋近 0(比如 Sigmoid 在两端), 反向传播时梯度会被这些区间反复乘小数,越往前层越接近 0, 深层网络就更新不动——这就是 梯度消失 。 ReLU 在正区间导数恒为 1,能在很大程度上缓解这个问题。 这里梯度消失只是「先记住名字」, 后面讲反向传播时会专门展开,现在不懂也没关系。
- 输出范围合理 : 有时希望输出限定在 (0, 1) 表示概率(Sigmoid), 有时希望以 0 为中心便于优化(Tanh), 有时希望保持原始数值范围(ReLU)。不同任务对值域有不同偏好。
总结一下: 非线性 是底线,其他性质决定训练效率与稳定性。 因此实践中我们会在「最便宜、最常用、表现也够好」的几个函数里选—— 这就是下一节要介绍的「激活函数三剑客」。
三种常见激活函数
初次见到这些激活函数的公式,很多人会困惑:「为什么要长这样?」—— 别想多了, 激活函数的公式本身没有什么深意 , 前人尝试过各种函数,能引入非线性、又便宜好算的就被留了下来。
因此面对它们, 不需要背公式 ,只要记住三件事就够了:
- 值域 :函数把输入映射到什么区间
- 大致形状 :曲线长什么样
- 特性 :常见用途与典型坑
- 值域 :
- 优点 :
- 缺点 :
现代深度学习中, ReLU 是默认选择 , Sigmoid 和 Tanh 主要出现在二分类输出层与一些 RNN 门控结构中。
全连接层
在正式介绍 MLP 之前,我们先把 MLP 最基本的「积木」单独拎出来—— 全连接层 (Fully-Connected Layer,简称 FC 层; 在 PyTorch 里对应 nn.Linear , 有时也叫 Dense 层、线性层)。
定义
一个全连接层把维度为 的输入向量, 映射到维度为 的输出向量。 每一个输出神经元,都是 所有输入神经元 的加权求和加偏置:
用矩阵形式写更紧凑(也是代码里最常见的写法):
「全连接」三个字直观说明了它的结构: 输入与输出之间,每一对神经元都有一条边 (即一个权重)。
超参数 vs 参数
- 超参数 :人为指定、训练前固定,决定层的「形状」 输入维度 输出维度 是否带偏置(PyTorch 里的 bias=True/False )
- 参数 :训练过程中由数据自动学出来 权重矩阵 共 个数 偏置向量 共 个数 合计可学习参数:
直观看一眼
下面是一个 的全连接层。 鼠标悬停在权重边上 可以看到具体的 值; 悬停或点击「权重」按钮 可以浮窗查看完整的权重矩阵。 共 个可学习参数。
全连接层是最朴素也最通用的网络层。MLP、CNN 的分类头、Transformer 的 FFN, 本质上都靠它在不同维度间做线性映射。
多层感知机的标准结构
把 全连接层 和 激活函数 交替堆叠起来, 就得到了 多层感知机 (Multilayer Perceptron, MLP):
每个隐藏层做两件事:
- 线性变换 :
- 非线性激活 :
输出层根据任务类型而不同——回归任务通常不接激活,分类任务接 Softmax。
交互式 MLP 拓扑
下面这个 MLP 你可以 自由调节 : 点击顶部 + / − 增减隐藏层; 点击每一列下方的 + / − 增减该层神经元数。 每条边的颜色与粗细会反映权重值(红正蓝负、绝对值越大越粗); 鼠标悬停在边上看权重数值,点击或悬停顶部「权重」按钮可以打开 权重矩阵 表查看完整参数。
随便加几层试试——隐藏层越多、每层越宽, 网络就能拟合越复杂的边界,但也意味着参数更多、需要更多数据来训练。
实践:手写 MLP 前向传播
我们用 PyTorch 手动堆出一个最小的 MLP, 给定一组输入,逐层算出输出,验证激活函数确实把数据揉出了非线性。
点下方的「下一步」逐步推进——左边代码会高亮当前正在执行的行, 右侧画布会同步刷新对应的张量。
看到了吗? ReLU 把 中的负元素「清零」, 破坏了从 到 整体的线性关系—— 这就是激活函数发挥作用的瞬间。
Playground:MLP 决策边界探索
前面我们一直在「描述」MLP,下面真刀真枪地训一个—— 看看在 XOR、同心圆、月牙这类 非线性可分 数据上, MLP 到底能画出什么样的决策边界。
玩法 :选数据集、选激活函数、调网络结构, 然后点「开始」让 TF.js 在浏览器里直接训练。 训练时拓扑图里的权重会持续刷新, 左下散点图叠加预测概率热力图(红色 = 预测为 1,蓝色 = 预测为 0), 右下是 loss 曲线。
附录:激活函数对比表
除了上面三剑客,工程中还会用到 Leaky ReLU、GELU 等。 下表罗列了五个最常见的激活函数,方便日后查阅; 每张图都支持鼠标悬停查看具体坐标。
- 导数 :
- 值域 :
- 典型场景 :
- 缺点 :