跳至主要内容

Python 中的 Softmax 激活函数:完整指南

了解 softmax 激活函数如何将 logits 转换为多分类概率。比较 softmax 与 sigmoid,并结合 TensorFlow 与 PyTorch 在 Python 中实现。
· 12分钟 阅读

使用 AI 探索

激活函数是神经网络的基石。它们引入非线性,使网络能够学习复杂模式。softmax 激活函数尤为重要,尤其是在处理多分类问题时。

虽然 Sigmoid 和 ReLU 等替代方案各有用武之地,但在需要将输出解释为互斥类别上的概率时,softmax 更胜一筹。

softmax 激活函数会将一个数值向量整体转换为一个概率分布。这个独特特性使其在需要将输入归类到多个可能类别之一的任务中不可或缺。

从识别成千上万物体类别的图像识别系统,到预测句子下一个词的自然语言处理模型,softmax 为在多种可能性中做出决策提供了数学基础。

本文将介绍 softmax 激活函数是什么、其数学原理以及在您的神经网络架构中何时使用它。我们还将介绍在 Python 中的实用实现。

什么是 Softmax 激活函数?

Softmax 激活函数是一种将模型的原始输出向量(称为 logits)转换为概率分布的数学函数。简单来说,它把一组数转换为相加为 1 的概率。

与对单个数值独立作用的一些激活函数不同,softmax 作用于整个数值向量,将其整体变换为元素之和恰为 1 的概率分布。

在神经网络中,softmax 通常用于为多分类任务设计的网络的最后一层。当存在多个可能类别并需要模型给出各类别的概率时,softmax 激活函数是标准选择。

softmax 函数如何工作?

神经网络最后一层的原始输出常称为“logits”。这些值可以从负无穷到正无穷,不能直接解释为概率。softmax 激活函数通过以下步骤将这些 logits 转换为更可解释的形式:

  1. 对每个输入值取指数(确保所有值为正)。
  2. 用所有指数之和去除每个指数值(确保输出之和为 1)。

这种变换至关重要,因为它让我们可以将网络输出解释为概率分布。例如,如果一个神经网络将图像分为三类(猫、狗、鸟),softmax 输出可能为 [0.7, 0.2, 0.1],表示猫的概率为 70%,狗为 20%,鸟为 10%。

softmax 激活函数在构造有效概率分布方面起着关键作用,因为:

  • 它确保所有输出值介于 0 和 1 之间。
  • 它保证所有输出之和恰好为 1。
  • 它保留输入值的排序(更大的输入对应更大的概率)。
  • 它会放大较大值之间的差异,同时抑制较小值之间的差异。

softmax activation function

Softmax 激活函数

上图展示了 softmax 激活函数如何将原始 logits(输入值 2.0、1.0 和 0.5)转换为概率分布(0.7、0.2 和 0.1)。softmax 框中的公式表示:每个输出概率通过对某个输入值取指数,并除以所有指数之和来计算。 

右侧突出显示的关键属性说明了为什么 softmax 对多分类问题有用。下一节我们将看看数学公式及其工作原理。

Softmax 激活函数公式及其工作原理

现在我们已经了解了 softmax 激活函数是什么,接下来看看它的数学表述,以及如何将输入转换为概率分布。

softmax 激活函数的公式可以表示为:

其中:

  • ​zi 表示第 i 类的输入值(logit)
  • ezi 是对 zi 的指数运算(也可写作 exp(zi ​))
  • K 为类别总数
  • 分母对所有输入值的指数求和,确保输出之和为 1

softmax 激活函数按以下步骤将输入向量转换为概率分布:

  1. 计算指数:首先对每个输入值计算指数(ex),这一步确保所有值为正,因为对任意实数 x 都有 ex > 0
  2. 对指数求和:计算第 1 步所得指数值的总和。
  3. 归一化:用第 2 步的和去除每个指数值。该归一化确保所有输出介于 0 和 1 之间,且总和恰为 1。

