向量归一化和相似度

13 min read

1、向量归一化

在机器学习和深度学习中,对嵌入向量进行归一化(Normalization)至关重要,它能带来多方面的益处,尤其是在涉及到相似度计算、模型训练和数据表示时。归一化通常指的是将向量的长度(或范数)缩放为单位长度(通常为 L2 范数等于 1)。以下是归一化的主要必要性:

统一尺度,公平比较:

  • 避免特征尺度差异的影响: 不同的特征或模型生成的原始嵌入向量可能具有不同的尺度和范围。例如,一个模型的嵌入向量的数值可能在 -1 到 1 之间,而另一个模型的可能在 -100 到 100 之间。如果不进行归一化,在计算向量之间的距离或相似度时(如欧氏距离、点积),尺度较大的向量会占据主导地位,导致模型更关注这些尺度较大的特征,而忽略了尺度较小的特征中可能包含的重要信息。
  • 确保每个特征的贡献相等: 归一化可以将所有嵌入向量的元素缩放到相似的尺度,使得每个维度在距离和相似度计算中具有更公平的权重,从而提高模型的鲁棒性和泛化能力。

优化相似度计算:

  • 提高余弦相似度的准确性: 余弦相似度是衡量两个向量方向之间夹角的余弦值,常用于计算文本、图像等嵌入的语义相似性。当向量未归一化时,向量的长度(magnitude)会影响点积的值,从而影响余弦相似度的结果。归一化后,向量的长度变为 1,此时余弦相似度就直接等于向量的点积,能够更准确地反映向量方向上的相似程度。
  • 改善基于距离的度量: 对于依赖欧氏距离等距离度量的算法(如 K-近邻、K-均值聚类),归一化可以确保距离的计算不会被尺度较大的特征所主导,从而得到更符合语义或特征实际分布的结果。

稳定模型训练,加速收敛:

  • 避免梯度爆炸或消失: 在神经网络训练中,如果输入的特征尺度过大或过小,可能导致梯度在反向传播过程中变得非常大(梯度爆炸)或非常小(梯度消失),从而影响模型的收敛速度和训练稳定性。归一化可以将输入特征限制在合理的范围内,有助于缓解这些问题。
  • 加快收敛速度: 当特征具有相似的尺度时,梯度下降等优化算法可以更有效地找到最优解,因为模型不需要在不同尺度的特征空间中来回震荡。

提高模型性能和泛化能力:

  • 减少模型对异常值的敏感性: 某些归一化方法(如 Z-score 归一化)可以降低异常值对模型的影响,使模型更加稳定。
  • 提升模型在不同数据集上的泛化能力: 通过统一特征的尺度,归一化可以帮助模型学习到更通用的模式,而不是过度依赖于特定数据集的尺度分布。

方便不同嵌入模型的比较和集成:

  • 统一不同模型的输出尺度: 不同的嵌入模型可能产生具有不同尺度范围的向量。归一化可以将它们的输出统一到一个共同的尺度,使得可以直接比较或组合来自不同模型的嵌入,从而进行更复杂的分析或构建更强大的系统。

总结

归一化是处理嵌入向量的一个关键步骤,它可以确保相似度计算的准确性,优化模型训练过程,提高模型性能和泛化能力,并方便不同嵌入之间的比较和集成。在大多数使用嵌入向量的机器学习和深度学习应用中,都强烈建议进行归一化处理。最常用的归一化方法是 L2 归一化,它将向量的长度缩放为单位长度。

2、向量相似度

相似度量用于衡量向量之间的相似性。选择合适的距离度量有助于显著提高分类和聚类性能。向量的相似度量有:余弦相似度(Cosine Similarity)、内积(Inner Product, IP)、欧氏距离(Euclidean Distance)、曼哈顿距离(Mahalanobis Distance)、余弦距离(Cosine Distance)、汉明距离(Hamming Distance)、Jaccard 相似度(Jaccard Similarity)、BM25 相似度(BM25 Similarity)等等。

余弦相似度(Cosine Similarity):

