☰
从二分类到多分类:Softmax、交叉熵与网络设计实战
2026/10/15 19:03:19 网站建设 项目流程

1. 从二分类到多分类:为什么这不是简单的“多来几次”

很多人学神经网络,走到图像分类这一步,脑子里第一个蹦出来的场景往往是“猫和狗”——一个典型的二分类问题。输出层放一个神经元,接Sigmoid,大于0.5判为猫,小于0.5判为狗,干净利落。但现实中的图像分类任务,几乎不会给你只有两个类别的奢侈条件。手写数字识别有10个类别,CIFAR-10有10类,ImageNet有1000类,细粒度分类动辄上万类。这时候如果你还想着“我训练10个二分类器不就行了”,那就掉进了一个经典的思维陷阱。

我先说结论:多分类不是把二分类重复多次,而是输出层结构、损失函数、标签编码方式三个层面同时发生变化的一套完整方案。这三个层面必须协同改动,缺一个都会导致训练不收敛或者精度惨不忍睹。

先讲一个我早期踩过的坑。当时做一个花卉分类的小项目,5个类别。我图省事,用了5个独立的二分类器,每个负责判断“是不是某一类”。训练完发现两个致命问题:第一,推理时一张图可能同时被多个分类器判为“是”,也可能一个都不判,输出结果根本没法用;第二,每个分类器只看到了正样本和负样本的二元信息,类别之间的相对关系完全丢失了。比如月季和玫瑰长得很像,二分类器各自独立学习,根本没法利用“这两个类容易混”这种有价值的结构信息。

正确的做法是让网络在同一个输出向量上做决策。假设有C个类别,输出层就放C个神经元,每个神经元对应一个类别的“得分”。但这里有个关键问题:得分本身只是实数,可能是3.2,也可能是-1.7,怎么把它变成概率?怎么让概率之间互相制约?

这就引出了多分类的核心机制——Softmax函数。它的作用不是简单地把每个得分独立映射到0到1,而是做一个全局归一化:所有类别的概率加起来必须等于1。这意味着一个类别概率上升,其他类别必然下降。这种“竞争关系”才是多分类和二分类最本质的区别。二分类里猫的概率是0.8,狗的概率就是0.2,天然互斥;多分类里你必须显式地构造这种互斥。

再往深一层想,为什么不用Sigmoid逐个处理?因为Sigmoid对每个输出独立作用,得到的值不满足“和为1”的约束。你可能会说,那我手动归一化不就行了?可以,但手动归一化在反向传播时的梯度行为和Softmax不一样。Softmax的导数形式非常优雅:对于正确类别,梯度是(预测概率-1);对于错误类别,梯度就是预测概率本身。这个形式让梯度计算极其简洁,也解释了为什么Softmax和交叉熵损失搭配在一起时,梯度会变得特别干净——这个后面讲损失函数时我会展开。

所以这一章你要记住的核心认知是:多分类的本质是在一个共享的特征表示之上,通过Softmax构造类别间的竞争关系,让网络学会“相对判断”而不是“绝对判断”。这个认知会贯穿后面所有章节,包括数据准备、模型设计、训练调参和结果分析。

2. 标签的表示方式:从整数到One-Hot的完整链路

搞清楚了输出层的结构,接下来必须解决一个看似简单但极其容易出错的问题:标签怎么表示?

在二分类里,标签就是一个0或1的标量,网络输出一个值,直接做差算损失。但多分类里,标签是一个类别索引,比如“3”代表第3类。问题来了:你能直接拿这个整数3去和网络的输出向量做运算吗?显然不能。网络输出的是一个长度为C的向量,标签也必须变成同样长度的向量才能逐元素计算损失。

这就引出了One-Hot编码。假设有5个类别,标签“3”会被编码成[0, 0, 0, 1, 0]。这个向量的长度等于类别数,正确类别的位置是1,其余是0。看起来很简单,但这里有几个实操中必须注意的细节。

