最近在做一个基于RAG的私有知识库问答,之前用Milvus搭的demo效果还行,但为了省运维成本,把向量存储换到了PostgreSQL的pgvector扩展。同样用bge-large-zh的768维向量,数据量大概20万条,距离函数用的L2,建了IVFFlat索引。结果换过去之后,top-5召回的相关性明显变差,特别是长尾问题,感觉像是索引聚类没建好。我按默认的lists=100建的索引,probes也设了10,难道是数据分布的问题?还是说pgvector的IVFFlat对高维向量支持不太好,必须换HNSW?有没有大佬踩过类似的坑,求指点一下调参方向。
RAG项目从Milvus换到pgvector后,召回效果崩了,是我参数没调对吗?
全部回复
共 44 条20万数据用lists=100太小了,IVFFlat得按sqrt(n)来建,你试试lists调到450左右,probes也拉到20以上看看。
20万条数据用lists=100有点太粗了,按官方建议大概sqrt(n)来算,200左右才比较合适,聚类太粗确实会漏召回。另外L2在归一化向量上其实和cosine差不多,但bge官方推荐的是cosine,你可以先确认下向量有没有归一化。还有probes=10偏小,试试调到20-30,能明显改善长尾。真要说的话,IVFFlat在高维上确实不如HNSW稳,但你这个量级调好参数应该能救回来。
20万条数据IVFFlat默认lists=100其实偏少了,一般建议lists设成sqrt(n)到n/1000之间,你这种情况可以试试lists=400左右,probes也拉到20-30看看效果。另外L2和bge模型其实不太搭,bge系列一般推荐用cosine距离,归一化之后再算L2也行但别忘了这步。pgvector的IVFFlat在高维上确实不如HNSW稳,尤其长尾query容易掉到错的聚类里,如果调参还救不回来就直接上HNSW吧,建索引慢点但召回稳很多。
你这情况我第一反应就是lists设太少了,20万条数据用lists=100,平均每个簇2000个向量,对高维语义空间来说簇内区分度直接稀释掉了,长尾问题基本就是被大簇吞了。pgvector官方建议是rows/1000以下、sqrt(rows)以上,你这量级可以试试lists=400到800,probes也跟着提到20-50跑个召回对比。另外你用L2配bge有点亏,bge系列是归一化过的,余弦和内积才是它训练时的度量,换成vector_cosine_ops或者vector_ip_ops再建索引,效果可能比调参还明显。IVFFlat本身是近似索引加聚类,跟Milvus那种默认更偏HNSW的召回曲线本来就不一样,不是pgvector不支持高维,是它这套组合对参数更敏感。真嫌调参麻烦就直接上HNSW,pgvector 0.5以后支持了,m和ef_construction给16和64起步,召回稳定性比IVFFlat好很多,代价就是建索引慢、内存吃得多。还有个容易忽略的点,换库之后filter条件、top-k截断逻辑、甚至embedding入库时的归一化有没有保持一致,这些对长尾的影响有时候比索引参数还大。