在大型语言模型 (LLM) 和更广泛的自然语言处理 (NLP) 领域中,相似性 (Similarity)相关性 (Relevance) 是两个经常被提及但又有所区别的核心概念。它们都量化了两个文本片段之间的某种关联程度,但在具体含义、度量方法和应用场景上存在微妙但重要的差异。理解这两者的区别与联系,对于构建和优化基于 LLM 的智能系统至关重要。

核心思想:相似性通常指文本内容在语义或结构上的“形似”或“意近”,强调固有属性的匹配;而相关性则指文本内容与特定“查询”、“任务”或“上下文”之间的“关联程度”或“有用性”,强调功能性匹配。


一、为什么相似性与相关性在 LLM 中如此重要?

LLM 通过将文本数据转换为高维向量空间中的数值向量(即嵌入),从而能够捕捉词语和文本的复杂语义。这种表示方法使得计算机可以进行超越简单关键词匹配的语义理解。而相似性和相关性正是这种语义理解的两个重要视角:

  • 语义理解的基石:它们让 LLM 能够理解文本的实际含义,而不仅仅是表面文字。
  • 信息检索的核心:无论是搜索、问答还是推荐,核心都是找出“最相似”或“最相关”的信息。
  • 生成质量的衡量:评估 LLM 生成的文本与用户意图或参考答案的匹配程度。
  • 知识整合:在检索增强生成 (RAG) 等应用中,它们指导模型如何有效地从外部知识库中提取有用信息。

二、核心概念:嵌入与向量空间

在探讨相似性和相关性之前,我们必须理解 LLM 如何表示文本。

2.1 嵌入 (Embeddings)

  • 定义: 嵌入是将高维、离散的文本数据(如词、短语、句子或文档)转换成低维、连续的实数向量的过程。这些向量捕捉了原始文本的语义和句法信息。在向量空间中,语义相似的文本片段,其对应的向量在空间中的距离会更近,而语义不相似的文本则距离较远。
  • 生成方式: LLM 通过各种神经网络架构(如 Transformer 编码器部分)学习生成嵌入。例如,BERT、Word2Vec、GloVe 等模型都能生成不同粒度的嵌入。
  • 特性:
    • 语义丰富性: 向量的每个维度都捕获了文本在某种抽象特征上的信息。
    • 维度连续性: 相较于独热编码等稀疏表示,嵌入是密集且连续的。
    • 降维: 将高维文本特征映射到相对低维的向量空间,便于计算和处理。

2.2 向量空间 (Vector Space)

  • 定义: 嵌入将文本映射到的数学空间称为向量空间。在这个空间中,每个文本片段都被表示为一个点或一个从原点出发的向量。
  • 几何直观: 我们可以将向量看作带有方向和大小的箭头。相似性或相关性度量就是计算这些箭头之间的几何关系(如夹角或距离)。

三、相似性 (Similarity) 详解

3.1 定义

相似性通常指两个文本片段之间在固有语义内容、词汇选择或句法结构上的**“接近程度”**。它回答的是“这两个文本自身有多像?”的问题。

3.2 主要度量方法

在向量空间中,相似性主要通过计算两个向量之间的几何距离或夹角来度量。

3.2.1 余弦相似度 (Cosine Similarity)

  • 定义: 余弦相似度衡量了两个非零向量之间夹角的余弦值。它的值域在 -1 到 1 之间。
    • 1 表示两个向量方向完全相同(极度相似)。
    • 0 表示两个向量相互正交(不相关)。
    • -1 表示两个向量方向完全相反(极度不相似)。
  • 计算公式:
    对于两个向量 AB,它们的余弦相似度计算如下:
    $$
    \text{similarity} = \cos(\theta) = \frac{A \cdot B}{||A|| \cdot ||B||} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}}
    $$
  • 优点: 与向量长度无关,只关注方向,非常适合文本嵌入,因为文本长度可能不同但含义相似。
  • Go 语言代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package main

import (
"fmt"
"math"
)

