文本表示、距离与归一化
向量不是文档的无损压缩。Embedding把文本映射到训练目标所塑造的空间:同义句可能近,数字、否定词或领域缩写仍可能出错。查询与文档应使用匹配的模型、tokenizer和输入模板;有些模型区分query和passage前缀,不能自行删去。
先用人工二维向量隔离距离语义,不下载模型:
python
from math import sqrt
def dot(a, b):
if len(a) != len(b):
raise ValueError('dimension mismatch')
return sum(x*y for x,y in zip(a,b))
def cosine(a, b):
denom = sqrt(dot(a,a)*dot(b,b))
if not denom:
raise ValueError('zero vector')
return dot(a,b)/denom
q, a, b = [1,0], [2,2], [1,0]
assert dot(q,a) > dot(q,b)
assert cosine(q,a) < cosine(q,b)
print(round(cosine(q,a), 3), cosine(q,b))点积偏爱a的较大长度;余弦只看方向,b更一致。单位向量时平方欧氏距离等于2-2*cosine,所以按距离升序和余弦降序等价。不归一化时不能套这个结论;也不能把距离的“越小越好”误当相似分数的“越大越好”。
向量维数相同仅意味着算术可运行,不意味着语义可比较。更换模型但沿用旧文档向量,会得到看似正常的分数和错误的邻居。索引manifest至少记录模型修订、维数、归一化、输入模板、最大长度、切分版本。长文本截断之后再编码意味着尾部事实从表示中消失;增加检索top-k不会恢复不存在的特征。
从小实验走向语义模型
Bi-encoder单独编码文档和查询,文档向量可离线缓存;Cross-encoder联合编码两者,不能把文档分数跨查询缓存。训练领域、语言覆盖、对称/非对称任务都影响选择。用自己的标注查询测相关性,不能仅比较维数或通用榜单。
旧TF-IDF/BM25系统仍适合唯一编号,BM25的词频饱和和长度归一化缓解长文重复词占优,但不天然识别同义词。中文分词、大小写、连字符和专有名词要固定;新向量召回与词法召回可以互补,而非一刀切替换。
练习
计算三个单位向量之间的平方欧氏距离,验证它与余弦排序一致;零向量应如何处理?
完成后查看本章答案。
下一章:精确扫描、近似索引与过滤。