激活函数(Activation Function)

目录


激活函数(Activation Function) 是神经网络中一个至关重要的组成部分。它的核心作用是为神经网络引入非线性,使得网络能够学习和模拟复杂的现实世界数据(如图像、语音、文本等),而不仅仅是进行简单的线性变换。

一、为什么需要激活函数?

如果没有激活函数,一个多层神经网络无论有多少层,本质上都等价于一个单层线性变换:

y = W 2 ( W 1 x + b 1 ) + b 2 = ( W 2 W 1 ) x + ( W 2 b 1 + b 2 ) = W x + b y=W_2(W_1x+b_1)+b_2=(W_2W_1)x+(W_2b_1+b_2)=Wx+b y=W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2)=Wx+b

也就是说,多层线性网络叠加,最终还是线性的,无法拟合 XOR 这类非线性问题。激活函数的作用就是在每层输出后做一个非线性映射,让网络具备万能逼近能力。

大多数激活函数都是非线性的:激活函数通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算。

二、常见激活函数

(一)Sigmoid

sigmoid也被称为挤压函数,将一个定义域在R中的输入变换为区间(0,1)上的输出。

sigmoid ( x ) = 1 1 + e − x \text{sigmoid}(x)=\frac{1}{1+e^{-x}} sigmoid(x)=1+e−x1

  • 优点:输出可解释为概率,适合二分类输出层。
  • 缺点:
    • 容易梯度消失(饱和区导数接近0)
    • 含指数运算,计算稍慢

sigmoid在隐藏层中已经较少使用,在大部分时候被更简单、更容易训练的ReLU取代。

sigmoid函数:

python 复制代码
y = torch.sigmoid(x)
d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5))

sigmoid函数的导数:

d d x s i g m o i d ( x ) = e x p ( − x ) ( 1 + e x p ( − x ) ) 2 = s i g m o i d ( x ) ( 1 − s i g m o i d ( x ) ) \frac{d}{dx}sigmoid(x)=\frac{exp(-x)}{({{1+exp(-x)}})^2}=sigmoid(x)(1-sigmoid(x)) dxdsigmoid(x)=(1+exp(−x))2exp(−x)=sigmoid(x)(1−sigmoid(x))

当输入为0时,sigmoid函数的导数达到最大值0.25,输入在任一方向越远离0点时,导数越接近0。

python 复制代码
# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of sigmoid', figsize=(5, 2.5))

(二)Tanh(双曲正切)

与sigmoid函数类似, tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上:

tanh ( x ) = 1 − e − 2 x 1 + e − 2 x \text{tanh}(x)=\frac{1-e^{-2x}}{1+e^{-2x}} tanh(x)=1+e−2x1−e−2x

  • 输入为0附近时,tanh函数接近线性变换;
  • tanh函数形状类似于sigmoid函数,不同的是tanh函数关于坐标系原点中心对称,tanh收敛比sigmoid快;
  • 缺点:仍然存在梯度损失问题。

tanh函数:

python 复制代码
y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))

tanh函数的导数:

d d x t a n h ( x ) = 1 − t a n h 2 ( x ) \frac{d}{dx}tanh(x)=1-tanh^2(x) dxdtanh(x)=1−tanh2(x)

当输入接近0时,tanh函数的导数接近最大值1,输入在任一方向越远离0点时,导数越接近0。

python 复制代码
# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of tanh', figsize=(5, 2.5))

(三)RELU函数

ReLU函数: 修正线性单元(Rectified linear unit,ReLU)

给定元素 x x x,ReLU函数定义为该元素与0的最大值,也就是说ReLU函数是分段线性的,仅保留正元素丢弃负元素。

ReLU ( x ) = max ⁡ ( x , 0 ) \text{ReLU}(x)=\max(x,0) ReLU(x)=max(x,0)

  • 优点:
    • 计算极快(只需比较)
    • 相比Sigmoid或Tanh,ReLU在正区间的梯度恒为1,缓解梯度消失
    • 稀疏激活,效率高
  • 缺点:
    • 神经元死亡:负区间梯度为0,某些神经元可能永久失活
    • 输出不是零中心

ReLU函数:

python 复制代码
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))

ReLU函数的导数: 输入为负时,导数为0;输入为正时,导数为1。

python 复制代码
y.backward(torch.ones_like(x), retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5))

ReLU求导后:要么让参数消失,要么让参数通过。 这使得优化表现得更好,并且ReLU减轻了困扰以往神经网络的梯度消失问题。

(四)GELU

GELU ( x ) = x ⋅ Φ ( x ) \text{GELU}(x)=x\cdot \Phi(x) GELU(x)=x⋅Φ(x)

其中, Φ ( x ) \Phi(x) Φ(x)是标准正态分布的累积分布函数。

  • 特点:平滑、非单调,在Transformer(如BERT、GPT)中广泛使用
  • 优点:性能通常优于 ReLU,尤其在 NLP 任务中。

GELU 函数的直观解释是:它结合了线性和非线性特性,当输入值较小的时候接近于0,当输入值较大的时候接近于1,这使得它在处理不同范围的输入时具有不同的激活特性。这种特性使得 GELU 在某些情况下比传统的ReLU激活函数表现得更好。

三、应用

(一)Transformer中前馈神经网络使用的激活函数

Transformer中的每个编码器和解码器层除了自注意力机制外,还包含一个两层的前馈神经网络(FFN) ,该网络应用在每个位置的向量上,是位置无关的。

