ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从竞赛复盘到落地实践:小样本细粒度图像识别全流程方案解析

从竞赛复盘到落地实践:小样本细粒度图像识别全流程方案解析

我们不想只停留在“准确率多少”的表面,而是想拿出一个真正能解释、能落地、能迁移到同类问题的方案。这篇博文就是我基于自己的参赛经历写的一份完整复盘:赛题怎么拆、数据怎么处理、模型怎么选、坑怎么避,以及最后为什么这套方案能拿到优秀作品奖。如果你正在准备数据挖掘或视觉类的竞赛,或者工作中恰好遇到“小样本细粒度图像识别”这类任务,这篇文章应该能给你一些可以直接抄作业的参考。

1. 赛题分析与项目整体思路拆解

1.1 赛题到底在考什么:先读懂题目再动手

第九届“泰迪杯”挑战赛B题给的核心任务是“岩石样本智能识别”,通俗讲就是给你一堆岩石照片,让计算机判断每一张照片属于哪种岩石类型。乍一听好像就是普通的图像分类问题,但真正接触过地质样本数据的人都知道,这题没那么简单。

岩石识别和日常的猫狗分类有本质区别。猫和狗在形态、颜色、纹理上的差异非常大,而很多岩石样本在外观上非常接近,比如粉砂岩和泥岩,肉眼看起来都是灰扑扑的一团,区别可能只在颗粒大小、断口形态、层理构造这类非常细微的纹理特征上。再加上岩石图像通常不是在标准影棚里拍摄的,而是来自野外露头、手标本、岩心扫描等多种场景,光照条件、拍摄角度、尺度比例都千差万别。这实际上是一道典型的“细粒度图像识别”任务,难点在于类间差异小、类内差异大。

赛题的另一层隐含考点是数据处理能力。官方给出的数据集往往不是干净的“一行路径对应一个标签”的理想状态,可能存在样本类别不均衡、部分标签错误、图像质量参差不齐等问题。评委想看到的不是一个只会调库调参的队伍,而是一个能从原始数据出发,自主完成数据清洗、增强、建模、评估、解释全流程的团队。

所以我们在拿到赛题后的第一步不是急着写代码,而是把题目拆解成几个子问题:

  • 数据层面:样本量是否充足?类别分布是否均衡?图像尺寸和格式是否需要统一?
  • 模型层面:用经典卷积网络还是预训练模型?是否需要引入注意力机制?
  • 工程层面:训练时间是否可控?推理速度是否满足要求?显存是否够用?
  • 解释层面:模型识别结果的依据是什么?哪些类别容易混淆?如何直观呈现?

这个拆解过程看似简单,但直接决定了后面所有技术方案的选择方向。

1.2 为什么选择深度学习技术路线:传统方法的局限

有些队伍可能会想,岩石识别能不能用传统图像处理方法来做,比如提取颜色直方图、灰度共生矩阵、局部二值模式之类的特征,再丢给支持向量机或随机森林分类器。这类方法在图像数量少、类别差异大的场景下确实有它的用武之地,而且可解释性强、计算开销小。但在岩石识别这个任务上,传统方法有明显的天花板。

岩石类别之间的差异往往体现在局部纹理结构上,比如矿物颗粒的排列方式、微裂缝的走向、可见孔隙的分布等。灰度共生矩阵能捕捉到一定程度的纹理统计信息,但它是基于像素灰度统计的,对几何形变、光照变化非常敏感。野外拍摄的两张同类岩石照片,可能因为一个侧光一个顶光,灰度统计特征就差得十万八千里。而深度卷积神经网络通过多层的卷积和池化操作,能够自动学习从边缘、纹理到语义层次的特征,对光照、尺度、背景偏移等干扰具有更强的鲁棒性。

我在这套方案里选择深度学习还有一个现实考量:数据量。泰迪杯B题提供了数千张岩石图像,这个量级对于从头训练一个深层卷积网络来说不算充裕,但配合预训练权重加数据增强,已经足够支撑一个十层量级的网络学习到有效的判别特征。如果数据量只有几百张,我会认真考虑传统方法加手工特征;但既然数据规模达到几千张,深度学习无疑是性价比更高、上限也更高的选项。

