Softmax得分原来是这样算的!小白也能看懂
你有没有好奇过:当模型说“这张图是猫的概率是0.9645”,这个0.9645到底是怎么来的?它真的是“概率”吗?为什么所有类别的得分加起来刚好是1?它和原始输出的数字(比如23.7、18.2、5.1)之间到底发生了什么?
别急——这篇文章不讲公式推导,不列数学符号,不堆术语。我们就用一张图、几行代码、一次真实推理过程,把Softmax得分从“黑箱输出”变成“你亲手算出来的数字”。全程不用背定义,就像拆解一个自动售货机:投币、按键、出货,每一步你都看得见。
1. 先看结果:识别一张猫图,模型到底输出了什么?
我们用镜像「万物识别-中文-通用领域」识别一张常见的橘猫照片。运行推理.py后,控制台打印出这样的Top-5结果:
Top-5 识别结果: 1. [猫] 置信度: 0.9645 2. [动物] 置信度: 0.9872 3. [宠物] 置信度: 0.8721 4. [哺乳动物] 置信度: 0.7633 5. [家具] 置信度: 0.1023注意:这里有个小陷阱——“动物”的得分(0.9872)居然比“猫”(0.9645)还高?
这看起来反直觉,但其实完全合理。因为模型不是在做“单选题”,而是在对全部36个候选标签同时打分。它的原始输出是一串36个数字,我们看到的“置信度”,只是经过Softmax转换后的结果。
那原始输出长什么样?我们稍作修改,在predict函数里加两行打印:
# 在 predict 函数中,softmax 计算前插入: print("原始 logits(前10个):", logits_per_image.cpu().numpy()[0][:10]) # 输出示例: # 原始 logits(前10个): [23.71 18.22 15.09 12.44 11.87 10.93 9.65 8.33 7.21 6.88]这些数字就是模型“内心的真实想法”:它认为“猫”最像(23.71)、“动物”次之(18.22)、“宠物”再次(15.09)……但它们不能直接比较大小,也不能叫“概率”——因为它们有正有负、有大有小、总和远大于1。
Softmax要做的,就是把这些“想法”翻译成人类能理解的“可能性”。
2. Softmax不是魔法,它只做了三件事
你可以把Softmax想象成一个“标准化翻译器”。它不改变模型的判断顺序(谁分最高、谁第二),只做三步干净利落的操作:
2.1 第一步:全部抬高,消除负数干扰
模型原始输出可能包含负数(比如 -4.2、-1.8),而负数在指数运算中会变得极小,影响后续归一化。所以Softmax先给每个数加上一个偏移量,让最小值变成0。
但这一步在实际代码中通常被跳过——因为PyTorch的torch.softmax内部已自动做了数值稳定处理(减去最大值)。我们手动模拟时,可以更直观地理解:
# 假设原始 logits 是这5个数(为简化,只取前5个) logits = torch.tensor([23.71, 18.22, 15.09, 12.44, 11.87]) # Step 1: 减去最大值(23.71),避免 exp 溢出 logits_shifted = logits - logits.max() # → tensor([0.00, -5.49, -8.62, -11.27, -11.84])你看,现在最大值变成了0,其余全为负数。这样后面取指数时,就不会出现exp(100)这种爆炸值。
2.2 第二步:取指数——把“差距”拉得更大
为什么用exp?因为它有一个关键特性:放大差异。
- 差1分 →
exp(1) ≈ 2.7倍 - 差2分 →
exp(2) ≈ 7.4倍 - 差5分 →
exp(5) ≈ 148倍
这对分类任务极其友好:模型越确信某个类别,它的指数结果就远超其他类,最终Softmax得分自然就接近1。
继续刚才的例子:
# Step 2: 对平移后的值取指数 exp_logits = torch.exp(logits_shifted) # → tensor([1.0000, 0.0041, 0.0002, 0.0000, 0.0000])注意:exp(-5.49) ≈ 0.0041,已经比最大值小了240多倍;exp(-8.62)更是只有0.0002——差距被指数级放大。
2.3 第三步:归一化——让所有结果加起来等于1
最后一步最简单:把所有指数结果加起来,再让每个数除以这个总和。这样,每个结果就变成了0~1之间的数,且总和严格为1。
# Step 3: 归一化 sum_exp = exp_logits.sum() probs = exp_logits / sum_exp # → tensor([0.9958, 0.0041, 0.0001, 0.0000, 0.0000])四舍五入保留4位小数,就得到了我们熟悉的“置信度”:
[猫]: 0.9958 →0.9958[动物]: 0.0041 →0.0041- ……
等等,这和之前看到的0.9645不一样?
没错——因为我们刚才只用了前5个logits做演示。真实场景中,模型要对全部36个中文标签打分。当你用全部36个logits计算时,“猫”对应的得分才会是0.9645(因为其他相似标签如“宠物”“哺乳动物”也分走了一部分概率质量)。
一句话记住Softmax的本质:
它不是“计算概率”,而是“把模型的相对偏好,翻译成一组加起来为1的、可解释的权重”。
3. 动手验证:用计算器重算一次“猫”的得分
我们来复现一次真实推理中的关键步骤。假设某次运行中,模型对36个标签的原始logits如下(节选关键5个,单位:分):
| 标签 | 原始logit |
|---|---|
| 猫 | 23.71 |
| 动物 | 18.22 |
| 宠物 | 15.09 |
| 哺乳动物 | 12.44 |
| 家具 | 5.10 |
提示:你不需要记下全部36个数。只要知道“猫”是最高分,其他都比它低,就够了。
我们用最基础的Python(甚至可以用手机计算器)一步步算:
3.1 手动计算(无代码,纯逻辑)
找出最大值:
max = 23.71每个logit减去23.71:
- 猫:
23.71 - 23.71 = 0.00 - 动物:
18.22 - 23.71 = -5.49 - 宠物:
15.09 - 23.71 = -8.62 - 哺乳动物:
12.44 - 23.71 = -11.27 - 家具:
5.10 - 23.71 = -18.61
- 猫:
取自然指数(
e^x):e^0.00 = 1.0000e^-5.49 ≈ 0.0041(查表或用计算器)e^-8.62 ≈ 0.0002e^-11.27 ≈ 0.000013e^-18.61 ≈ 0.00000007
求和:
1.0000 + 0.0041 + 0.0002 + 0.000013 + 0.00000007 ≈ 1.0043归一化“猫”的得分:
1.0000 / 1.0043 ≈ 0.9957
→ 四舍五入到小数点后4位:0.9957
这个数字,就是模型告诉你“这张图是猫”的“置信度”。
它不是凭空编的,不是训练时硬塞的,而是从原始输出中严格、可重复、可验证地算出来的。
4. 为什么不能直接用原始logits?三个现实原因
你可能会问:“既然logits已经能排序,为什么还要多此一举算Softmax?”
答案是:原始logits无法跨样本、跨模型、跨任务横向比较。举三个真实例子:
4.1 同一张图,不同模型输出天差地别
| 模型 | “猫” logits | “动物” logits | 差值 |
|---|---|---|---|
| 万物识别(本镜像) | 23.71 | 18.22 | 5.49 |
| 某开源ResNet50 | 4.21 | 3.87 | 0.34 |
| 某轻量MobileNetV3 | 1.05 | 0.92 | 0.13 |
你能说“万物识别更确信是猫”吗?不能。因为它们的数值尺度完全不同。但Softmax后:
- 万物识别:“猫” → 0.9645
- ResNet50:“猫” → 0.5821
- MobileNetV3:“猫” → 0.5317
这时你一眼就能看出:第一个模型对“猫”的判断最坚定。
4.2 同一模型,不同图片的logits范围浮动很大
试两张图:
- 一张高清正脸猫照 → logits最高分 23.71
- 一张模糊侧影猫照 → logits最高分 16.33
但它们的Softmax得分可能都是0.92+。因为Softmax关注的是相对关系,而不是绝对大小。
4.3 业务系统需要统一接口
你的APP后端要对接多个AI服务:图像识别、语音转写、文本摘要。每个服务返回格式不同。但只要约定“置信度必须是0~1之间、总和为1的浮点数”,前端就无需为每个模型写一套解析逻辑。
Softmax,本质上是一种工程友好型标准化协议。
5. 实战技巧:如何让Softmax得分更“靠谱”?
Softmax本身很忠实,但它输出的数字是否“可信”,取决于前面的logits质量。以下是我们在使用「万物识别-中文-通用领域」时验证有效的3个实操建议:
5.1 别迷信“0.99”,重点看Top-3是否语义连贯
模型给出:
[猫] 0.9645[宠物] 0.8721[哺乳动物] 0.7633
好信号:三个标签属于同一语义层级(具体→泛化),说明模型理解了“猫”是“宠物”的一种,也是“哺乳动物”的一种。
❌ 危险信号:
[猫] 0.9645[咖啡杯] 0.0021[台风] 0.0018
这种跳跃式分布,往往意味着图像质量差、主体不突出,或模型对当前图片存在误判倾向。
5.2 扩展候选标签时,避免语义重叠
错误示范(标签互相包含,导致Softmax“分心”):
CANDIDATE_LABELS_ZH = ["猫", "布偶猫", "英短猫", "宠物", "动物"]→ “布偶猫”是“猫”的子类,“猫”又是“宠物”的子类。Softmax会把分数分散在多个相关标签上,导致“猫”得分被稀释。
正确做法(保持粒度一致):
CANDIDATE_LABELS_ZH = [ "布偶猫", "英短猫", "橘猫", "暹罗猫", # 细粒度品种 "金毛犬", "柯基犬", "哈士奇", "泰迪" # 同级对比 ]5.3 对低分结果,用“阈值+人工兜底”比硬截断更稳妥
不要写:
if score < 0.5: print("识别失败")而是:
if score < 0.7: print(f"低置信度识别:[{label}] {score:.4f},建议人工复核")因为:
- 0.65的“猫”可能是暗光下的真实猫;
- 0.82的“家具”可能是把猫窝误认成了沙发。
得分高低 ≠ 对错,只是模型的自我评估。
6. 总结:Softmax不是终点,而是理解模型的起点
我们从一张猫图出发,一路拆解到指数运算,亲手验证了0.9645是怎么算出来的。现在你应该清楚:
- Softmax不是神秘函数,它只做三件事:平移、取指数、归一化;
- 它输出的不是“真实概率”,而是“模型内部偏好”的标准化表达;
- 它的价值不在数学多漂亮,而在让不同模型、不同图片、不同任务的结果,有了可比、可解释、可集成的基础;
- 真正决定识别效果的,永远是前面的特征提取和语义对齐能力——Softmax只是那个诚实汇报结果的“翻译官”。
下次再看到“置信度:0.9645”,你心里会多一层笃定:这不是玄学,这是可追溯、可验证、可调试的工程结果。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。