这个前馈网络通常有以下结构:

  • 两层全连接网络:第一层将输入的向量维度扩展到一个更高的维度,第二层将其缩小回原维度;
  • 非线性激活函数:在两层之间插入一个非线性激活函数来引入非线性能力。
    FFN ( x ) = max ⁡ ( 0 , x W 1 + b 1 ) W 2 + b 2 \text{FFN}(x)=\max(0,xW_1+b_1)W_2+b_2 FFN(x)=max(0,xW1+b1)W2+b2
    这里的 max ⁡ ( 0 , ⋅ ) \max(0,\cdot) max(0,⋅)表示激活函数ReLU。

其他变体中的激活函数:有些后续模型(如BERT)使用了GELU作为替代激活函数,以实现更好的性能。

(二)常见变体

  1. ReLU FFN(原始)

    FFN ( x ) = W 2 ⋅ ReLU ( W 1 x + b 1 ) + b 2 \text{FFN}(x)=W_2 \cdot \text{ReLU}(W_1x+b_1)+b_2 FFN(x)=W2⋅ReLU(W1x+b1)+b2

  2. GELU FFN(BERT、GPT)

    FFN ( x ) = W 2 ⋅ GeLU ( W 1 x + b 1 ) + b 2 \text{FFN}(x)=W_2 \cdot \text{GeLU}(W_1x+b_1)+b_2 FFN(x)=W2⋅GeLU(W1x+b1)+b2

  3. SwiGLU(LLaMA、PaLM)

    GLU(Gated Linear Unit)变体,引入门控:

    FFN SwiGLU ( x ) = W 2 ⋅ ( Swish ( W 1 x ) ⊙ ( W 3 x ) ) \text{FFN}_{\text{SwiGLU}}(x)=W_2 \cdot (\text{Swish}(W_1x)\odot(W_3x)) FFNSwiGLU(x)=W2⋅(Swish(W1x)⊙(W3x))

    其中 ⊙ \odot ⊙是逐元素乘, W 3 W_3 W3是额外的门控投影。

    特点:需要三个权重矩阵

关于GLU和SwiGLU

  • GLU(Gated Linear Unit,门控线性单元)
    GLU ( x ) = ( W 1 x + b 1 ) ⊙ σ ( W 2 x + b 2 ) \text{GLU}(x)=(W_1x+b_1)\odot \sigma(W_2x+b_2) GLU(x)=(W1x+b1)⊙σ(W2x+b2)
    其中, ⊙ \odot ⊙表示逐元素乘, σ \sigma σ表示Sigmoid, W 1 x + b 1 W_1x+b_1 W1x+b1表示内容分支(要传的信息), σ ( W 2 x + b 2 ) \sigma(W_2x+b_2) σ(W2x+b2)表示门控分支(0~1,控制放行多少)
    • 优点:①门控让网络可以动态控制信息流;②缓解梯度消失(Sigmoid 门控提供额外梯度通路);③比单纯线性层表达能力强。
    • 缺点:参数量翻倍(需要两组权重)
  • SwiGLU(Swish-Gated Linear Unit)
    SwiGLU ( x ) = Swish ( W 1 x ) ⊙ ( W 3 x ) \text{SwiGLU}(x)=\text{Swish}(W_1x)\odot(W_3x) SwiGLU(x)=Swish(W1x)⊙(W3x)
    或者写作:
    SwiGLU ( x ) = Swish ( W 1 x + b 1 ) ⊙ ( W 3 x + b 3 ) \text{SwiGLU}(x)=\text{Swish}(W_1x+b_1)\odot(W_3x+b_3) SwiGLU(x)=Swish(W1x+b1)⊙(W3x+b3)
    然后接输出投影:
    FFN SwiGLU ( x ) = W 2 ⋅ SwiGLU ( x ) \text{FFN}_{\text{SwiGLU}}(x)=W_2 \cdot \text{SwiGLU}(x) FFNSwiGLU(x)=W2⋅SwiGLU(x)
    其中Swish(也叫SiLU):
    Swish ( x ) = x ⋅ σ ( β x ) , β = 1 时为SiLU \text{Swish}(x)=x\cdot \sigma(\beta x), \beta = 1\text{时为SiLU} Swish(x)=x⋅σ(βx),β=1时为SiLU
相关推荐
虫无涯1 小时前
Claude Code 频繁卡住?一文搞懂Spinner状态标识、卡顿根源与排查方案
人工智能·claude
虫无涯1 小时前
踩坑实战:Roo Code 调用本地模型卡顿?手把手教你优化到原生速度
人工智能
tellmewhoisi1 小时前
机器学习:集成学习4(XGBoost前置知识泰勒展开式2)
人工智能·机器学习·集成学习
西部驯兽师1 小时前
制造企业的信息化选型课题(二)
大数据·人工智能·制造
怪奇云呼军1 小时前
从 ElevenLabs 看工具调用:闪电智能 Voice Agent 的企业集成验收设计
android·大数据·运维·服务器·网络·人工智能·kotlin
云杂项1 小时前
A Survey of Privacy Attacks in Machine Learning(个人笔记)
人工智能·安全·机器学习
Runwise创新社区1 小时前
开源大模型从本地跑通到生产上线:以千问为例的五级部署阶梯与六项选型检查
人工智能·系统架构·开源
云上先途2 小时前
对话智能体和普通聊天机器人有什么区别?能不能对接企业自有知识库?
大数据·人工智能·机器人
我是小白呀2 小时前
17-企业Workflow引擎怎么选:定时、队列、n8n、Temporal与Camunda
java·开发语言·人工智能·dubbo·workflow