目的
理解Embedding模型的重要性以及现有Emebdding模型的优劣以及选型的指标。
Embedding是RAG的"心脏"
RAG系统工作流程与Embedding的作用
| 处理阶段 | 核心任务 | Embedding的角色 | 输入→输出转换 |
|---|
| 文档处理 | 知识库准备 | 将文档切片转换为向量 | 文本 → 数字向量 |
| 检索阶段 | 查找相关信息 | 计算查询与文档的语义相似度 | 问题向量 vs 文档向量 |
| 增强阶段 | 构建提示词 | 基于语义相关度筛选最佳上下文 | 相关文档 + 原始问题 |
| 生成阶段 | 生成答案 | 为LLM提供精准的上下文信息 | 增强提示 → 最终答案 |
Embedding在RAG中的核心价值
| 价值维度 | 传统关键词搜索 | 基于Embedding的语义搜索 | 优势说明 |
|---|
| 语义理解 | 仅匹配字面词项 | 理解同义词、相关概念 | "汽车"也能匹配"车辆"、"机动车" |
| 多语言支持 | 需要语言特定配置 | 跨语言语义映射 | 中文问题可检索英文相关内容 |
| 长尾查询 | 对生僻查询效果差 | 基于概念相似性工作 | 即使查询词不常见也能找到相关内容 |
| 上下文感知 | 忽略词语上下文 | 考虑词语在上下文中的含义 | "苹果公司"vs"水果苹果"区分明确 |
什么是Embedding?从文字到向量的魔法
Embedding技术基础概念
| 概念 | 定义 | 直观比喻 | 技术实现 |
|---|
| Embedding向量 | 文本的数值表示 | 文字的"数字指纹" | 浮点数数组,如[0.1, -0.5, 0.8, ...] |
| 向量空间 | 所有向量存在的数学空间 | "语义宇宙" | 高维坐标系(通常256-1536维) |
| 语义相似度 | 向量间的距离关系 | "语义距离" | 余弦相似度、欧氏距离等 |
| 维度 | 向量的长度 | "指纹的复杂程度" | 模型输出固定长度的向量 |
文本到向量的转换示例
| 文本内容 | 可能的向量表示(简化) | 语义相近文本 | 相似度计算 |
|---|
| "我喜欢机器学习" | [0.2, 0.7, -0.1, 0.5, ...] | "我热爱人工智能" | 余弦相似度: 0.89 |
| "今天天气很好" | [0.1, 0.3, 0.9, -0.2, ...] | "阳光明媚的一天" | 余弦相似度: 0.85 |
| "编程很难学" | [-0.5, 0.6, 0.2, 0.4, ...] | "写代码有挑战" | 余弦相似度: 0.82 |
| "苹果很好吃" | [0.3, -0.2, 0.1, 0.8, ...] | "水果很美味" | 余弦相似度: 0.78 |
Embedding模型选型的关键考量因素
选择Embedding模型时,需要从多个维度进行权衡:
1. 性能指标
- 检索准确率:模型找到相关文档的能力
- 排序质量:相关文档在结果中的排名位置
- 泛化能力:在未见过的数据上的表现
2. 技术参数
- 上下文长度:单次能处理的文本长度(512 tokens? 8K? 32K?)
- 向量维度:输出向量的尺寸(384维? 768维? 1536维?)
- 推理速度:生成向量的快慢
3. 资源成本
- 计算资源:模型运行需要的GPU/CPU资源
- 存储成本:向量数据库的存储需求
- 部署复杂度:本地部署还是使用API服务
4. 业务适配
- 语言支持:是否支持你的业务语言
- 领域适配:在专业领域(医疗、法律等)的表现
- 数据隐私:是否需要本地化部署
如何选择Embedding模型
模型介绍
| 模型系列 | 代表模型 | 主要语言 | 上下文长度 | 向量维度 | 关键优势 |
|---|
| OpenAI | text-embedding-3-large | 多语言 | 8192 | 3072(可调整) | 多语言、通用质量高,API支持,无需本地部署 |
| QWen3 | QWen3-8B | 中英文 | 32K | 可配置 | 8B 型号在 MTEB 多语言榜单排名第一 |
| BGE系列 | bge-large-zh | 中文优化 | 512 | 1024 | 中文SOTA表现 |
| E5系列 | e5-large-v2 | 多语言 | 514 | 1024 | 平衡性能与效率 |
| M3E系列 | m3e-base | 中文 | 512 | 768 | 中文社区优化 |
| GTE系列 | gte-large | 多语言 | 8192 | 1024 | 长文本支持 |
| Sentence-BERT | all-MiniLM-L6 | 英文 | 256 | 384 | 轻量快速 |
如何选择
评估指标详细说明
| 指标 | 数学公式 | 代码实现关键点 | 适用场景 |
|---|
| Hit Rate @ K | HR@K=总查询数命中查询数 | any(doc_id in relevant_docs for doc_id in top_k_results) | 快速评估模型是否能在前K个结果中找到相关文档 |
| MRR @ K | MRR@K=N1∑i=1Nranki1 | 找到第一个相关文档的排名,计算其倒数 | 关注第一个相关文档的排名质量 |
| NDCG @ K | NDCG@K=IDCG@KDCG@K DCG@K=∑i=1Klog2(i+1)reli | 计算实际排序和理想排序的折扣累积增益比 | 考虑多级相关性和排序位置的综合评估 |
- Hit Rate @ K:前K个结果中至少包含一个相关文档的概率
def hit_rate_at_k(search_results: List[List[str]],
ground_truth: List[List[str]],
k: int) -> float:
"""
计算Hit Rate @ K
Args:
search_results: 模型返回的排序结果列表,每个子列表包含文档ID
ground_truth: 真实相关文档列表,每个子列表包含相关文档ID
k: 考虑的前K个结果
Returns:
hit_rate: Hit Rate @ K 值
"""
hits = 0
total_queries = len(search_results)
for i in range(total_queries):
top_k_results = search_results[i][:k]
relevant_docs = set(ground_truth[i])
if any(doc_id in relevant_docs for doc_id in top_k_results):
hits += 1
return hits / total_queries if total_queries > 0 else 0.0
- Mean Reciprocal Rank (MRR) @ K:衡量第一个相关文档的排名质量
def mean_reciprocal_rank_at_k(search_results: List[List[str]],
ground_truth: List[List[str]],
k: int) -> float:
"""
计算Mean Reciprocal Rank @ K
Args:
search_results: 模型返回的排序结果列表
ground_truth: 真实相关文档列表
k: 考虑的前K个结果
Returns:
mrr: Mean Reciprocal Rank @ K 值
"""
reciprocal_ranks = []
for i in range(len(search_results)):
top_k_results = search_results[i][:k]
relevant_docs = set(ground_truth[i])
first_relevant_rank = None
for rank, doc_id in enumerate(top_k_results, 1):
if doc_id in relevant_docs:
first_relevant_rank = rank
break
if first_relevant_rank is not None:
reciprocal_ranks.append(1.0 / first_relevant_rank)
else:
reciprocal_ranks.append(0.0)
return np.mean(reciprocal_ranks) if reciprocal_ranks else 0.0
- Normalized Discounted Cumulative Gain (NDCG) @ K:考虑排序位置的综合指标
def ndcg_at_k(search_results: List[List[str]],
ground_truth: List[List[str]],
k: int,
relevance_scores: Dict[str, float] = None) -> float:
"""
计算Normalized Discounted Cumulative Gain @ K
Args:
search_results: 模型返回的排序结果列表
ground_truth: 真实相关文档列表
k: 考虑的前K个结果
relevance_scores: 文档相关性得分字典,如果不提供则使用二值相关性
Returns:
ndcg: Normalized Discounted Cumulative Gain @ K 值
"""
ndcg_scores = []
for i in range(len(search_results)):
top_k_results = search_results[i][:k]
relevant_docs = set(ground_truth[i])
dcg = 0.0
for rank, doc_id in enumerate(top_k_results, 1):
if relevance_scores and doc_id in relevance_scores:
rel = relevance_scores[doc_id]
else:
rel = 1.0 if doc_id in relevant_docs else 0.0
dcg += rel / math.log2(rank + 1)
ideal_relevance = []
for doc_id in relevant_docs:
if relevance_scores and doc_id in relevance_scores:
ideal_relevance.append(relevance_scores[doc_id])
else:
ideal_relevance.append(1.0)
ideal_relevance.sort(reverse=True)
ideal_relevance = ideal_relevance[:k]
idcg = 0.0
for rank, rel in enumerate(ideal_relevance, 1):
idcg += rel / math.log2(rank + 1)
if idcg > 0:
ndcg_scores.append(dcg / idcg)
else:
ndcg_scores.append(0.0)
return np.mean(ndcg_scores) if ndcg_scores else 0.0
总结
为RAG系统选择合适的Embedding模型至关重要,它直接决定了检索质量,进而影响生成答案的准确性。我在验证RAG的稳定性时经常使用两种以上的模型进行调试,如Qwen3、BAAI等开源模型系列。最后,希望大家在落地RAG的时候都能为RAG找到合适的"心脏"。
参考资料
- RAG系统嵌入模型怎么选?选型策略和踩坑指南
- 从 Word2Vec 到 LLM2Vec:如何为 RAG 选择合适的嵌入模型
- Embedding模型本地部署及其作用解析
- A comprehensive benchmark of single-cell Hi-C embedding tools
- 阿里开源通义千问3向量模型