让我们用一个简单示例应用 softmax 激活函数的公式,看看其实际效果。

假设我们有一个三分类问题(例如识别图像中是猫、狗还是鸟),神经网络的输出层有三个神经元。最终计算后,网络输出如下 logits:z = [2.0, 1.0, 0.5]。

要用 softmax 将这些 logits 转换为概率:

  1. 计算指数:
    • e^2.0 = 7.389
    • e^1.0 = 2.718
    • e^0.5 = 1.648
  2. 对指数求和:
    • 7.389 + 2.718 + 1.648 = 11.755
  3. 归一化:
    • P(class 1) = 7.389 / 11.755 ≈ 0.628,约 62.8%
    • P(class 2) = 2.718 / 11.755 ≈ 0.231,约 23.1%
    • P(class 3) = 1.648 / 11.755 ≈ 0.140,约 14.0%

得到的概率分布 [0.628, 0.231, 0.140] 之和为 1,且最高概率对应于最高的 logit 值。本示例说明了 softmax 激活函数如何在将输入转为有效概率分布的同时保留输入值的排序。

请注意,原始输入值(2.0、1.0、0.5)在输出概率中保持了相对排序,但它们之间的差异被放大了。这一特性使 softmax 在希望有把握地识别最可能类别的分类任务中尤为有用。

下一节将介绍在 Python 中实现 softmax 激活函数的不同方式。

在 Python 中实现 Softmax 激活函数

在理解了 softmax 激活函数背后的理论后,接下来看看如何在 Python 中实现。我们将先用 NumPy 从零编写一个 softmax 函数,然后看看如何在 TensorFlow/Keras 和 PyTorch 等流行深度学习框架中使用它。

用 NumPy 从零实现 softmax

在使用框架之前,理解如何从零实现 softmax 激活函数很重要。这有助于建立对底层机制的直观认识。

import numpy as np

def softmax(x):
    """
    Compute softmax values for each set of scores in x.
    
    Args:
        x: Input array of shape (batch_size, num_classes) or (num_classes,)
        
    Returns:
        Softmax probabilities of same shape as input
    """
    # For numerical stability, subtract the maximum value from each input vector
    # This prevents overflow when calculating exp(x)
    shifted_x = x - np.max(x, axis=-1, keepdims=True)
    
    # Calculate exp(x) for each element
    exp_x = np.exp(shifted_x)
    
    # Calculate the sum of exp(x) for normalization
    sum_exp_x = np.sum(exp_x, axis=-1, keepdims=True)
    
    # Normalize to get probabilities
    probabilities = exp_x / sum_exp_x
    
    return probabilities

该实现严格遵循 softmax 激活函数的公式,但有一个重要补充:在指数运算前,我们从每个输入向量中减去其最大值。 

这种“平移”操作不会改变数学结果,但能防止在对大数取指数时发生数值溢出。

让我们用一个简单示例测试实现:

# Sample logits from a neural network (batch of 2 examples, 3 classes each)
logits = np.array([
    [2.0, 1.0, 0.5],  # First example
    [3.0, 2.0, 1.0]   # Second example
])

probabilities = softmax(logits)
print("Logits:\n", logits)
print("\nSoftmax probabilities:\n", probabilities)
print("\nSum of probabilities (should be 1 for each example):", np.sum(probabilities, axis=1))

输出:

运行上述代码后,您会看到每个样本的概率之和均为 1,说明我们的 softmax 实现生成了有效的概率分布。对于第一个样本,最大概率对应最大的 logit(2.0);第二个样本同理。

在 TensorFlow/Keras 中使用 softmax

TensorFlow 和 Keras 让您可以轻松在神经网络中使用 softmax 激活函数。让我们一步步为 MNIST 数据集构建一个简单分类器。

首先,导入必要的库并加载数据集:

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Softmax
from tensorflow.keras.datasets import mnist
import matplotlib.pyplot as plt
import numpy as np

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# Normalize pixel values to be between 0 and 1
x_train, x_test = x_train / 255.0, x_test / 255.0

