趋近智
神经网络 (neural network)的强大表达能力很大程度上源于非线性激活函数 (activation function)。如果没有它们,多层网络实质上会退化为单一的线性变换,这会严重限制其对数据中复杂关系的建模能力。激活函数引入了这些必要的非线性,通常在神经元或层内的线性变换(权重 (weight)和偏置 (bias))之后应用。在构建 Flux.jl 模型时,理解并正确使用这些函数是实现有效网络设计的一个主要步骤。
Flux.jl 提供了一系列常用激活函数,它们易于使用且经过性能优化。这些函数是标准的 Julia 函数,可以逐元素应用于数组,也可以直接集成到层定义中。这种灵活性既能实现标准网络结构,也能支持更多创新架构。
让我们查看一些最常用的激活函数、它们的特性以及如何在 Flux 中使用它们。
每种激活函数都有其独特的特点,会影响神经网络 (neural network)的学习动态和表现。
Sigmoid 函数,也称为逻辑函数,将任意实数值映射到 0 到 1 的范围内。 其数学形式是: 历史上,Sigmoid 曾是隐藏层的流行选择,但由于其某些缺点,它已在很大程度上被 ReLU 等函数取代。
特性:
在 Flux.jl 中,您可以使用 sigmoid:
using Flux
input_data = randn(Float32, 5) # 示例输入
output_data = sigmoid.(input_data) # 逐元素应用 Sigmoid
println(output_data)
# 在层内
layer = Dense(10, 5, sigmoid) # 具有 5 个输出神经元并使用 Sigmoid 激活的全连接层
Sigmoid 激活函数 (activation function),将输入映射到 (0, 1) 范围。
双曲正切函数,即 tanh,是另一个与 Sigmoid 类似的 S 形函数,但它将输入映射到 (-1, 1) 的范围。
其数学形式是:
特性:
tanh 的输出是零中心的,这对于优化来说可能是有益的,因为梯度不太可能偏向一个方向。tanh 也存在梯度消失问题,尤其对于非常大或非常小的输入,尽管其梯度通常比 Sigmoid 的更陡峭。在 Flux.jl 中,您使用 tanh:
using Flux
input_data = randn(Float32, 5)
output_data = tanh.(input_data)
println(output_data)
layer = Dense(10, 5, tanh) # 具有 tanh 激活的全连接层
双曲正切 (tanh) 激活函数 (activation function),将输入映射到 (-1, 1) 范围。其零中心输出通常是有益的。
整流线性单元,即 ReLU,由于其简单和高效,已成为许多类型神经网络 (neural network)中隐藏层的标准激活函数 (activation function)。 其数学形式是:
特性:
在 Flux.jl 中,relu 是该函数:
using Flux
input_data = randn(Float32, 5)
output_data = relu.(input_data)
println(output_data)
layer = Dense(10, 5, relu) # 具有 ReLU 激活的全连接层
整流线性单元 (ReLU) 激活函数。它在输入为正时直接输出输入,否则输出零。
为了解决“ReLU 死亡”问题,人们提出了 ReLU 的几种变体。一个常见的是 Leaky ReLU。
Leaky ReLU: 允许在单元不活跃时存在一个小的非零梯度。 其数学形式是: 这里 是一个小的正数常数,通常在 0.01 到 0.2 左右。
特性:
Flux.jl 提供了 leakyrelu。可以指定 参数 (parameter)(默认为 0.01)。
using Flux
input_data = randn(Float32, 5)
output_alpha_default = leakyrelu.(input_data) # 默认 alpha = 0.01
output_alpha_custom = leakyrelu.(input_data, 0.2f0) # 自定义 alpha = 0.2
println("Leaky ReLU (alpha=0.01): ", output_alpha_default)
println("Leaky ReLU (alpha=0.2): ", output_alpha_custom)
layer = Dense(10, 5, x -> leakyrelu(x, 0.1f0)) # 在层中使用自定义 alpha 的 leakyrelu
其他变体包括:
prelu 函数可以将 作为 Dense 层激活参数的一部分来学习,但可以通过自定义层来实现。elu(x, α=1.0f0)。ELU 有时可以比 ReLU 带来更快的学习和更好的泛化能力。ReLU 和 Leaky ReLU 的比较。Leaky ReLU 为负输入引入了一个小的斜率。
Softmax 函数通常用于神经网络 (neural network)的输出层,处理多分类问题。它将一个原始分数(logits)向量 (vector)转换为 个不同类别的概率分布。 对于 个 logits 的向量 ,第 个元素的 softmax 是:
特性:
在 Flux.jl 中,softmax 对数组进行操作,如果处理批量数据,通常会沿着特定维度进行。对于单个实例(向量),它计算标准的 softmax。
using Flux
logits = randn(Float32, 3) # 3 个类别的示例 logits
probabilities = softmax(logits)
println("Logits: ", logits)
println("Probabilities: ", probabilities)
println("Sum of Probabilities: ", sum(probabilities)) # 应该接近 1.0
# 在一个用于 3 分类问题的模型中:
model = Chain(
Dense(10, 3), # 具有 3 个神经元(每个类别一个)的输出层
softmax # 应用 softmax 获取概率
)
# 注意:当与交叉熵损失函数一起使用时,通常将 logits 直接传递给损失函数
# (例如 `Flux.logitcrossentropy`),它在内部应用 softmax 或其稳定等效形式。
# 然而,要从模型获取直接概率输出,则需要显式应用 softmax。
需要注意的是,当使用像 Flux.logitcrossentropy 这样的损失函数 (loss function)时,通常会将原始 logits(Dense 层在 softmax 之前的输出)直接传递给损失函数。这是因为 logitcrossentropy 将 softmax 操作与交叉熵计算结合起来,以获得更好的数值稳定性和效率。然而,如果在推理 (inference)期间需要从模型中获取实际的概率输出,则需要显式应用 softmax。
如示例所示,Flux.jl 使将激活函数集成到层中变得简单直观。例如,Dense 层接受激活函数作为其第三个参数 (parameter):
# 具有 10 个输入特征、20 个输出特征和 ReLU 激活的全连接层
hidden_layer = Dense(10, 20, relu)
# 用于二分类的输出层,20 个输入,1 个输出,sigmoid 激活
output_layer_binary = Dense(20, 1, sigmoid)
如果在 Dense 层中省略激活函数,它会默认为 identity,这意味着不应用任何激活(一个线性层)。
linear_layer = Dense(5, 5) # 等同于 Dense(5, 5, identity)
您还可以使用 Julia 的广播语法将激活函数直接应用于层的输出或任何数组:
using Flux
# 示例:在线性层计算后应用 relu
W = randn(Float32, 3, 5) # 权重矩阵
b = randn(Float32, 3) # 偏置向量
x = randn(Float32, 5) # 输入向量
z = W * x .+ b # 线性变换
h = relu.(z) # 逐元素应用 relu
println(h)
这种逐元素应用是激活函数如何作用于神经元输出的基础。
选择合适的激活函数可以显著影响模型的表现,但没有普遍适用的规则。不过,存在一些通用的指导原则和常见做法:
sigmoid 来获取正类的概率输出。softmax 来获取所有类别的概率分布。identity)。如果输出受限制(例如,始终为正),则可以考虑使用 relu 或 softplus 等适当的函数。当您在 Flux.jl 中构建更复杂的网络时,您会越来越熟悉这些函数,并培养出选择哪种函数的直觉。请记住,Julia 和 Flux 的灵活性甚至允许您在应用程序需要独特功能时定义自定义激活函数。接下来的部分将介绍损失函数 (loss function)和优化器,它们与您的网络架构和激活函数配合工作,以有效训练您的模型。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•