第一个细节:类别索引从0开始还是从1开始。这个看似无关紧要的问题,在实际项目中坑过无数人。Python的列表索引从0开始,但很多数据集(尤其是从MATLAB时代遗留下来的)标签从1开始。如果你用PyTorch的CrossEntropyLoss,它期望的标签是0到C-1的整数,内部会自动做One-Hot展开。但如果你手动做One-Hot,然后送进BCELoss或者自己写的损失函数,索引错一位就是灾难性的——网络会把所有样本都学成错误的类别,而且损失曲线看起来可能还挺正常,因为它在稳定地优化一个错误的目标。

第二个细节:标签平滑(Label Smoothing)。标准的One-Hot是“硬标签”,正确类别是1,其他是0。但硬标签有一个问题:它鼓励网络把正确类别的logit推向无穷大,因为只有这样才能让Softmax输出无限接近1。这会导致模型过度自信,泛化能力下降。标签平滑的做法是把1变成0.9,把0变成0.1/(C-1),让目标分布不那么极端。这个技巧在ImageNet级别的任务上几乎是标配,但在小数据集上要谨慎使用,因为小数据集本身就不容易过拟合,平滑可能反而欠拟合。

第三个细节:类别不均衡时的标签处理。假设你有10个类别,但第3类占了80%的样本。这时候如果直接用One-Hot,网络会倾向于把所有样本都预测成第3类,因为这样损失最小。解决方案有两种:一种是在损失函数里给每个类别加权,权重和类别频率成反比;另一种是在采样时做重采样,让每个batch里各类别比例均衡。我个人的经验是,中等程度的不均衡(最大类不超过最小类的10倍)用类别权重就够了,极端不均衡(超过100倍)必须配合重采样,否则模型根本学不到少数类的特征。

还有一个容易被忽略的点:推理阶段的标签处理。训练时我们用One-Hot算损失,但推理时我们只需要argmax拿到预测类别索引。这里要注意,argmax返回的是索引,如果你在训练时把标签做了偏移(比如从1开始),推理时也要做相应的逆偏移。我见过一个项目,训练时标签是0-4,推理时忘了这个约定,结果所有预测都偏了一位,排查了半天才发现是标签编码的问题。

下面这张表总结了多分类中标签处理的关键决策点:

决策点常见做法注意事项
标签起始索引0(PyTorch/TF默认)与框架API对齐,避免手动偏移
编码方式One-Hot或整数索引整数索引配合CrossEntropyLoss更高效
是否平滑大数据集用,小数据集慎用平滑系数通常取0.1
类别不均衡类别权重或重采样极端不均衡必须重采样
推理输出argmax取索引注意与训练时的索引约定一致

3. 交叉熵损失:多分类任务的方向盘

输出层有了Softmax,标签有了One-Hot,接下来最关键的一环就是损失函数。多分类任务几乎无一例外地使用交叉熵损失(Cross-Entropy Loss)。但很多人只是知道“分类用交叉熵”,却说不清楚它为什么比MSE(均方误差)好,也不知道它在反向传播时到底发生了什么。这一章我把交叉熵拆开讲透。

先看交叉熵的数学形式。对于单个样本,假设真实标签是One-Hot向量 $y$,网络预测概率是 $\hat{y}$,交叉熵定义为:

$$L = -\sum_{i=1}^{C} y_i \log(\hat{y}_i)$$

因为 $y$ 是One-Hot,只有正确类别的位置是1,其余是0,所以这个求和实际上只剩一项:

$$L = -\log(\hat{y}_{\text{correct}})$$

也就是说,交叉熵损失只关心正确类别的预测概率。正确类别概率越接近1,损失越接近0;越接近0,损失越大,而且是大到无穷大。这个性质非常关键:它给网络一个极其强烈的信号去提升正确类别的概率。

那为什么不用MSE?MSE的形式是 $\sum (y_i - \hat{y}_i)^2$。问题在于,当预测概率接近0或1时,MSE的梯度会变得非常小,因为平方函数的导数在极值点附近趋近于0。这意味着网络在“错得离谱”的时候反而学得很慢。而交叉熵的梯度在预测概率接近0时是 $1/\hat{y}$,会变得非常大,网络会被狠狠地“推一把”。这个差异在实操中非常明显:用MSE训练多分类,收敛速度可能慢好几倍,而且容易卡在局部极小值。

