1
为什么需要 Softmax?
从一个实际问题开始
假设你训练了一个神经网络,让它识别图像中是 猫、狗还是鸟 (3分类问题)。网络的最后一层通常有3个神经元,每个输出一个数值,我们称这些数值为 logits (原始得分)。
在一次推理中,网络可能输出:
[5.2, 1.3, -0.8]第一个神经元(猫)输出 5.2
第二个神经元(狗)输出 1.3
第三个神经元(鸟)输出 -0.8
现在面临的问题
这些数字很难解释 :5.2 比 1.3 大,我们知道模型更倾向于“猫”,但“更倾向于”是什么意思?能说“猫的概率是 5.2”吗?概率应该在 0~1 之间。
数字范围不固定 :下一次推理可能输出 [10.5, -2.3, 5.1],范围完全不一样,无法统一比较。
我们需要概率 :在很多应用中,我们需要知道“模型有多确信”。比如在机器人决策中,如果模型对“猫”的置信度只有 51%,我们可能选择不行动或请求人工确认。
我们的需求
我们需要一个函数,能把任意实数向量转换成:
这就是 Softmax 要解决的问题。
2
Softmax 是什么?
数学定义
对于输入 向量 $z=[z1,z2,...,zK]$,Softmax 的输出 $pi$为:
其中 $K$ 是类别总数。
逐步拆解
用上面的例子 [5.2, 1.3, -0.8]:
第1步:取指数
$e^{5.2} \approx 181.27$
$e^{1.3} \approx 3.67$
$e^{-0.8} \approx 0.45$
第2步:求和
总和 = 181.27 + 3.67 + 0.45 = 185.39
第3步:归一化
$p_1=181.27/185.39 \approx 0.978$(猫的概率 97.8%)
$p_2=3.67/185.39 \approx 0.020$(狗的概率 2.0%)
$p_3=0.45/185.39 \approx 0.002$(鸟的概率 0.2%)
关键观察
指数的作用:$e^{5.2}$远大于 $e^{1.3}$,放大了原始差异。5.2 和 1.3 原本只差 4 倍左右,经过指数后变成 181 vs 3.7,相差约 50 倍。这让模型对“最可能”的类别更加自信。
分母的作用:除以总和,确保所有输出加起来为 1。
为什么用指数,不用其他函数? 指数函数有几个好性质:
3
Softmax 的核心作用
作用 | 说明 | 为什么重要 |
|---|---|---|
归一化为概率 | 将任意实数映射到(0,1),且和为1 | 输出有明确概率意义,便于解释和决策 |
放大差异 | 让大的值更大,小的值更小 | 增强模型对主要预测的置信度 |
提供不确定性估计 | 概率值反映了模型的置信程度 | 机器人可根据置信度决定是否执行动作 |
可导 | 处处光滑可导 | 可以用梯度下降优化 |
一个实际场景
在机器人抓取任务中,模型需要判断抓取是否可能成功:
输出 [成功概率 0.95, 失败概率 0.05] → 机器人可以自信执行
输出 [成功概率 0.51, 失败概率 0.49] → 机器人应该谨慎,可能需重新观察
没有 Softmax,你只能得到像 [8.2, 7.9] 这样的原始得分,无法做出这种置信度判断。
特点 | 说明 | 示例/类比 |
|---|---|---|
输出和为1 | 所有类别概率之和为1 | [0.7, 0.2, 0.1] |
对输入敏感 | 输入微小的变化会导致输出变化 | 指数运算放大了差异 |
不变性 | 所有输入加上同一个常数,输出不变 | 因为指数和分母会同步变化 |
通常搭配交叉熵损失 | Softmax + 交叉熵是分类任务的标准配置 | 梯度形式简洁 |
4
怎么使用 Softmax
在 PyTorch 中的使用
import torch
import torch.nn as nn
# 假设模型的原始输出(logits)
logits = torch.tensor([[5.2, 1.3, -0.8]]) # shape: (1, 3),1个样本,3个类别
# 方法1:手动计算(理解原理)
softmax = nn.Softmax(dim=1) # dim=1 表示在类别维度上做softmax
probs = softmax(logits)
print(probs) # tensor([[0.9775, 0.0198, 0.0027]])
# 方法2:直接调用函数(更简洁)
probs = torch.softmax(logits, dim=1)
# 方法3:获取预测类别(最常用)
pred_class = torch.argmax(logits, dim=1) # 直接取最大值位置,不用先softmax
print(pred_class) # tensor([0]),表示类别0(猫)推理时的使用
在推理时,根据需求不同:
model.eval()
with torch.no_grad():
logits = model(image)
# 如果需要概率(如不确定性估计)
probs = torch.softmax(logits, dim=1)
# 如果只需要类别
pred = torch.argmax(logits, dim=1)
# 如果需要前几个候选
top_probs, top_indices = torch.topk(probs, k=3, dim=1)5
Softmax 的“温度”概念
这是一个进阶但很有用的概念。有时我们希望控制 Softmax 输出分布的“平滑度”,这时引入 温度参数 $T$:

应用场景:
示例:
def softmax_with_temperature(logits, temperature=1.0):
return torch.softmax(logits / temperature, dim=-1)
# 原始输出
logits = torch.tensor([5.2, 1.3, -0.8])
probs_T1 = softmax_with_temperature(logits, T=1) # [0.978, 0.020, 0.002]
probs_T2 = softmax_with_temperature(logits, T=2) # [0.825, 0.137, 0.038] 更平滑
probs_T05 = softmax_with_temperature(logits, T=0.5) # [0.999, 0.001, 0.000] 更尖锐6
Softmax 与其他激活函数的对比
激活函数 | 输出范围 | 主要用途 | 特点 |
|---|---|---|---|
Softmax | (0,1),和为1 | 多分类输出层 | 输出可解释为概率 |
Sigmoid | (0,1) | 二分类输出层 | 每个输出独立,可多标签 |
Tanh | (-1,1) | 隐藏层 | 零中心,适合RNN |
ReLU | [0,∞) | 隐藏层 | 计算简单,缓解梯度消失 |
关键区别:
7
一个完整的例子
import torch
import torch.nn as nn
# 一个简单的分类模型
class SimpleClassifier(nn.Module):
def __init__(self, input_size=784, num_classes=10):
super().__init__()
self.fc1 = nn.Linear(input_size, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, num_classes) # 输出层,没有激活函数
self.relu = nn.ReLU()
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x) # logits,没有softmax
return x
# 训练
model = SimpleClassifier()
criterion = nn.CrossEntropyLoss() # 内部包含softmax
optimizer = torch.optim.Adam(model.parameters())
# 前向传播
logits = model(images) # 得到 logits
loss = criterion(logits, labels) # 直接计算损失
# 推理
model.eval()
with torch.no_grad():
logits = model(image)
probs = torch.softmax(logits, dim=1) # 需要概率时加softmax
pred = torch.argmax(logits, dim=1) # 只需要类别时直接取最大值