// cosineSimilarity calculates the cosine similarity between two vectors.
// Returns -2.0 for error conditions (e.g., vectors of different lengths or zero vectors where similarity is ambiguous).
func cosineSimilarity(vec1, vec2 []float64) float64 {
if len(vec1) != len(vec2) {
// fmt.Println("Error: Vectors must have the same dimension.")
return -2.0 // Sentinel for error
}

var dotProduct float64
var normVec1Sq float64 // Squared norm for optimization
var normVec2Sq float64 // Squared norm for optimization

for i := 0; i < len(vec1); i++ {
dotProduct += vec1[i] * vec2[i]
normVec1Sq += vec1[i] * vec1[i]
normVec2Sq += vec2[i] * vec2[i]
}

normVec1 := math.Sqrt(normVec1Sq)
normVec2 := math.Sqrt(normVec2Sq)

if normVec1 == 0 || normVec2 == 0 {
// handle zero vectors:
// if both are zero, they are considered perfectly similar (1.0)
// if one is zero, typically undefined, we return -2.0 as an error.
if normVec1 == 0 && normVec2 == 0 {
return 1.0 // Or 0.0, depending on convention. Here, same 'direction' conceptually.
}
// fmt.Println("Error: One or both vectors are zero vectors, similarity undefined.")
return -2.0 // Sentinel for error
}

return dotProduct / (normVec1 * normVec2)
}

func main() {
fmt.Println("--- Cosine Similarity Examples ---")

// Example 1: Highly similar (semantic synonyms)
// Embeddings of "cat" and "kitten" might be very close.
vecCat := []float64{0.8, 0.6, 0.1}
vecKitten := []float64{0.7, 0.55, 0.15}
simCK := cosineSimilarity(vecCat, vecKitten)
fmt.Printf("Similarity('cat', 'kitten'): %.4f (Expected: High)\n", simCK)
// Example 2: Less similar (related but distinct)
// Embeddings of "cat" and "dog" might be somewhat close but not identical.
vecDog := []float64{0.6, 0.7, 0.2}
simCD := cosineSimilarity(vecCat, vecDog)
fmt.Printf("Similarity('cat', 'dog'): %.4f (Expected: Moderate)\n", simCD)
// Example 3: Dissimilar
// Embeddings of "cat" and "car" should be quite far apart.
vecCar := []float64{0.1, 0.2, 0.9}
simCCar := cosineSimilarity(vecCat, vecCar)
fmt.Printf("Similarity('cat', 'car'): %.4f (Expected: Low)\n", simCCar)

// Example 4: Same content (perfect similarity)
vecHello1 := []float64{1.0, 2.0, 3.0}
vecHello2 := []float64{1.0, 2.0, 3.0}
simHH := cosineSimilarity(vecHello1, vecHello2)
fmt.Printf("Similarity('hello', 'hello'): %.4f (Expected: 1.0)\n", simHH)

// Example 5: Opposite directions (distinct meaning)
vecOpposite1 := []float64{1.0, 0.0}
vecOpposite2 := []float64{-1.0, 0.0}
simOpp := cosineSimilarity(vecOpposite1, vecOpposite2)
fmt.Printf("Similarity('good', 'bad' - theoretical opposite vectors): %.4f (Expected: -1.0)\n", simOpp)

// Error examples
fmt.Println("\n--- Error Handling Examples ---")
vecErr1 := []float64{1.0, 2.0}
vecErr2 := []float64{1.0, 2.0, 3.0}
simErrDim := cosineSimilarity(vecErr1, vecErr2)
if simErrDim == -2.0 {
fmt.Println("Similarity (different dimensions): Error handled.")
}

vecZero := []float64{0.0, 0.0}
vecNonZero := []float64{1.0, 1.0}
simZeroNonZero := cosineSimilarity(vecZero, vecNonZero)
if simZeroNonZero == -2.0 {
fmt.Println("Similarity (one zero vector): Error handled.")
}

vecBothZero := []float64{0.0, 0.0}
simBothZero := cosineSimilarity(vecZero, vecBothZero)
if simBothZero == 1.0 { // Our convention for both zero vectors
fmt.Printf("Similarity (both zero vectors): %.4f (Expected: 1.0)\n", simBothZero)
}
}

3.2.2 欧几里得距离 (Euclidean Distance)

  • 定义: 欧几里得距离是两个向量在多维空间中最短的直线距离。距离越小,相似度越高。
  • 计算公式:
    $$
    \text{distance} = \sqrt{\sum_{i=1}^{n} (A_i - B_i)^2}
    $$
  • 与余弦相似度的关系: 虽然是距离度量,但与余弦相似度高度相关。如果向量长度都被归一化到 1(即 ||A||=||B||=1),那么欧几里得距离与余弦相似度之间存在简单关系:distance^2 = 2 * (1 - cosine_similarity)

