最近在做一个图片检索的小项目,把图片用ResNet提取特征向量,存进了Milvus。测试的时候发现,明明两张图内容很相似(比如都是猫但颜色不同),返回的相似度排名却很低,反而一些完全不相关的图排前面。我用的是L2距离,索引类型是IVF_FLAT,nlist设了1024。是特征向量没归一化的问题吗?还是索引参数调得不对?或者Milvus本身就不太适合这种细粒度相似度?求有经验的大佬指点一下,项目快卡住了,头大。
用向量数据库做图片相似度搜索,为什么返回的结果总是不太对?
全部回复
共 144 条说实话我觉得你这个问题大概率出在特征向量本身,而不是Milvus的锅。ResNet提特征的时候,最后几层的输出分布差异很大,尤其是没做L2归一化的话,向量模长会直接影响L2距离的计算,结果就是模长小的向量天然容易被判成“相似”,这跟你图片内容有啥关系?你先试试把特征向量做一下L2归一化,然后距离度量改成内积(IP),很多检索场景下这个组合比纯L2稳得多。
另外IVF_FLAT这个索引,nlist设1024对于小项目来说可能太粗了,你想想,nlist越大,聚类中心越多,查询时搜的桶越少,召回率就会下降,尤其是你这种细粒度相似度任务,稍不注意就把真正相似的图片过滤掉了。建议先调小nlist,比如256或者128,再把nprobe调大,先保证召回率,别急着追求速度。
还有个容易被忽略的点,你有没有检查过Milvus里插入的数据类型和维度对不对?ResNet输出的维度一般是2048或者512,如果建集合时写错了维度,后面算距离全是乱套的。另外,图片内容相似但颜色不同,这种任务本身对特征提取模型的要求就高,ResNet这种分类预训练模型提取的特征更偏语义类别,不一定能捕捉到颜色、纹理这种细节差异,你可以试试用CLIP或者专门做图像表征的模型,比如DINOv2,效果可能会好很多。
最后想问你一下,你测试的时候用的是query图本身去搜库里同一张图吗?如果连自己都排不到第一,那肯定是特征或索引的问题;如果自己没问题,只是相似图排得靠后,那就要考虑是不是特征表达能力不够了。建议你先用暴力搜索(FLAT索引)跑一遍同样的查询,如果暴力搜索结果也不理想,那就跟Milvus完全没关系,纯粹是特征工程的问题。
说实话你这个情况我太熟了,之前用ResNet50跑商品图检索也踩过一模一样的坑。你提到特征没归一化,这基本就是首要嫌疑,L2距离下向量模长差异会直接淹没方向上的相似性,猫和狗的特征模长可能都差好几倍,自然排序乱掉。建议先试下把特征做L2归一化再入库,很多情况下这个改动就能让结果正常不少。另外IVF_FLAT这个索引本身对细粒度检索不太友好,nlist设1024意味着聚类中心比较粗,查询时会漏掉不少真正近邻的向量,可以试试调小nlist比如256,或者直接用暴力检索跑一遍对比下,排除索引带来的误差。还有个容易忽略的点,Milvus里相似度计算和返回的分数含义要确认清楚,L2返回的是距离不是相似度,距离越小才越相关,如果你排序逻辑写反了也会出现“不相关排前面”的假象。至于细粒度问题,ResNet的最后一层特征其实更偏语义分类,颜色差异大的猫可能确实在特征空间里离得远,可以考虑用倒数第二层或者换用CLIP这类对比学习模型,对颜色变化会更鲁棒。先改归一化和索引参数,大概率能解决你现在的尴尬,别急着换库。
别急着换库,大概率是特征没归一化的问题。ResNet提的特征直接算L2距离,不同图片的向量模长差异会主导距离,导致颜色比内容影响更大。你先试下把所有向量L2归一化,再用余弦距离或者内积,效果应该会立刻改善。另外IVF_FLAT的nlist对召回影响没那么大,主要看nprobe,查询时设个50-100试试。我之前也踩过这个坑,归一化之后结果正常多了。
大概率是没归一化的问题,ResNet提的特征直接算L2距离,向量模长影响太大了,颜色深的猫特征范数就大,容易把不相关的图也拉近。你先试试把所有向量L2归一化,再用余弦相似度或者归一化后的L2,效果应该会立竿见影。另外IVF_FLAT的nlist=1024对召回率影响不算大,但nprobe查的时候要调高一点,比如设个128,不然粗聚类就把候选集限制死了。Milvus做这个没问题,主要是特征处理和检索参数要配套。