ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

细粒度图像检索:从特征学习到复现避坑指南

细粒度图像检索:从特征学习到复现避坑指南 最近把桌面上的论文文件夹重新翻了一遍挑出细粒度图像检索这条线准备系统整理成系列阅读笔记。以前读论文经常犯一个毛病就是从头到尾把方法啃完才发现自己连题目里的问题都没完全想清楚这次换了个方式先梳理问题定义再串方法脉络最后落到复现细节上发现效率高了很多。这篇算是笔记的第一篇把关于细粒度图像检索的阅读记录和踩坑经验一起写出来给同样在研究这条线的朋友做个参考。细粒度图像检索Fine-Grained Image Retrieval要解决的核心问题通俗说就是在大类别之下把极其相似的子类别或个体区分开并基于这些细微差异完成检索。比如鸟的种类、汽车的品牌年款、植物的品种、飞机的型号甚至某个具体的人或某个独一无二的商品。它和普通图像检索最大的不同在于难度来源不是“猫和狗差异多大”这种一眼可见的区分而是“同一类物种里两个亚种之间只差羽毛边缘那一小圈颜色”这种级别的区分。做研究和做工程的人看到这类问题都会头大因为网络稍微一深过拟合就跟着来数据标注又贵特征稍微粗糙一点检索回来的结果就完全不可用。这篇笔记适合三类人看刚接触细粒度检索、想知道这个方向在解决什么问题的人准备复现相关论文、但不清楚实验细节有哪些坑的研究生以及做图像检索工程时发现通用模型在细分品类上表现不佳、想找解决思路的算法工程师。下面从问题定义、方法脉络、评价指标、复现心得四个部分展开内容都是我在读论文和实际跑实验过程中的原始记录。1. 细粒度图像检索到底在研究什么——动手读论文前先想清楚的事1.1 细粒度检索和普通检索的差别在哪普通图像检索解决的是“找相似”细粒度检索解决的是“找同一个或找同一小类”。同样是给定一张查询图普通检索关心的是库里有没有同类别的东西比如你拍了一张柯基库里的柯基都能算命中细粒度检索往往要求更严格比如查的是“这只带白色围脖的柯基的具体个体”或者“某个特定款式的运动鞋在电商库里的所有图”。这种从“类”到“个体”的跨度直接决定了模型要关注的信息层级。我自己的理解里细粒度检索和普通检索的本质差别可以浓缩成两点。第一是判别性信息非常局部可能只占整张图片的百分之几第二是类内差异往往大于类间差异。就拿鸟来说同一个物种在不同姿态、不同光照背景下拍出来的差异可能比两个不同物种之间的差异还大。这就导致模型很容易抓住背景、姿态这些“捷径”特征来偷懒而不是真正学会区分细微的外观差异。还有一个很多人忽略的点是细粒度检索的评价对象。分类任务通常只要求预测正确类别检索任务却要求特征空间里有稳定的排序关系同ID的图片要在距离上比同类别不同ID的图片更近。这个约束听起来只是多了一个层次实际操作起来难度翻倍因为同一个ID意味着可能是同一个物体在不同角度的照片光照、遮挡、视角变化全部叠加在一起。读论文时如果不先把这个目标想清楚后面看方法设计就会觉得很乱。1.2 细粒度检索的典型应用场景细粒度检索最经典的落地场景之一是生物多样性监测。生态学家拍到的照片往往需要鉴定到物种甚至亚种人工鉴定费时费力计算机辅助鉴定系统就很有价值。这里检索比分类更实用因为新物种或罕见亚种单独建类很难但只要有已知个体库就能通过检索让专家在候选结果里做二次确认。电商领域是另一个大场景。用户上传一张商品图系统要在几十万甚至上亿的商品库里找出同款这里的“同款”就完全是细粒度级别的。服装的版型、袖口设计、面料纹理鞋子的鞋帮高度、鞋底纹路、拼接配色任何一个细节不同都可能指向完全不同的商品。我在实际项目中就遇到过类似问题通用检索模型在“连衣裙”类别下返回的结果五花八门款式细节根本对不上后来切到细粒度思路才把准确率拉起来。零售行业的货架管理和自动结算也用得上细粒度检索。同一个品牌下的不同口味薯片包装外观差异可能只是包装袋角落的一小片颜色这在普通分类模型里几乎是不可分的信息但结合细粒度特征和检索排序就能在复杂货架场景中稳定识别。除此之外医学图像里的病变亚型检索、遥感图像里的建筑物细分类检索本质上也都是细粒度问题只是数据模态不同。1.3 绕不开的经典数据集读细粒度检索论文一定会遇到几个高频数据集。无论方法怎么更新大家都在这几个基准上对比理解它们的特点非常关键。数据集内容类别数图像数主要难点CUB-200-2011鸟类200约11788细粒度差异极小、姿态丰富Stanford Cars汽车196约16185车型年份区分、拍摄角度变化大Oxford Flowers花卉102约8189颜色形状相似、背景干扰FGVC-Aircraft飞机100约10000机型系列差异细微iNaturalist自然物种5000数十万类别极多、长尾分布严重CUB-200-2011是最常出现的鸟类数据集几乎所有细粒度方法的论文都会在它上面报数。Stanford Cars和FGVC-Aircraft也是经典选择它们比鸟类数据更“刚性”因为车的型号和飞机型号是严格离散的不像鸟类在不同姿态下外观变化那么大。iNaturalist则是后来兴起的更大规模细粒度数据集类别上千长尾现象明显更贴近真实场景。需要注意一点细粒度检索论文里用的数据集划分方式五花八门有的按类别划分训练集和测试集有的按个体划分还有的干脆用无监督的方式不划分。这个细节直接决定结果能不能复现后面专门讲。2. 细粒度检索方法脉络——从全局一刀切到局部精雕细琢读了一圈论文我发现细粒度检索的方法演进其实有一条相对清晰的脉络大致可以分成三个阶段早期靠手工局部特征和双线性特征中期靠注意力机制让网络自己找关键区域近期则被代理学习和预训练模型主导。三个阶段不是完全替代关系很多经典技巧到今天仍然有效。2.1 早期思路双线性池化与局部特征对齐细粒度图像检索最早期的代表性方法里双线性池化是一类绕不开的工作。这类方法的核心想法很简单粗暴用两个特征提取器分别处理同一张图然后把两者的特征做外积得到二阶统计信息。为什么要这么做因为一阶特征也就是常见的平均池化特征在细粒度问题上丢失了太多局部关联信息而二阶统计可以捕捉特征通道之间的共现关系。你可以把它理解成看一个人的时候不仅看眼睛和嘴巴各自长什么样还要看眼睛和嘴巴之间的相对位置关系很多亚种的鉴别点恰恰就藏在这种关系中。双线性池化的缺点是特征维度爆炸。假设两个网络都输出512维特征外积之后直接得到512×512262144维向量计算和存储开销都非常大。后续有不少论文在这上面做压缩比如用低秩近似、用Compact Bilinear Pooling当时的实验效果确实有明显提升也为后来的细粒度方法奠定了“局部特征特征交互”的基本范式。早期另一类思路是显式的局部特征对齐。比如先从图像里检测出语义部件鸟的头部、翅膀、尾部再分别提取每个部件的特征最后拼接或者聚合。这类方法直观因为细粒度鉴别几乎都依赖局部区域但它有一个致命问题需要额外的部件标注。CUB数据集确实提供了部件标注但真实场景里不可能手工标这么多框所以这类方法在科研基准上效果不错工程落地很难推广。2.2 中期主流注意力机制与判别性区域发现后来注意力机制兴起细粒度检索一下子找到了更好的工具。注意力方法的思路是让网络自己在训练过程中找出哪些区域对判别最有用而不需要显式的部件标注。这里的思想类似于我们平时看两张非常相似的图时会不自觉来回对比局部区域注意力就是把这个过程参数化到网络里。最具代表性的工作之一是SENet提出的通道注意力虽然它最早是给图像分类设计的但用在细粒度检索上也很有帮助。通道注意力让网络知道哪些特征通道更重要本质上是给特征做了一轮加权筛选一举把计算量控制住的同事也提升了判别性。后续的双线性注意力、非局部注意力等方法又进一步把注意力从通道扩展到空间维度让网络能够动态聚焦到最具判别性的局部区域。不过注意力机制在细粒度检索里有个隐藏问题网络可能聚焦到背景、姿态等非判别性区域。尤其是训练数据有偏的时候注意力图会变得很迷惑。所以很多论文都加入了注意力多样性的正则化强迫网络关注多个不同的区域而不是每次都盯着一块地方。我复现相关论文的感受是这部分正则项对最终检索指标的影响比想象中的大有些人把它当成无关痛痒的辅助损失实际上它才是防止退化的关键。2.3 近期趋势代理学习、对比学习与预训练模型的冲击最近两三年细粒度检索的主流思路被代理学习和对比学习深刻影响。代理学习的核心做法是每一类学一个可训练的代理向量训练时让样本特征靠近自己类别的代理、远离其他类别的代理。这个做法的好处是训练收敛快、显存占用相对小而且对类别多的大规模数据集效果更友好。细粒度检索里用代理学习相当于在特征空间里为每一个品种甚至每一个个体都建立了一个语义锚点排序时再根据距离来检索整个流程非常自然。对比学习对细粒度检索的贡献也是不可忽视的。尤其是自监督对比学习它让模型可以在无需大量细粒度标注的情况下先学到数据本身的丰富表征。最近有些工作直接把对比学习框架改造到细粒度检索上利用同ID的不同增强视角作为正样本对让网络学会“同一个体不同视角不变”的特征这正好踩中了细粒度检索“类内差异大于类间差异”的痛点。不过我在实践中发现纯自监督细粒度检索的指标通常还是赶不上有监督的方法更实际的用法是把它当作预训练阶段。预训练模型这块近年来大家喜欢用大规模数据上预训练过的ViT或CLIP模型做初始化再在细粒度数据集上微调。CLIP这类模型对自然图像有很强的通用理解能力但直接拿来做细粒度检索效果并不理想。原因在于CLIP的训练目标是图文对齐它的特征空间更适合描述“这是什么”而不擅长描述“这是同一只鸟里的哪一只”。直接微调CLIP时需要配合代理学习或度量学习损失把特征空间重新拉回细粒度判别方向我自己测试下来微调后再在CUB上做检索比直接用CLIP特征提升非常显著。这些方法脉络看起来是线性演进的但实际使用时它们更像是工具箱里不同类型的工具可以互相组合。读论文时不必执着于“用最新方法替换旧方法”我见过很多工作把双线性池化的思路和注意力、代理学习结合起来用效果反而比单一新方法更稳。3. 论文里的评价指标与实验设计的门道——复现前必须看懂的部分3.1 召回率RecallK到底怎么算细粒度图像检索论文里最常出现的评价指标是RecallK有的论文也写成Top-K Accuracy。它的计算逻辑是对每一个查询图模型从检索库里返回相似度最高的K张图如果这K张图里包含至少一张和查询图属于同一个ID或同一个细粒度类别的图就算一次命中。所有查询的命中次数除以查询总数就是RecallK。这里有一个很关键的细节很多论文把“同ID”当成命中标准而不仅仅要求“同类别”。比如CUB数据集里同一张鸟的图片可能有多个视角但它们属于同一个个体。检索的目标是找出这些同一视角或同一拍摄个体的图片。这个标准比同类别严格得多同类别命中的话只要返回任意一只同种鸟都行但同ID要求返回特定那一只鸟。所以读论文时一定要先看清楚了作者是在哪个匹配粒度上报的数否则两个模型的指标之间根本没有可比性。计算RecallK时还有一个容易踩坑的点查询图本身如果也在检索库里那它必然排在第一位直接命中导致指标虚高。所以严谨的实验中通常会把查询图自己从库里剔除掉或者用“查询集”和“检索库”分离的设置。有些论文没有写清楚这一点我刚开始复现时就被这种虚高的指标误导过后来仔细看代码才发现评测时已经做了排除。不同的K值反映模型不同的能力。K1最严格要求最相似的候选图必须就是目标这考验的是特征空间的最近邻质量K5、K10则相对宽容给了一定的容错空间。实际搜索引擎里用户通常只看第一页结果所以K20左右的Recall往往更贴近真实体验但论文里最常报的是Recall1和Recall2这两个数也是最难刷的。3.2 数据集划分方式对结果的影响超出想象细粒度检索论文里数据划分是一个容易被忽略但影响巨大的实验设计细节。同一个数据集训练集和测试集如果划分方式不同最后的指标可能差好几个点甚至十几个点。我最开始复现时没注意这个问题怎么跑都跟论文对不上后来发现是数据集划分版本的问题。CUB等经典数据集的标准划分方式是训练集和测试集按类别划分也就是说训练时模型见过的类别集合和测试时的类别集合完全不相交。这种“零样本类别泛化”的设定更贴近真实应用因为现实里新出现的鸟类品种不可能都在训练集里出现过。但也有一些检索论文用的是“按个体划分”的方式训练集可能已经包含了测试集个体的其他视角照片这相当于模型在测试时面对的是“见过的类别里没见过的个体”难度比零样本类别泛化低很多。前者称为跨类别泛化后者叫做同类别跨个体检索。很多论文为了报出高指标会倾向于用后者因为模型只需要学会特征提取不需要泛化到新类别。但真实业务场景里新类别总是源源不断出现的这时前者才更有参考价值。读论文时我会先翻实验部分看数据划分如果论文没有写清楚就直接去代码仓库里找数据加载的逻辑这是判断一篇论文可复现性的第一步。数据集的图像预处理方式也会影响结果。CUB默认的输入分辨率是224×224但鸟类这种细粒度问题上更高分辨率往往能带来明显提升。有的论文用448×448甚至更高分辨率训练指标自然更漂亮但代价是计算量大幅上升。研究新方法时我会先固定输入分辨率方便和其他论文对比工程落地时再根据算力情况选合适分辨率。3.3 复现论文时常被忽略的隐藏设置除了数据划分还有几个实验设置的细节复现时一旦忽略了结果就会与论文相差甚远。第一个是特征归一化。大部分细粒度检索方法在提取特征后会做L2归一化再进行相似度计算。这个操作把特征向量统一映射到单位超球面上距离度量变得更稳定而且不同特征的尺度差异不会影响排序。如果忘了加这个归一化检索结果会有明显下降我在复现时吃过这个亏。第二个是测试时的数据增强。有的论文在评测时会使用水平翻转增强即对查询图和库图同时做原图翻转特征的平均/拼接这样得到的特征更鲁棒指标也会提升。但如果论文里没有明确提到测试增强你加上了就是在“作弊式”地提高指标导致和其他论文不可比。判断方法同样藏在代码里看eval阶段的transform和训练阶段是否一致。第三个是backbone的选择。有的方法论文里用的backbone是ResNet50有的用ViT-B甚至有的直接用ImageNet预训练的Swin Transformer。不同backbone的base能力差异已经很大方法本身的增益反而可能被淹没。对比论文时我会优先看同backbone下的指标差距再看跨backbone的对比避免被绝对数值误导。另外在细粒度检索里一般不建议在特征提取后直接接一个大FC做分类再取倒数第二层特征这种做法很容易让网络的表征退化成只对训练类别有效。更符合检索问题的做法是用度量学习损失比如Triplet Loss、Proxy Anchor Loss直接优化特征空间让同类样本靠近、异类样本远离。复现时如果发现论文里既用了分类损失又用了度量损失需要特别关注两者的权重配比这个配比对最终检索效果的影响非常敏感。4. 实操心得从读论文到跑通实验的完整记录4.1 环境配置与数据准备的常见做法我自己复现细粒度检索实验时环境配置相对固定PyTorch 1.x或2.x配合CUDA和一块显存足够的显卡。CUB这种规模的数据集ResNet50的batch size设为64基本就能跑得动如果换成ViT-B或者用更大的分辨率显存压力会快速上升需要把batch size降到32甚至16。数据处理时有几个细节值得注意。CUB数据集的原始图片中很多图片主体比较小直接缩放到224×224会把鸟缩成很小一块。标准做法是先用标注的bounding box把鸟类主体裁剪出来再缩放和增强。这个预处理在论文中经常被写得含糊其辞只提一句“using provided bounding box”。我第一次复现时忽略了这一步直接用原始图做训练结果指标掉了不少后来才发现原来大家都在用bbox裁剪后的数据。训练时我习惯用SGD优化器momentum设为0.9weight decay设为5e-4初始学习率从0.01开始并配合warmup和余弦退火。如果换成AdamW通常需要把学习率调小到1e-4左右。细粒度数据量相对小过拟合风险高所以数据增强比通用检索更激进随机裁剪、水平翻转、颜色抖动都用上MixUp或CutMix也能带来一定提升只是需要仔细调权重。4.2 我整理的复现trick清单把复现实验过程中反复验证过的trick整理成了一份清单这些点虽然很细节但对最终指标的影响往往比换一个新loss更大。特征归一化是强制项。在提取特征后做L2归一化再用余弦相似度排序是细粒度检索的标配。评测时一定要排除查询图本身。哪怕库里只有一张相同的图如果不排除Recall1直接多一个点。Batch内采样策略要谨慎。随机采样时每个batch里同一个ID大概率只有一张图Triplet Loss根本找不到有效的anchor-positive对。推荐用PK采样器每个ID采样P张图一个batch包含K个ID这样能保证有足够多的正样本对。Margin参数要按特征空间的实际分布调节。Triplet Loss的margin如果设得太大训练很难收敛设为0.10.3比较常见Proxy Anchor类的loss也有自己的默认参数不要上来就乱改。特征维度不是越高越好。很多方法默认输出2048维甚至更高但高维特征在排序时不一定更好甚至容易过拟合。我自己实验里512维特征在CUB上效果与2048维接近但存储和检索速度都快很多。冻结backbone前几层可以防止灾难性遗忘。如果模型是加载ImageNet预训练的训练初期把前几层冻结只训练后面的层然后再解冻做全量微调收敛更快最终指标也更稳。这些trick不是每篇论文里都会写明的属于“代码里不会告诉你、但跑实验时非常关键”的那类经验。我刚开始复现时几乎每一步都踩过坑把坑填完才发现很多论文的指标提升来源很大一部分就藏在这些实验细节里方法本身反而只贡献了一小半增益。4.3 判断一篇细粒度检索论文值不值得精读读论文这件事本身也是有技巧的。细粒度检索方向的论文每年产出很多不可能每篇都精读。我自己的筛选流程大致是先看题目和摘要是否解决了我当前关心的问题。如果问题是“如何提升跨类别泛化的检索指标”那我就只关注提出新损失函数或者新特征聚合策略的论文而把偏应用、偏分类的论文先放下。再看有没有开源代码。没有开源代码的论文复现成本极高除非里面的idea非常新颖否则我往往会往后放一放。然后跳着看实验部分重点看数据集、划分方式、backbone设置以及是否和我要对比的方法在同一设置下。实验部分还要多留意一个细节作者是否在多个数据集上都验证了方法。如果只在CUB上有提升在其他数据集上反而掉点这类方法很可能过拟合了单一数据集泛化能力存疑。真正可信的方法通常会在CUB、Cars、Aircraft三个数据集上都能稳定提升。读完一篇值得精读的论文后我习惯用三句话做笔记第一句它解决了什么问题第二句它的核心方案是什么第三句它的适用条件和限制是什么。这个习惯帮我后来翻笔记检索的时候省了很多时间。这次整理细粒度图像检索的论文阅读笔记也是沿用了这个习惯只不过把三句话扩展成了完整的实验记录和踩坑记录。从双线性池化的年代到现在代理学习、预训练模型大行其道细粒度检索的发展脉络清晰地说明了同一个道理决定检索上限的从来不只是backbone的堆砌而是特征空间中如何刻画那些细微但决定性的差异。我自己的体会是读这类论文时不能只看方法的数学形式更要深入实验设计里把数据划分、评价指标、训练细节每一层吃透。有时候几个点在代码里悄悄改一下复现结果就会出现天壤之别。按这个思路整理笔记比单纯追求刷通所有论文要高效得多这套方法也推荐给正在这条路线上摸索的朋友。
返回列表