上一篇结尾留下了单个神经元的两条边界:它只能画出线性决策边界;它只能使用我们交给它的输入。这两条其实是同一个问题的两面。数据在原始输入上线性不可分时,机器学习的常规做法是改输入,也就是做特征工程。神经网络换了一个做法:加一层隐藏层,让模型自己学出新的输入。
这一篇讲清楚两件事。第一,隐藏层要学出有用的特征,层与层之间为什么必须有一个非线性函数。第二,这个函数为什么通常选 ReLU 这一类曲线,而很少直接用阶跃函数或 Sigmoid。
一个逻辑回归处理不了的规则
舒适温度区间
只取一个输入:房间温度 (摄氏度)。标签是房间是否舒适。
| 温度 | ||||||
|---|---|---|---|---|---|---|
| 舒适 | 否 | 否 | 是 | 是 | 否 | 否 |
假设规则是 18 到 24 度之间算舒适:
这类「过低和过高都不行」的规则在实际数据里很常见,比如药物剂量、商品定价。
线性分数只能切一刀
逻辑回归计算 ,在 时预测为类别 。 时,预测为 的区域是 ; 时是 。两种情况都只有一个端点,而目标区间需要两个。
z = w·x + b外层的 Sigmoid 帮不上忙。它是单调函数,只改变分数的尺度,不改变分数的大小顺序。问题出在 关于 单调,而目标不单调。
熟悉的解法:加一个特征
把 放进输入
熟悉逻辑回归的话,第一反应多半是加一个二次项。把输入从 换成 ,模型变成
取 、、:
这是一条开口向下的抛物线,只在 时非负,目标区间就被写出来了。
模型本身没有变。它对参数 、、 仍然是线性的,训练方法也和上一篇完全一样。变的是输入: 不是 的线性函数,它给了模型一个新坐标,在这个坐标下答案变得线性可分。
这个办法的代价
是我们选的。选它之前,我们已经知道目标的形状是「中间高、两边低」。
一维时这不难。输入一多就难了:温度和湿度两个变量,二次特征有 、、 三项; 个变量有约 项。一张 的彩色图像有 个输入,二次项超过 个,而且我们并不知道「是不是猫」应该用二次特征来描述。
所以问题变成:能不能把「从 算出新特征」这一步也交给模型,用数据训练出来?
让模型自己学特征
用一层线性变换提取特征
最直接的尝试是在输入和逻辑回归之间插一层可训练的变换:
就是想让模型学出来的特征,、 和输出层参数一起训练。
线性层会合并成一层
把第一式代入第二式:
结果仍然是 的形式,和原来的逻辑回归没有区别。叠多少层都一样,因为线性(严格说是仿射)变换的复合还是仿射变换。
对照上一节就能看出原因。 有用,是因为它不是 的线性函数;而 的每个分量都是 的线性函数,输出层能从中得到的信息,直接用 也能得到。
在中间加一个非线性函数
修正方法是对每个分量套一个非线性函数 :
逐元素作用,这就是激活函数。只要 不是 的形式,上面的合并就不再成立, 才可能成为 那样的新特征。
注意这里说的是隐藏层的激活函数。上一篇输出层的 Sigmoid 负责把分数转成概率,是另一件事。
ReLU 作为可学习的特征
两个 ReLU 单元
最常用的激活函数是
它在 时输出 ,在 时原样输出,只在原点有一个折点。在隐藏层放两个单元:
是温度高出 22 度的部分, 是温度低于 20 度的部分。输出层取
| 预测 | ||||
|---|---|---|---|---|
| 否 | ||||
| 是(边界) | ||||
| 是 | ||||
| 是(边界) | ||||
| 否 |
分段看: 时 ; 时 ;中间 。所以 恰好对应 。
z = 2 − ReLU(x − 22) − ReLU(20 − x)关键在 ReLU 有一侧输出恒为 。温度偏低时 ,只有 影响结果;偏高时反过来。如果把两个 ReLU 换成线性单元,它们的和仍是 的线性函数,这张表就算不出来。
和 对照
和 的作用相同:原始输入上线性不可分的问题,在新坐标下变成线性可分,输出层只需要做加权求和。
z ≥ 0 ⇔ 18 ≤ x ≤ 24区别在于形状由谁决定。 的形状在训练前就固定了。 的折点在 ,打开的方向由 的符号决定,而 、 都是参数。上面的 22 和 20 是手写的;在真实训练中,梯度下降会调整它们,数据决定折点落在哪里。
这就是隐藏层所说的「学习表示」:特征的形式(分段线性)是结构给定的,具体的特征由数据训练出来。
单元越多,折点越多
一个 ReLU 单元贡献一个折点。 个单元的线性组合是最多 段的分段线性函数,段数足够多时,可以逼近区间上的任意连续函数。Cybenko(1989)和 Hornik 等人(1989)对 Sigmoid 类激活证明了这一点,通常称为万能逼近定理,后来的结果也覆盖了 ReLU。
这个定理只说明这样的参数存在。它没有说明需要多少单元,也没有说明梯度下降能不能找到这些参数。前一个问题是下一篇的主题,后一个问题要等到反向传播。
为什么常用这几条曲线
只看表达能力,任何非线性函数都可以,阶跃函数也行。实际选择由训练决定:梯度下降需要激活函数的导数,而且导数不能太小。上一篇的参数更新里,梯度要乘上 Sigmoid 的导数;多层网络中,每经过一层就要乘一次该层激活函数的导数。
阶跃函数:没有梯度
感知机使用的阶跃函数在 时输出 ,否则输出 。它在 处导数为 ,在 处不可导。参数稍微改变,输出通常完全不变,梯度下降没法用。
σ(z) ≈ 1{z ≥ 0}1986 年 Rumelhart、Hinton 和 Williams 的 Learning representations by back-propagating errors 把阈值单元换成 Sigmoid,误差才能逐层传回隐藏层,隐藏层也才开始学习表示。
Sigmoid:导数最大只有 0.25
Sigmoid 的导数是 ,在 处取到最大值 , 较大时接近 。
σ′(z) = σ(z)(1 − σ(z))一层时影响不大。十层网络的梯度要连乘十个这样的因子,在权重不大的情况下,前面几层的梯度会变得非常小,几乎不更新。这称为梯度消失。Glorot 与 Bengio(2010)的实验分析了 Sigmoid 在深层网络中的这个问题。
ReLU:正半轴导数为 1
ReLU 的导数在 时为 ,在 时为 。只要单元处于打开状态,梯度经过它时不会缩小。Glorot、Bordes 与 Bengio(2011)的 Deep Sparse Rectifier Neural Networks 之后,ReLU 成为深层网络隐藏层的默认选择。它的计算也很便宜,只需要一次比较。
代价在负半轴:如果某个单元对所有训练样本都有 ,它的梯度始终为 ,参数不再更新。这种现象叫 dying ReLU。
Tanh 和 GELU
另外两个常见的选择:
- Tanh:,输出范围 ,以 为中心,原点处导数为 。两端依然饱和,常见于 RNN 等需要有界状态的结构。
- GELU:, 是标准正态分布的 CDF(Hendrycks & Gimpel, 2016)。可以把它看作平滑版的 ReLU,在 附近没有硬折点,负输入会保留一小部分。BERT、GPT-2 等 Transformer 模型使用它。
φ(z)实际选择时,普通 MLP 和 CNN 先用 ReLU,Transformer 沿用 GELU,一般就够了。
运行对照实验
下面的代码不做训练,只把三种模型在整个温度范围上算一遍:原始输入上的逻辑回归、加入 的逻辑回归、两个 ReLU 单元。点击「运行」后 Python 在浏览器里执行,第一次需要下载 Pyodide、NumPy 与 Matplotlib。
运行代码后,文本与图表会显示在这里。
后两种模型的判断结果相同。区别只在于新特征是人写的,还是由参数决定、可以训练的。
小结与下一篇
这一篇的论证是:
- 逻辑回归在原始输入上只能切一刀,舒适区间需要两刀。
- 传统解法是人工加 这样的非线性特征,但高维输入下很难手工设计。
- 用线性层来学特征会失败,因为线性层会合并成一层。
- 在线性层之后加入激活函数,隐藏层就能学出非线性特征;ReLU 的每个单元提供一个位置可训练的折点。
- 激活函数的具体形状由训练决定:需要导数,并且导数不能太快趋向 。
这里只用到一层隐藏层。根据万能逼近定理,一层足够宽就能逼近任意连续函数,那为什么实际网络要做得很深?下一篇讨论深度。