再来看交叉熵配合Softmax的梯度。这是整个多分类训练中最优雅的部分。设Softmax之前的logit为 $z$,Softmax输出为 $\hat{y}$,交叉熵损失为 $L$。经过推导(这里不展开完整链式法则,只给结论),对于每个logit $z_i$,梯度是:

$$\frac{\partial L}{\partial z_i} = \hat{y}_i - y_i$$

这个形式太漂亮了。对于正确类别,$y_i=1$,梯度是 $\hat{y}_i - 1$,是个负数,梯度下降会增大这个logit;对于错误类别,$y_i=0$,梯度是 $\hat{y}_i$,是个正数,梯度下降会减小这个logit。而且梯度的幅度直接和预测误差挂钩:预测越离谱,梯度越大。这就是为什么Softmax+交叉熵成为多分类的黄金组合——梯度形式简洁、数值稳定、收敛快。

实操中有一个必须注意的坑:数值稳定性。Softmax涉及指数运算,如果logit很大(比如100),$e^{100}$ 会溢出;如果logit很小(比如-100),$e^{-100}$ 会下溢到0,导致除零错误。解决方案是Log-Sum-Exp技巧:在计算Softmax之前,先减去logit中的最大值。这个操作不改变Softmax的输出(因为分子分母同乘一个常数),但能保证指数运算的输入不超过0,彻底避免溢出。PyTorch的CrossEntropyLoss内部已经做了这个优化,所以直接用它就行。但如果你自己手写Softmax和交叉熵,一定要记得加这个保护。

还有一个进阶话题:Focal Loss。这是交叉熵的一个变体,专门解决类别不均衡问题。它的核心思想是给容易分类的样本降权,让网络聚焦在难分类的样本上。公式是在交叉熵前面乘一个 $(1-\hat{y})^\gamma$ 的调制因子,$\gamma$ 通常取2。当预测概率接近1时,调制因子接近0,损失被大幅降低;当预测概率接近0时,调制因子接近1,损失基本不变。这个技巧在目标检测里非常流行,但在普通图像分类中,如果类别不均衡不严重,用普通的类别加权交叉熵就够了,Focal Loss反而可能引入额外的超参数调优负担。

4. 从LeNet到ResNet:多分类网络结构的关键设计

输出层和损失函数确定之后,剩下的问题就是:用什么网络来提取特征。多分类任务的网络结构设计和二分类没有本质区别,但有几个关键点需要特别注意,这些点直接决定了模型能不能学到有判别力的特征。

先回顾一下最经典的LeNet-5。这个1998年提出的网络结构极其简单:两个卷积层、两个池化层、三个全连接层,最后输出10维(对应MNIST的10个数字)。但就是这么一个简单的结构,奠定了CNN做图像分类的基本范式:卷积提取局部特征,池化降低空间维度,全连接做最终决策。LeNet的输出层就是10个神经元接Softmax,这个设计至今没有变过。

但LeNet有一个明显的局限:它太浅了。两个卷积层的感受野有限,只能捕捉非常局部的纹理和边缘。对于复杂图像(比如CIFAR-10里的飞机、汽车),需要更深的网络来捕捉更大范围、更抽象的特征。于是有了VGG、GoogLeNet、ResNet这一系列演进。

这里我不打算逐个介绍网络结构,而是聚焦一个在多分类任务中极其关键的问题:全连接层的设计和全局平均池化的取舍。

传统的做法(LeNet、AlexNet、VGG)是在卷积层之后接一个或多个全连接层,最后再接输出层。全连接层的作用是把卷积提取的二维特征图展平成一维向量,然后做非线性组合。但全连接层有一个致命问题:参数量巨大。假设最后一个卷积层的输出是 $7 \times 7 \times 512$,展平后是25088维,如果接一个4096维的全连接层,参数量就是25088×4096≈1亿。这1亿参数不仅占内存,还极易过拟合。

