神经网络多分类:一对多和 Softmax
文章目录
- [神经网络多分类:一对多和 Softmax](#神经网络多分类:一对多和 Softmax)
-
- [1. 选项从两个变成多个](#1. 选项从两个变成多个)
- [2. 一对多:每个类确认一次](#2. 一对多:每个类确认一次)
-
- [2.1 放到一张网络里](#2.1 放到一张网络里)
- [3. Softmax:在类与类之间分概率](#3. Softmax:在类与类之间分概率)
- [4. 汽车例子:按重量分三档](#4. 汽车例子:按重量分三档)
- [5. 和多标签区分](#5. 和多标签区分)
- [6. 动手:Iris 上对比逻辑回归与 MLP](#6. 动手:Iris 上对比逻辑回归与 MLP)
- [7. 实现时几个容易混的点](#7. 实现时几个容易混的点)
- [8. 适用边界与常见误区](#8. 适用边界与常见误区)
-
- [8.1 适用边界](#8.1 适用边界)
- [8.2 常见误区](#8.2 常见误区)
- [9. 关键术语速查](#9. 关键术语速查)
- [10. 延伸阅读](#10. 延伸阅读)
- [11. 小结](#11. 小结)
摘要 :专栏前半段的分类多是「高效 / 非高效」二选一。车型档位、故障类型、手写数字这类问题,选项往往有三个以上,而且每个样本只属于其中一类 。第 13 篇从通用角度介绍过多分类;本篇把它接到神经网络输出层上,讲清 一对多(one-vs-all) 和 Softmax 差在哪里、概率要不要加起来等于 1,以及什么时候不该用 Softmax。适合读完浅层网络实践的读者。
1. 选项从两个变成多个
二分类输出一个概率 p p p,再靠阈值切成 0/1。多分类时,标签变成 0 , 1 , ... , K − 1 0,1,\ldots,K-1 0,1,...,K−1,模型要给出 K K K 个分数或概率,最后通常取最大的那一类。

| 二分类 | 多分类(单标签) | |
|---|---|---|
| 例子 | 是否高效 | 轻 / 中 / 重 三档 |
| 输出 | 1 个概率 | K K K 个值 |
| 约束 | p ∈ ( 0 , 1 ) p\in(0,1) p∈(0,1) | Softmax 下各类概率和为 1 |
和第 13 篇一样,先分清:
- 多分类:单选题,每辆车只能落在一档
- 多标签:多选题,一张图可以同时有「猫」和「户外」
本篇默认讲单选题。多标签一般是每个标签各接一个 Sigmoid,不要用 Softmax 硬凑。
第 8~12 篇的阈值、混淆矩阵、精确率 / 召回率,在多分类里仍然用得上,只是混淆矩阵从 2 × 2 2\times 2 2×2 变成 K × K K\times K K×K。第 13 篇已从「不用神经网络」的角度讲过 OvR / Softmax;本篇重点是:这些头接在 MLP 最后一层时该怎么选。
2. 一对多:每个类确认一次
一对多(One-vs-All / One-vs-Rest) :有 K K K 个类,就准备 K K K 个二分类问题。
对「轻 / 中 / 重」三档:
- 是不是轻?(对比:不是轻)
- 是不是中?
- 是不是重?

预测时看哪个分类器最「肯定」,或比较各自的分数。类数不多时,这个想法很直观;类数上到成百上千,就维护成本高、也慢。
2.1 放到一张网络里
不必真的训练 K K K 个完全独立的模型。可以共用前面的隐藏层,输出层放 K K K 个节点,每个节点各自接 Sigmoid:

每个输出表示「是不是这一类」的概率,彼此独立 估计。因此三者加起来不一定等于 1------有可能都偏低或都偏高。
水果图的例子里,网络可能给出「梨 0.84、葡萄 0.07......」,加总超过 1,这在一对多设定里并不奇怪:它在分别回答「是不是梨」「是不是葡萄」,而不是在「四种水果里只能选一个」的约束下分配概率。
预测时常见做法是:看哪一路 Sigmoid 输出最大,就把该类当作预测;若业务允许「都不像」,也可以要求最大概率超过某阈值,否则判为未知。阈值仍建议在验证集上定,和第 09 篇同一思路。
3. Softmax:在类与类之间分概率
若业务要求「轻、中、重三者互斥,必须落在一档」,更常见的是 Softmax(也常叫一对一 / one-vs-one 语境下的相对概率,和第 13 篇用语一致即可)。
先对每个类算一个未归一化分数 z j z_j zj(logits),再:
p j = e z j ∑ k = 1 K e z k p_j = \frac{e^{z_j}}{\sum_{k=1}^{K} e^{z_k}} pj=∑k=1Kezkezj
于是 p j > 0 p_j>0 pj>0,且 ∑ j p j = 1 \sum_j p_j = 1 ∑jpj=1。

可以把它看成逻辑回归里 Sigmoid 往多类的推广:Sigmoid 处理「是 / 否」;Softmax 处理「在 K K K 个选项里挑一个」。
手算一个小数:设三档分数 z = ( 1.0 , 2.0 , 0.0 ) z=(1.0,\ 2.0,\ 0.0) z=(1.0, 2.0, 0.0),则
e 1 ≈ 2.72 , e 2 ≈ 7.39 , e 0 = 1 e^{1}\approx 2.72,\quad e^{2}\approx 7.39,\quad e^{0}=1 e1≈2.72,e2≈7.39,e0=1
分母 ≈ 11.11 \approx 11.11 ≈11.11,于是
p ≈ ( 0.24 , 0.67 , 0.09 ) p \approx (0.24,\ 0.67,\ 0.09) p≈(0.24, 0.67, 0.09)
「中」档分数最高,概率也被抬得最高;提高某一档的 z z z,会挤占其他档的概率------这就是「互相竞争」。
| 一对多 + Sigmoid | Softmax | |
|---|---|---|
| 各类是否互斥建模 | 不强求 | 强求,概率互相挤占 |
| 概率和是否为 1 | 不一定 | 是 |
| 典型用途 | 类可近似独立判断 | 单标签多分类默认选择 |

类特别多(例如上万类)时,完整 Softmax 算分母会贵,工程上会用候选采样等近似;入门阶段类数不大,用完整 Softmax 即可。
数值上算 Softmax 时,常先减去 max k z k \max_k z_k maxkzk 再取指数,避免 e z e^{z} ez 溢出。手算小例子不必纠结;自己用 NumPy 实现时记得这一步。框架里的 Softmax 一般已处理稳定计算。
4. 汽车例子:按重量分三档
把专栏里的重量粗分成三档标签(演示用,阈值可改):

| 档位 | 条件(千磅) | 标签 |
|---|---|---|
| 轻 | < 3.0 < 3.0 <3.0 | 0 |
| 中 | 3.0 3.0 3.0~ 4.0 4.0 4.0 | 1 |
| 重 | > 4.0 > 4.0 >4.0 | 2 |
二分类问「高不高效」;这里问「偏轻、适中还是偏重」。特征仍可用重量、排量等,输出层改为 3 个节点。
- 若用 Softmax:三个概率和为 1,取 argmax 得档位。
- 若用三个 Sigmoid:更像三个独立「是不是这一档」的问题;对互斥档位,一般不如 Softmax 合适。
损失方面,Softmax 多分类常用交叉熵 (多类版 Log Loss)。训练仍是前向算 p p p、算损失、反向更新(第 25 篇),只是输出维数从 1 变成 K K K。
若训练集里「重」车特别少,Softmax 仍可能偷懒少报「重」------第 20 篇的类别不平衡问题在多分类里同样存在,需要看每类召回,必要时加类别权重或重采样。
5. 和多标签区分
| 场景 | 该用什么 |
|---|---|
| 每张图只有一种主果蔬 | Softmax 多分类 |
| 一碗里同时有苹果和橙子 | 多个 Sigmoid / 多个二分类,不用 Softmax |
| 邮件:垃圾 / 正常 | 二分类 Sigmoid 即可 |
| 故障码:互斥的 5 种故障 | Softmax |
Softmax 的假设是:每个样本恰好属于一类。不满足就换输出头,别硬套。
训练标签也要一致:若标注规范里允许「既算中型又算偏重」,那是多标签问题,应拆成多个是/否,而不是强行塞进三个互斥档。和业务对齐标签定义,往往比纠结隐藏层宽度更重要。
6. 动手:Iris 上对比逻辑回归与 MLP
python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score, log_loss, classification_report
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=0, stratify=y
)
def eval_model(name, clf):
pipe = Pipeline([("scaler", StandardScaler()), ("clf", clf)])
pipe.fit(X_train, y_train)
proba = pipe.predict_proba(X_test)
pred = pipe.predict(X_test)
print(name, {
"acc": round(accuracy_score(y_test, pred), 3),
"logloss": round(log_loss(y_test, proba), 3),
})
# Softmax 概率应按行求和为 1
print(" prob row sums:", proba.sum(axis=1).round(3)[:5], "...")
return pipe
# 逻辑回归多分类默认可用 Softmax(multinomial)
eval_model(
"logreg",
LogisticRegression(max_iter=1000, multi_class="multinomial"),
)
eval_model(
"mlp",
MLPClassifier(
hidden_layer_sizes=(8,),
activation="relu",
learning_rate_init=0.01,
max_iter=2000,
random_state=0,
),
)
看两件事:准确率 / log loss 是否合理;proba 每一行是否加起来接近 1(Softmax 行为)。MLPClassifier 在多类问题上会走 Softmax 风格的输出;若你自己用底层框架搭网络,输出层要明确写成 Softmax + 交叉熵,别和隐藏层的 ReLU 搞混。
汽车三档数据可以把 y 换成按重量分箱的标签,特征用 weight、displacement 等,流程相同:先标准化,再 MLP,再用混淆矩阵看哪两档最容易混(第 09、13 篇)。
打印 classification_report 时,重点看每一档的 precision / recall,而不是只看总 accuracy。三档样本若几乎均衡,准确率还算有参考价值;若某一档极少,总准确率容易虚高。
7. 实现时几个容易混的点
输出激活和隐藏激活不是一回事。
隐藏层继续用 ReLU(第 24、26 篇);多分类输出才是 Softmax 或若干 Sigmoid。
一对多的 Sigmoid 概率不要当成「已经归一化」。
若产品文案写「三类概率加总 100%」,就应上 Softmax,或事后自己归一化并说明含义变了。
类很多时。
完整 Softmax 分母要对所有类求和。类数极大时,候选采样等技巧会只对部分负类算分,那是工程优化,入门先把小 K K K 做对。
评估。
准确率在类别不平衡时仍可能骗人(第 20 篇);多分类更要看每类召回、宏平均 / 加权 F1,以及 K × K K\times K K×K 混淆矩阵。
和浅层网络实践怎么接。
第 26 篇的圆环是二分类。改成三类点(例如内、中、外三环)后,把 MLPClassifier 的标签换成 0/1/2,输出自动变成三维 Softmax 概率,调隐藏层宽度和学习率的方法不变。先保证二分类能分弯,再加类数,排查时更轻松。
8. 适用边界与常见误区
8.1 适用边界
- 本篇接在神经网络输出层;第 13 篇的 OvR / OvO 思想对树模型、SVM 同样适用。
- Softmax 管「选哪个类」,不管特征脏不脏、有没有泄漏------第 18~22 篇的检查仍然要做。
- 回归(预测 MPG 连续值)不是 Softmax 的场景。
8.2 常见误区
| 误区 | 更稳妥的理解 |
|---|---|
| 多分类就是多标签 | 单选 vs 多选,输出头不同 |
| 有 K K K 个 Sigmoid 输出,概率必加总为 1 | 一对多不保证;Softmax 才保证 |
| 隐藏层也要用 Softmax | Softmax 一般在输出层 |
| 类一多就先上很深的网 | 先确认标签是否互斥、数据是否够 |
| 只报总准确率 | 结合每类指标和混淆矩阵 |
| Softmax 概率能直接当校准很好的置信度 | 未校准前别过度解读;需要时再做概率校准 |
9. 关键术语速查
| 术语 | 含义 |
|---|---|
| 多分类 | 每样本恰好一类, K ≥ 3 K\ge 3 K≥3 |
| 一对多 | 每类一个「是该类 / 不是」问题 |
| Softmax | 将 K K K 个分数变成和为 1 的概率 |
| logit | Softmax 之前的原始分数 z j z_j zj |
| 交叉熵 | 多分类常用损失,对接 Softmax |
| 多标签 | 每样本可同时有多个标签 |
10. 延伸阅读
| 资源 | 适合看什么 |
|---|---|
| 专栏第 13 篇 | OvR、OvO、多分类与多标签 |
| 专栏第 24 篇 | Sigmoid 与输出激活 |
| sklearn LogisticRegression | multi_class='multinomial' |
| sklearn MLPClassifier | 多类 MLP |
| Iris 数据集 | 三类小样本练习 |
11. 小结
神经网络做多分类,关键在输出层怎么接:一对多用 K K K 个 Sigmoid,各类概率可以独立、加总不必为 1;标签互斥时用 Softmax,让概率在 K K K 类里互相竞争且加总为 1。隐藏层仍用 ReLU 一类激活,和输出头分开选。
text
互斥单标签 → Softmax + 交叉熵
可重叠多标签 → 每标签 Sigmoid
二分类 → 一个 Sigmoid 即可
汽车从「是否高效」换成「轻 / 中 / 重」后,只是 K K K 从 2 变成 3,前面的划分、标准化和过拟合处理并不过时。
接输出头之前先确认标签互不互斥:互斥用 Softmax,可重叠用多个 Sigmoid。头接错了,后面网络再深,优化的也是错问题。
下一篇会对神经网络做一次总结,然后过度到后面的 Embedding 等内容。
系列导航:
- 上一篇:【机器学习】(26)------ 浅层神经网络
- 下一篇(预告):神经网络小结与embedding
如果本篇对你有帮助,欢迎点赞、收藏、关注博主,机器学习专栏持续更新中,下次更新不迷路。