选错 embedding 模型,整个 RAG 都白做
选错 embedding,后面再调都白调
embedding 是 RAG 的召回器——它从你的几万到几百万条 chunk 里挑出"可能相关的几十条"交给后续环节,挑错了后面再强的 LLM 也变不出答案。
麻烦在于市面上的 embedding 模型现在有几十个,光 BGE 一个系列就十几个变体,每个看起来都说自己 SOTA。新手大多数会落进两个坑:要么照搬国外早年的教程选 OpenAI text-embedding-ada-002(已经全面落后了,新项目千万不要选),要么直接捞 MTEB 排行榜冠军(很可能是个 7B 参数的 LLM-based 模型,1 万条 chunk 嵌入要跑几小时)。
真正决定选哪个的,不是榜单分数,是你的硬约束。下面按"硬约束 → 软指标 → 候选模型 → 决策树"这条链一节节展开。
三道硬约束,先把候选砍到三五个
选型本质上是这样:先用硬约束砍掉绝大多数候选,再用软指标在剩下的里挑。混着看是新手最常犯的错。
第一道硬约束是语种。这是最容易看走眼的——一个模型在 MTEB 英文榜冠军,C-MTEB 中文榜可能差三档。原因不复杂:它训练时见过的中文语料就那么多。中英混合产品里如果用了纯英文模型,会出现"用户用中文问搜不到、英文问能搜到"的诡异 case。所以第一步先把候选限定到"覆盖你语种"那一栏。
第二道是合规与部署。能不能让数据出公网?金融、医疗、政府客户合同里经常写死了"任何用户输入不得发往境外服务"——这条直接判 OpenAI / Voyage / Cohere 出局,候选只剩自托管开源模型。即使没有这种硬合规,也常因为"调用量太大、API 费用爆炸"而被迫自托管——这是软约束变硬约束的常见路径。
第三道是预算。两个层面:API 路线按 token 收费,看起来便宜,规模上来非常可观——10 亿 chunk 用 OpenAI text-embedding-3-small($0.02/M token)就是 2 万美元一次嵌入,你重新嵌一遍又来一次。自托管路线没有 token 费,但要算 GPU 摊销 + 运维人力,小规模反而比 API 贵。
三道硬约束筛完,候选通常就只剩三五个了。这时候再看软指标。
三道软指标,在剩下的里挑
维度 决定向量库的存储和检索成本——它们和维度成线性关系。常见 384 / 768 / 1024 / 1536 / 3072,多数中文场景 1024 维是甜区。番外 09 详细聊过这个权衡。
上下文长度 直接限制你切片的上限——bge-large-zh-v1.5 上下文 512,超过就被静默截掉,没有报错(番外 13 会讲这是切片环节常见的坑);bge-m3 8K,是开源里最舒服的。
MTEB / C-MTEB 分数 是综合性能榜单。看的时候要看 Retrieval 这一栏而不是总分——你做 RAG 关心检索任务,分类、聚类的分数对你没意义。同时警惕榜单过拟合:有些模型为了刷分对评测集做了优化,真实业务上未必好。榜单分相近的两个模型实战可能差很多。
三道硬约束 + 三道软指标,按这个顺序看。下面分两条路线展开候选模型——API 路线和开源 / 本地路线。
路线一:API 模型
API 模型的优势是零运维——一行代码能跑,质量也通常稳定。代价是按 token 付费、数据要出公网、不可控(厂商可能下线或调价)。适合个人项目、原型阶段、合规要求不高的场景。
OpenAI text-embedding-3-small / text-embedding-3-large
OpenAI 当前的主力 embedding 系列,2024 年初发布,质量明显超越前一代 ada-002。
| 项 | small | large |
|---|---|---|
| 维度 | 1536(默认)/ 可降至 256 起 | 3072(默认)/ 可降至 256 起 |
| 上下文 | 8192 tokens | 8192 tokens |
| 价格 | $0.02 / 百万 token | $0.13 / 百万 token |
| MTEB | 中等偏上 | 当前 API 一档前列 |
text-embedding-3 系列用了 Matryoshka Representation Learning(番外 10 讲过),传 dimensions=256 参数能直接拿到前 256 维。这对成本敏感的场景非常友好——降到 256 维存储成本是 1/12,质量损失通常不到 5%。
中文表现不算最强,但在中英混合场景里足够用。如果你的业务以英文为主、对中文是兼容需求,这个就行。ada-002 已经全面落后,新项目不要选。
from openai import OpenAI
client = OpenAI()
resp = client.embeddings.create(
model="text-embedding-3-small",
input=["你好世界", "Hello world"],
dimensions=256, # 可选,Matryoshka 截断
)
vectors = [item.embedding for item in resp.data]
Voyage AI(voyage-3、voyage-3-large)
2024 年起 MTEB 上常常霸榜的新势力,专门做 embedding 和 reranker。voyage-3-large 在 MTEB 上整体超过 OpenAI text-embedding-3-large,价格也接近——voyage-3-large 是 $0.18 / 百万 token。对质量敏感的英文场景值得考虑。Anthropic 推荐的官方 embedding 配套就是 Voyage。
中文支持有 voyage-multilingual-2,但中文场景上不如下面要讲的开源 BGE-m3 稳定。
Cohere embed-v3
Cohere 的 embed-multilingual-v3.0 支持 100+ 种语言,1024 维。它最有意思的是 Matryoshka + binary embeddings 的工程友好性——官方提供 int8 和 binary 两种压缩输出,部署到大规模向量库时存储能省 32 倍。
价格:$0.10 / 百万 token。质量在多语言场景里稳定,但单中文上不如 BGE-m3。
Jina jina-embeddings-v3
德国公司 Jina AI 的多语言 embedding,1024 维(Matryoshka 可降到 32)、上下文 8192。开源和 API 双轨——既可以付费用 API($0.018 / 百万 token,比 OpenAI 还便宜),也可以下载模型自己跑。中文表现中等,多语言均衡。
路线二:开源 / 本地模型
数据合规要求高、调用量极大、或想做领域微调时,自托管开源模型是必选。模型权重几百 MB 到几 GB,CPU 也能跑(短文本几十毫秒),有 GPU 自然更快。
BGE 系列(北京智源研究院)
中文 RAG 领域的事实标准,bge 是 BAAI General Embedding 的缩写。值得记住的有三个:
| 模型 | 维度 | 上下文 | 语种 | 大小 | 何时用 |
|---|---|---|---|---|---|
BAAI/bge-m3 | 1024 | 8192 | 100+ 多语言 | 2.3 GB | 默认首选,中文和中英混合都最强 |
BAAI/bge-large-zh-v1.5 | 1024 | 512 | 纯中文 | 1.3 GB | 纯中文短文本检索 |
BAAI/bge-large-en-v1.5 | 1024 | 512 | 纯英文 | 1.3 GB | 纯英文短文本检索 |
bge-m3 是 90% 的中文 RAG 项目应该选的起点。它支持 100 多种语言、上下文 8K(很关键,很多文档切片都比 512 长)、同时支持稠密向量、稀疏向量(类似 BM25)、ColBERT 多向量三种模式。如果你只学一个开源 embedding 模型,学它。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-m3")
vectors = model.encode(
["你好世界", "Hello world"],
normalize_embeddings=True, # 直接做余弦相似度
)
print(vectors.shape) # (2, 1024)
M3E(Moka Massive Mixed Embedding)
国产 embedding 早期的代表,有 m3e-base(768 维)和 m3e-large(1024 维)。在 BGE 之前是中文 RAG 的常规选择。现在主流推荐已经是 BGE-m3,但很多老项目还在用 M3E——遇到不要慌,知道它的位置就好。新项目没必要选 M3E,BGE-m3 在大多数中文任务上已经超过它。
GTE 系列(阿里达摩院)
gte-multilingual-base(304M 参数,768 维,多语言)和 gte-Qwen2-7B-instruct(基于 Qwen2 的 LLM-based embedding,3584 维)。后者特殊在它是用 LLM 当底座做对比学习训练的,质量高但模型大、推理慢,适合对召回质量要求极高的场景。MTEB 上 LLM-based embedding 经常占据榜首位置——但成本对应也高。
Stella、Nomic、Mixedbread 等
英文场景里的小众但高性能选项。Stella 系列在 MTEB 英文榜上排名靠前,nomic-embed-text 是开源 + 完全开放的训练数据,mxbai-embed-large-v1 在二值化检索上做得不错。中文场景不推荐。
NV-Embed-v2(NVIDIA)
基于 Mistral-7B 的 LLM-based embedding,4096 维。MTEB 英文榜历史最高分之一。但模型太大(14GB),推理需要 GPU,部署成本高。中文支持有限。
MTEB 榜单怎么读
MTEB 排行榜 是当前最权威的综合评估,但不要无脑看总分排名,几条经验:
看你的实际任务列。 MTEB 包括 56 个任务(检索、聚类、分类、语义相似、问答匹配等),如果你做 RAG,最相关的是 Retrieval 这一栏的分数。一个模型在分类上很强不代表它在检索上也强。
看语种。 英文 MTEB 和中文 C-MTEB 分别看。一个模型在英文榜冠军可能在中文上拉胯。
警惕过拟合榜单的模型。 有些模型为了刷分专门在 MTEB 训练数据上做了优化,真实业务数据上未必好。MTEB 分相近的两个模型(差 1-2 分),实战表现可能完全相反。最终一定要在你自己的业务数据上做评测——这个评测怎么搞,番外 15 专门讲。
注意模型大小。 榜单前几名的 LLM-based embedding 通常 7B+ 参数,推理 1 万条 chunk 可能要几小时。如果你的业务规模不到百万级,用更小但 90% 性能的模型常常更划算。
中文场景具体怎么选
把上面所有信息浓缩成几句大白话——
刚开始做 RAG,不知道选什么:直接 BAAI/bge-m3。1024 维、8K 上下文、中英混合都强、开源免费、社区文档最齐全。这是 90% 中文 RAG 项目正确的起点。先把它跑起来做出 baseline,其他都是后话。
不能或不想自托管:起点 OpenAI text-embedding-3-small,便宜、能用。规模上去(每月 10 亿+ token)再考虑换 Voyage voyage-3-large 或 Cohere——它们质量更好但贵。再强调一遍:text-embedding-ada-002 已经全面落后,新项目别选。
纯英文且追求最高质量:Voyage voyage-3-large 或 NV-Embed-v2(自托管)。但说实话这种诉求不多——多数业务上 bge-m3 已经够了,"再上个 5%" 的边际收益往往配不上工程成本。
垂直领域(医疗、法律、金融、代码):先用 bge-m3 跑 baseline,然后用自己的领域语料做对比学习微调(sentence-transformers 几行代码就能跑)。领域适配的中等模型几乎总是优于换一个更大的通用模型——这条经验在番外 15 评估那一篇会反复印证。
存储吃紧(向量数 1000 万条以上):选支持 Matryoshka 的模型(text-embedding-3-large、jina-embeddings-v3、bge-m3 都支持),用前 256 或 512 维就够。或者直接上 binary embedding(番外 10 提过),32 倍压缩。
最后一条比上面所有都重要:永远在自己的业务数据上跑评测。MTEB 是参考、不是答案。两个 MTEB 分接近的模型,在你的业务上可能一个 Recall@10 = 0.85、另一个 0.65——只有自己的评测能告诉你哪个对。番外 15 详细讲怎么搭这套评测。
一个简单的对比脚本
最后给一个真·实战可用的小工具——把同一组测试句子喂给三个候选模型,看它们的相似度排序是否和你的直觉一致。
import numpy as np
from sentence_transformers import SentenceTransformer
from openai import OpenAI
# 三个候选模型
local_models = {
"bge-m3": SentenceTransformer("BAAI/bge-m3"),
"m3e-base": SentenceTransformer("moka-ai/m3e-base"),
}
openai_client = OpenAI()
def embed_local(model, texts):
return model.encode(texts, normalize_embeddings=True)
def embed_openai(texts, model_name="text-embedding-3-small"):
resp = openai_client.embeddings.create(model=model_name, input=texts)
vecs = np.array([item.embedding for item in resp.data])
return vecs / np.linalg.norm(vecs, axis=1, keepdims=True)
# 你自己业务里抽 10 对"应该相似"的句子
test_pairs = [
("如何重置密码", "忘记密码怎么办"),
("Python 异步编程", "asyncio 用法详解"),
("注册流程", "如何成为新用户"),
# ...至少 20 对,越多越能反映真实业务
]
for name, m in [("bge-m3", local_models["bge-m3"]),
("m3e-base", local_models["m3e-base"])]:
sims = []
for q, d in test_pairs:
vq, vd = embed_local(m, [q, d])
sims.append(float(vq @ vd))
print(f"{name:20s} 平均相似度: {np.mean(sims):.3f} 最低: {np.min(sims):.3f}")
# OpenAI 同理
sims = []
for q, d in test_pairs:
vq, vd = embed_openai([q, d])
sims.append(float(vq @ vd))
print(f"{'text-embedding-3-small':20s} 平均相似度: {np.mean(sims):.3f} 最低: {np.min(sims):.3f}")
平均相似度高 + 最低相似度也不太低,说明这个模型在你的业务上稳。还有一种更严谨的做法是同时准备"应该不相似"的负例对,确保模型在它们上的相似度足够低——这就是 MTEB 检索任务的简化版评测,番外 15 会展开。
收尾
embedding 模型选型不是"找一个最强的",是"找一个在你的语种、长度、预算、合规边界内最合适的"。中文场景 90% 的项目用 BGE-m3 起步就够;嫌部署麻烦用 text-embedding-3-small;规模和质量都吃紧再去看 Voyage / Cohere / NV-Embed。
下一篇番外 13 我们讲 RAG 的另一个生死决定——文本切分。embedding 模型选得再准,切片切错了召回还是会糟。
参考资料
- MTEB 排行榜 — 全球 embedding 综合评测
- C-MTEB(中文 MTEB) — 中文场景必看
- BGE 系列模型仓库 — bge-m3 / bge-large-zh 等
- BGE-M3 论文 — 多功能 / 多语言 / 多粒度 embedding
- OpenAI Embeddings 文档
- Voyage AI 文档
- sentence-transformers 文档
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:番外 12:Embedding 模型选型实战
本文链接:https://www.sshipanoo.com/blog/ai/ai-for-python/番外12-Embedding模型选型/
本文最后一次更新为 天前,文章中的某些内容可能已过时!