1.3 项目整体技术架构:三个模块的分工与协作

我们的最终方案可以拆成三个清晰的技术模块,这也是答辩时评委最容易理解的一种组织方式:

第一个模块是数据预处理与增强模块。负责统一图像尺寸、去噪归一化、数据增强、样本均衡。这个模块的输出是干净、多样化、类别均衡的训练集。第二个模块是特征提取与分类模块。核心是深度卷积神经网络,我们最终采用了预训练的残差网络结构,并针对岩石图像的纹理特性做了针对性优化。第三个模块是评估与可视化模块。这里不只看最终的准确率,更要分析混淆矩阵、逐类别的精确率和召回率、模型注意力区域的合理性,形成一整套可解释的评估报告。

这三个模块是串行的工作流,但在实际开发中,我建议不要等到前面完全搞定再开始后面。我的做法是先用少量数据搭建一个最简版本的完整流程,让数据从输入到输出先跑通,然后再回头逐个模块精修。先把链路打通,再在链路中找痛点,这种“粗糙先行、逐步求精”的开发节奏比线性推进要高效得多。

2. 数据是比赛的第一道分水岭:岩石样本处理与增强方案

2.1 岩石图像中的四大干扰源:看清难点再设计对策

在真正拿到数据后,我花了一个下午的时间把每张图都翻了一遍,用肉眼看,也统计了一些基本属性,比如尺寸分布、亮度均值、通道均值等。总结下来,岩石图像的主要干扰源有以下四类。

第一类是光照条件不一致。有些图像在自然光下拍摄,色温偏暖;有些是在补光灯或室内光源下拍摄,色温偏冷;还有少部分图像存在明显的阴影区。如果模型总是看到同一类岩石以一种固定亮度出现,它会误把亮度当作类别特征。

第二类是背景干扰严重。部分样本不是标准的岩石平铺图,而是包含了手部、地面、拍摄台、标签纸等背景元素。模型如果学歪了,可能不是学“这块岩石长什么样”,而是学“这个拍摄台配套哪类岩石”,这在赛题里是致命的。

第三类是尺度跨度大。有的图像是岩石手标本的近景特写,纹理颗粒非常清晰;有的图像是露头岩层的远拍,能看到层理和节理,但矿物颗粒的细节完全消失。同一个类别内部,图像尺度可以相差一个数量级。

第四类是细微纹理差异。前面提到的粉砂岩与泥岩,还有玄武岩与辉绿岩,这类成分类似、结构接近的岩石,其判别信息高度集中在局部纹理和断口形貌上,需要模型具备很强的细粒度特征捕捉能力。

针对这四类干扰源,我们在数据处理阶段做了针对性设计,说白了就一句话:尽量把模型需要关注的信息凸显出来,把不该出现的干扰压下去。

2.2 图像清洗与归一化操作:先给数据“立规矩”

数据处理的第一步是清洗。我梳理了一下,大概做了这几件事:移除完全无法辨认的模糊图像、剔除标注与内容明显不符的噪声样本、检查是否有重复图像。这些操作不能只靠肉眼,我写了个小脚本统计每张图像的清晰度(用拉普拉斯算子的方差做模糊度评估)、宽度和高度,以及亮度分布,把所有超过阈值范围的异常样本列出来,再逐张人工确认。

那种拉普拉斯方差低于一定数值的图像,基本可以判断为对焦不准或运动模糊,留着只会增加训练噪声。重复图像某种程度上会影响数据集的独立性评估,一旦训练集和验证集之间混入了重复样本,模型评估结果会虚高,这个坑很多队伍会踩。解决方法是计算每张图像的感知哈希,把近似重复的样本找出来,只保留其中一张。

清洗完之后是归一化。所有图像统一调整到固定尺寸,我们选择的是224×224像素,这是主流预训练模型的默认输入尺寸。这里有个小细节:直接拉伸缩放会改变岩石颗粒的长宽比例,对纹理特征是有影响的。我建议在缩放时优先做中心裁剪加等比缩放,而不是简单粗暴地把长边压扁。当然,裁剪也要注意别把岩石主体的关键区域裁掉。

