RAG中余弦相似度

传统搜索的缺点

传统检索(比如百度搜索)靠 “关键词匹配”,会出现这种问题:

  • 用户问:“苹果的充电速度快吗?”(指苹果手机);
  • 检索到:“苹果的成熟度越高,甜度越快”(指水果苹果);

原因是只匹配了“苹果”“快”这两个关键词,没理解语义。

解决

RAG通过 余弦相似度 解决

余弦相似度是什么?

在RAG的向量检索环节,余弦相似度是用来 衡量两个向量“方向相似度” 的核心指标——你可以把它理解成:

把 用户问题向量 和 文档片段向量 想象成 两根箭头线段,余弦相似度就是这 两根箭头线段 之间的 夹角大小:

  • 夹角越小(比如0°)→ 余弦相似度≈1 → 语义几乎完全一致;
  • 夹角越大(比如90°)→ 余弦相似度=0 → 语义完全无关;
  • 夹角180° → 余弦相似度=-1 → 语义完全相反。

注意:与长度无关

公式

\frac{\vec{A} \cdot \vec{B}}{||\vec{A}|| \times ||\vec{B}||}

余弦相似度:两个向量的点积 / 两个向量模长的乘积

结果:范围是 [-1, 1],越接近1,夹角越小,越相似

二维例子

向量 (0.5,0.5) 和 (0.7,0.5) 的夹角,如下:

点积计算过程:0.5 * 0.7 + 0.5 * 0.5 = 0.6

第1个向量的模长:\sqrt{0.5^2 + 0.5^2} = 0.71

第2个向量的模长:\sqrt{0.7^2 + 0.5^2} = 0.86

最终:0.6 / (0.71 * 0.86) = 0.98

非常接近1,说明这两个向量很相似

高维度例子

3维、256维等计算方式同上

余弦相似度在RAG中的作用

在RAG的“语义检索”环节,余弦相似度是唯一核心的匹配标准,替代了传统的“关键词匹配”,这也是RAG能解决大模型“答非所问”的关键,核心作用有3点:

1. 突破“关键词匹配”的局限(解决“字面相似但语义不同”)

当用户问:“苹果的充电速度快吗?”(指苹果手机)

余弦相似度基于 向量方向(语义)匹配:

  • 手机相关的“苹果” 向量方向,和水果相关的“苹果” 向量方向 完全不同;

  • 即使没有完全相同的关键词,只要语义相似也能精准匹配,比如:RAG怎么做 和 搭建RAG的步骤 ,关键词不同但语义一致,余弦相似度接近1

✅ 对RAG的影响:这是RAG能实现“语义检索”的核心,也是它比传统关键词检索更智能的根本原因。

2. 不受向量长度影响(适配不同维度的向量)

在RAG中,不同嵌入模型生成的向量“长度”(模长)可能不同(比如768维向量的模长是10,1024维是12),但余弦相似度只关注“方向”,不关注“长度”:

  • 比如“RAG流程”的768维向量和1024维向量,只要语义一致,余弦相似度依然接近1;
  • 避免了“向量长度不同导致匹配不准”的问题,适配你之前关注的“不同维度向量”场景。

✅ 对RAG的影响:不管选768维还是1024维向量,余弦相似度都能稳定衡量语义相似度,不用额外调整向量长度。

3. 计算高效(适配RAG的快速检索需求)

相比其他相似度计算方式(如欧氏距离),余弦相似度的计算逻辑更简单:

  • 中小规模RAG(10万条以内):单次计算耗时<1ms,能实现“用户提问→毫秒级检索→快速回答”;
  • 即使是百万级向量库,通过向量索引(如IVF、HNSW)优化后,余弦相似度计算依然能保持高效。

✅ 对RAG的影响:保证RAG的检索速度,避免用户等待太久,提升使用体验。

RAG中使用余弦相似度的避坑提醒

  1. 不要只看相似度数值:不是相似度=1才是匹配,通用场景下 ≥0.7 就说明语义高度相似,可纳入检索结果;专业场景(法律/医疗)建议 ≥0.8;
  2. 结合元数据过滤:比如“相似度≥0.7 + 文档类型=PDF”,避免检索到相似但无关类型的文档;
  3. 异常值处理:如果所有文档的相似度都 <0.3,说明知识库中无相关内容,RAG应直接提示“暂无相关信息”,而非强行生成回答(避免幻觉)。

例子

import math


def dot(vec1, vec2):
    """
    计算两个向量的点积(逐元素相乘后求和)

    参数:
        vec1: 第一个向量(列表/元组形式)
        vec2: 第二个向量(列表/元组形式)

    返回:
        两个向量的点积(float)
    """
    # 先检查向量维度是否一致
    if len(vec1) != len(vec2):
        raise ValueError("计算点积时,两个向量的维度必须相同!")

    res = 0.0
    for i in range(len(vec1)):
        e1 = vec1[i]
        e2 = vec2[i]
        res += e1 * e2
    return res


def norm(vec):
    """
    计算单个向量的模长(长度)

    参数:
        vec: 待计算模长的向量(列表/元组形式)

    返回:
        向量的模长(float)
    """
    norm_sum = 0.0
    for e in vec:
        norm_sum += e ** 2
    # 开平方根得到最终模长
    res = math.sqrt(norm_sum)
    return res


def cos_sim(vec1, vec2):
    """
    计算两个向量的余弦相似度、

    参数:
        vec1: 第一个向量(列表/元组形式)
        vec2: 第二个向量(列表/元组形式)

    返回:
        余弦相似度值(float)
    """
    # 调用独立函数计算点积
    dot_res = dot(vec1, vec2)

    # 调用独立函数计算两个向量的模长
    norm1 = norm(vec1)
    norm2 = norm(vec2)

    # 避免除以零的情况
    if norm1 == 0 or norm2 == 0:
        return 0.0

    # 计算余弦相似度
    res = dot_res / (norm1 * norm2)
    return res


# 定义示例向量
vector1 = (0.5, 0.5)
vector2 = (0.7, 0.5)

# 计算并输出结果
res = cos_sim(vector1, vector2)
print(f"向量 {vector1} 和 {vector2} 的余弦相似度为: {res:.4f}")

总结

  1. 核心定义:余弦相似度是衡量两个向量“方向相似度”的指标,结果越接近1,语义越相似,范围是[-1,1];
  2. RAG核心作用:实现精准的语义检索(突破关键词局限)、适配不同维度向量、保证检索效率;
  3. 选择逻辑:相比欧氏距离,余弦相似度更关注“语义”而非“数值”,是RAG检索的最优选择;
  4. 使用技巧:通用场景相似度≥0.7即可纳入结果,结合元数据过滤提升精准度。

原文出处:/show_1GW2pXaUywx8.html