# One-hot encode the labels
y_train_one_hot = tf.keras.utils.to_categorical(y_train, 10)
y_test_one_hot = tf.keras.utils.to_categorical(y_test, 10)

MNIST 数据集包含 28x28 像素的手写数字(0-9)灰度图像。我们将像素值归一化到 0 到 1 之间,以获得更好的训练动态,并将类别标签转换为 one-hot 编码,这是 softmax 输出偏好的标签格式。

现在,创建带有 softmax 输出层的神经网络模型:

# Method 1: Using softmax as the activation function in the final layer
model1 = Sequential([
    Flatten(input_shape=(28, 28)),  # Convert 28x28 images to 784-length vectors
    Dense(128, activation='relu'),   # Hidden layer with ReLU activation
    Dense(10, activation='softmax')  # Output layer with softmax activation
])

# Method 2: Using a separate Softmax layer
model2 = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(10),  # Linear output (logits)
    Softmax()   # Separate softmax layer
])

这里展示了在神经网络中引入 softmax 激活函数的两种等价方式:

  • 方法一:在最终的 Dense 层中直接指定 'softmax' 作为激活函数
  • 方法二:在输出原始 logits 的 Dense 层之后添加一个单独的 Softmax 层

两种方式产生相同结果,但第二种方法更明确地区分了 logits 与概率,在某些场景中会更有用。

接下来,编译并训练模型:

# Compile the model
model1.compile(
    optimizer='adam',
    loss='categorical_crossentropy',  # This loss works well with softmax
    metrics=['accuracy']
)

# Print model summary
print("Model with softmax activation:")
model1.summary()

# Train the model
print("\nTraining the model...")
history = model1.fit(
    x_train, y_train_one_hot,
    epochs=5,
    batch_size=128,
    validation_split=0.1,
    verbose=1
)

输出:

我们使用类别交叉熵损失函数来编译模型,它与 softmax 输出配合良好。该损失函数衡量预测的概率分布与真实的 one-hot 标签之间的差异。我们使用 Adam 优化器,在训练过程中自适应调整学习率。

训练完成后,我们可以用模型进行预测并可视化结果:

# Let's check predictions on a sample
sample_idx = 42
sample_image = x_test[sample_idx]
true_label = y_test[sample_idx]

# Get model predictions (probabilities across all classes)
predictions = model1.predict(sample_image[np.newaxis, ...])

# Visualize the results
plt.figure(figsize=(10, 4))

# Plot the image
plt.subplot(1, 2, 1)
plt.imshow(sample_image, cmap='gray')
plt.title(f"True label: {true_label}")
plt.axis('off')

# Plot the probability distribution
plt.subplot(1, 2, 2)
plt.bar(range(10), predictions[0])
plt.xticks(range(10))
plt.xlabel('Digit')
plt.ylabel('Probability')
plt.title('Softmax Probabilities')

这段代码选取一个测试图像,将其传入已训练模型,并同时可视化图像及 softmax 输出的概率分布。概率图中最高的柱表示模型的预测。

输出:

TensorFlow 也提供了直接应用 softmax 的内置函数:

# Demonstrate using a custom softmax function in TensorFlow
def custom_softmax(logits):
    """Custom implementation of softmax in TensorFlow"""
    exp_logits = tf.exp(logits - tf.reduce_max(logits, axis=-1, keepdims=True))
    return exp_logits / tf.reduce_sum(exp_logits, axis=-1, keepdims=True)

# Example usage of custom softmax
logits = tf.constant([[2.0, 1.0, 0.5], [3.0, 2.0, 1.0]])
custom_probs = custom_softmax(logits)
tf_probs = tf.nn.softmax(logits)

print("\nCustom softmax:", custom_probs.numpy())
print("TensorFlow softmax:", tf_probs.numpy())

输出:

此示例比较了自定义 softmax 与 TensorFlow 内置 tf.nn.softmax() 函数的结果。两者应产生相同输出。自定义实现同样包含了在指数运算前减去最大值的数值稳定性技巧。

