很多“召回不准”,其实不是模型不行
在向量检索项目里,常见的抱怨包括:
-
“这个 embedding 好像不太行”
-
“换个模型试试?”
-
“是不是 TopK 太小了?”
但有一个问题,经常被直接跳过:
**我们到底是在一个什么样的“空间”里,
定义什么叫“相似”?**
向量检索的整个系统,
其实建立在两个非常底层、但经常被默认的选择之上:
-
向量维度
-
距离函数
而这两个选择,一旦定下:
**你就已经决定了:
哪些东西更容易被找出来,
哪些东西会被系统性忽略。**
先给一个必须先接受的结论(非常重要)
在展开之前,我先把全文最重要的一句话写出来:
**向量维度和距离函数,
不是“精度调节器”,
而是“相似性定义器”。**
如果你把它们当成“技术参数”,
而不是“世界观选择”,
那你几乎一定会被召回结果反复背刺。
第一层:向量检索,本质上是在一个“人为构造的几何世界”里生活
我们先把一件事说清楚。
向量检索并不是在“发现相似性”,
而是在:
**一个你亲手定义的空间里,
计算点与点之间的关系。**
这个空间的性质,来自于:
-
embedding 的训练目标
-
向量维度
-
距离函数
你可以把它理解成:
**你先决定了“世界长什么样”,
然后才问“谁离谁更近”。**
第二层:向量维度,决定了“相似性表达的自由度”
很多人对向量维度的直觉是:
“维度越高,信息越多,效果越好。”
这在某些条件下成立,
但在工程里,这句话非常危险。
向量维度到底代表什么?
你可以把每一维理解为:
模型用来区分语义差异的一条“坐标轴”。
维度越多,意味着:
-
可表达的差异类型越多
-
空间越“稀疏”
-
点之间的关系越复杂
但这并不意味着:
相似性会更可靠。
第三层:维度过低时,召回结果会“塌缩”
当向量维度太低时,常见的问题是:
- 很多不太相关的内容
被压在同一个方向上
-
相似度分布过于集中
-
TopK 看起来“都差不多”
这会导致:
-
召回缺乏区分度
-
系统很难把真正相关的内容排到前面
从工程角度看,这种情况的风险是:
**模型看到了东西,
但看到的全是“泛泛相关”。**
第四层:维度过高时,召回结果会“失去直觉”
高维空间的问题,恰恰相反。
在高维空间中,一个非常经典但反直觉的现象是:
“距离集中”(distance concentration)。
简单说就是:
-
在高维空间里
-
最近的点和最远的点
-
距离差异会变得越来越小
这意味着什么?
意味着:
-
排名对噪声非常敏感
-
微小扰动,就会改变 TopK
-
召回结果稳定性下降
你会发现:
-
同一个 query
-
不同批次召回的结果差异很大
这不是系统抖动,
而是高维空间的自然后果。
第五层:维度,其实在决定“你更容易犯哪种错”
这是一个非常重要、但很少被这样表述的点。
- 维度偏低
* 更容易召回“主题相关但不够具体”的内容
* 风险偏向:证据不足
- 维度偏高
* 更容易召回“看似很近但语义不稳”的内容
* 风险偏向:不稳定排序 + 冲突证据
换句话说:
**向量维度,不是在变好或变坏,
而是在改变系统的错误形态。**
第六层:距离函数,才是真正定义“什么叫近”的地方
如果说向量维度决定了“空间大小”,
那距离函数决定的就是:
**在这个空间里,
什么样的差异是重要的。**
常见的距离函数包括:
-
L2(欧氏距离)
-
Cosine(余弦相似度)
-
Dot Product(内积)
它们看起来只是数学公式不同,
但在工程里,含义完全不一样。
不同距离函数 → 不同相似性观
第七层:L2 距离 —— 把“幅度”和“方向”一起算进去
L2 距离关心的是:
两个向量在空间中的“绝对距离”。
这意味着:
-
向量长度(norm)会影响相似度
-
不只是方向,还包括“强度”
在以下情况下,L2 往往表现不稳定:
-
embedding 未归一化
-
不同文本长度差异大
-
模型对某些输入给出“更强激活”
你可能会发现:
**不是最相关的内容排在前面,
而是“数值更大”的内容。**
第八层:Cosine 相似度 —— 忽略幅度,只看方向
Cosine 相似度关注的是:
两个向量指向是否一致。
它天然做了一件事:
-
把向量长度的影响抹掉
-
专注于“语义方向”
这也是为什么:
-
大多数文本检索
-
默认更偏好 Cosine
但 Cosine 也有自己的代价:
-
它忽略了“强相关 vs 弱相关”的差异
-
很多“勉强相关”的内容
可能被放到相似的位置
工程上的常见后果是:
召回更稳,但区分度不够。
第九层:Dot Product —— 把“自信度”也算进相似性
Dot Product 本质上是:
cosine × 向量长度
这意味着:
- 方向一致 + 激活强
→ 非常高的相似度
这在某些 embedding 设计中是刻意为之的:
-
模型用向量长度表示“自信度”
-
dot product 用来放大这种差异
但在通用检索系统中,这也很危险:
-
某些内容会被系统性偏好
-
长文档 / 高频表达更容易被召回
你会看到一种现象:
**召回结果“看起来很确定”,
但并不一定更适合当前问题。**
第十层:向量维度 × 距离函数,是一个“耦合选择”
很多人犯的一个错误是:
只讨论维度,
或者只讨论距离函数。
但在真实系统中,这两者是强耦合的。
举几个典型组合:
- 高维 + Cosine
* 稳定,但排序区分度有限
- 高维 + Dot Product
* 排名极不稳定,容易放大噪声
- 低维 + L2
* 空间塌缩风险高
这也是为什么:
**同一个 embedding,
在不同向量库配置下,
表现可以天差地别。**
维度 × 距离函数 组合效应矩阵
第十一层:为什么“换个距离函数”,TopK 会完全变样
这是一个很多人亲眼见过、但说不清原因的现象。
原因其实很简单:
TopK 排名是距离函数的直接产物。
你一旦换了距离函数,相当于:
-
换了一套“价值排序规则”
-
换了一种“相似性定义”
于是你看到的不是:
- 结果略有变化
而是:
整个召回世界被重新排序。
这不是 bug,
而是系统按你的新规则,
在“忠实执行”。
第十二层:一个极简示意,理解“世界观切换”
# 同一组向量
query = q
vectors = docs
# 不同距离函数
rank_l2 = sort_by_l2(query, vectors)
rank_cos = sort_by_cosine(query, vectors)
rank_dot = sort_by_dot(query, vectors)
这三行代码的差异,
不是“计算方式不同”,
而是:
**你在问三个不同的问题:
-
谁绝对最近
-
谁方向最像
-
谁又像又“自信”**
第十三层:什么时候这些选择开始“主导系统行为”
你可以留意一些非常工程化的信号:
-
调 TopK 已经救不了效果
-
rerank 越来越重
-
同类 query 结果差异极大
-
系统对输入微调异常敏感
这些都在暗示:
**问题已经不在“召回多少”,
而在“什么被认为相似”。**
一个非常实用的判断问题(强烈建议)
在你准备换 embedding 或距离函数之前,问自己一句话:
**我现在更怕哪一种错误?
- 找不到该找的
- 找到一堆“看起来像但用不了的”**
- 前者 → 关注维度表达能力
- 后者 → 重新审视距离函数
这个问题,比任何 benchmark 都真实。
很多团队在向量检索效果不稳定时,第一反应是换 embedding 模型,却忽略了向量维度和距离函数本身就在重塑召回空间。用LLaMA-Factory online对不同向量配置下的召回与生成结果进行对照,更容易看清:问题是出在“模型能力”,还是出在“相似性定义”。
总结:你不是在调参数,而是在定义“相似的意义”
我用一句话,把这篇文章彻底收住:
**向量维度和距离函数,
不是技术细节,
而是你告诉系统:
这个世界里,
什么值得被靠近。**
当你开始:
-
把向量空间当成设计对象
-
把召回结果当成几何后果
-
在调参数前先想“我想放大哪种相似”
你才真正开始工程化地使用向量数据库。
