问题出现得很突然,日志倒是留得挺全。用 IndexFlatIP 建了十万条向量,检索出来的相似度超过 1,排序结果和人工判断差得很远,换成 L2 索引反而正常。
刚开始盯着「建索引与查询用的归一化方式不一致」,折腾了半个多小时,排掉了。
排查方向其实有限,先把可能性排一排。内积计算的是向量乘积,没有做长度归一化。另一种情况是不同文本的向量模长差别很大,长文本天然占优。
少数情况下是模型输出未归一化,却被当成余弦相似度使用。
建库前先把向量归一化
faiss.normalize_L2(vectors)
再往下查询向量做同样处理
faiss.normalize_L2(q); D, I = index.search(q, 5)
然后检查相似度是否落在零到一之间
python -c "import numpy as np;print(np.linalg.norm(vectors[0]))"
剩下的就是改用余弦相似度的等价写法对比结果
index = faiss.IndexFlatIP(dim)
改完先别撤,确认这几项。相似度回落到零到一之间,排序结果与人工标注的前几条基本一致,长文本不再无脑排前面。
容易忽略的是,归一化必须建库和查询两边都做,只做一边时结果不会报错,只会安静地变差,这类问题很难靠肉眼发现。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。