资讯动态

半监督图像分类实战:一致性正则化与伪标签如何降低标注成本

发布时间:2026/10/19 16:30:33 来源:尧图企业网站定制
做图像分类做到一定阶段大家都会撞上同一堵墙标注成本。我早期做商品图分类时一批数据动辄上万张雇人打标一张几毛钱还得反复质检一个类目下来几万块没了。后来我把目光转向了图像分类半监督学习才意识到过去扔掉的那些无标注图片其实才是真正的富矿。这篇文章不是论文复现笔记也不是纯理论科普而是我从一个做落地项目的工程师视角把半监督学习从原理到实操、再到工程化过程中踩过的坑和总结的经验完整梳理一遍。适合手里有大量无标注图片、但只有少量人力打标预算的团队参考也适合想系统理解半监督学习的同学。我会尽量把每个选择背后的为什么讲清楚而不是只丢一堆配置参数。1. 标注成本困局半监督学习解决的到底是哪个问题1.1 我被一张标注报价单敲醒的经历有一次某项目要做服饰类目识别建模之前先看了下数据现状服务平台有60万张商品主图但只有之前人工打过的3000张有效标签。按照供应商的报价按类目标签10元/100张算60万张全量打标要6万元还不算二次质检的时间。项目经理当时问我能不能用3000张标注图先把模型训练出来后续按需增量打标。放在两年前我大概率会摇头因为常规经验是数据不够模型难产。但那次我刚好在调研半监督学习于是决定赌一把——先拿3000张标注加5万张无标注图做一次半监督训练看看能到什么水平。结果让我意外同样的测试集上只用3000张标注做全监督训练准确率约82%而加入5万张无标注图做半监督训练准确率冲到了89.6%。虽然还比不上全量打标的93%但成本差距是数量级的。从那时起我就把半监督学习当作分类项目中性价比最高的一条技术路线。1.2 半监督学习的基本设定与边界所谓半监督就是指训练数据被分成两部分一部分是有标注样本一部分是没有标注的样本。模型在训练时同时利用这两部分信息让无标注数据用某种方式帮上忙。它适合解决的问题有三个非常明显的特征标注成本高需要专家、需要长时间、需要反复质检无标注数据量大原始图片容易获取只是没人打标签类别体系相对固定分类任务的目标类别在训练前已经确定不是开放式目标。如果这三点都满足半监督的收益通常会很大。反过来如果你的类别本身不稳定随时可能加新的类或者你的无标注数据来源混乱、和标注数据分布严重不一致那半监督带来的风险可能比收益更大。这部分的判断很重要我后面会详细讲。1.3 为什么传统数据增强加预训练替代不了半监督这里我经常被问到一个问题我已经用了ImageNet预训练再加数据增强是不是就够用了我觉得要看清楚一点预训练解决的是模型初始化的问题它的收益在样本量不足时确实显著但它无法把无标注数据中蕴含的类别结构显式传给模型。打个比方预训练相当于给一个学生请了一个基础科目的老师让他先打好了基本功但半监督相当于让这个学生在大量没批改过的作业里自己反复做题做完再找老师批改一小部分通过反馈一点点矫正。后者能学到的东西和前者是不一样的。数据增强同样如此。增强是让模型对同一张图的变换版本保持稳定但它只在单样本层面起作用半监督的一致性正则则把同一图像的不同增强版本与无标注数据有机结合本质上是在无标注数据上构造了一种自训练的信号。预训练和数据增强都能和半监督叠加使用但它们之间的分工并不重叠。2. 一致性正则化半监督里最值得先吃透的思想2.1 换个问法答案应该一样我开始理解一致性正则化时脑海里浮现的是这么个场景数学辅导课上老师把同一道应用题换了两种表述方式问学生学生给出的最终答案应该是同一个。如果学生一种表述下答对、另一种表述下答错说明他对这个知识点的理解是有漏洞的。图像分类也是一回事。给模型看一张猫的照片水平翻转一下、轻微平移一下、调一点颜色只要这些变换不改变语义模型的预测结果应该基本保持一致。如果模型在变换前后输出的类别概率差别很大说明它对这张图的判断不够鲁棒还有可以学习的空间。这就是一致性正则化的核心思想对无标注图像施加扰动后模型在不同扰动版本上的预测分布应当尽量一致。在实现上通常把这种一致性约束写作损失项叠加在有监督交叉熵损失之上。早期的方法比如Mean Teacher、Π-model用的都是这个思路后来MixMatch、FixMatch等一批方法也都是围绕这个核心思想在演化。2.2 弱增强与强增强的分工逻辑具体操作中需要区分两种增强弱增强包括随机水平翻转、小幅平移、缩放这类温和的变换尽量保留原始图像的完整语义信息强增强是RandAugment这类更激进的变换比如随机改变颜色饱和度、对比度、亮度、旋转、切割等使图像在保持类别信息的情况下产生较明显的视觉变化。这里的分工不是随意的弱增强版本会被用来生成伪标签目的是让伪标签尽量可靠强增强版本则作为学生输入逼迫模型在更大的扰动范围内依然保持稳定。换句话说弱增强负责提出问题强增强负责挑战模型两相结合帮助模型学到更本质的特征而不是纹理表面的偶合。我在实践中最常采用的是FixMatch提出的框架先对同一张无标注图做弱增强得到模型的预测概率当最大置信度超过阈值τ时将其取argmax作为伪标签再对同一张图做强增强用模型对强增强结果的预测与该伪标签计算交叉熵损失。整个过程不需要额外的复杂结构实现成本很低但效果却非常好。2.3 阈值τ模型自信心的一个门禁FixMatch里的阈值τ默认是0.95意思是对无标注样本只有模型预测置信度达到95%以上才把它当作有效伪标签参与训练。这个门禁设计得非常高我最初觉得是不是太严了会浪费大量样本。实际跑下来才发现0.95的阈值在大多数分类数据集上都行之有效原因有两层第一如果阈值太低错误伪标签的比例会快速上升模型会在错误的监督信号上自我强化出现所谓确认偏误第二高阈值下虽然单步样本利用率低但随着训练推进模型对越来越多无标注样本都能达到高置信度整体利用率是逐步提高的。用一句大白话总结宁可错过一些可能有效的样本也不要放那些不靠谱的伪标签进来。很多半监督方法后来做的改进本质上都还是在如何让伪标签更可靠、如何让可靠性判断更灵活上下文章这说明这个门禁确实是整个方案的命门。3. 伪标签的置信度阈值与动态调整我踩过的几个坑3.1 第一个坑0.8阈值下的自我欺骗最早做实验时我把τ从0.95降到0.8本意是想让更多无标注数据参与训练提升样本利用率。刚开始的十几个epochloss下降很快验证集指标也在涨我一度以为发现了更高效的配置。但跑到第30个epoch问题爆发了测试准确率开始缓慢下降而且训练loss继续降低、测试loss却反弹典型过拟合症状。后来分析发现错误根源不在模型结构而在伪标签本身——0.8置信度的样本中有不少是易混淆类别比如针织衫和卫衣在商品图上非常接近模型给了一个置信度0.82的错误预测。这批错误标签进入训练后模型开始系统性地把某些类别拉偏。所以我的经验教训是伪标签质量优先于数量置信度阈值宁可设高也不要为了利用率牺牲可靠性。对于类别之间高度相似的场景阈值甚至要提高到0.98以上才安全。3.2 第二个坑训练初期的高置信度样本并不总是可信还有一个容易忽视的问题训练初期模型的置信度整体偏低但即便如此它仍然会对一些样本输出接近1.0的过度自信预测。这类高置信度往往不是因为它真的认对了而是因为模型还没见过足够多样的真实样本把输入中的一些偶然特征当成了决定性特征。比如我有一批背景颜色非常统一的产品图训练初期模型会倾向把红色背景和某个类别强关联产生看起来置信度很高、本质却错误的伪标签。如果这时候让这些样本进入半监督loss错误信号就会在后续训练中持续放大。解决思路是在训练初期加入一个预热阶段前几个epoch只使用有标注数据计算有监督损失不启用无标注损失让模型的特征表达先达到一个基本可用水平再逐渐放开无标注信号的权重。我用5到10个epoch的预热期后稳定性明显提升。3.3 第三个坑类别不平衡导致伪标签越来越不平衡实际项目中无标注数据的类别分布几乎不可能是均匀的。常见的情形是头部类别占70%长尾类别每个占不到1%。半监督训练在这个场景下天然有马太效应模型对头部类别置信度高产生的伪标签多长尾类别置信度普遍低伪标签少于是头部类别越训越强长尾类别越训越弱。对此我试过两种有效的调整手段对无标注损失按类别做加权统计当前批次伪标签的类别频率给低频类别的样本更高权重对置信度阈值做类别自适应长尾类别的阈值适当降低头部类别的阈值适当提高让模型更愿意学习少见类。这两种方法都涉及伪标签分布与真实分布的一致性假设如果完全不知道真实分布可以先用有标注数据上的类别频率作为先验估计。注意不要做太激进否则会把噪声放大。3.4 阈值动态调整从固定阈值到自适应策略坦白说固定阈值0.95在简单数据集上足够好用但是在长尾数据集或小标注集场景下还是有优化空间。现在很多改进工作都围绕动态阈值展开我实验下来稳定有效的做法是随着训练推进逐步降低阈值下限。具体逻辑是训练初期模型弱阈值要高只接受最可靠的样本训练中后期模型逐渐强壮阈值可以逐步放宽让更多样本进入训练。比如从0.98线性降到0.90或者按照验证集上的无监督损失曲线决定何时放宽。这个动态策略不一定在每个数据集上都带来很大提升但基本不会造成明显退步。我在一个模拟的长尾商品数据集上试过最终准确率比固定0.95高1.2个百分点代价是超参数多了一个。如果不追求极限性能固定阈值已经完全够用。4. 一个可复现的半监督图像分类实践流程4.1 数据准备与划分策略先说数据划分。半监督训练里有一个很容易被忽略的原则验证集一定要是有标注的、且不能参与任何无监督训练过程。我见过有人把验证集里的图片也顺便拿去给模型做一致性正则短期看不出问题实际上会污染验证结果导致模型选择失真。以我常用的配置为例假设总共只有4000张有标注样本、5万张无标注样本。我会这样划分数据集数量用途有标注训练集3000计算有监督损失也参与无标注损失计算有标注验证集1000模型选择、早停、超参数调整无标注训练集50000仅参与无标注一致性损失计算这里有标注训练集也参与无标注损失计算是常见做法也就是对这部分样本既可以算交叉熵也可以算一致性损失相当于多了一层正则。4.2 训练流程与核心代码框架整个训练流程可以归纳为几个步骤构造有标签和无标签数据加载器、定义弱增强和强增强变换、计算有监督损失与无监督损失、按比例加权求和、反向传播更新。我给出一个简化但能直接运行的PyTorch核心框架方便直观理解训练过程# model、optimizer、criterion 省略重点关注训练逻辑 for epoch in range(epochs): for batch_l, batch_u in zip(labeled_loader, unlabeled_loader): x_l, y_l batch_l x_u_w, x_u_s batch_u # 无标注的弱增强/强增强版本 # 有监督部分 pred_l model(x_l) loss_sup criterion(pred_l, y_l) # 用弱增强版本生成伪标签 with torch.no_grad(): pred_u_w model(x_u_w) prob, pseudo_label pred_u_w.max(dim1) mask prob.ge(threshold).float() # 置信度门控 # 无监督部分强增强版本与伪标签计算损失 pred_u_s model(x_u_s) loss_unsup criterion(pred_u_s, pseudo_label, reductionnone) loss_unsup (loss_unsup * mask).mean() loss loss_sup lambda_u * loss_unsup optimizer.zero_grad() loss.backward() optimizer.step()这个框架虽然简单但已经是FixMatch的核心结构。实际的代码还需要处理分布式采样、批次大小差异、EMA模型等一系列细节。如果只是验证思路上面的代码已经足够跑起来看趋势。4.3 关键超参数与优化细节下面几个超参数是半监督训练里影响最大的我列出实践中的常用配置和选择理由超参数常用值理由置信度阈值τ0.95控制伪标签质量太低会引入噪声无监督损失权重λ_u1.0与有监督损失同量级多数任务上比较稳无标注batch比例μ5或7每个有标注batch对应5到7倍无标注样本优化器SGD Momentum 0.9半监督中SGD普遍优于Adam收敛更稳权重衰减5e-4防止小样本下过拟合学习率调度Cosine前期不要太大后期平滑下降关于优化器的选择我在多个实验里对比过Adam训练前期收敛快但后期容易在无标注样本上出现过拟合式的震荡SGD配合动量虽然收敛慢一些但最终泛化更稳定。如果数据集规模特别小可以用Adam做短训练快速验证正式跑长训练还是建议SGD。4.4 我的实验过程与指标变化拿一个模拟项目举例有标注3000张、无标注50000张、12个类别ResNet-50作为骨干网络。实验设置三组对照全监督3000张、半监督3000加5万、全监督5万张。训练过程中的典型变化是前半段前20个epoch半监督模型的准确率和全监督基本持平甚至略低因为无标注损失还在拖后腿从中段开始半监督逐步超过全监督到80个epoch时拉开明显差距到训练后期差距继续扩大但增速变缓。最终测试集结果大致如下全监督3000张82.1%半监督3000加5万89.5%全监督5万张93.0%这个结果说明两件事第一无标注数据确实能弥补标注数据的不足近8个百分点的提升非常可观第二半监督的上限仍然低于全量有监督它解决的是成本问题而不是魔法期望值要合理。如果标注预算允许优先还是补标注预算受限时半监督是最值得投入的方向。5. 从实验到工程化半监督落地时必须想清楚的几件事5.1 训练时长和算力预算要按3到5倍准备半监督学习一个很现实的成本是训练时间。我最初以为多加5万张无标注图只是多了一些数据没想到训练时长是纯有监督的好几倍。原因在于无标注数据量通常远大于有标注数据量而且每个epoch都要做两组增强计算前向传播的压力天然翻倍。在我自己参与的项目里一个纯有监督ResNet-50跑150个epoch单卡V100大约需要10小时加上5万张无标注图后同样条件下跑150个epoch大约要25到35小时。如果使用更强的增强策略或者更大的无标注batch比例时间还会往上走。所以立项阶段就要把算力预算按纯有监督的3到5倍预估否则容易在训练中途遇到时间不够只能砍epoch的窘境。另外多卡并行时的数据采样也要注意尽量保证每个设备上的无标注数据分布接近否则BatchNorm统计量会出问题。5.2 伪标签服务化不是训练时算一次就完事很多初学者以为伪标签只在训练循环里生成一次训练完就扔了。但实际上在真实业务里模型上线后每天都会有新图片进来无标注池是动态的。如果我们希望把半监督的收益持续积累就需要一个伪标签服务定期对新增无标注图生成伪标签并筛选高置信度样本补充到训练集里。我在某项目中就是这么做的每天离线任务扫描当天的无标注图得到置信度大于0.95的伪标签进入候选池当候选池积累到一定数量后触发一次增量训练增量训练后的新模型先在验证集上跑一遍如果没有下降再发布。这套流程本质上把半监督训练变成了一种自动数据飞轮。它带来的收益不是一次性提升而是让模型能持续吸收新数据中的分布变化对线上图片风格漂移的鲁棒性也更好。但代价是工程复杂度上了一个台阶需要专门处理版本管理、样本去重、标签冲突等问题。5.3 无标注数据的质量清洗不能省半监督对无标注数据的质量同样有要求。如果无标注池里混入大量与任务无关的图片哪怕它们本身是清晰的模型也可能学到无关的特征拉低分类效果。我做过一个测试在无标注池中混入20%完全不相关的场景图半监督最终准确率比干净无标注池低了近4个百分点。后来我在数据进入半监督训练前增加了一道简单的前置过滤先用现有的有监督模型对无标注图做一次粗筛剔除那些置信度极低的离群样本。这个操作成本很低但对稳定性的提升非常明显。需要注意的是粗筛不要做得太狠否则会把长尾类别里的困难样本也一起过滤掉导致类别失衡加剧。过滤的目标是删掉根本不属于当前任务范畴的图像而不是删掉模型暂时认不出的样本。5.4 模型更新与灰度发布的节奏半监督模型训练完成后最常见的问题是如何安全上线。分类模型一旦误判在业务端可能直接造成客诉所以我的习惯是先走灰度发布把新模型和旧模型同时部署对相同的线上请求做预测实时比对分歧样本人工抽检后再决定是否全量切换。这是因为半监督模型在某些伪标签污染的类别上可能表现得很差但平均指标看起来还可以。只盯着总准确率做决策容易漏掉局部风险。我在具体操作中还会留一个可回滚的口子灰度期保留旧模型一段时间一旦发现问题可以快速切回不至于影响线上稳定性。最后再分享一个我调整心态上的小变化以前我总觉得只有标注数据才是数据现在我会把无标注数据看成更基础的资产。半监督学习的价值不在于取代标注而在于把标注预算花在刀刃上——用一小部分高质量的标注撬动大规模无标注数据中的结构信息。如果你手头正好有一批分类图片标注成本又卡得紧很值得先用FixMatch这套流程跑一版基线。哪怕最终只提升了两三个点省下的标注预算也足够覆盖几个季度的迭代成本了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