灰度化和标准化也是归一化的一环。虽然岩石颜色本身是重要特征,但考虑到光照不稳定,我们额外保留了一路灰度图分支作为补充输入,实验证明对光照干扰有抑制作用。标准化则是把每个通道的像素值减去均值再除以标准差,让数据分布落在模型激活函数的敏感区间里,这一步对训练的稳定性帮助非常大。

2.3 数据增强方法论:把一张图变成一群图

数据增强是这次比赛里性价比最高的操作之一。几千张原始图像看起来不少,但对于深度学习来说,每类岩石可能只有几百张甚至更少,远远不够模型充分学习类内的形态变化。数据增强的核心思路是人为“制造”出更多符合真实分布的样本变体,提升模型泛化能力。

我用了一个增强组合,按重要性排序,清单如下:

  • 随机水平翻转:水平翻转不会改变岩石的实质结构,相当于让模型看到不同朝向的石头。垂直翻转在地质图像里要谨慎,因为某些沉积岩有明确的层理方向,垂直翻转可能改变地质语义。
  • 随机旋转:步长取90度的倍数,因为岩石纹理在任意角度旋转后本质上还是同一种岩石。
  • 随机亮度与对比度扰动:模拟不同光照条件,这一步直接针对前面说的光照干扰源。
  • 随机高斯噪声:模拟图像传感器噪声。
  • 随机裁剪与缩放:模拟不同拍摄距离带来的尺度变化,等价于某种尺度增强。
  • Mixup:把两张训练图像按比例混合,标签也按同样比例混合。这个技巧我之前在图像分类里用过,它能迫使模型学习更平滑的决策边界。

增强幅度控制是我踩过坑的地方。增强不是为了炫技,增强太猛反而会让模型学到不存在的特征。比如亮度扰动如果拉满,岩石的暗色矿物会被抹成一团黑,模型就很难学了。经过测试,我们最终把亮度扰动范围控制在原始值的正负20%左右,Mixup的混合系数取Beta分布内采样,参数在两个类别之间自由浮动,让模型逐步适应混合样本。

2.4 类别不均衡处理:让少数类被框外看见

经过统计分析,数据集里各类岩石的样本数差异不小,头部类别可能有六七百张,尾部类别只有两百张左右。如果不做任何处理,模型天然会偏向头部类别,因为在训练中它只要无脑预测头部类别就能获得较高准确率,这种貌似高分的模型在真实场景中基本不可用。

我采取的方案是“多管齐下”:在图像层面,对样本量少的类别做更高倍数的增强,相当于变相增加这小部分类别的数据;在采样策略上,使用加权采样器,让每个批次里少数类出现的概率适当提升;在损失函数上,给少数类分配更高的类别权重,让模型在训练时对这些类别投入更多注意力。

这里我想提醒一点:加权采样和类别权重都不是越高越好,过高的权重会让模型在少数类上过拟合,把噪声也学了进去。我们是通过验证集的逐类别F1分数来调节权重的。F1分数是精确率和召回率的调和平均,综合性更强,比单独看准确率要可靠得多。

3. 模型选型与训练细节:从骨架网络到迁移学习

3.1 三个候选模型的实测对比:结构、精度、开销的综合权衡

模型选型是我花时间最长的一个环节。我先后试了三个具有代表性的模型结构,每一轮都记录了精度、参数数量和单次训练时间,我给出一份简化版对比表:

模型结构参数量级验证集准确率单周期训练时间备注
轻量卷积网络数百万约86%较快自建特征结构,易过拟合
残差网络约2000万约93%中等深度适中,泛化能力强
高效卷积网络约3000万约94%较慢精度更高,但调参空间更大

自建的轻量卷积网络只有四五层卷积,优点是训练快、内存占用小、便于绘制结构图,但问题也很明显:对岩石这种富含细节纹理的任务来说,浅浅的网络学不到足够深的抽象特征,验证集准确率卡在86%上下就很难继续提升。残差网络在精度和开销之间取得了最好的平衡,它引入残差连接,把跳跃连接作为学习恒等映射的捷径,有效缓解了深层网络梯度衰减的问题,这是它能比浅层网络学得更深、学得更稳的关键。高效卷积网络精度最高,但训练时间几乎翻倍,而且在我们的数据集规模下优势并不明显,考虑到比赛的训练成本和最终落地效率,我还是选择了更均衡的残差网络。

