激活函数#
学习目标与记号#
掌握 sigmoid 函数、tanh 函数、 ReLU 函数以及 Leaky ReLU 函数的函数形式及其优缺点;
根据公式和张量维度分析 Leaky ReLU,并识别常见实现错误;
用
Python实现或验证输出层约束;在其他条件相同的情况下,只改变一个需要研究的因素进行比较,并根据实验结果分析该因素可能带来的影响。
本节沿用统一记号:普通小写字母表示标量,粗体小写字母表示向量,粗体大写字母表示矩阵或高阶张量;层编号写作上标 \([l]\),样本或时间编号写作下标;转置写作 \(\trans\)。除非另有说明,批量样本按行存放。正文与练习中的程序都应同时检查数值结果和数组维度。本节练习的参考答案见 激活函数答案。
阅读本节前,可先回顾 逻辑回归中的 sigmoid 函数;选择输出层激活时,还应结合 交叉熵 与 Softmax 回归 理解输出的概率含义。
激活函数(activation function)把仿射变换的结果映射为神经元输出,并为网络引入非线性。若各层都不使用非线性激活,多个仿射变换的复合仍是一个仿射变换,增加深度并不会扩大这类函数的表示范围。隐藏层激活函数主要影响梯度传播、计算代价和表示稀疏性;输出层激活函数则应由任务与损失共同决定。
Sigmoid#
Sigmoid 函数及其导数为
其值域为 \((0,1)\),且 \(0<\sigma'(z)\leq 1/4\);导数在 \(z=0\) 处取得最大值 \(1/4\),并在 \(z\to\pm\infty\) 时趋近于 0。
图 2 Sigmoid 函数及其导数的变化特征#
二分类模型训练时,通常把尚未经过 sigmoid 映射的线性运算结果 \(z\) 直接交给数值稳定的交叉熵实现,例如 PyTorch 的 BCEWithLogitsLoss。先手工计算 sigmoid 函数的输出,再对接近 0 或 1 的概率取对数,容易产生舍入误差或数值溢出。
主要优点
输出范围与概率的取值范围一致。 Sigmoid 函数把任意实数映射到 \((0,1)\),因此适合表示二分类中正类的概率,也适合表示多标签分类中每个标签各自出现的概率。不过,输出位于 \((0,1)\) 并不自动保证它是可靠的概率;概率解释还取决于模型假设、损失函数、训练数据和训练过程。
函数平滑且严格单调。 Sigmoid 函数处处可导,输入越大,输出也越大。其导数可以直接由输出写成 \(\sigma(z)[1-\sigma(z)]\),因此后向传播时可以复用前向传播已经得到的函数值。
适合构造软门控。 输出接近 0 时可以抑制信息,接近 1 时可以保留信息,中间取值则表示部分保留。因此,LSTM 和 GRU 等模型常用 sigmoid 函数控制信息通过的比例。在这些位置上,它承担的是门控作用,而不是作为一般深层网络隐藏层的默认选择。
输出有界。 无论输入的绝对值多大,输出始终位于 0 与 1 之间。这可以限制单个神经元输出的数值范围,避免激活值本身无限增大。
主要局限
两端饱和会削弱局部梯度。 当 \(|z|\) 较大时,sigmoid 函数的输出接近 0 或 1,导数随之接近 0。若许多层或许多时间步同时处于饱和区,链式法则中的局部导数连乘会增加梯度衰减的风险。不过,整个网络的梯度还取决于权重矩阵、归一化方法和残差连接等因素,不能仅凭激活函数就断言一定发生梯度消失。
最大局部斜率不超过1/4。 即使 \(z\) 位于非饱和的中心区域,sigmoid 函数对上游梯度的局部缩放也不超过 \(1/4\)。在没有其他结构补偿时,隐藏层反复使用 sigmoid 函数比使用中心斜率较大的激活函数更容易使梯度幅度减小。
输出不以 0 为中心。 因为 \(\sigma(z)>0\),即使输入关于 0 对称,输出也通常集中在 \(1/2\) 附近,而不是 0 附近。这可能使后续层中部分参数的梯度产生较强的同向变化,令优化路径更加迂回;归一化方法和自适应优化器可以缓解这种影响,但不会改变 sigmoid 函数的输出范围。
计算和数值实现需要额外注意。 Sigmoid 函数包含指数运算,单独计算时通常比 ReLU 的比较与截断需要更多运算;完整模型的耗时是否明显增加,仍取决于矩阵乘法、内存访问和硬件实现。直接计算 \(\exp(-z)\) 还可能在极端输入下溢出或溢出,因此程序应采用数值稳定的实现。
双曲正切函数#
双曲正切函数满足
双曲正切函数的值域为 \((-1,1)\),它是奇函数,并满足 \(0<1-\tanh^2(z)\leq1\);导数在 \(z=0\) 处等于 1,并在 \(z\to\pm\infty\) 时趋近于 0。
图 3 tanh 函数及其导数的变化特征#
主要优点
输出可以为正数或负数。 Tanh 函数关于原点对称。若线性输入的分布也近似关于 0 对称,其输出均值通常比 sigmoid 函数的输出均值更接近 0,这可以减少传给下一层的系统性正偏移,并可能使优化更加平稳。这里的“以 0 为中心”是函数结构上的性质;对于偏斜的输入分布,输出均值并不一定恰好为 0。
中心区域的局部斜率较大。 在 \(z=0\) 处,tanh 函数的导数为 1。因此,当输入位于非饱和的中心区域时,上游梯度不会被该激活函数缩小太多;这通常比 sigmoid 函数至多为 \(1/4\) 的局部斜率更有利于梯度传播,但不能保证整个网络的梯度始终稳定。
输出幅度有界。 Tanh 函数把输出限制在 \((-1,1)\),可以避免单个激活值无限增大。循环神经网络 常用它生成候选状态或更新隐藏状态;当回归目标经过合理变换并确实位于 \([-1,1]\) 附近时,也可以考虑将其用于输出层。
函数平滑且便于求导。 Tanh 函数处处可导,其导数可以由已经计算出的输出,即 \(\tanh'(z)=1-\tanh^2(z)\),便于后向传播时复用前向传播结果。
主要局限
仍然存在双侧饱和。 当 \(|z|\) 较大时,输出接近 \(-1\) 或 \(1\),导数接近 0。在很深的网络或很长的时间序列中,许多单元同时进入饱和区会增加梯度衰减的风险;最终结果还会受到权重初始化、门控结构、归一化方法和残差连接的共同影响。
有界输出可能压缩有效的幅度信息。 当输入绝对值很大时,不同输入都会被映射到接近 \(-1\) 或 \(1\) 的狭小区域,原有的幅度差异会变得难以区分。因此,tanh 函数不适合作为所有回归任务的输出激活,也不能直接表示取值必须位于 \((0,1)\) 的概率。
函数对称不等于实际输出均值必为 0。 Tanh 函数的对称性通常比 sigmoid 函数恒为正的输出更有利,但实际输出是否以 0 为中心仍取决于输入分布。该性质本身不能保证模型一定收敛得更快。
逐元素计算通常比分段线性激活更复杂。 Tanh 函数需要计算指数函数或专门的双曲函数,其逐元素运算量通常多于下面要介绍的 ReLU。不过,成熟框架往往具有优化实现,完整模型也可能主要受矩阵乘法或内存访问限制,因此应在实际硬件上比较运行时间。
ReLU#
整流线性单元(Rectified Linear Unit,ReLU)定义为
ReLU 在 \(z=0\) 处不可导,自动微分框架会选取一个次梯度,常见约定是 0。单点不可导通常不妨碍随机梯度方法的实际使用。
图 4 ReLU 函数及其导数的变化特征;空心点表示 \(z=0\) 处不可导#
主要优点
计算形式简单。 ReLU 只需比较输入与 0 的大小,不包含指数或对数运算。单独计算激活函数时,它通常比 sigmoid 函数和 tanh 函数需要的运算更少。不过,完整模型的训练与预测速度还取决于矩阵乘法、内存访问、软件实现和硬件,不能仅凭激活函数的公式断定模型一定更快。
正半轴的局部梯度不会被 ReLU 缩小。 当 \(z>0\) 时,\(\operatorname{ReLU}'(z)=1\),所以上游梯度经过这一激活函数时保持不变。这有助于缓解 sigmoid 函数和 tanh 函数在饱和区造成的局部梯度衰减,但权重矩阵、网络深度及其他运算仍可能使整个网络出现梯度过小或过大的问题。
可以产生稀疏激活。 当 \(z<0\) 时,ReLU 的输出恰好为 0,因此一个批量中往往只有部分神经元具有非零输出。这使不同神经元能够有选择地响应输入特征。在能够利用稀疏数据结构的实现中,稀疏性还可能节省部分存储或计算;普通稠密数组仍会处理这些零值,所以稀疏激活并不自动带来运行速度提升。
正半轴保留输入的幅度差异。 ReLU 在正半轴是恒等映射,不会像 sigmoid 函数或 tanh 函数那样把较大的正输入压缩到狭小区间。这有利于保留正输入之间的相对大小,但也带来了输出无上界的问题。
主要局限
可能出现 ReLU 神经元失活。 若某个神经元对训练样本长期满足 \(z<0\),则其局部导数为 0。根据链式法则,传向该神经元输入端的梯度也为 0,其相关参数可能很难继续更新,这称为 ReLU 神经元失活(Dying ReLU)。过大的学习率、不合适的初始化或持续为负的偏置都可能增加这种风险,但某一次输出为 0 并不表示该神经元一定会永久失活。
输出通常不以 0 为中心。 ReLU 的值域为 \([0,\infty)\),一组激活值的均值通常大于 0。这可能使下一层部分参数的梯度具有较强的同向变化,从而使优化路径不够直接;实际影响还与数据分布、归一化方法和优化器有关。
正半轴输出没有上界。 当 \(z\) 增大时,ReLU 的输出也线性增大。若输入尺度、权重初始化或学习率不合适,较大的激活值可能继续放大后续层的数值尺度。无界输出本身并不等同于梯度爆炸,是否出现问题需要结合整个网络的权重和梯度共同判断。
在 0 处不可导。 ReLU 在 \(z=0\) 处的左导数为 0、右导数为 1,因此通常意义下的导数不存在。由于 ReLU 是凸函数,它在该点的次梯度可以取 \([0,1]\) 中的值;自动微分框架常采用 0 等固定约定。实现导数或进行 中心差分核验 时,应明确所用约定,并避开 \(z=0\) 判断解析导数是否正确。
Leaky ReLU#
Leaky ReLU 为负半轴保留一个小斜率:
除 \(z=0\) 外,其导数在负半轴为 \(\alpha\)、正半轴为 1。实践中通常取 \(0<\alpha<1\),例如 \(\alpha=0.01\);本节图像使用 \(\alpha=0.1\),以便更清楚地显示负半轴的斜率。
图 5 Leaky ReLU 函数及其导数的变化特征;此处 \(\alpha=0.1\),空心点表示 \(z=0\) 处不可导#
主要优点
负半轴仍能传递梯度。 当 \(z<0\) 时,Leaky ReLU 的局部导数为 \(\alpha\),所以上游梯度会被乘以 \(\alpha\),而不是直接变为 0。只要 \(\alpha>0\),负输入区域仍有机会更新相关参数,因此它可以降低 ReLU 神经元长期失活的风险。不过,若 \(\alpha\) 很小,梯度仍可能明显减弱;网络中的其他结构也可能造成梯度问题,所以 Leaky ReLU 不能保证训练一定稳定。
保留分段线性的简单计算。 Leaky ReLU 只包含比较和标量乘法,不需要指数或对数运算。它通常具有较小的逐元素计算代价,但完整模型是否更快仍应在相同硬件、批量大小和实现条件下进行比较。
正半轴保留 ReLU 的性质。 当 \(z>0\) 时,Leaky ReLU 的输出和局部导数分别为 \(z\) 和 1,因此正输入的幅度不会被压缩,上游梯度也不会被这一激活函数缩小。
能够保留一部分负输入信息。 ReLU 会把所有负输入都映射为 0,而 Leaky ReLU 会输出 \(\alpha z\)。不同负输入因此仍可产生不同输出,模型可以保留它们的符号和相对大小。
主要局限
需要确定负半轴斜率。 \(\alpha\) 是需要选择的超参数。当 \(\alpha\) 很小时,Leaky ReLU 与 ReLU 接近,负半轴梯度仍然较小;当 \(\alpha\) 接近 1 时,两侧斜率接近,函数也更接近线性映射,非线性作用随之减弱。因此,应结合验证集和框架默认值比较不同取值,不能认为某个 \(\alpha\) 对所有任务都最合适。
稀疏性通常低于 ReLU。 负输入会得到 \(\alpha z\),而不是精确的 0,因此 Leaky ReLU 通常产生更少的零激活。这可以保留负输入信息,但也减弱了 ReLU 的稀疏激活特征;这一变化是否有利取决于具体任务和实现。
输出不保证以 0 为中心,并且仍然没有上界。 Leaky ReLU 允许出现负输出,有时可使激活均值比 ReLU 更接近 0,但均值是否为 0 仍取决于输入分布和 \(\alpha\)。同时,其输出会随输入绝对值增大,因此仍需注意初始化、归一化和学习率对数值尺度的影响。
负半轴梯度仍可能逐层减小。 当许多层的输入都位于负半轴时,每层会将上游梯度乘以 \(\alpha\);经过 \(L\) 层后,仅由这些局部导数造成的缩放可能达到 \(\alpha^L\) 的数量级。因此,非零斜率只能降低梯度完全变为 0 的风险,不能消除梯度衰减。
在 0 处通常仍不可导。 当 \(\alpha\ne1\) 时,Leaky ReLU 在 \(z=0\) 处的左导数为 \(\alpha\)、右导数为 1,两者不同。自动微分框架会按照具体实现选择一个后向传播值;编写程序时应说明这一约定,使用 中心差分核验 时也应避开 0 点。
输出层激活与选择原则#
激活函数不能脱离任务和损失单独选择。常见组合如下:
实值回归通常使用恒等映射;若输出必须为正,可考虑 softplus 或对目标做适当变换。
二分类可使用一个线性运算结果配合二元交叉熵;推理时再用 sigmoid 函数转换为概率。
互斥多分类使用多个线性运算结果配合多分类交叉熵;需要展示概率时再计算 Softmax。
多标签分类对每个标签分别使用 sigmoid 函数,因为多个标签可以同时成立。
隐藏层没有对所有任务都最优的激活。全连接与卷积网络可把 ReLU 或其变体作为起点;Transformer 常使用 GELU 等平滑函数;门控循环网络会在门中使用 sigmoid 函数、在候选状态中使用 tanh。最终选择应通过验证集比较,并同时检查激活分布、梯度尺度、训练速度和硬件成本。
Shiny 交互演示:激活函数及其导数
下面的交互页面可以同时绘制 sigmoid、tanh、ReLU 和 Leaky ReLU 函数及其导数。读者可以改变横轴范围和 Leaky ReLU 的负半轴斜率,观察函数值与导数怎样随参数变化。
核心推导与实现核验#
核心关系
对于sigmoid激活函数而言,我们有
推导路径。 由商法则或写成 \((1+e^{-z})^{-1}\) 直接求导,整理后得到 sigmoid 函数的导数;令 \(a=\sigma(z)\),可写成计算更方便的 \(a(1-a)\)。
关键条件
令 \(u=\sigma(z)\in(0,1)\),则 \(u(1-u)=1/4-(u-1/2)^2\le 1/4\),等号仅在 \(z=0\) 处成立。
数据规模
逐元素激活函数会分别处理输入中的每一个数,因此输出的行数、列数和其他维度都不会改变,局部导数也与输入一样大。后向传播时,局部导数与上游梯度按对应位置相乘,而不是进行矩阵乘法。
常见误区
隐藏层与输出层的选择依据不同:输出层必须配合目标取值范围与损失,不能因 ReLU 常用就用于所有输出。
数值稳定性与规模
Sigmoid 函数采用正负分支避免指数溢出;ReLU 在 0 处采用与框架一致的次梯度。
本节小结#
激活函数决定非线性与局部梯度传播。
输出层的神经元个数和激活函数,应根据预测目标的取值范围及损失函数对模型输出的要求进行选择。
比较激活函数时应控制初始化、数据与优化器。
综合练习#
本组练习围绕四类经典激活函数的导数、饱和现象、稳定实现和计算效率展开。程序与实验应固定数据划分、随机种子集合、网络结构和训练预算,并同时核验数值与维度。全部参考答案见 激活函数答案。
四类激活函数及其导数。 分别推导 sigmoid 函数、tanh 函数、ReLU 和 Leaky ReLU 的导数,写出值域与导数的取值范围。对于 ReLU 和 Leaky ReLU,明确说明 \(z=0\) 处的可导性及程序采用的约定。
导数上界与饱和。 证明 \(0<\sigma'(z)\leq1/4\) 以及 \(0<1-\tanh^2(z)\leq1\),给出取等条件,并证明当 \(|z|\to\infty\) 时两者的导数趋近于 0。解释这些结论与隐藏层梯度消失之间的联系和区别。
后向传播中的局部作用。 设上游梯度为 \(\bdelta_a\),推导经过逐元素激活函数后 \(\bdelta_z=\bdelta_a\odot g'(\bz)\)。比较四类激活函数在 \(z\ll0\)、\(z\approx0\) 和 \(z\gg0\) 时对梯度的影响,并指出 ReLU 正半轴导数为 1 为什么不能保证整个网络不存在梯度消失或爆炸。
稳定程序实现。 仅使用
NumPy统一实现四类激活函数及其导数,使函数支持标量、向量和矩阵输入并保持输入维度。sigmoid 函数不得直接对所有输入机械计算exp(-z);程序还应检查有限值,并明确 ReLU 与 Leaky ReLU 在 0 处采用的导数约定。前向与后向计算效率。 对不同维度的随机数组,比较四类激活函数的向量化前向时间、前向加后向时间和峰值内存。预热后重复计时,保持数据类型和数组内容一致,并解释为什么单独的激活函数微基准不能完全预测整个网络的训练或预测速度。
初始化尺度与饱和。 对 sigmoid 函数和 tanh 函数,利用均值为 0、方差为 \(c/\sqrt{d}\) 的正态分布,对权重矩阵各个元素进行初始化,其中 \(c\in\{0.1,1,3,10\}\)。固定数据、网络、优化器、学习率、种子集合和训练预算,比较各层线性运算结果的分布、饱和比例、梯度范数、验证损失、训练时间和峰值内存。
预测性能与计算效率。 在同一非线性二分类任务上比较四类隐藏层激活函数。各组使用相同的数据划分、网络结构、初始化方法和训练预算,只改变激活函数;仅用验证集选择学习率和 Leaky ReLU 的 \(\alpha\),方案确定后再评价测试集。报告训练时间、达到给定验证损失所需时间、固定批量预测时间和峰值内存以及训练好的模型在测试集上的“代价函数”值,并说明结论适用的数据、初始化与训练预算。