ResNet引入了一个优雅的替代方案:全局平均池化(Global Average Pooling, GAP)。做法很简单:对每个特征图求全局平均值,把 $7 \times 7 \times 512$ 变成 $1 \times 1 \times 512$,然后直接接输出层。这样参数量从1亿降到几乎为零,而且每个特征图对应一个类别,可解释性更强。GAP现在几乎是所有现代分类网络的标准配置。

但GAP也不是万能的。如果你的数据集很小,GAP可能欠拟合,因为它没有足够的参数来拟合复杂的决策边界。这时候可以在GAP之前加一个 $1 \times 1$ 卷积做通道降维,或者用一个小型的全连接层(比如512维)做过渡。我个人的经验是:数据量大于5万张时直接用GAP,小于1万张时用GAP+小全连接层,介于两者之间看具体任务复杂度。

另一个关键设计点是输出层的维度。这个看似显然——有多少类就放多少个神经元。但有一个容易忽略的细节:输出层不要加激活函数。很多人习惯性地在最后一层加ReLU或Sigmoid,这是错误的。输出层应该输出原始的logit,让Softmax和交叉熵在损失函数内部处理。如果在输出层加了ReLU,所有负的logit会被截断为0,Softmax的输入分布被破坏,梯度也会出问题。PyTorch的CrossEntropyLoss期望的输入就是未经过Softmax的logit,这一点一定要记住。

还有一个实操中经常遇到的问题:迁移学习时的输出层替换。假设你用预训练的ResNet-50做1000类分类,现在要迁移到自己的10类任务。正确的做法是:把最后的全连接层(或GAP之后的输出层)替换成新的10维输出层,前面的卷积层全部冻结或微调。这里的关键是新输出层的初始化。如果随机初始化,初始梯度会很大,可能破坏预训练的特征。建议用较小的标准差(比如0.01)做高斯初始化,或者直接用Xavier初始化。另外,冻结层数也要根据数据量决定:数据少就多冻结,数据多就少冻结。

下面这张表对比了不同网络结构在多分类任务上的适用场景:

网络结构参数量适用数据量多分类特点
LeNet-5约6万极小(<1万)输出层直接接Softmax,简单直接
VGG-16约1.3亿中等(1-10万)全连接层参数大,需强正则化
ResNet-50约2500万大(>10万)GAP替代全连接,参数量小
MobileNet约400万移动端/小数据深度可分离卷积,轻量高效

5. 训练多分类模型的实操细节:学习率、Batch Size与正则化

网络结构搭好了,损失函数选定了,接下来就是训练。多分类任务的训练过程和二分类大同小异,但有几个参数和技巧需要特别调整。这一章我把自己在多个项目中总结的经验参数和踩坑记录分享出来。

先说学习率。多分类任务的输出层维度更高,Softmax的梯度行为比Sigmoid更复杂,所以学习率通常要比二分类小一些。我的经验值是:如果用SGD,初始学习率取0.01到0.1;如果用Adam,取0.001到0.0001。但这不是绝对的,关键是要配合学习率预热(Warmup)。多分类任务在训练初期,输出层的随机初始化会导致Softmax输出接近均匀分布,梯度方向不稳定。如果一开始就用大学习率,很容易震荡。Warmup的做法是在前几个epoch把学习率从0线性增加到初始值,让网络先稳定下来。这个技巧在Transformer时代被广泛使用,但在CNN多分类任务上同样有效,尤其是当batch size较大时。

再说Batch Size。多分类任务对batch size比二分类更敏感,因为每个batch里的类别分布直接影响梯度的方向。如果batch size太小(比如8),一个batch里可能只包含2-3个类别,梯度会偏向这些类别,导致训练不稳定。如果batch size太大(比如1024),虽然梯度稳定,但泛化能力可能下降。我的建议是:batch size至少是类别数的5-10倍。比如10分类任务,batch size至少取64;100分类任务,至少取512。如果显存不够,可以用梯度累积来模拟大batch。