3.2 迁移学习为什么在岩石识别上有效

这又是一个很容易被忽视但极其重要的选择。从头训练一个残差网络需要海量数据,我们的岩石数据集数量级完全不够看。但如果我们把网络换成在千万级自然图像上预训练好的权重,情况就完全不同了。

预训练模型在大型数据集上已经学会了大量底层特征,比如边缘检测、纹理提取、颜色分布统计等。这些底层视觉特征具有高度通用性,岩石图像的边缘、纹理、颜色渐变这些元素,在本质上与自然图像的边缘、纹理、颜色渐变没有太大差别。迁移学习做的事很简单:保留这些通用的底层特征提取能力,只替换并重新训练最后的全连接分类层,让它学会把通用特征映射到“哪类岩石”这个特定任务上。

我在预训练模型的基础上把两个阶段分开:第一阶段冻结底层所有参数,只训练新加的分类层;第二阶段再解冻部分深层网络,用较小的学习率对整个模型进行微调。这个两阶段策略非常关键,因为如果一上来就用大学习率微调全部参数,预训练权重很容易被破坏,模型性能反而会大幅下降。

3.3 训练超参数调试记录:学习率、批大小、优化器与正则化

超参数调试过程说白了就是“在探索中找感觉”。我给出几组有代表性的实验记录:

第一组实验是学习率对比。初始学习率设为0.01时损失函数在前期震荡非常剧烈,后面更换为余弦退火调度策略后,损失曲线变得平滑不少。最终确定基础学习率为0.001,并在训练中后期逐步衰减。

第二组实验是批大小对比。批大小为32时训练稳定但收敛偏慢,梯度方向噪声较大但也能正常工作;批大小提高到64后每个batch的计算效率更高,收敛速度明显变快。显存允许的话,批大小取64对训练效率比较友好。

优化器我用的是带动量的随机梯度下降,动量设为0.9。很多人习惯性用自适应优化器,它收敛快但容易收敛到尖锐极值,泛化性能往往不如稍微“笨”一点的随机梯度下降家族。

正则化方面,我在全连接层使用了Dropout,率设为0.5,并在损失函数上加了权重衰减来抑制过拟合。关于早停法我也设置了耐心阈值,如果连续若干周期验证集损失不再下降,就自动停止训练,把最佳权重复原回来,防止模型在后期过拟合到训练集的噪声上。

3.4 核心训练代码框架参考:PyTorch风格示意

这一节给出一段核心训练代码的简化版框架,需要说明的是,这不是完整可运行的项目代码,而是展示训练流程的关键骨架,方便你理解整体逻辑。完整代码包含了很多数据加载、日志记录的细节,我这里只保留主干。

# 伪代码框架:突出训练流程主干 model = models.resnet50(pretrained=True) num_features = model.fc.in_features model.fc = nn.Linear(num_features, num_classes) model = model.to(device) # 第一阶段:只训练分类层 for param in model.parameters(): param.requires_grad = False for param in model.fc.parameters(): param.requires_grad = True optimizer = optim.SGD(model.fc.parameters(), lr=1e-3, momentum=0.9) criterion = nn.CrossEntropyLoss(weight=class_weights.to(device)) for epoch in range(first_stage_epochs): model.train() for x, y in train_loader: x, y = x.to(device), y.to(device) pred = model(x) loss = criterion(pred, y) optimizer.zero_grad() loss.backward() optimizer.step() # 第二阶段:解冻深层网络微调 for param in model.parameters(): param.requires_grad = True optimizer = optim.SGD(model.parameters(), lr=1e-4, momentum=0.9) # 继续训练,使用余弦退火学习率调度与早停

有几个细节值得注意:类别权重在损失函数初始化时传入,这比在采样器上调整更直接;两个阶段使用不同的学习率,是因为第一阶段的分类层是随机初始化的,它需要稍大一点的学习率才能快速收敛,而深层网络已经具备较好的特征表达能力,只需要极小学习率微调。这些细节很难从论文里读到,是我实际跑了很多次实验才发现的规律。

4. 评估体系、效果复盘与常见问题排查

4.1 混淆矩阵才是真正的照妖镜:不要只看一个准确率

