先记住这个答案
余弦相似度是无量纲的夹角度量,对向量缩放不敏感。因此如果计算时显式除以两边长度,只归不归并不改变结果。但许多系统为了用内积近似余弦,会预先归一化所有向量。若只归一化查询而文档保持原始长度,点积结果等于||d||·cosθ,排序偏向范数大的文档,与小夹角但短文档的正确排序可能相反。要恢复余弦排序,必须同时归一化文档向量,或计算时补上文档范数除法。
- 余弦公式保留文档范数时结果不变
- 点积近似必须两侧都归一化
- 未归一化文档时排序偏向长向量
余弦与点积在长度处理上的本质差异
余弦相似度衡量两个向量的方向一致性,结果与模长无关。给定向量u和v,若将v整体放大或缩小,cos = (u·v)/(||u||·||v||)中的分子分母同步缩放,比值不变。因此“只归一化查询”对余弦公式本身没有任何数值影响,真正影响的是使用者如何用这段向量做后续计算。
若系统把q_hat·d当作相似度,即用点积近似余弦,那么q_hat = q/||q||后,该值等于||d||cosθ。此时文档模长作为乘数保留在每个得分里,长文档总是更容易获得高分。即使两个文档与查询夹角相同,范数大的会排在前面,这和余弦排序可能完全不同。
预设未归一化文档向量导致的检索反转
假设一个知识库包含两类文档:一类是简短的产品说明,向量范数约2;另一类是整章技术手册,范数可达20。查询向量为q=[1,0],两个候选文档分别为d1=[cos30°, sin30°](长度1)和d2=[2cos60°, 2sin60°](长度2)。真实余弦值cos(q,d1)≈0.866,cos(q,d2)=0.5,余弦排序应为d1更相关。
若只归一化q而库中d未归一化,点积得分为d1≈0.866、d2=1,排名反转为d2第一。检索结果把夹角60°的长文档顶掉了夹角30°的短文档,导致top-1错误。解决方式是入库时对所有文档做L2归一化,或在评分函数里保留||d||除法,保证评分与文档篇幅解耦。
哪些条件下单边归一化不会造成错误
当文档向量的范数在入库前已经被固定为相同常数,例如所有向量都已做过L2归一化,那么单边归一化查询不会造成额外错误,因为此时||d||=1,点积与余弦完全等价。这个条件是隐式的:语言模型通常不会保证输出等长,需要人工确认数据流水线是否统一长度。
另一种安全情形是文档本身长度自然一致,比如所有文本都是固定长度截断,且嵌入模型输出范数也接近固定值,但这种情况不稳定。建议通过抽样对比余弦排序与实际排序来验证范数差异的影响;若文档向量范数差异明显,则应双侧归一化或显式除以文档范数,以消除尺度偏差。
容易答错的地方
- 只归一化查询是变通的余弦近似
- 余弦相似度本身并不要求输入向量单位长度,错误在于用点积去近似。点积必须双方同尺度才等于余弦,只有查询归一化会保留文档模长,改变排序。正确做法是两边都归一化或显式除范数。
- 文档向量长代表信息多,所以优先合理
- 检索排序依据是相关性而非信息量。文档模长大可能是因为词频高或文本长,与查询语义无关。若不消除该因素,检索会系统性偏好长文档,丢失短而精确的匹配。应当在向量阶段控制长度影响。
面试官还会怎么问?
如果查询和文档都做了L2归一化,点积和余弦结果完全一样吗?
完全一样。归一化后内积等于余弦,其值在[-1,1]内。但需要注意数值范围,浮点精度可能带来极小误差,很多向量库直接用点积存放归一化后的向量。
为什么有些文档向量范数差异悬殊而余弦排序仍正确?
余弦已经除以范数,所以不看长度。如果系统不是用点积而是真正计算了余弦,那么排序正确,单边归一化不影响结果。只有在用内积近似而未除文档范数时才出问题。
在实际RAG代码里如何检查是否发生这种错误?
抽样对比:对若干查询分别计算余弦分数和当前系统评分,用目标相关文档的命中变化判断。也可以在检索前对文档向量做一次L2归一化,看召回是否有系统性变化。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。