第 7 / 21 篇

第6章 从词项向量到真实本地嵌入

向量只是数值表示的形式,不保证有语义能力。把双字词项计数装进向量,再计算余弦相似度,仍然依赖词面重叠。理解这一点,可以避免把一段很小的数学代码误称为已经完成了语义检索。

先把稀疏基线说清楚

默认 TermEncoder 用前一章的词项规则生成计数表。两个片段的余弦值等于词项计数点积除以各自长度。稀疏字典不需要预先固定词表,但只有共享词项才会产生非零点积;没有出现过的同义表达,不会因为采用余弦就自动接近。

词项模式的演示截断阈值为零点零五。这个值只控制是否把候选放进列表,没有经过业务相关性概率校准。词面和词项向量在当前语料里都得到零点九的 Recall@3,正好说明改变排序数学形式,并没有解决那条同义查询。

一次确实运行的模型实验

本书另行运行了 BAAI/bge-small-zh-v1.5 的中文表示模型,使用 Xenova/bge-small-zh-v1.5 仓库的社区量化 ONNX 导出。原模型和导出不是同一个分发物;书中记录了双方提交号、模型文件与 tokenizer 的 SHA-256,没有声称量化文件就是原始全精度权重。

编码器通过 ONNX Runtime 的 CPU 执行提供方直接推理,不依赖云 API。它读取 tokenizer,按图输入要求提供 input_ids、attention_mask 与可能需要的 token_type_ids,取末层输出第一个 token 的表示,再执行 L2 归一化,得到五百一十二维向量。

短查询前添加“为这个句子生成表示以用于检索相关文章:”,文档不添加该指令;输入最多五百一十二个 token。这与 BGE 原模型卡 中的 CLS、归一化与检索指令说明相对应。不同任务仍需比较是否加指令更合适。

有收益,也有明确的限制

在相同八篇文档与十三条查询中,真实模型向量找到了“旅馆住一夜能花多少钱”对应的差旅规定。十条正例的 Recall@3、MRR@3 都达到一点零,三个负例没有返回候选。词面召回在同一次模型实验中仍为零点九,便于检查输入没有随模型一起改变。

这个观察支持一个很窄的结论:该量化模型在这组构造数据里弥补了一条词面遗漏。它不支持中文检索普遍提升百分之十、适用于所有企业或胜过其他模型的结论。数据量小、标签由作者编写,阈值零点四也是演示参数,不是可靠相关性的通用分界。

代码把模型文件、tokenizer 指纹和编码方案放进编码器标识,因此替换权重需要重建索引。即使向量维度相同,不同模型或池化方案的坐标空间也不能混用。

本书没有引入近似最近邻库,而是对权限允许的片段逐一算余弦。这样可以看清授权先于评分的顺序,代价是线性扫描。实际规模增长时,需要重新评估过滤召回策略、向量存储和近邻索引,不能用这个小实验的毫秒数推断线上吞吐量。