2. 向量距离度量:选对尺子,才能量准距离
做向量检索这些年,我最大的感触就是:距离度量选错了,后面的索引结构再精妙也白搭。说白了,你用什么尺子量距离,直接决定了哪些向量会被认为是“相似的”。
这一章咱们就把六种最常见的距离度量掰开揉碎讲清楚。我会从数学原理讲到适用场景,再穿插一些我在项目中踩过的坑。
核心观点:没有最好的距离度量,只有最合适的。选度量之前,先想清楚你的数据长什么样、业务要什么。
2.1 欧氏距离:最直观的"直线距离"
欧氏距离就是你在初中几何课上学过的两点间直线距离。公式很简单:
d(x, y) = sqrt(Σ(xi - yi)²)
说白了,就是把两个向量每个维度上的差值平方,加起来再开根号。
适用场景:
- 连续型稠密向量,比如图像特征、音频特征
- 各维度量纲一致的数据
- 需要保留向量"大小"信息的场景
我的经验:做图像检索时,我习惯先用欧氏距离。因为它对"整体差异"敏感——两张图亮度不同但内容相似,欧氏距离会偏大。这时候我会考虑先做归一化。
2.2 余弦相似度:只看方向,不看大小
余弦相似度衡量的是两个向量之间的夹角。公式:
cos(θ) = (x · y) / (||x|| × ||y||)
结果范围在 [-1, 1] 之间。1 表示方向完全一致,-1 表示方向相反。
你想想看,文本向量化之后,文档长度差异很大,但内容可能很相似。这时候用欧氏距离就不太合适了——长文档和短文档的模长差异会干扰判断。余弦相似度正好解决这个问题。
适用场景:
- 文本检索、语义相似度
- 用户画像、推荐系统
- 任何需要忽略向量模长的场景
注意:余弦相似度对零向量没定义。我在处理稀疏向量时遇到过这个问题——全零向量的模长为 0,除零错误直接崩了。记得加个判空处理。
2.3 曼哈顿距离:城市街区的走法
曼哈顿距离也叫 L1 距离。想象你在曼哈顿街头,不能穿楼而过,只能沿着街道走。公式:
d(x, y) = Σ|xi - yi|
它和欧氏距离的区别在哪?欧氏距离是"直线过去",曼哈顿距离是"沿着坐标轴走"。高维空间里,曼哈顿距离对异常值没那么敏感。
适用场景:
- 稀疏向量、高维数据
- 推荐系统中的协同过滤
- 特征维度含义独立、可解释性强的场景
避坑指南:我曾经在一个推荐项目里用欧氏距离做协同过滤,结果效果很差。换成曼哈顿距离后,准确率提升了 12%。原因?用户评分数据是稀疏的,曼哈顿距离对缺失值更鲁棒。
2.4 点积:带方向的相似度
点积就是对应元素相乘再求和:
x · y = Σ(xi × yi)
点积同时考虑了方向和模长。两个向量方向越一致、模长越大,点积越大。
我个人习惯在推荐系统的排序阶段用点积。为什么?因为用户向量和物品向量的模长本身就包含了"偏好强度"的信息,点积能把这个信息保留下来。
适用场景:
- 未归一化的向量
- 推荐系统中的矩阵分解
- 注意力机制中的 score 计算
小技巧:如果向量已经归一化到单位长度,点积和余弦相似度是等价的。很多向量数据库(比如 Milvus)内部会把余弦相似度转成点积来加速计算。
2.5 Jaccard 相似度:集合的交并比
Jaccard 相似度不是算向量坐标的,它算的是集合的重叠程度:
J(A, B) = |A ∩ B| / |A ∪ B|
范围 [0, 1]。0 表示没有交集,1 表示完全重合。
用在向量上时,通常先把向量二值化(比如用户有没有购买某商品),然后算交集大小除以并集大小。
适用场景:
- 标签系统、分类数据
- 购物篮分析
- 文档去重(用 shingling 技术)
注意:Jaccard 对稀疏集合很敏感。两个用户都只买过 1 件商品,恰好相同,Jaccard 就是 1.0。这不一定代表他们兴趣一致。我一般会配合购买次数做加权处理。
2.6 汉明距离:二进制向位的差异计数
汉明距离统计的是两个等长字符串(或二进制向量)在对应位置上不同字符的个数。公式很简单:
d(x, y) = count of positions where xi ≠ yi
比如 10110 和 10010,只有第 3 位不同,汉明距离就是 1。
适用场景:
- 二进制向量、哈希编码
- 错误检测与纠正
- LSH(局部敏感哈希)中的近邻搜索
我的实践:做近似最近邻搜索时,我常用 LSH 把浮点向量转成二进制哈希码,然后用汉明距离做快速筛选。速度能比浮点计算快一个数量级,代价是精度略有损失。
2.7 六种度量对比一览
| 度量 | 数学本质 | 取值范围 | 对模长敏感 | 典型场景 |
|---|---|---|---|---|
| 欧氏距离 | L2 范数 | [0, +∞) | 是 | 图像、音频特征 |
| 余弦相似度 | 夹角余弦 | [-1, 1] | 否 | 文本、语义检索 |
| 曼哈顿距离 | L1 范数 | [0, +∞) | 是 | 稀疏数据、协同过滤 |
| 点积 | 内积 | (-∞, +∞) | 是 | 推荐排序、注意力 |
| Jaccard | 交并比 | [0, 1] | 不适用 | 集合、标签数据 |
| 汉明距离 | 位差异计数 | [0, d] | 不适用 | 二进制哈希、纠错 |
2.8 实战选型建议
说了这么多,到底怎么选?我总结了几条经验:
- 先看数据类型:连续稠密向量优先考虑欧氏或余弦;稀疏向量试试曼哈顿;二进制向量用汉明;集合数据用 Jaccard。
- 再看业务需求:要区分"大小"(比如文档长度)就用欧氏或点积;只看"内容相似"就用余弦。
- 最后考虑性能:点积和曼哈顿计算比欧氏快(没有平方根);汉明距离可以用位运算加速。
我的习惯:不确定选哪个时,我会在数据集上跑一个小实验。随机采样 1000 条数据,分别用不同度量做 KNN,看召回率。数据会告诉你答案。