💡 核心结论
在文本向量检索中,余弦相似度(Cosine Similarity)是黄金标准。
- 关注语义方向,忽略向量长度
- 取值范围[0, 1],阈值判断直观
- 所有主流向量数据库默认使用
- 在MTEB等基准测试中全面领先
| 算法 | 取值范围 | 越相似 | 计算速度 | 文本检索 |
|---|---|---|---|---|
| 余弦相似度 | -1~1 | 越大越好 | ⭐⭐⭐⭐ | ✅ 最佳 |
| 欧氏距离 | 0~∞ | 越小越好 | ⭐⭐⭐ | ⚠️ 较少 |
| 内积/点积 | -∞~∞ | 越大越好 | ⭐⭐⭐⭐⭐ | ✅ 归一化后 |
| 曼哈顿距离 | 0~∞ | 越小越好 | ⭐⭐⭐⭐ | ⚠️ 较少 |
| Jaccard | 0~1 | 越大越好 | ⭐⭐⭐⭐ | ⚠️ 仅二值 |
A · B
cos(θ) = -------
||A|| × ||B||
其中:
- A · B 是两个向量的点积(内积)
- ||A|| 是向量A的模(长度)
- ||B|| 是向量B的模(长度)
- θ 是两个向量的夹角
向量空间中的两个向量:
↗ B (机器学习)
/
/ θ ← 夹角越小,越相似
/
-----●------→ A (人工智能)
cos(θ) = 1.0 → 完全相同(夹角0°)
cos(θ) = 0.0 → 完全无关(夹角90°)
cos(θ) = -1.0 → 完全相反(夹角180°)
关键:文本向量中,方向代表语义,长度代表强度。
# 例子:语义相同但表达强度不同
A = "人工智能正在改变世界"
B = "AI技术正在改变世界"
C = "人工智能 人工智能 人工智能" # 重复多次
向量化后:
vec(A) = [0.1, -0.2, 0.3, ...] # 长度 ≈ 1.0
vec(B) = [0.12, -0.18, 0.28, ...] # 长度 ≈ 1.0,方向相近
vec(C) = [0.5, -1.0, 1.5, ...] # 长度变大,但方向相同
余弦相似度:
cos(A, B) = 0.92 ✅ 语义相似
cos(A, C) = 1.0 ✅ 方向相同(长度不影响)
欧氏距离:
dist(A, B) = 0.15 ✅ 距离近
dist(A, C) = 2.5 ❌ 距离远(误判!因为长度不同)
结论:余弦相似度只看方向,完美契合语义检索!
# 阿里云text-embedding-v4返回的向量已归一化
||vec|| ≈ 1.0
# 此时余弦相似度简化为:
cos(A, B) = A · B # 就是点积!
# 计算超快,只需:
similarity = np.dot(vec_a, vec_b)
import numpy as np
def cosine_similarity(vec1, vec2):
"""计算余弦相似度"""
# 如果向量已归一化,直接点积
return np.dot(vec1, vec2)
# 如果未归一化,需要除以模长
# return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
# 示例
question_vec = np.array([0.1, -0.2, 0.3, ...])
note_vec = np.array([0.12, -0.18, 0.28, ...])
similarity = cosine_similarity(question_vec, note_vec)
print(f"相似度: {similarity:.4f}") # 输出: 0.8523
相似度范围:0 ~ 1
常用阈值:
- ≥ 0.8:高度相关(同义词、同一概念)
- 0.5 ~ 0.8:中度相关(相关概念)
- 0.25 ~ 0.5:弱相关(可能相关)
- < 0.25:不相关(通常过滤)
你的项目阈值:0.25 ✅
d = √(Σ(Ai - Bi)²)
即:各维度差值的平方和,再开方
二维空间中的距离:
A ●
/|
/ |
/ |
●---●
B d = √((x2-x1)² + (y2-y1)²)
# 图像检索:关注像素的绝对差异
image_a = [100, 150, 200] # RGB值
image_b = [105, 148, 195] # 很接近
distance = √((105-100)² + (148-150)² + (195-200)²)
= √(25 + 4 + 25)
= 7.35 # 很小,说明相似
# 问题示例
A = "人工智能"
B = "人工智能 人工智能 人工智能" # 重复3次
向量化:
vec(A) = [0.1, -0.2, 0.3, ...] # 长度 1.0
vec(B) = [0.3, -0.6, 0.9, ...] # 长度 3.0(方向相同!)
余弦相似度:
cos(A, B) = 1.0 ✅ 正确!语义完全相同
欧氏距离:
dist(A, B) = 2.0 ❌ 错误!误判为不相似
A · B = Σ(Ai × Bi)
# 未归一化向量
cos(A, B) = (A · B) / (||A|| × ||B||)
# 如果已归一化(||A|| = ||B|| = 1)
cos(A, B) = A · B # 完全等同!
# 所以:
# - 归一化向量:用内积(更快)
# - 未归一化向量:用余弦相似度
# 阿里云的向量已归一化,所以可以直接用内积
def fast_similarity(vec1, vec2):
"""快速相似度计算(仅适用于归一化向量)"""
return np.dot(vec1, vec2)
# 这比完整的余弦相似度公式快!
d = Σ|Ai - Bi|
即:各维度差值的绝对值之和
# 网格状空间(城市街区距离)
# 从A到B,只能沿网格走:
A = (0, 0)
B = (3, 4)
曼哈顿距离 = |3-0| + |4-0| = 7
欧氏距离 = √(3² + 4²) = 5
J(A, B) = |A ∩ B| / |A ∪ B|
即:交集大小 / 并集大小
# 关键词匹配
doc_a = {"人工智能", "机器学习", "深度学习"}
doc_b = {"人工智能", "机器学习", "神经网络"}
交集 = {"人工智能", "机器学习"} # 2个
并集 = {"人工智能", "机器学习", "深度学习", "神经网络"} # 4个
Jaccard = 2 / 4 = 0.5
文本向量化的本质:
- 每个维度 = 一个"语义特征"
- 向量方向 = 语义的组合方式
- 向量长度 = 语义的强度(不重要)
余弦相似度只看方向,完美契合!
MTEB基准测试结果:
├─ 语义相似度任务: cosine 胜出 ✅
├─ 文本检索任务: cosine 胜出 ✅
├─ 聚类任务: cosine 胜出 ✅
└─ 分类任务: cosine 胜出 ✅
主流向量数据库默认算法:
├─ Milvus: cosine(默认)
├─ FAISS: InnerProduct(归一化后等同)
├─ Pinecone: cosine(默认)
├─ Weaviate: cosine(默认)
├─ Qdrant: cosine(默认)
└─ Chroma: cosine(默认)
主流Embedding API:
├─ OpenAI: 推荐cosine
├─ 阿里云百炼: 向量已归一化,适配cosine
├─ Cohere: 推荐cosine
└─ HuggingFace: 推荐cosine
# 归一化向量只需点积
similarity = np.dot(vec1, vec2)
# 793个向量,每个1024维:
# 计算时间:< 10ms
[0, 1] 范围:
1.0 → 完全相同
0.8 → 高度相关
0.5 → 中度相关
0.25 → 阈值(你的项目)
0.0 → 完全无关
# 问题
question = "什么是Transformer模型?"
# 候选笔记
A = "Transformer是一种深度学习架构..." # 高度相关
B = "机器学习的基础概念..." # 中度相关
C = "红烧肉的制作方法..." # 不相关
余弦相似度:
cos(question, A) = 0.85 ✅ 高度相关
cos(question, B) = 0.32 ⚠️ 中度相关
cos(question, C) = 0.08 ✅ 不相关
欧氏距离:
dist(question, A) = 0.15 ✅ 距离近
dist(question, B) = 0.68 ⚠️ 距离中
dist(question, C) = 1.25 ✅ 距离远
结论:都能区分,但余弦相似度更直观!
| 场景 | 最佳算法 | 原因 |
|---|---|---|
| 文本语义检索 | 余弦相似度 ✅ | 关注方向,忽略长度 |
| 图像检索 | 欧氏距离 | 关注像素绝对差异 |
| 推荐系统(评分) | 内积 | 用户-物品评分乘积 |
| 关键词匹配 | Jaccard | 集合重叠度 |
| 聚类分析 | 欧氏距离 | 需要绝对距离 |
| 高维稀疏数据 | 曼哈顿 | 计算更快 |
# 完美配置 ✅
算法: 余弦相似度
模型: text-embedding-v4
维度: 1024
阈值: 0.25
Top K: 3-5
# 1. 用户提问
question = "什么是Transformer?"
# 2. 问题向量化
question_vec = embedding(question) # 1024维,已归一化
# 3. 计算所有笔记的相似度
for note_vec in database:
similarity = np.dot(question_vec, note_vec) # 余弦相似度
# 4. 排序筛选
results = sorted(similarities, reverse=True)
# 5. 阈值过滤
relevant = [r for r in results if r.similarity >= 0.25]
# 6. 返回Top K
return relevant[:3]
# ✅ 快:直接点积(因为已归一化)
similarity = np.dot(vec1, vec2)
# ❌ 慢:完整余弦公式
similarity = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
# 一次计算多个相似度
question_vec = [...] # (1024,)
all_notes = [...] # (793, 1024)
# 矩阵乘法,超快!
similarities = all_notes @ question_vec # 793个相似度
# 如果数据量大(>10万),使用专用向量数据库
from milvus import MilvusClient
client = MilvusClient("ping.db")
results = client.search(
collection_name="notes",
data=[question_vec],
limit=3,
metric_type="COSINE" # 余弦相似度
)
学术论文
"A Comparative Study of Similarity Measures for Text Clustering"
实战经验
📝 笔记创建:2026-05-18
🔄 最后更新:2026-05-18
🏷️ 标签:#向量检索 #余弦相似度 #欧氏距离 #内积 #相似度算法 #RAG #语义检索