为了让余弦相似度计算简单,在服务端对每个向量做了 L2 归一化,结果检索返回的相似度全是 1.0,排序完全随机,用的是 numpy 1.26。

把日志和配置对完,能解释这个现象的基本就这几处。归一化写成了对整个矩阵求范数,所有向量被压成同一个方向;浮点下溢把分母算成 0,除零后得到 NaN 再被当作相等;客户端又归一化了一次,重复操作掩盖了问题;相似度用点积计算,归一化后等于纯余弦,缺少尺度区分。

先抽查几条向量的范数

python -c "import numpy as np;v=np.load('v.npy');print(np.linalg.norm(v,axis=1)[:5])"

接着改成按行归一化

python -c "import numpy as np;v=np.load('v.npy');v=v/np.linalg.norm(v,axis=1,keepdims=True);print(np.linalg.norm(v,axis=1)[:5])"

再往下检查是否出现 NaN

python -c "import numpy as np;v=np.load('v.npy');print(np.isnan(v).sum())"

剩下的就是用点积重算一次排序做对照

python -c "import numpy as np;q=np.load('q.npy');v=np.load('v.npy');print(np.argsort(-(v@q))[:5])"

改完先别撤,确认这几项。向量范数都接近 1 且没有 NaN,检索返回的分数呈梯度分布,排序与人工判断一致。

归一化本身没错,错在轴的选取,矩阵级范数和行级范数差一个参数,结果却是天壤之别。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。

作者 9823189