在 PyTorch 中使用 Softmax

现在,我们用 PyTorch 和 CIFAR-10 数据集实现一个类似的模型。首先导入库并进行数据准备,如下所示:

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

# Set the device
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# Load and preprocess the CIFAR-10 dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # Normalize RGB channels
])

# Load a small subset of the CIFAR-10 dataset for demonstration
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64,
                                         shuffle=False, num_workers=2)

# Define the class names for CIFAR-10
classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')

上述代码设置了如可用则使用 GPU 的 PyTorch 环境,加载 CIFAR-10 数据集(包含 10 个类别的 32x32 彩色图像),并准备训练与测试的数据加载器。

接下来,定义一个简单的卷积神经网络如下:

# Define a simple convolutional neural network
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        # Convolutional layers
        self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        
        # Fully connected layers
        self.fc1 = nn.Linear(32 * 8 * 8, 128)
        self.fc2 = nn.Linear(128, 10)  # 10 classes in CIFAR-10
        
        # Activation functions
        self.relu = nn.ReLU()
        
        # Note: We don't define softmax here as it will be applied with the loss function
    
    def forward(self, x):
        # Convolutional layers with ReLU and pooling
        x = self.pool(self.relu(self.conv1(x)))  # -> 16x16x16
        x = self.pool(self.relu(self.conv2(x)))  # -> 8x8x32
        
        # Flatten the output
        x = x.view(-1, 32 * 8 * 8)
        
        # Fully connected layers
        x = self.relu(self.fc1(x))
        x = self.fc2(x)  # Raw logits output
        
        # Note: No softmax here, as PyTorch's CrossEntropyLoss applies it internally
        return x

该 CNN 将 32x32 的 RGB 图像经过卷积层、ReLU 激活与池化处理,最后接全连接层。值得注意的是,模型在前向传播中不包含 softmax 层,而是输出原始 logits。

这是 PyTorch 中的常见模式,因为训练时使用的 CrossEntropyLoss 会在计算损失前内部应用 softmax,这样数值更稳定。 

如有需要,我们也可以显式加入 softmax:

# Applying softmax within the model
class ModelWithSoftmax(nn.Module):
    def __init__(self):
        super(ModelWithSoftmax, self).__init__()
        self.features = SimpleCNN()
        self.softmax = nn.Softmax(dim=1)
    
    def forward(self, x):
        logits = self.features(x)
        probabilities = self.softmax(logits)
        return probabilities

我们定义了两个模型变体:SimpleCNN 输出原始 logits,而 ModelWithSoftmax 显式应用 softmax 以生成概率。在训练时,我们配合 CrossEntropyLoss 使用 SimpleCNN,这是 PyTorch 的标准做法。

接着,定义一个训练函数:

# Create the model and move it to the device 
model = SimpleCNN().to(device) 
# Define loss function and optimizer 
criterion = nn.CrossEntropyLoss()  # Combines LogSoftmax and NLLLoss 
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
# Train the model (only a few epochs for demonstration)
def train_model(epochs=2):
    for epoch in range(epochs):
        running_loss = 0.0
        for i, data in enumerate(trainloader, 0):
            inputs, labels = data[0].to(device), data[1].to(device)
            
            # Zero the parameter gradients
            optimizer.zero_grad()
            
            # Forward + backward + optimize
            outputs = model(inputs)  # These are logits (pre-softmax)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            
            running_loss += loss.item()
            if i % 100 == 99:
                print(f'[{epoch + 1}, {i + 1}] loss: {running_loss / 100:.3f}')
                running_loss = 0.0
    
    print('Finished Training')

最后,创建一个可视化模型预测的函数:

# Function to display predictions with softmax probabilities
def show_prediction_example():
    # Get a batch of test images
    dataiter = iter(testloader)
    images, labels = next(dataiter)
    
    # Select a single image
    img = images[0].to(device)
    label = labels[0]
    
    # Get the model's prediction (logits)
    with torch.no_grad():
        logits = model(img.unsqueeze(0))
    
    # Apply softmax to get probabilities
    probabilities = torch.nn.functional.softmax(logits, dim=1)
    
    # Plot the image and probabilities
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
    
    # Show the image
    img_np = img.cpu().numpy()
    img_np = np.transpose(img_np, (1, 2, 0))
    # Denormalize the image for display
    img_np = img_np * 0.5 + 0.5
    ax1.imshow(img_np)
    ax1.set_title(f"True label: {classes[label]}")
    ax1.axis('off')
    
    # Show the probabilities
    probs = probabilities[0].cpu().numpy()
    ax2.bar(range(10), probs)
    ax2.set_xticks(range(10))
    ax2.set_xticklabels(classes, rotation=45)
    ax2.set_xlabel('Class')
    ax2.set_ylabel('Probability')
    ax2.set_title('Softmax Probabilities')
    
    plt.tight_layout()

该函数演示了在推理阶段如何从已训练模型获得 softmax 概率。它将图像传过模型得到 logits,使用 torch.nn.functional.softmax() 手动应用 softmax,然后同时可视化图像与概率分布。

现在,让我们训练模型:

train_model()

输出:

接着预测,看看测试图像的概率分布:

show_prediction_example()

输出:

下一节我们将看看 softmax 的最佳使用场景,讨论何时应使用 softmax 激活函数,并介绍在各种深度学习模型中的实际应用。

何时使用 Softmax 激活函数?

了解何时使用 softmax 激活函数对设计高效神经网络至关重要。softmax 激活函数的主要应用场景是用于分类任务的神经网络输出层。

Softmax 用于多分类

当模型需要将输入归入几个互斥类别之一时,softmax 是标准选择。常见示例包括:

  • 图像分类(识别照片中的对象)
  • 文档分类(为文本分配主题)
  • 命名实体识别(识别人名、地点等)
  • 语音识别(将音频映射为文本)

概率解释与置信度分数

当我们需要模型输出概率而不仅仅是类别预测时,softmax 为所有可能类别的置信度提供了概率化的解释。

强化学习与注意力机制中的 Softmax

在强化学习与序列决策问题中,softmax 可用于将动作价值转换为用于动作选择的概率分布。

现代基于 Transformer 的架构使用 softmax 在输入序列的不同部分之间分配注意力。

坚实的数学基础与直观的概率解释,使 softmax 激活函数尤为适合类别互斥的分类问题。

深度学习模型

多种深度学习架构都会使用 softmax 激活函数。 

图像分类网络如 ResNet、VGG 和 Inception 都在其最终层使用 softmax 将图像分类到成千上万个类别。例如,在 ImageNet 上训练的模型通常拥有一个 1000 维的 softmax 层,对应 1000 个对象类别。

在语言建模中,基于 RNN 的模型使用 softmax 从可能包含数万词的词表中预测序列中的下一个词。

现代 NLP 架构如 BERT、GPT 和 T5 在多个组件中使用 softmax:

  • 在注意力机制中决定对输入不同部分的关注程度
  • 在最终分类层中用于情感分析或文档分类等任务

变分自编码器(VAE)以及某些生成对抗网络(GAN)会在判别器组件或类别潜在变量建模中使用 softmax。

下一节我们将比较 softmax 激活函数与 sigmoid 激活函数的差异。

Softmax 与 Sigmoid 激活函数对比

Sigmoid 函数定义为 σ(x) = 1/(1+e^(-x)),将单个实数映射到 0 到 1 之间。相比之下,softmax 激活函数作用于一个数值向量,将其转换为概率分布。

以下是这两种激活函数的根本区别:

输入与输出维度

  • Sigmoid: 接受单一标量输入,输出一个介于 0 到 1 之间的标量。
  • Softmax: 接受 n 维向量输入,输出 n 维向量且各元素之和为 1。