正则化是多分类任务的重中之重。因为输出层维度高,模型很容易过拟合。常用的正则化手段有三种:L2正则(权重衰减)、Dropout、数据增强。L2正则的系数通常取1e-4到1e-5,太大导致欠拟合,太小不起作用。Dropout在全连接层后面加,丢弃率0.5;在卷积层后面加,丢弃率0.2-0.3。数据增强对图像分类尤其重要,随机裁剪、水平翻转、颜色抖动这三件套几乎是标配。但要注意:不是所有增强都适合所有任务。比如数字识别任务,水平翻转会把“6”变成“9”,这种增强反而有害。垂直翻转对自然图像通常也没意义,因为重力方向是固定的。

还有一个容易被忽略的细节:Batch Normalization的位置。BN通常放在卷积层之后、激活函数之前。但在多分类任务的输出层之前,要不要加BN?我的经验是不要。因为BN会改变logit的分布,而Softmax对logit的绝对大小敏感。如果在输出层前加BN,相当于强制logit的均值为0、方差为1,这会限制Softmax输出的动态范围,导致模型难以产生高置信度的预测。如果非要用,可以在BN之后加一个可学习的缩放参数,但这增加了调参复杂度,不如直接不用。

早停(Early Stopping)也是多分类任务中必须用的技巧。因为多分类的验证集准确率曲线通常比二分类更平滑,不容易出现剧烈的过拟合信号。我的做法是:监控验证集损失,如果连续10个epoch没有下降就停止训练,并回滚到验证集损失最低的模型。注意是监控损失而不是准确率,因为准确率是离散的,可能长时间不变化,而损失是连续的,能更早地反映过拟合趋势。

下面这张表总结了我常用的多分类训练参数配置:

参数推荐值说明
初始学习率(SGD)0.01-0.1配合Warmup使用
初始学习率(Adam)0.001-0.0001更稳定,适合小数据
Batch Size类别数×5-10保证每batch类别多样性
权重衰减1e-4到1e-5L2正则,防止过拟合
Dropout(全连接)0.5输出层前通常不加
Dropout(卷积)0.2-0.3可选,视过拟合程度
早停耐心值10个epoch监控验证集损失

6. 评估与调优:混淆矩阵比准确率更有信息量

模型训练完了,准确率90%,看起来不错。但如果你只盯着准确率,可能会错过很多关键信息。多分类任务的评估远比二分类复杂,因为类别之间的错误模式才是真正有价值的东西。这一章我讲怎么用混淆矩阵和每类指标来诊断模型,以及遇到具体问题时怎么调优。

先说什么叫混淆矩阵。它是一个 $C \times C$ 的矩阵,行是真实类别,列是预测类别。对角线上的数字是正确分类的样本数,非对角线是错误分类的样本数。这个矩阵能一眼看出模型在哪些类别上容易混淆。比如在手写数字识别中,混淆矩阵经常显示“4”被预测成“9”、“3”被预测成“8”、“5”被预测成“6”。这些错误不是随机的,而是因为字形相似。知道了这一点,你就可以针对性地做数据增强(比如对4和9增加旋转和形变),或者调整类别权重。

每类精确率、召回率和F1分数也是必须看的指标。精确率是“预测为某类的样本中,真正属于该类的比例”;召回率是“真正属于某类的样本中,被正确预测的比例”。在多分类中,这两个指标往往不一致。比如一个类别样本很少,模型可能倾向于不预测它,导致召回率低但精确率高。F1是两者的调和平均,能综合反映。我的习惯是:先看混淆矩阵找主要错误模式,再看每类F1找短板类别,最后针对短板类别做定向优化。

常见的调优手段有几种。如果某个类别的召回率特别低,说明模型“不敢”预测这个类别,可能是类别权重不够,或者这个类别的特征不够有判别力。解决方案是增加这个类别的样本权重,或者对这个类别做更强的数据增强。如果某个类别的精确率特别低,说明模型“过度”预测这个类别,可能是这个类别的样本太多,或者和其他类别太像。解决方案是降低这个类别的权重,或者增加负样本。

