原问题:「在深度学习中,RELU作为一种激活函数,只是把输入的负数输出为0,输入的正数还是不变,这怎么看都是一种毫无意义的信息损失,为什么却在实践中广受欢迎?」
这又是一个很基础、但很有意义的问题。如果你扎扎实实把这个问题想清楚,对理解深度神经网络会非常有帮助。
首先,这肯定不是「一种毫无意义的信息损失」,它引入了「非线性」,从而让一个拥有单层足够多个隐藏节点的浅层网络,就能够理论上拟合任意一个函数。这就是通用近似定理(Universal Approximation Theorem)。
但这个结论是怎么推导出来的呢?很多老师都是不讲的,只告诉你用「非线性」的激活函数就好了。这样学生往往只知其然,不知其所以然,理解不透彻。最近有个编辑朋友送了我一本教材,是 Simon Prince 教授的 Understanding Deep Learning,我觉得他的讲述是非常到位的,把这个知识点一步步地拆解给你看了,推荐你可以去读一下这本书的第三章,网站上有免费的英文版:Understanding Deep Learning。另外,我隐约记得李宏毅老师的视频里也讲述过类似的视角,年代有点久我记不住是哪一个课程了。
下面简单复述一下这个推导:
首先,ReLU 长下面这样,就不过多解释了:
假设我们有一个浅层神经网络,输入一个单值变量,有一个隐藏层,其中有三个隐藏节点,最后输出一个单值,这个神经网络所代表的函数:
其中 是第一层的参数, 是第二层的参数, 就是激活函数 ReLU。
graph LR
%% 输入层
X["输入 x"]
%% 隐藏层节点
H1["隐藏节点1<br>ReLU(θ₁₀ + θ₁₁x)"]
H2["隐藏节点2<br>ReLU(θ₂₀ + θ₂₁x)"]
H3["隐藏节点3<br>ReLU(θ₃₀ + θ₃₁x)"]
%% 输出层
Y["输出 y<br>φ₀ + φ₁h₁ + φ₂h₂ + φ₃h₃"]
%% 连接关系
X --> H1
X --> H2
X --> H3
H1 --> Y
H2 --> Y
H3 --> Y
%% 样式定义
classDef inputStyle fill:#e1f5fe,stroke:#0277bd,stroke-width:2px,color:#000
classDef hiddenStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
classDef outputStyle fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px,color:#000
classDef paramStyle fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:#000
%% 应用样式
class X inputStyle
class H1,H2,H3 hiddenStyle
class Y outputStyle
这个函数「能够」长成什么样呢?下面是几个例子:
它们的共性是:一个有三个「关节」的分段线性函数。也就是说,每一个(使用 ReLU 做激活函数的)隐藏节点,贡献了一个「关节」。随着隐藏节点增多,关节也会增多,这个网络所能拟合的函数形态也就自然越来越灵活。
这又是为什么呢?看下面这张图:
我觉得你一定能看懂这张图,我只做简要的提示:
- 子图 a-c:对输入 x 进行线性变换,计算隐藏层的 pre-activation,三条直线的斜率()和截距()不同。
- 子图 d-f:应用 ReLU 激活,把负值部分截断了,正值部分保留。这一步引入了非线性。
- 子图 g-i:对上一层的结果再进行一次「缩放」, 是隐藏层到输出层的权重。
- 子图 j:把缩放后的三个结果再加起来,其实最后这两步合起来就是一个线性加权,作者生怕你看不明白,给拆的非常细。
- 最后,我们就得到了这个拥有三个「关节」的分段线性函数。
其中「关节」的由来不言自明。