概率解释

  • Sigmoid: 输出表示单个二元事件的概率(如“这封邮件是垃圾邮件吗?”)。
  • Softmax: 输出为多个互斥类别上的概率(如“这是 0-9 中的哪个数字?”)。

输出的相互独立性

  • Sigmoid: 当跨多个神经元使用时,各输出彼此独立。多个输出可以同时很高或同时很低。
  • Softmax: 输出相互依赖——若一个概率增加,其他概率必须减少以保持总和为 1。

数学关系

有趣的是,当类别数为 2 时,sigmoid 实际上是 softmax 激活函数的一个特例。这也是 sigmoid 常被称为“二元 softmax”的原因。

何时用 softmax,何时用 sigmoid

选择合适的激活函数对于确保神经网络输出有意义的预测至关重要,在 softmax 与 sigmoid 之间的决策取决于分类任务的性质。

在以下情况下使用 sigmoid:

  • 解决二分类问题(两个类别)
  • 每个输出需要独立解释
  • 实现多标签分类(同一样本可同时属于多个类别)
  • 在循环神经网络中实现注意力或门控机制

在以下情况下使用 softmax:

  • 处理多分类问题(多于两个类别)
  • 类别互斥(输入恰属于一个类别)
  • 需要在所有可能结果上得到一个合格的概率分布
  • 实现大多数图像分类网络的最终层

例如,在将手写数字(0-9)进行分类的神经网络中,输出层应使用 softmax 激活函数,因为每张图像只对应一个数字。另一方面,在检测图像中多个对象(如“包含人”、“包含车”、“包含树”)的网络中,更适合使用 sigmoid 激活,因为多个对象可以同时存在。

下面是一张对照表,快速比较 softmax 与 sigmoid 激活函数:

特性

Sigmoid

Softmax

输入

单个标量

向量

输出范围

介于 0 和 1 之间

介于 0 和 1 之间,且总和为 1

用例

二分类

多分类

输出解释

独立概率

概率分布

多个输出

可同时很高或同时很低

受限于总和为 1

梯度

可能遭遇梯度消失

较不易出现梯度消失问题

训练效率

可配合二元交叉熵损失训练

配合类别交叉熵损失训练

理解这些激活函数的区别对设计有效的神经网络至关重要。softmax 激活函数是多分类问题的不二之选,而 sigmoid 在二分类及需要独立概率的任务中仍然非常有价值。

下一节我们将探讨使用 softmax 激活函数时常见的问题与注意事项。

常见问题与注意事项

在深度学习模型中使用 softmax 激活函数时,若干挑战会影响模型性能与可靠性。了解这些问题并掌握应对方法,将有助于构建更健壮的模型。

数值稳定性挑战

数值稳定性是使用 softmax 激活函数时的关键考量。softmax 激活函数公式中的指数运算若处理不当,可能导致数值溢出或下溢。

例如,当输入值非常大(如 1000 或更高)时,指数函数会产生极大的数,可能超过系统可表示的浮点数上限。这会导致溢出错误,产生“无穷大”值,从而使最终的除法运算得到未定义结果(NaN)。

如我们在实现部分所述,标准解决方案是在应用指数函数前,将所有输入减去其最大值。该平移不会改变归一化后的相对比例,但能让数值保持在可控范围内。 

现代深度学习框架会在内部处理这一问题,但当您实现自定义运算或调试异常行为时,理解该问题仍十分重要。

Softmax 与过度自信预测

softmax 的另一个常见问题是神经网络容易对其预测过于自信,即便预测是错的。导致过度自信的原因包括:

  1. softmax 的指数性质会放大小的 logits 差异。
  2. 现代深度神经网络足以完美记忆训练数据。
  3. 即使模型已达到较好准确率,交叉熵损失仍会推动正确类别的概率逼近 1。

在实践中,这意味着模型可能会为实际上错误的预测赋予 99% 的概率。在需要可靠不确定性估计的关键应用中,这会带来问题。例如,若一个医疗诊断系统对错误预测过于自信,可能导致不当治疗决策。