很多参赛队伍在报告里只写一句“最终准确率93%”,然后就结束了,这个说服力是很弱的。准确率是一个整体指标,它掩盖了模型在不同类别上的表现差异。比如模型可能把细粒度的两类岩石认错了一大半,但因为这两类在整体数据集中占比很小,准确率数字不明显下滑。你要想在竞赛中拿高分,就要把评估做细。

我们着重分析了混淆矩阵,发现两个主要问题:一是粉砂岩和泥岩之间存在大量互相误判,二是部分样本被识别为背景类。针对这两个问题,我们做了两方面改进。对第一类问题,我们把模型最深层的注意力可视化出来,发现模型在处理这两类时关注的区域经常落在岩石图像中的裂隙或者阴影区域,而不是矿物颗粒的纹理特征。于是我们专门调整了对光照扰动和局部裁剪的增强策略,让模型学会聚焦岩石表面。对第二类问题,我们加强了背景区域的裁剪预处理,确保训练时模型主要集中在岩石主体区域。

最后我们的评估报告中不只是给出一张准确率表格,还附上了每个类别的精确率、召回率和F1分数曲线,以及几个典型正确和错误样本的可视化解释。这种多维度的评估结果,在答辩时才是真正能打动评委的内容。

4.2 提升精度的两个关键技巧:注意力可视化和困难样本挖掘

在模型训练到接近90%准确率的时候,常规的调参手段提升已经非常有限,我是靠两个技巧实现进一步突破的。

第一个技巧是注意力可视化。我使用梯度加权类激活映射(Grad-CAM)来查看模型在预测一张岩石图像时究竟在“看”哪些区域。有一次我发现模型判断一块花岗岩时,注意力几乎全部集中在图像右上角的一块深色裂缝上,而花岗岩典型的粗粒结构和长石晶体反而没有获得足够的关注。这说明模型学到的是一个“岔路特征”。解决办法并不复杂:在数据增强阶段对岩石图像做随机局部遮挡,强迫模型去关注整体的纹理结构,而不是某个局部强特征。实验之后,易混淆类别的准确率提升了约3个百分点。

第二个技巧是困难样本挖掘。训练过程中我记录下每个批次里损失值最大的样本,这些就是模型最容易出错的困难样本。在下一轮训练时我适当提高这些样本的采样权重。这相当于给模型布置“针对性的错题本”,让它反复练习那些最难的样本。需要注意的是,困难样本挖掘尤其要搭配早停机制使用,否则模型会在这些特例上过拟合。

4.3 实战问题排查:训练和推理阶段的高频Bug与解法

我把这次比赛中遇到的最具代表性的问题和解决方案整理成了速查表,如果你在复现这个项目时也遇到类似现象,可以对照排查:

问题现象可能原因解决思路
训练损失持续不下降学习率设置过大或过小先做一次学习率扫描,找到损失下降最快的区间
验证集准确率高但测试集明显偏低数据泄露或过拟合检查训练集与测试集是否有重复/近似样本;增加正则化
模型对所有样本都预测为同一类类别极不均衡或分类层未正确初始化检查类别权重;重置分类层为较小方差初始值
图像出现明显失真、边界伪影缩放方式不当改用等比缩放配合中心裁剪,保留图像主体比例
显存溢出导致训练中断批大小或输入尺寸过大降低批大小、启用梯度累积,或减少输入分辨率
不同类别收敛速度差异极大优化器更新不均或数据增强强度不一致采用类别权重采样,让每个batch类别分布更均衡
使用预训练权重时模型效果反而差微调学习率过大,权重被破坏拷贝权重后先做逐层参数冻结,再小学习率微调

这里特别想强调数据泄露问题的重要性,它在竞赛中是判定作弊嫌疑的红线。我见过有队伍在数据预处理时误把验证集图像通过数据增强混入了训练集,结果训练损失极低、验证集准确率高达98%,一到现场答辩用真实场景图像测试就崩盘。这个教训提示我们,一切数据处理步骤都应该在划分训练集和验证集之后进行,数据增强的参数应该只从训练集统计得到。把这一条原则刻在脑子里,能帮你避开最尴尬的技术事故。

5. 从竞赛到落地的思考与参赛心得