3.2.3 点积 (Dot Product)

  • 定义: 两个向量的点积衡量了它们在相同方向上的投影长度。如果向量是单位向量(长度为1),那么点积就等于余弦相似度。
  • 计算公式:
    $$
    \text{dot product} = A \cdot B = \sum_{i=1}^{n} A_i B_i
    $$
  • 在 LLM 中的应用: 在一些神经网络架构(如 Transformer 的注意力机制)中,点积被广泛用于计算查询 (Query) 向量和键 (Key) 向量之间的相似度。

3.3 相似性的类型

  1. 词汇相似性 (Lexical Similarity): 基于词语的共享或表面字符串匹配。
    • 示例: “run” 和 “running”。
  2. 句法相似性 (Syntactic Similarity): 基于句子的语法结构或词序的相似性。
    • 示例: “猫追老鼠” 和 “老鼠被猫追” (虽然语义有差异,但结构可能某些方面相似)。
  3. 语义相似性 (Semantic Similarity): 衡量文本片段在含义或意义上的相似程度。这是 LLM 最关注的。
    • 示例: “汽车” 和 “车辆”;”狗在花园里玩” 和 “一只小狗在后院嬉戏”。
  4. 语境相似性 (Contextual Similarity): LLM 能够理解词语在不同语境下的含义。同一词语在不同语境中生成的嵌入向量可能大相径庭。语境相似性是指考虑了周围词语对目标词语含义的影响后的相似性。
    • 示例: “买了一个 Apple 手机”中的“Apple”和“树上结满了 apple”中的“apple”会生成不同的嵌入,因此它们与“科技公司”或“水果”的相似度也会不同。

四、相关性 (Relevance) 详解

4.1 定义

相关性是指一个文本片段对于特定的查询 (Query)任务 (Task)上下文 (Context) 的有用性、重要性或匹配程度。它回答的是“这个文本对我的需求有多大帮助?”的问题。

4.2 影响相关性的因素

相关性不仅仅是语义相似度高那么简单,它还可能受以下因素影响:

  1. 查询意图 (Query Intent): 用户的真实目的。例如,搜索“苹果”可能意图是水果,也可能是科技公司,也可能是某个品牌。
  2. 时效性 (Timeliness): 信息是否最新、是否过期。
  3. 权威性 (Authority): 信息来源是否可靠、可信。
  4. 完整性 (Completeness): 信息是否全面,能否解答用户的所有疑问。
  5. 领域专业性 (Domain Specificity): 在特定领域内,某些词语或概念的相关性可能更高。
  6. 用户个性化 (User Personalization): 用户的历史行为、偏好等也会影响相关性判断。
  7. 上下文 (Context): 当前的对话历史、之前的搜索结果等。

4.3 相关性的度量

相关性通常不是通过简单的几何关系直接计算,而是通过更复杂的模型或系统来判断:

  • 排序模型 (Ranking Models): 在信息检索系统中,相关性是一个复杂的排序问题。模型会综合考虑文本的语义相似度、关键词匹配、文档质量、时效性、用户点击行为等多种特征来预测相关性分数。
  • 标注数据 (Labeled Data): 通过人工标注大量查询-文档对的真实相关性,来训练监督学习模型。
  • A/B 测试与用户反馈: 通过在线实验,观察用户对不同排序结果的满意度、点击率等,来迭代优化相关性模型。
  • 结合 LLM 的评估: LLM 可以被训练来评估给定查询与文本之间的相关性,甚至可以解释为什么某个文本是相关的。例如,prompt LLM :“请判断以下文本与查询’[查询]’的相关性,并给出评分(1-5分)及理由。”

五、相似性与相关性的区别与联系

特征 相似性 (Similarity) 相关性 (Relevance)
关注点 文本自身的语义、结构上的接近程度 文本对于特定查询/任务/上下文的有用性或匹配程度
标准 文本内在属性的匹配 外部意图的功能性匹配
是/否 通常是一个连续的程度值(0-1 或 -1-1) 也常是连续值,但可能受二元判断影响(相关/不相关)
度量方法 几何距离(余弦相似度、欧几里得距离)、点积等 复杂的排序模型、机器学习、人工标注、用户反馈等
评价对象 两个文本之间的静态关系 文本与动态查询/任务之间的动态关系
例子 “汽车”与“车辆”高相似;“猫”与“狗”中度相似 搜索“如何修复漏水的龙头”,关于水管维修的文章比关于猫咪健康的更相关

