type
Page
status
Invisible
date
Mar 2, 2026
slug
summary
动手深度学习v2课程
tags
机器学习
深度学习
category
icon
password

4.1.1. 隐藏层

仿射变换, 它是一种带有偏置项的线性变换。 首先,回想一下3.4节中的softmax回归的模型架构。该模型通过单个仿射变换将我们的输入直接映射到输出,然后进行softmax操作。如果我们的标签通过仿射变换后确实与我们的输入数据相关,那么这种方法确实足够了。但是,仿射变换中的线性是一个很强的假设。

4.1.1.1. 线性模型可能会出错

线性模型往往会出错,是因为它隐含了一个很强的假设:某个特征变大时,模型输出要么一直增大(权重为正),要么一直减小(权重为负),也就是单调、近似线性的关系。
在一些问题上这说得通,比如用收入预测还款概率时,收入更高的人通常更可能还款,但这种影响未必是线性的,可能“从0到5万”的变化比“从100万到105万”的变化更关键,因此可以通过对数等特征变换让关系更接近线性;也有些问题甚至连单调性都不成立,比如用体温预测死亡风险时,体温高于37℃越高风险越大,但低于37℃反而越低风险越大,这时可以把“与37℃的距离”作为特征来缓解。可一旦到了图像分类(比如猫狗识别),线性模型基本等价于只给每个像素强度加权求和,默认“区分类别只需要看单个像素强度”,却忽略了像素之间的空间结构和上下文关系,因此即使做一些简单预处理也很难解决。
正因为很多任务需要捕捉特征之间复杂的相关与交互,我们更希望让模型从数据中自动学习合适的表示(隐藏层表示),再在这种表示之上进行预测,这也是深度神经网络的核心思路。

4.1.1.2. 在网络中加入隐藏层

可以通过在网络中加入一个或多个隐藏层来克服线性模型的限制,使其能处理更普遍的函数关系类型。要做到这一点,一个最简单的方法是把许多全连接层堆叠在一起。每一层都输出到上面那一层,直到生成最后的输出。
我们可以把前层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机(multilayer perceptron),通常缩写为MLP。其示意图如下:
notion image
该多层。感知机有4个输入、3个输出,其隐藏层包含5个隐藏单元。输入层不涉及任何计算,因此使用此网络产生输入只需要实现隐藏层和输出层的计算。因此,该多层感知机的层数为2。注意,这两个层都是全连接的。每个输入都会影响隐藏层中的每个神经元,而隐藏层中的每个神经元又会影响输出层中的每个神经元。
不过,如3.4.3节所述,采用全连接层的多层感知机通常参数量较高,因而计算与存储成本可能较为可观。即使不改变输入输出大小,也需要在参数开销与模型性能之间做取舍。

4.1.1.3. 从线性到非线性

我们通过矩阵来表示个样本的小批量,其中每个样本有个输入特征。对于具有个隐藏单元的单隐藏层多层感知机,用表示隐藏层的输出,称为隐藏表示(hidden representations)。在数学或代码中,也被称为隐藏层变量(hidden-layer variable)隐藏变量(hidden variable)。因为隐藏层和输出层都是全连接的,所以我们有隐藏层权重和隐藏层偏置以及输出层权重和输出层偏置。形式上,按如下方式计算单隐藏层多层感知机的输出
注意在添加隐藏层之后,模型现在需要跟踪和更新额外的参数。可我们能从中得到什么好处呢?在上面定义的模型里,我们没有好处!因为仿射函数的仿射函数本身就是仿射函数,但是我们之前的线性模型已经能够表示任何仿射函数。
我们可以证明这一等价性,即对于任意权重值, 我们只需合并隐藏层,便可产生具有参数的等价单层模型:
为了发挥多层架构的潜力,我们还需要一个额外的关键要素:在仿射变换之后对每个隐藏单元应用非线性的激活函数(activation function)。激活函数的输出被称为活性值(activations)。一般来说,有了激活函数,就不可能再将多层感知机退化为线性模型:
由于中的每一行对应小批量中的一个样本,出于记号习惯的考量,我们定义非线性函数也按行的方式作用于其输入,即一次计算一个样本。在3.4.5节中,以相同的方式使用了softmax符号来表示按行操作。但是本节应用于隐藏层的激活函数通常不仅按行操作,也按元素操作。这意味着在计算每一层的线性部分之后,我们可以计算每个活性值,而不需要查看其他隐藏单元所取的值。对于大多数激活函数都是这样。
为了构建更通用的多层感知机, 我们可以继续堆叠这样的隐藏层,例如,从而产生更有表达力的模型。