5.1 这套方案为什么能评优:评委视角的三个加分点

比赛结束后我在复盘时认真想过,这套方案能在众多参赛队伍中脱颖而出的原因,可能不只是最终那个准确率数字。

第一个加分点是完整的技术闭环。我们不是只给了一个模型文件和一份预测结果,而是从数据清洗、增强、模型设计、超参数实验到评估可视化的全流程都有记录。每个环节都有“为什么这么做”的说明,评委能清楚地看到我们的思考过程。

第二个加分点是问题导向而非方法导向。有些队伍喜欢堆砌复杂模型,比如深度可分离卷积、坐标注意力、各种新颖模块,但很难说清它们到底解决了岩石识别中的什么问题。我们在答辩中始终围绕“岩石图像的四大干扰源”来展开,所有的技术选择都直接指向某一个具体问题,这种叙事逻辑更有说服力。

第三个加分点是结果的可解释性。深度学习模型常常被人诟病为“黑盒”,我们通过注意力可视化让评委直观看到模型关注的区域确实是岩石的矿物纹理和结构特征,而不是背景或者阴影。这种可视化的结果在答辩中用一张图就能说明问题。

5.2 岩石智能识别的落地场景:竞赛之外的价值

泰迪杯是一项竞赛,但赛题内容通常都来源于真实行业需求。岩石样本智能识别在地质勘探领域有着非常实际的应用场景。

勘探工作中,地质工作者每天要面对大量岩心样本,传统人工鉴定依赖个人经验,效率低且一致性差。一个是经验丰富的地质工程师可以准确识别上百种岩石类型,但培养这样一名专家需要多年野外积累,而且不同专家的判断标准可能存在差异。这类识别模型若能落地为辅助工具,可以在现场快速给出初步分类建议,帮助地质人员缩小范围、提高决策效率。另一个落地点是矿石分选。在矿山生产线上,通过视觉识别快速区分矿石和废石,已经有不少企业在尝试,深度学习模型的实时推理能力完全能够支撑产线上的高速决策。此外,在地质教学和科普领域,这类识别工具也能帮助初学者快速入门岩石分类。

当然,从竞赛方案到真实落地,中间还有很长的路要走。真实场景中存在更多未见的岩石类别、更复杂的光照环境、更严重的图像退化,模型的鲁棒性和泛化能力需要经过更充分的实地验证。但这正是这项技术的价值所在,它给传统地质工作提供了一个全新的智能化切入角度。

5.3 给下一届参赛队伍的一些实操经验:少走弯路的五个建议

如果你明年也要参加类似的数据挖掘或视觉类竞赛,我愿意把这次最深的几点体会分享给你。

第一,题目拿到手先别急着跑模型。花半天时间认真看数据,统计各类别数量、图像尺寸、标签分布,把数据“看透”再动手,后面会省很多返工的时间。

第二,从最简版本开始。先用一个最简单的网络和最低辨率的输入图像把整个数据加载、训练、验证链路跑通,再逐步加大模型规模和输入尺寸。很多队伍一上来就上超大模型,结果在数据管道上浪费了大量时间。

第三,一切实验都要有记录。我用一个表格记录每次实验的配置和结果,包括数据增强参数、学习率、批大小、模型结构、准确率、训练时间等。有了这些记录,调参就不是凭感觉,而是可以回溯和对比的。

第四,评估指标不要只盯准确率。把混淆矩阵、逐类别指标、可视化结果都做出来,这些在答辩时的价值远高于一个漂亮但空洞的数字。

第五,代码和文档同步管理。提交时除了代码,一定要有清晰的说明文档,把你的思路、实验过程、结果分析都逻辑清楚地写出来。竞赛评审的是整个解决方案,而不只是一段能跑的程序。

最后说一点我自己的真实感受。比赛前我认为“智能识别”最难的部分是模型的精度,但走过完整参赛流程后我发现,最难的是把一个问题从数据到算法到评估体系地完整想清楚,并且每一步都经得起追问。这种能力的锻炼,比最终的名次更有价值。希望这篇复盘能给你一些启发,如果你在复现过程中遇到问题,欢迎按着文章里的排查思路试一试,很多坑是可以提前避开的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进