模型置信度(来自 softmax 概率)与其实际准确率之间的差异称为校准误差。校准良好的模型会输出与其准确率相匹配的置信分数——如果模型以 80% 的置信度进行预测,那么这些事件应大约有 80% 的概率发生。

提升性能的正则化技术

幸运的是,有多种技术可帮助应对 softmax 激活函数相关的问题:

温度缩放

温度缩放引入一个参数 T 来控制概率分布的“平滑度”。在应用 softmax 之前用该温度参数去除 logits,可以调整结果分布的尖锐或均匀程度。

较高温度(T > 1)会产生更平滑、极值更小的概率分布,有助于降低过度自信。较低温度(T < 1)使分布在最大值附近更尖锐。温度缩放常作为训练后的后处理技术,用于校准模型置信度,而不改变类别排序。

标签平滑

标签平滑用稍微平滑的分布替代严格的 one-hot 目标向量。与其训练模型输出精确的 0 与 1,不如令其稍微不那么自信,例如在四分类问题中将正确类目标设为 0.9,错误类各为 0.025。

通过使用平滑标签训练,模型会学得不那么自信,从而提升泛化能力并增强对标签噪声的鲁棒性。该技术已成为许多最先进图像分类模型中的标准做法。

Dropout 与集成方法

训练中使用 dropout,并在推理时保持启用(称为蒙特卡洛 dropout),可以对同一输入采样多次预测并估计不确定性。若模型在不同 dropout 模式下对同一输入给出一致预测,则更可能对该预测有把握。

类似地,集成方法结合多个模型的预测以提升性能,并提供更好的不确定性估计。集成中模型之间的分歧可用作不确定性的度量。

校准技术

Platt 缩放等校准方法可在训练后应用,以确保 softmax 输出的置信分数与真实概率相符。这些方法通常使用验证集来学习将原始模型输出映射为良好校准概率的参数。

例如,前述简单的温度缩放方法可在验证数据上优化以最小化校准误差。更复杂的方法包括各向同性回归与贝叶斯分箱。

Softmax 瓶颈

近来的研究发现了 softmax 的一个根本限制,称为“softmax 瓶颈”。这指的是基于 softmax 的语言模型的表达能力受制于最终层权重矩阵的秩。在具有复杂依赖的自然语言环境中,这会阻碍模型充分刻画潜在条件分布。

为缓解该限制,提出了如 Mixture of Softmaxes(MoS)等高级架构,通过对多个 softmax 分布进行加权组合来提升表达力。

了解这些常见问题并实施适当方案,您就能提升使用 softmax 激活函数进行分类任务的模型的可靠性与性能。

结论

softmax 激活函数是多分类神经网络中的关键组件,它将原始 logits 转换为可解释的概率分布。本文探讨了其数学基础、在 Python 中的实现、与 sigmoid 的比较、实际用例,以及应对数值不稳定与过度自信等常见挑战的技术。

准备好进一步深入理解神经网络了吗? 

Softmax 激活函数常见问答

softmax 激活函数用于什么?

Softmax 将神经网络的输出转换为概率分布,适用于类别互斥的多分类问题。

softmax 与 sigmoid 激活函数有什么区别?

Sigmoid 为二元结果生成相互独立的概率;softmax 则在多个类别上生成相互依赖且总和为 1 的概率。

softmax 公式如何处理数值稳定性问题?

稳定的 softmax 实现会在指数运算前从所有输入中减去最大值,以防止溢出且不改变结果。

我什么时候应该使用 softmax 而不是其他激活函数?

当需要将输入分类为多个互斥类别之一时(如图像分类或文档分类),在输出层使用 softmax。

softmax 的输出可以解释为概率吗?

可以。softmax 输出表示在不同类别上的概率,且总和为 1;更高的数值表示模型对该类别更有信心。

作者

Rajesh Kumar

我是一名数据科学内容写作者,热衷于创作与 AI/ML/DS 相关的内容。我也会探索新的 AI 工具并撰写相关文章。

主题