qinghuan
← 回到知识探索

AI 直觉 / 动手实验

向量放大两倍,为什么相似度没变?

用二维向量比较点积和余弦,观察“方向相近”与“数值更大”是两种不同判断。

展开本文目录
  1. 先动手看看
  2. 点积看方向,也看长度
  3. 归一化保留方向,也放弃长度
  4. 相似度分数不是事实概率
  5. 记住这三点
  6. 别把例子用过头
  7. 自己试一小步
  8. 查原始资料
  9. 找一份资料继续练
开始前,会这些就够了
  • 知道向量是一组有顺序的数
  • 理解平方与平方根
遇到不熟悉的词,先看这 2 个解释
向量
按固定顺序排列的一组数,可以表示一个对象的多个特征。每个位置是一维;比较两个向量前,要确认这些位置的含义、单位和生成方式能够对应。
余弦相似度
比较两个非零向量方向是否接近的数值,由点积除以两个向量的长度得到,取值在负一到一之间。只把向量乘以正数、保持方向不变时,余弦相似度不变。
打开完整术语小抄 ↗

把一支箭头沿原方向拉长一倍,它仍指向同一个方向。余弦相似度关注的就是这种方向关系,所以两条长度不同的向量也可能得到相似度一。点积还会受到长度影响,同一对数据用不同打分方式,数值和排序都可能变化。

这里先不运行文本模型,而是自己写四个二维向量。只有看清计算究竟保留了什么,才不容易把检索分数误读成“有多大概率说的是同一件事”。

方向变了,还是只变长了?

AB
余弦相似度
0.7071
B 的坐标
(1.41, 1.41)

A 固定为 (1, 0),B 是二维示意向量,不是文字的真实 embedding。余弦只比较非零向量的方向;相似度高不代表内容真实,也不保证适合任何检索任务。

点积看方向,也看长度

点积把对应位置相乘后相加。余弦则把点积再除以两个向量长度的乘积;长度在这里采用平方和开根号得到的欧氏长度。对于非零向量,这一步相当于先把箭头调整成单位长度,再比较方向,所以正比例放大不会改变余弦。

下面固定查询向量为 (1,2)。same 与它相同,scaled 只是加倍,turned 改了方向,opposite 指向反方向。程序同时打印点积与余弦,便于看到究竟是哪一项发生了变化。输入都是小数值的二维例子,计算没有训练任何语义模型。

小实验 · python
import math

def scores(a, b):
    if len(a) != len(b):
        raise ValueError("different dimensions")
    dot = sum(x * y for x, y in zip(a, b))
    na = math.sqrt(sum(x * x for x in a))
    nb = math.sqrt(sum(y * y for y in b))
    if na == 0 or nb == 0:
        raise ValueError("zero vector has no direction")
    return dot, dot / (na * nb)

query = (1.0, 2.0)
candidates = {
    "same": (1.0, 2.0),
    "scaled": (2.0, 4.0),
    "turned": (2.0, 1.0),
    "opposite": (-1.0, -2.0),
}
for name, vector in candidates.items():
    dot, cosine = scores(query, vector)
    print(name, f"dot={dot:.1f}", f"cos={cosine:.3f}")

怎样运行:Python 3.10+;保存为 cosine_demo.py,在该目录运行 python cosine_demo.py。只用标准库,无需联网。

运行后对照结果
same dot=5.0 cos=1.000
scaled dot=10.0 cos=1.000
turned dot=4.0 cos=0.800
opposite dot=-5.0 cos=-1.000

归一化保留方向,也放弃长度

same 与 scaled 的余弦相同,并不表示两组数字逐项相等,而是它们方向相同。若任务只想比较方向,这很有用;若模型训练时让长度也承载了有用信息,统一长度可能丢掉这部分信号。因此不能不看模型说明,就认定余弦总比点积好。

如果两个非零向量本来都已经归一化成单位长度,点积与余弦就相等。BGE 文档用其示例展示了这种关系,但实际使用仍应检查自己的模型和编码选项:是否归一化、查询与文档如何编码、检索端采用什么距离,都需要相互对应。算法名称相同不等于输入处理相同。

零向量没有方向,数学上的余弦分母为零。本地代码明确拒绝它;有些库会通过数值约定处理零长度或极小长度,这种返回值不应被解释成正常的几何方向。对非常大、非常小或高维的数据,还需考虑稳定计算与浮点误差,本例不提供通用数值库。

相似度分数不是事实概率

把文本编码成向量后,方向有多大语义价值,来自模型的训练目标和数据,而不是余弦公式自己理解了语言。相似度 0.8 不能直接读成“八成概率正确”或“八成内容相同”。某个阈值能否分开相关与不相关材料,需要用具体任务的已标注样本检查。

可以先准备几条同义、同主题但结论相反、只有关键词相同的文本,观察检索顺序是否符合目标。这个过程是在评价整套编码和打分方案,不是在证明余弦本身会判断真假。需要保留精确名称、版本或否定条件时,还要回到原文核对,不能只看一个高分就接受结论。

记住这三点

  • 余弦比较非零向量的方向,正比例放大不会改变它。
  • 两个向量都为单位长度时,点积与余弦数值相同。
  • 文本相似度的阈值要在具体任务中验证,分数不是事实概率。

别把例子用过头

  • 二维箭头只是计算直觉,不能据此判断真实文本模型的语义能力。
  • 零向量、维度错误及浮点范围要单独处理,示例只面向小型有限数值输入。

自己试一小步

能否找出点积排序改变、余弦排序却不变的例子?

  1. 准备两个方向不同的非零候选,先计算它们与同一查询的两种分数。
  2. 只用一个正数放大其中一个候选,比较新旧排序。
  3. 把正数改为负数前先预测结果,再核对方向变化。

做到这些,就算完成

  • 分别记录点积和余弦,没有只看同一列。
  • 正比例缩放与反向缩放被分开讨论。
  • 没有把实验分数解释成模型正确率。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

  • scikit-learn:cosine_similarity (新标签页)↗scikit-learn 开发团队 · 英文

    余弦是归一化点积,以及 L2 归一化后与线性核的关系。

    公开 API 文档;stable 路径随版本更新,本例不依赖该库。

  • BGE:向量相似度与点积 (新标签页)↗北京智源人工智能研究院 BGE 项目 · 英文

    点积同时包含长度和方向、余弦只保留方向,以及归一化示例。

    公开开发者教程;本文只采用公式和归一化关系,不复用页面示例文本或将其模型结果推广成通用保证。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。