联系:

  • 相关性经常以相似性为基础: 在很多情况下,语义相似度是判断相关性的一个重要(甚至是主要)特征。如果两个文本在语义上完全不相似,它们通常也不会相关。
  • 相关性是更复杂的判断: 相关性是在相似性基础上,结合了用户意图、上下文、时间、质量等多种因素的综合判断。

举例

  • 查询: “如何清洗我的运动鞋?”

  • 文档 A: “清洗鞋子的最佳方法” (包含关于洗涤剂、刷子、晾干等信息)

  • 文档 B: “如何保养你的真皮皮鞋” (详细介绍了皮鞋的护理)

  • 文档 C: “选择适合跑步的运动鞋” (关于购买运动鞋的建议)

  • 相似性视角

    • 查询和文档A:高语义相似性(都关于“清洗鞋子”)。
    • 查询和文档B:中低语义相似性(都关于“鞋子保养”,但类型和方法不同)。
    • 查询和文档C:中等语义相似性(都关于“运动鞋”,但侧重“选择”而非“清洗”)。
  • 相关性视角

    • 对于查询“如何清洗我的运动鞋?”,文档 A 的相关性最高,因为它直接回答了如何清洗运动鞋。
    • 文档 B 尽管语义上与“清洗”和“鞋子”都有所关联,但其相关性远低于文档 A,因为它涉及的是“皮鞋保养”而非“运动鞋清洗”。用户需要的信息无法从文档 B 中获取。
    • 文档 C 尽管和“运动鞋”高度概念相关,但对“如何清洗”这一具体任务的相关性很低

六、在 LLM 中的应用场景

6.1 相似性的典型应用

  1. 语义搜索 (Semantic Search): 将查询和文档都嵌入,然后计算它们之间的余弦相似度来排序。
  2. 文本聚类与发现: 将语义相似的文本嵌入分组,发现不同主题的文本簇。
  3. 重复检测与去重: 识别出语义上重复或高度相似的文本片段。
  4. 内容推薦: 基于用户已消费内容的嵌入,寻找相似的新内容。
  5. 知识图谱构建: 发现实体和概念间的相似关系。

6.2 相关性的典型应用

  1. 检索增强生成 (RAG - Retrieval-Augmented Generation):
    • 用户提问后,系统将问题嵌入。
    • 利用相似性搜索(通常是余弦相似度)从外部知识库中检索出语义相似的文本块。
    • 在此基础上,RAG 更进一步,通过一个排序器 (Re-ranker) 模块对检索出的文本块进行相关性评估和重新排序。这个排序器通常是一个更小的、针对相关性任务微调过的 LLM,它不仅仅看语义相似度,还会考虑文本对回答当前查询的实际有用性。最后,将最相关的文本作为上下文提供给主 LLM。
  2. 问答系统 (Question Answering): 找出对问题回答最有用的段落或句子。
  3. 信息过滤与推荐: 确保推荐的内容不仅相似,而且是用户当前可能最感兴趣、最有价值的。
  4. 搜索引擎排序: 这是相关性最经典的场景,搜索引擎的核心任务就是根据用户查询,返回最相关的网页。

七、总结

相似性是文本的内在属性比较,主要通过向量的几何关系度量;相关性是文本对特定需求的外在有用性判断,它在相似性的基础上,融合了用户意图、上下文、时效性等更多复杂因素。

在 LLM 应用中,这两者相辅相成:

  • 相似性通常作为初步筛选的有效工具,快速从海量数据中缩小范围,找出潜在的候选集合。
  • 相关性则在此基础上进行精细化排序和过滤,确保最终呈现给用户或 LLM 的信息是最准确、最有用的。

尤其是在 RAG 这样的高级应用中,我们看到一个明确的流程:先用高效的相似性搜索进行粗粒度检索,再用更复杂的模型进行精确的相关性重排序。这种结合方式体现了对相似性和相关性两者深刻理解和有效利用的价值。