还有一个进阶技巧:Top-K准确率。在类别数很多时(比如1000类),Top-1准确率可能不高,但Top-5准确率很高,说明模型其实“知道”正确答案在候选列表里,只是排序不够靠前。这时候可以通过标签平滑或者知识蒸馏来提升排序质量。知识蒸馏的做法是:用一个更大的教师模型(比如ResNet-152)的输出概率作为软标签,让学生模型(比如ResNet-18)去拟合。软标签包含了类别之间的相似性信息(比如“猫”和“狗”的概率都比“汽车”高),比硬标签更有信息量。这个技巧在模型压缩和提升小模型精度上非常有效。

最后说一个我踩过的坑:验证集和测试集的类别分布不一致。有一次做项目,验证集准确率95%,测试集只有80%。排查后发现,验证集是从训练集随机划分的,类别分布和训练集一致;但测试集是另一个时间段采集的,某些类别的比例差异很大。解决方案是:验证集必须和测试集同分布,如果做不到,至少要在验证集上做类别分层采样,保证每个类别的比例和测试集接近。这个教训让我后来养成了一个习惯:拿到数据第一件事就是画类别分布图,训练集、验证集、测试集各画一张,对比着看。

7. 一个完整的多分类项目复盘:从数据到部署

前面六章把多分类的各个环节都拆开讲了,这一章我用一个完整的项目案例把它们串起来。这个项目是交通标志分类,数据集是GTSRB(German Traffic Sign Recognition Benchmark),共43个类别,训练集约3.9万张,测试集约1.2万张。我选择这个案例是因为它规模适中、类别多、类别不均衡明显,非常能体现多分类任务的典型挑战。

数据准备阶段。GTSRB的图片尺寸不一,从15×15到250×250都有。第一步是统一尺寸,我选了32×32,因为这是CIFAR-10的标准尺寸,很多预训练模型可以直接用。但缩小尺寸会丢失细节,尤其是小标志上的数字和图案。我的做法是:先用32×32训练一个基线模型,如果某些类别的准确率明显偏低,再对这些类别用更大的尺寸(比如64×64)单独训练一个专家模型。这种“全局+专家”的架构在实际项目中很常见。

类别不均衡是GTSRB的另一个特点。最多的类别有2000多张,最少的只有200多张。我用了加权采样:在构造DataLoader时,给每个样本一个权重,权重和类别频率成反比。这样每个batch里各类别的期望数量大致相等。同时用了类别加权交叉熵,权重是类别频率的倒数再归一化。两个手段叠加,少数类的召回率从最初的60%提升到了85%以上。

模型设计阶段。我选了ResNet-18作为骨干网络,因为它在32×32输入上的表现和ResNet-34差不多,但参数量少一半。输出层替换成43维,用GAP替代全连接。这里有一个细节:ResNet原本是为224×224设计的,第一层是7×7卷积、步长2,然后接3×3最大池化。对于32×32输入,这个下采样太激进了,特征图会变得太小。我的改动是:把第一层换成3×3卷积、步长1,去掉最大池化,这样特征图在进入残差块之前保持32×32。这个改动让准确率提升了3个百分点。

训练阶段。用了Adam优化器,初始学习率0.001,每20个epoch衰减0.1。Batch size取128,因为43个类别,128大约是类别数的3倍,再小就不稳定了。数据增强用了随机旋转(±15度)、随机缩放(0.9-1.1)、颜色抖动(亮度、对比度、饱和度各±0.2)。没有用水平翻转,因为交通标志翻转后可能变成另一个标志(比如左转和右转)。训练了80个epoch,验证集准确率在60个epoch后基本饱和,早停触发。

评估阶段。最终测试集Top-1准确率97.2%,Top-5准确率99.8%。混淆矩阵显示主要错误集中在几个相似的标志上:限速30和限速50(数字形状相似)、禁止超车和禁止大型车超车(图案相似)。针对这两个混淆对,我做了两件事:一是对这两对类别增加额外的训练样本(从其他数据源补充),二是用对比损失在特征层面拉开它们的距离。对比损失的做法是:在GAP之后加一个投影头,把特征映射到128维空间,然后让同类样本的特征距离近、异类样本的特征距离远。这个技巧让这两对类别的F1分数从0.89提升到了0.95。