Cosine(A, B) = (A · B) / (||A|| * ||B||)。它衡量的是两个向量方向上的一致性,忽略了向量的长度(模长)。它的取值范围是 [-1, 1],越接近 1 表示方向越一致。 例如,两个正比向量的余弦相似度为1,两个正交向量的余弦相似度为0,两个相反向量的余弦相似度为-1,余弦越大,两个向量之间的夹角越小,说明这两个向量之间的相似度越高。 用 1 减去它们的余弦相似度,就可以得到两个向量之间的余弦距离。

内积(Inner Product, IP):

IP(A, B) = A · B。它计算的是两个向量的点积。这个值同时受到向量方向和向量模长的影响。如果两个向量方向一致,它们的模长越大,内积通常也越大。IP 的取值范围没有固定界限(取决于向量模长)。

3、同样的数据,内积 (IP) 能查到,但是余弦相似度查询不到

因为内积查询需要计算两个向量的点积,而余弦相似度只需要计算两个向量的余弦值。

$~$

  • 如果向量是归一化的(即模长为1,||A|| = 1||B|| = 1),那么Cosine(A, B) = A · B = IP(A, B)。在这种情况下,使用余弦相似度和内积进行检索,得到的结果排序应该是完全相同的。

  • 如果向量不是归一化的,那么Cosine(A, B) = (A · B) / (||A|| * ||B||),而IP(A, B) = A · B。在这种情况下,使用余弦相似度和内积进行检索,得到的结果排序可能不同。


具体到这个现象来说:


  • 内积 (IP) 成功: 这很可能意味着你想要查找的那个目标数据向量,与你的查询向量之间的点积 (A · B) 相对较高。这个高点积可能是因为它们的方向比较接近,也可能是因为一个或两个向量的模长(长度)非常大,即使方向不是最接近的,较大的模长也导致了较高的内积得分,使其排在了前面。
  • 余弦相似度 (Cosine) 失败: 这表明,虽然目标向量和查询向量的内积可能较高(被 IP 检索到),但当除以各自的模长进行归一化计算后 ((A · B) / (||A|| * ||B||)),得到的余弦相似度得分反而不高。可能有其他向量,虽然它们的模长较小(导致 IP 得分不如目标向量),但它们与查询向量的方向更一致(角度更小),因此具有更高的余弦相似度得分,把真正的目标挤出了 top-k 结果。

总结来说可能原因是:


  • 向量未归一化: 这是最主要的原因。gte-Qwen2-7B-instruct 或你使用的其他嵌入模型产生的向量可能其模长不为 1。
  • 模长的影响: 能够被 IP 检索到的目标数据,很可能与查询向量的点积较大,这其中较大的模长起到了重要作用,弥补了方向上可能不是绝对最优的劣势。
  • 方向 vs. 模长: 其他数据可能在方向上与查询向量更接近(余弦相似度更高),但由于模长较小,其内积得分不如目标数据高。

检查向量是否归一化

取一个由模型产生的向量 v,计算其 L2 范数(模长):norm = np.linalg.norm(v)。如果 norm 不接近 1,那么向量就没有归一化。


检查向量是否归一化,可以使用以下代码:
from llama_index.embeddings.ollama import OllamaEmbedding
import numpy as np

def is_normalized(vector):
    return np.isclose(np.linalg.norm(vector) 1.0

def normalize(vector):
    if is_normalized(vector):
        return vector
    return vector / np.linalg.norm(vector)

def normalize_vectors(vectors):
    return [normalize(vector) for vector in vectors]

embedd = OllamaEmbedding(
    # model_name="Embedding-GGUF/gte-Qwen2-1.5B-instruct-Q4_K_M-GGUF",
    model_name="modelscope.cn/Embedding-GGUF/gte-Qwen2-7B-instruct-Q4_K_M-GGUF:latest",
    base_url="http://localhost:11434",
    normalize=True
)
embedding = embedd.get_text_embedding("hello")
print(f"向量是否归一化: {is_normalized(embedding)}")
Last updated on 2025-04-12