4.1.1.4. 通用近似定理

多层感知机可以通过隐藏神经元,捕捉到输入之间复杂的相互作用,这些神经元依赖于每个输入的值。 我们可以很容易地设计隐藏节点来执行任意计算。例如,在一对输入上进行基本逻辑操作,多层感知机是通用近似器。即使是网络只有一个隐藏层,给定足够的神经元和正确的权重,我们可以对任意函数建模,尽管实际中学习该函数是很困难的。神经网络有点像C语言。C语言和任何其他现代编程语言一样,能够表达任何可计算的程序。但实际上,想出一个符合规范的程序才是最困难的部分。
而且,虽然一个单隐层网络能学习任何函数,但并不意味着我们应该尝试使用单隐藏层网络来解决所有问题。事实上,通过使用更深(而不是更广)的网络,我们可以更容易地逼近许多函数。我们将在后面的章节中进行更细致的讨论。

4.1.2. 激活函数

激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算。大多数激活函数都是非线性的。由于激活函数是深度学习的基础,下面简要介绍一些常见的激活函数。

4.1.2.1. ReLU函数

最受欢迎的激活函数是修正线性单元(Rectified linear unit,ReLU),因为它实现简单,同时在各种预测任务中表现良好。ReLU提供了一种非常简单的非线性变换。给定元素,ReLU函数被定义为该元素与的最大值:
下面画出函数的图像来直观感受一下:
notion image
当输入为负时,ReLU函数的导数为0,而当输入为正时,ReLU函数的导数为1。注意,当输入值精确等于0时,ReLU函数不可导。在此时,我们默认使用左侧的导数,即当输入为0时导数为0。 我们可以忽略这种情况,因为输入可能永远都不会是0。这里引用一句古老的谚语,“如果微妙的边界条件很重要,我们很可能是在研究数学而非工程”,这个观点正好适用于这里。下面我们绘制ReLU函数的导数。
notion image
注意,ReLU函数有许多变体,包括参数化ReLU(Parameterized ReLU,pReLU)函数。该变体为ReLU添加了一个线性项,因此即使参数是负的,某些信息仍然可以通过:

4.1.2.2. sigmoid函数

对于一个定义域在中的输入,sigmoid函数将输入变换为区间(0,1)上的输出。因此sigmoid通常称为挤压函数(squashing function):它将范围(-inf, inf)中的任意输入压缩到区间(0, 1)中的某个值:
当人们逐渐关注到到基于梯度的学习时,sigmoid函数是一个自然的选择,因为它是一个平滑的、可微的阈值单元近似。当我们想要将输出视作二元分类问题的概率时,sigmoid仍然被广泛用作输出单元上的激活函数 (sigmoid可以视为softmax的特例,二分类的情况)。
下面绘制sigmoid函数的图像, 注意,当输入接近0时,sigmoid函数接近线性变换。
notion image
其导数如下:
sigmoid导数图像如下所示。
notion image

4.1.2.3 tanh函数

与sigmoid函数类似,tanh(双曲正切)函数也能将其输入压缩转换到区间(-1,1)上。tanh函数的公式如下:
下面我们绘制tanh函数。注意,当输入在0附近时,tanh函数接近线性变换。函数的形状类似于sigmoid函数,不同的是tanh函数关于坐标系原点中心对称。
notion image
tanh的导数是:
其导数图像如下所示,当输入接近0时,tanh函数的导数接近最大值1。与我们在sigmoid函数图像中看到的类似,输入在任一方向上越远离0点,导数越接近0。
notion image

4.1.3. 小结

  • 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。
  • 常用的激活函数包括ReLU函数、sigmoid函数和tanh函数。
Loading...