部署阶段。模型导出为ONNX格式,用ONNX Runtime做推理。这里有一个坑:训练时用了GAP,但ONNX对GAP的支持在不同版本间有差异。我最后是把GAP手动展开成ReduceMean操作,确保兼容性。推理时的预处理必须和训练时完全一致:同样的归一化参数(均值0.5、标准差0.5)、同样的通道顺序(RGB)、同样的尺寸(32×32)。任何不一致都会导致精度下降,而且这种下降往往很隐蔽,因为模型仍然会输出结果,只是结果错了。

这个项目让我最深的体会是:多分类任务的精度提升,80%来自数据质量和标签一致性,20%来自模型和调参。GTSRB的标签质量很高,所以模型很容易做到95%以上。但如果标签有噪声(比如某些图片被标错了类别),模型精度会卡在一个瓶颈上,怎么调参都上不去。所以如果你发现模型精度不达预期,第一件事不是换模型,而是抽样检查标签。我通常随机抽100张图,人工核对标签,如果错误率超过5%,就要考虑清洗数据了。

8. 多分类任务的边界与常见误区

写到这里,多分类的核心内容基本讲完了。但还有几个边界问题和常见误区需要澄清,这些是我在带新人和做技术评审时经常遇到的。

第一个误区:多分类和二分类的损失函数可以混用。有人觉得二分类就是C=2的多分类,所以用交叉熵就行了。理论上没错,但实操中二分类用BCELoss(配合Sigmoid)和多分类用CrossEntropyLoss(配合Softmax)在数值行为上有差异。BCELoss对每个输出独立计算,而CrossEntropyLoss做全局归一化。对于C=2的情况,两者在数学上等价,但梯度尺度不同。我的建议是:C=2时用二分类的写法,C>2时用多分类的写法,不要为了统一而统一,因为框架对两种写法的优化程度不同。

第二个误区:类别数越多,模型越难训练。这个说法不完全对。类别数增加确实会增加输出层的参数量,但现代网络的瓶颈通常在特征提取部分,不在输出层。真正影响训练难度的是类别之间的可分性。1000个差异很大的类别(比如ImageNet)可能比10个非常相似的类别(比如不同品种的狗)更容易训练。所以不要被类别数吓到,先分析类别之间的视觉差异有多大。

第三个边界:多分类 vs 多标签分类。这是两个完全不同的任务。多分类是“一个样本属于且仅属于一个类别”,多标签是“一个样本可以同时属于多个类别”。多标签的输出层用Sigmoid而不是Softmax,损失函数用二元交叉熵而不是多类交叉熵。我见过有人把多标签任务当多分类做,结果模型只能预测一个标签,漏掉了其他标签。判断方法很简单:看你的标注数据,如果一个样本可以同时打多个标签,那就是多标签;如果只能打一个,那就是多分类。

第四个边界:开集识别(Open-Set Recognition)。标准的多分类假设测试集的类别和训练集完全一致。但现实中,测试时可能出现训练时没见过的类别。这时候Softmax会强行把新类别分到某个已知类别,而且可能给出很高的置信度。解决方案有几种:一是用阈值法,如果最大Softmax概率低于某个阈值,就判为“未知”;二是用原型网络,计算样本特征和每个类别原型的距离,距离都很大就判为未知;三是用生成模型,估计每个类别的数据分布,分布外的样本判为未知。这些方法各有优劣,选择取决于具体场景。

最后一个实操建议:永远保留一个“其他”类别。如果你的数据里有一些难以归类的样本,不要强行把它们塞进某个类别,而是单独设一个“其他”类。这个类别可以包含各种杂项,让模型学会“这些不是我要分类的目标”。在部署时,如果“其他”类的概率最高,就触发人工审核或者拒绝预测。这个策略在工业质检、内容审核等场景中非常实用,能显著降低误报率。

多分类是神经网络中最基础也最核心的任务之一。把多分类吃透了,再去看目标检测、语义分割、实例分割这些更复杂的任务,你会发现它们本质上都是在多分类的基础上增加了空间维度的处理。所以这一块的基础打牢了,后面的路会好走很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询