资讯详情

层级分类实战:B-CNN分支网络如何解耦粗粒度与细粒度识别

📅 2026/10/14 23:02:17 | 华诺云谱 👁 阅读
层级分类实战:B-CNN分支网络如何解耦粗粒度与细粒度识别
我去年在某个细粒度图像分类项目里被一个问题卡了很久数据集本身带清晰的层级标签先是大的生活场景分类再往下是物体具体种类但用普通多分类网络去训练效果总是不尽如人意。后来想明白了一个关键点——层级结构里的粗粒度信息和细粒度信息应该分开处理而不是逼着一个平坦分类网络同时兼顾两者。于是我把方案切到B-CNNBranch CNN上用分支结构去承接不同粒度的分类任务粗粒度和细粒度的准确率才同时上来。这篇是层级分类系列的续篇默认你已经了解层级分类的基本定义类别不是平铺在同一条线上而是从粗到细形成树状结构比如动物 - 鸟 - 麻雀。本文重点讲怎么用B-CNN把这种层级关系真正落到网络结构里——包括分支分叉点怎么选、损失函数怎么配、推理时怎么把粗细两个分支的结果融合起来以及我实际训练中踩过的几个坑。适合已经能训通普通CNN、想进一步做层级分类或细粒度识别的朋友参考。1. 为什么B-CNN适合层级分类从全局分类到分支分类的转变1.1 普通CNN在层级标签上的三个缺陷先回顾一下我为什么放弃所有类别丢进一个Softmax的做法。表面上看类别树最底层有N个叶子节点那网络输出N类就可以覆盖任务需求粗粒度类别完全可以靠叶子类别的自然分组推导。但实际训练时会遇到三个问题。第一是特征挤压。叶子类别越多共享分类层要拟合的判别边界就越细。粗粒度的共性特征比如猫科动物都有圆脸和竖瞳和细粒度的差异特征比如不同猫品种之间耳朵间距不同被塞进同一个特征向量网络只能被动地选择保留对最终Top-1损失最有利的信息粗粒度的语义往往被削弱。第二是梯度主导。训练数据通常是细粒度标签不均衡的。某一组粗类别下的细类样本特别多网络就容易把所有特征都推向区分这一大类的内部差异而忽略了其他粗类之间的边界。结果验证集上粗类准确率忽高忽低。第三是推理缺乏约束。平坦分类网络预测结果不会自觉遵守层级规则比如同一个父类下的两个子类概率之和本来应该等于父类概率但全局Softmax根本不管这个关系。我甚至遇到过网络输出狗的置信度高达0.9、但同时又输出一个和狗完全不同父类的第二高概率类别这在业务上是没法解释的。1.2 B-CNN的直觉让特征在每个粒度上各尽其职B-CNN的做法很直观主干网络先把通用特征提取出来然后在某个特征层分出两个分支——一个分支负责预测粗粒度类别另一个分支在这个基础上继续做深层次的特征提取负责预测细粒度叶子类别。两个分支共享主干大部分参数但各自有独立的分类头和独立梯度。这样做的好处是粗粒度分支不用和细粒度分支抢同一个全连接层它可以专注于这个物体属于哪一大类的判别细粒度分支也不用被迫在浅层就做出过于精细的区分它可以从主干已经比较稳定的特征继续往下钻。而且B-CNN天然契合层级推理的逻辑。粗分支的输出是一个概率分布细分支的输出是另一个概率分布两者相乘后可以得到真实叶子类别的联合概率叶子类别的最终得分 它所属父类在粗分支上的概率 × 该叶子在细分支上的条件概率。这个乘法操作在测试时非常简单但带来的约束效果却很明显。2. B-CNN网络结构设计主干、分支与级联推理的具体实现2.1 网络拓扑从哪里分叉、分叉后怎么走我最终采用的B-CNN结构基于ResNet-50但这里有一个很关键的设计决策分叉点放在哪里不能拍脑袋。我一开始把分叉点放在ResNet第一个Stage的输出位置也就是刚做完底层边缘纹理提取的地方。结果粗粒度分支倒也能学但细粒度分支因为缺少整个主干的特征逐步抽象过程收敛很慢。后来我把分叉点放到第四个Stageres4输出之后效果明显改善。原因也很简单粗粒度分类通常靠中级语义特征就能判断比如整体轮廓、颜色分布、大体形状这些信息在res4阶段已经非常丰富而细粒度分类需要更高阶的部件级特征例如鸟类的喙部、翅膀纹路、脚爪形状这需要额外的res5阶段继续抽象。具体来说我的网络拓扑是这样的主干部分复用ResNet-50的前四组残差块输出res4特征图尺寸为批次×1024×7×7输入224×224时。粗粒度分支对res4特征做全局平均池化接一个256维的全连接层最后接一个维度等于粗类数K的输出层。细粒度分支从res4特征继续接原ResNet-50的第五个Stageres5得到2048×7×7特征图再做全局平均池化、接一个512维全连接层最后输出维度等于叶子类别数N。这里要注意res4特征图同时被馈送到粗分支和细分支两个分支不会互相遮挡梯度。反向传播时粗分支的梯度只更新它自己的全连接层和主干中res4及之前的部分细分支的梯度除了更新自己的分支外也会更新主干。但二者在减法上没有直接竞争——唯一的竞争发生在主干参数的叠加更新方向上这一点我后面会在训练节奏部分详细讲。2.2 推理逻辑先粗后细与条件概率的自然融合训练完两个分支后有两个推理方案可用。方案一是级联预测输入一张图先看粗分支输出取概率最大的粗类然后只看该粗类下包含的所有细类在细分支中的概率取其中最大值。这种方案的优点是简单但坏处是如果粗分支自己就判断错了细分支永远没有机会纠正。比如粗分支把一只猫判断成狗那么就算细分支对猫下的某个具体品种概率很高也不会被选中。方案二是概率乘积融合对每个叶子类别c记其父类为p(c)计算最终分数 score(c) P_coarse(p(c)) × P_fine(c)。其中P_coarse是粗分支的Softmax输出P_fine是细分支的Softmax输出。最终预测取所有叶子类别中score最大的那个。这个方案不仅保留了细分支纠正粗分支错误的能力还天然满足层级约束——某个叶子类别的最终分数不可能超过它父类被预测的概率这样就消除了父类概率低但子类概率高的荒谬情况。我自己在实验里也验证过方案二比方案一的Top-1叶子准确率高大约2到3个百分点。所以在实际部署时我都会建议用概率乘积而不是简单的夹逼式推理。计算量只多了一个Softmax和一次逐元素相乘几乎可以忽略。3. 损失函数的设计粗分支和细分支为什么必须被同时监督3.1 损失函数组合与λ参数调控B-CNN的损失函数表面上看只是两个交叉熵相加L L_fine λ × L_coarse但λ怎么取非常影响最终效果。我试过λ0.1、λ1.0和λ5.0发现λ太大时网络会过度优化粗分类细分支的特征学习受到压制λ太小粗分支又起不到正则化作用。最终我选择λ1.0作为初始值然后针对不同需求微调。如果业务更看重粗分类准确率λ可以设为2.0如果更看重细分类准确率λ可以降到0.5。另外我实际训练时还会对两个损失做加权调整比如当某个粗类的样本特别多时我会对该粗类的粗分支损失项做类别权重缩放。更关键的一点是两个分支的损失不是一起反传就完了。粗分支对应的梯度只更新主干和粗分支细分支的梯度会更新主干、res5和细分支。我测试过让粗分支只做前向不参与主干梯度更新也就是把粗分支从梯度流中截断结果细分支Top-1准确率反而下降了。这说明粗分支的监督信息对主干特征学习是有帮助的它提供了一种额外的语义正则化让主干特征不仅在细粒度层面可区分在粗粒度层面也保持结构。3.2 条件Softmax与全局Softmax的差异一个关键实验训练细分支网络时我一开始用的是全局Softmax对N个叶子类别直接算交叉熵。直观上这没问题但结合层级分类语义后我发现它有个隐含的缺陷全局Softmax会让不同父类下的叶子类别互相竞争。比如在父类鸟下细分支会努力区分麻雀和燕子但鸟和哺乳动物下的某个类别在同一个Softmax里也会互相竞争导致细分支参数的一部分精力花在区分跨父类的粗语义上而这些能力粗分支已经具备了。于是我改用了条件Softmax也叫层级Softmax的做法对每个叶子类别c只在它同一父类下的所有兄弟类别中做归一化P_fine(c) exp(z_c) / Σ_{j ∈ siblings(c)} exp(z_j)这样细分支的输出被限制在每个父类内部网络只需要学习我已经知道这是鸟它到底是麻雀还是燕子。实验结果非常直观使用全局Softmax时细分支容易在所有叶子类别上做均匀分布Top-1细分类准确率大约86%改成条件Softmax后同一个分支结构下准确率提升到89%以上而且训练曲线震荡明显减少。原因就是条件Softmax把跨父类比较的负担完全拿掉了细分支的所有参数都被用来深化父类内部的判别。不过要注意条件Softmax有一个前提父类标签在训练时需要一起提供而且父类内部必须至少有两个叶子类别。对于只有一个子类的父类它的细分支概率恒为1等于没有监督这种情况建议直接把该父类合并到它的上一级避免浪费参数。4. 层级标签组织与样本不均衡处理4.1 标签表的组织方式与批次采样B-CNN不像普通分类网络那样只需要一个label.txt它至少需要两个标签列一列是叶子类别ID一列是父类ID。我的做法是用一个CSV文件组织字段包括image_path、coarse_id、fine_id。fine_id是全数据集的叶子类别全局编号coarse_id对应该叶子所在父类的编号。训练时要注意一个很容易忽略的点PyTorch的DataLoader每次随机采样一个批次如果批次内某个父类下的叶子类别样本特别多就会导致这个批次里粗分支的监督信号向某个父类偏斜。我在训练初期没管这些问题结果粗分支在单个Epoch内准确率波动很大。后来我改成了按父类均衡采样的策略每个批次先从父类集合里均匀采样K个父类再从每个父类里均匀采样若干个样本。这样能保证每个批次内粗类数量相对均衡训练更稳定。4.2 粗类不均衡与细类样本过少怎么处理层级分类的数据集往往天然不均衡。比如动物这个父类下可能有100个叶子类别而植物下只有5个。如果直接训练细分支在植物内部几乎没有判别压力。对于粗类不均衡我的做法是在粗分支的交叉熵损失上加权权重取该类样本数占总样本数比例的倒数。这比简单的上采样下采样要好因为它不会造成某个父类重复样本过多、过拟合。对于细类样本过少我走了另一条路细分支先不直接学叶子分类而是在预设一个中层粗类分支的情况下把每个细类样本做混合增强。具体来说我对同一父类下的不同叶子类别进行Mixup增强比如把一张麻雀和一张燕子的图像按0.7与0.3比例混合让细分支学习更细的局部纹理差异而不是简单记忆整体外观。这个技巧在鸟类模拟数据集上把样本数少于50的长尾细类准确率提升了接近5个百分点。5. 训练过程中的关键时刻与踩坑记录5.1 从分叉点冻结到渐进解锁主干与分支的配合节奏这是我踩过最大、也最隐性的一个坑主干参数同时接收粗分支和细分支的梯度如果一开始就用大学习率让两个分支一起冲击主干主干很容易发生灾难性遗忘式的震荡。表现就是训练前几个Epoch损失下降很快然后突然回升验证集准确率始终上不去。后来我调整了训练节奏分三个阶段走第一阶段冻结主干只训练两个分支的全连接头。这时主干用ImageNet预训练权重两个分支需要从随机初始化开始学学习率可以设为0.01做5到8个Epoch让分支先适应主干特征的分布。第二阶段解锁主干后两个Stageres4和res5学习率降到0.001主干和分支一起训练但细分支的梯度回传比例稍微下降我通过给细分支损失乘0.8来做到。第三阶段全网络解锁学习率降到0.0001继续训练直到收敛。这种渐进式的解冻方式避免了主干刚被唤醒时两个分支的梯度在方向上反复拉锯。我在对比实验中验证过如果一开始就全网络解锁最终细分类精度大约只有渐进方案的三分之二。5.2 验证损失震荡、过度自信与不收敛的排查细分支在条件Softmax时特别容易出现过度自信的情况训练集上损失很低但验证集准确率一般。原因是父类内部往往有一些极其相似、连人类都很难分的叶子类别模型学会了用捷径特征去区分但对遮挡、模糊样本毫无泛化能力。我排查后做了三件事在细分支的全连接层之前加入Dropout概率设在0.3。这个操作对细分支有效但粗分支不要加因为粗分支本身特征就够粗加了Dropout反而损伤语义。验证集上同时观察粗分支和细分支的混淆矩阵。如果粗分支的混淆主要发生在相邻粗类之间比如猫科和犬科说明主干特征在粗粒度上还不够清晰我应该提升λ如果细分支混淆集中于同一父类说明父类内部特征还需要加强这时应该加深细分支而不是增加主干容量。学习率调度用了余弦退火并在最后10个Epoch做了EMA平均。这个细节让验证集细分类准确率稳定提升了约1.2个百分点。5.3 推理时的常见错误为什么会出现粗分支说鸟、细分支说猫这个现象是很多第一次用B-CNN的朋友最容易慌的地方。我用概率乘积方案后依然遇到过一次奇怪结果一张明明是鸟的测试图粗分支输出鸟的概率是0.72但细分支输出最高概率的却是猫下的某个品种。算乘积后鸟下的叶子类别依然能排前面吗能但有些猫品种的细分支置信度极高比如0.95乘上0.28的粗分支概率也可能压过鸟的子类。问题的根子在于细分支的条件Softmax只在同一父类内归一化不同父类之间的细分支输出根本没有互相约束。所以在做概率乘积融合时我额外对细分支输出做了一个全局校准在验证集上统计每个叶子类别的平均细分支概率然后用这个值去校准预测分数。这就相当于给每个叶子类别除以它先验出现的频率消除某些父类内部本身容易被高置信度预测的系统性偏差。6. 实测对比B-CNN、普通CNN与多任务CNN在模拟数据集上的表现6.1 实验配置与数据集为了说明B-CNN在层级分类任务中的真实收益我在一个自建的模拟数据集X上做了对比。数据集包含48个粗类、312个叶子类别总样本约12万张。每个粗类下的叶子数量并不均匀最少只有4个最多有23个样本分布整体呈长尾。对照组有三个普通CNNResNet-50直接输出312类全局Softmax。多任务CNN共享主干但两个完全独立的分类头一个头输出48类一个头输出312类各损失相加。B-CNN本文方案条件Softmax 概率乘积推理。所有模型使用相同的数据划分、图像分辨率224×224、预训练权重和总共60个Epoch的训练预算。评估指标包括粗类Top-1准确率、叶子类Top-1准确率以及层级匹配准确率预测的叶子类别和真实叶子类别属于同一个粗类视作正确。6.2 结果与解读结果如下表所示。模型粗类Top-1准确率叶子类Top-1准确率层级匹配准确率普通CNN91.2%81.6%89.5%多任务CNN94.0%83.1%91.0%B-CNN95.6%88.7%94.2%普通CNN表现最差这在预期之内多任务CNN粗类准确率比普通CNN高不少但叶子类准确率提升有限。原因在于多任务CNN的两个分类头共享的还是同一层特征粗分类头和细分类头对特征的需求方向不同形成了一定程度的特征拉扯。B-CNN在叶子类准确率上比多任务CNN高出了5.6个百分点这个差距主要来自两方面一是细分支通过res5获得了更深层特征二是条件Softmax消除了跨父类竞争。层级匹配准确率也最高说明即使叶子预测错了B-CNN也倾向于把错误控制在同一父类内部这一点对业务场景非常重要——系统可以在提示识别不准的同时至少给一个正确的大类范围。6.3 我个人还会继续钻研的改进方向B-CNN在模拟数据集X上的表现让我对层级分类产生了更多信心但它不是终点。我接下来的探索方向有两个一是让分叉点可以从数据中学习。手工指定res4之后作为分叉点虽然不是大问题但不同数据集的最优分叉位置可能不同。我打算参考可微架构搜索的思路让网络自己决定从哪一层开始分叉这样可以更自动地适应不同数据集的细粒度差异。二是利用粗细分支之间的关系做更轻量的知识蒸馏。我从实验中观察到粗分支的预测分布其实携带了这个物体在粗粒度语义上接近哪些类别的信息这些信息可以用来约束同一个粗类下的细分类特征让细分类特征在继承粗分支语义的同时更加紧凑。目前我在一个小规模植物数据集上做尝试早期结果还算不错。如果后面有新进展我会在博客里继续记录细节。希望这篇B-CNN的实现过程能帮你在层级分类任务上少走几圈尤其是分叉点选择、损失配比和推理融合这三块多花一点时间是值得的。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