未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用范围与待维护事项
本文用于 RAG/语义搜索的候选模型初筛与教学验证,不构成生产选型、容量或费用承诺。“2025 年”榜单、MTEB 规模、模型参数、API 定价与限额尚未按具体版本和账户核验;性能表、FP16 加速、维度缩减及量化损失数字保留为原稿未核实参考,不可用于采购或 SLA。上线前须用目标语料评测,核对模型许可、数据出境条件和 SDK;示例未覆盖 token 校验、空文本业务规则、鉴权、背压及异步推理隔离。
Embedding 模型选型指南
tip 前置阅读
建议先读 Embedding工程概览 了解背景,再看本文的具体选型。
选模型这件事,很多人的做法是:看到哪个名字熟悉就用哪个。这可能导致一个结果:未经目标语料评测就用某个模型处理中文法律文书,效果不符合预期,然后怀疑 Embedding 这个技术本身有问题;不能据此断言 OpenAI 模型在该场景必然效果差。
问题不在技术,在选型。本文给你一套有依据的选型方法。
MTEB:评估 Embedding 模型的标准
什么是 MTEB
MTEB(Massive Text Embedding Benchmark)是目前业界公认的 Embedding 模型评测基准,由 HuggingFace 维护。
规模:
- 56 个任务,覆盖检索、分类、聚类、语义相似度等类型
- 112 种语言,包含完整的中文评测集(C-MTEB)
- 排行榜实时更新:https://huggingface.co/spaces/mteb/leaderboard
如何读懂 MTEB 榜单
榜单上最重要的指标不是 Average(平均分),而是 Retrieval(检索)任务的得分。
原因:RAG 和语义搜索的核心操作是"给定 query,找最相关文档",这正是 Retrieval 任务考察的。一个模型在分类任务上很强,不代表它检索效果好。
important 核心原则
选型时,优先看目标场景对应的子任务得分,而不是 Average。中文场景看 C-MTEB/Retrieval;英文场景看 BEIR 子集。
2025 年 C-MTEB 头部模型
以下为原稿列出的模型候选与参数参考,重点关注中文检索能力;未提供可复核的 2025 年榜单快照及评测配置,表中定性评价不应视为已验证的 C-MTEB 排名或得分:
| 模型 | 参数量 | C-MTEB 平均 | 向量维度 | 最大 Token | 推荐场景 |
|---|---|---|---|---|---|
| BAAI/bge-m3 | 568M | 待目标任务评测 | 1024 | 8192 | 中英混合,生产候选 |
| BAAI/bge-large-zh-v1.5 | 326M | 待中文任务评测 | 1024 | 512 | 纯中文,评测效果/成本平衡 |
| BAAI/bge-base-zh-v1.5 | 102M | 良好 | 768 | 512 | 纯中文,轻量化 |
| jinaai/jina-embeddings-v3 | 570M | 优秀 | 1024(可变) | 8192 | 多语言,支持 Matryoshka |
| OpenAI/text-embedding-3-large | 未知 | 优秀(英文更强) | 256-3072 | 8191 | 英文为主,预算充足 |
| OpenAI/text-embedding-3-small | 未知 | 良好 | 512-1536 | 8191 | 快速原型,成本敏感 |
| shaw/dmeta-embedding-zh | 102M | 中文良好 | 768 | 512 | 极轻量,资源受限场景 |
note 关于 bge-m3
bge-m3 支持三种检索模式:Dense(稠密向量)、Sparse(稀疏向量,类似 BM25)、ColBERT(多向量交互)。生产环境可以评测 Dense + Sparse 混合检索,是否提升召回率及提升幅度取决于语料、查询和融合策略。
选型决策树
%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TD
A[开始选型] --> B{内容是否包含中文?}
B -->|是| C{数据是否敏感?}
B -->|否| D{预算是否充足?}
C -->|数据敏感/不能出境| E{GPU 显存 >= 8GB?}
C -->|不敏感| F[OpenAI text-embedding-3-small\n或 Cohere embed-multilingual-v3]
E -->|是| G{是否有垂直领域专业术语?}
E -->|否| H[BAAI/bge-base-zh-v1.5\n或 shaw/dmeta-embedding-zh\nCPU 可运行]
G -->|否,通用场景| I[BAAI/bge-m3\n或 bge-large-zh-v1.5\n生产候选,需评测]
G -->|是,领域术语密集| J[先评测 bge-m3 基线\n有足够训练数据时考虑 Fine-tune\n见 02-工程实践]
D -->|预算充足| K[OpenAI text-embedding-3-large\n质量优先候选,按用量付费]
D -->|成本敏感| L[OpenAI text-embedding-3-small\n或 Cohere embed-english-v3]
style I fill:#d4edda,color:#000
style K fill:#d4edda,color:#000
style J fill:#fff3cd,color:#000
云端 API:OpenAI text-embedding-3 系列
两个模型的定位
| text-embedding-3-small | text-embedding-3-large | |
|---|---|---|
| 最大维度 | 1536 | 3072 |
| 常用维度 | 512 | 1024 或 3072 |
| 速度 | 快 | 慢 |
| 成本 | $0.02 / 1M tokens \vert{} $0.13 / 1M tokens | |
| 质量 | 良好 | 优秀 |
| 适用 | 原型、成本敏感 | 生产、质量优先 |
Matryoshka 维度缩减
OpenAI 的 text-embedding-3 系列支持 Matryoshka 表示学习:你可以在 API 请求时指定维度,截断后的向量仍然保持语义相对质量,不需要重新训练模型。
实际意义:在存储成本和检索质量之间找平衡点。在元素类型相同且仅计向量本体时,512 维所需存储为 3072 维的 1/6,约节省 83.3%;原稿所列“质量损失通常在 5% 以内”未提供评测依据,仅作未核实参考,实际损失需按目标任务指标测量。
批量调用最佳实践
import openai
import time
from typing import List
client = openai.OpenAI(api_key="your-api-key")
def embed_texts_batch(
texts: List[str],
model: str = "text-embedding-3-small",
dimensions: int = 512,
batch_size: int = 100,
max_retries: int = 3,
) -> List[List[float]]:
"""
批量 Embedding,带重试和速率限制处理。
原稿参考限制:每分钟 1M tokens,单次请求最多 2048 个文本;未核实。
实际限额须按账户、模型和当前官方文档确认,不能视为通用保证。
batch_size=100 仅为示例值,还需校验单条及整批 token 数。
"""
if batch_size <= 0 or max_retries <= 0:
raise ValueError("batch_size 和 max_retries 必须为正整数")
all_embeddings = []
for i in range(0, len(texts), batch_size):
batch = texts[i : i + batch_size]
# 空文本替换为占位文本以避免 API 报错;生产中须明确该替换的业务含义
batch = [t.strip() for t in batch]
batch = [t if t else "空" for t in batch]
for attempt in range(max_retries):
try:
response = client.embeddings.create(
input=batch,
model=model,
dimensions=dimensions,
)
embeddings = [item.embedding for item in response.data]
all_embeddings.extend(embeddings)
break
except openai.RateLimitError:
if attempt == max_retries - 1:
raise
wait_time = 2 ** attempt # 指数退避:1s, 2s, 4s
print(f"Rate limit,等待 {wait_time}s 后重试...")
time.sleep(wait_time)
except openai.APIError as e:
if attempt == max_retries - 1:
raise
print(f"API 错误 {e},重试 {attempt + 1}/{max_retries}")
time.sleep(1)
# 批次间稍作停顿,避免触发速率限制
if i + batch_size < len(texts):
time.sleep(0.1)
return all_embeddings
# 使用示例
texts = ["这是第一个文档", "这是第二个文档", "This is English text"]
embeddings = embed_texts_batch(texts, dimensions=512)
print(f"生成 {len(embeddings)} 个向量,维度 {len(embeddings[0])}")
本地部署:BAAI/bge-m3
bge-m3 是 BAAI(北京智源人工智能研究院)发布的多语言 Embedding 模型,可作为中文检索任务的开源候选;其相对效果应通过目标语料评测确定。
最简单的方式:sentence-transformers
from sentence_transformers import SentenceTransformer
import torch
# 自动下载模型(约 2GB),第一次慢,之后用缓存
model = SentenceTransformer(
"BAAI/bge-m3",
device="cuda" if torch.cuda.is_available() else "cpu",
)
texts = [
"BGE M3-Embedding是一个支持多语言的嵌入模型",
"This is an English sentence",
"日本語のテキスト",
]
# normalize_embeddings=True:归一化后余弦相似度等价于点积,向量数据库通常推荐开启
embeddings = model.encode(
texts,
batch_size=32,
normalize_embeddings=True,
show_progress_bar=True,
)
print(f"向量维度: {embeddings.shape}") # (3, 1024)
批量推理优化
import numpy as np
from sentence_transformers import SentenceTransformer
import torch
def load_optimized_model(model_name: str = "BAAI/bge-m3"):
"""
加载并优化模型:
- 半精度(FP16)减少一半显存占用,速度提升约 30%
- GPU 可用时自动使用
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
model = SentenceTransformer(model_name, device=device)
if device == "cuda":
model = model.half() # FP16
print(f"使用 GPU(FP16),显存占用约 1.1GB")
else:
print(f"使用 CPU,推理速度约 5-10 条/秒")
return model
def batch_encode(
model: SentenceTransformer,
texts: list[str],
batch_size: int = 64,
) -> np.ndarray:
"""
GPU 推荐 batch_size=64-128;CPU 推荐 batch_size=16-32。
batch_size 太大会 OOM,太小 GPU 利用率低。
"""
return model.encode(
texts,
batch_size=batch_size,
normalize_embeddings=True,
show_progress_bar=len(texts) > 100,
convert_to_numpy=True,
)
性能基准参考
| 硬件 | batch_size | 速度(条/秒) | 显存/内存占用 |
|---|---|---|---|
| A100 (FP16) | 128 | ~2000 | ~2GB 显存 |
| RTX 3090 (FP16) | 64 | ~800 | ~2GB 显存 |
| RTX 3090 (FP32) | 32 | ~400 | ~4GB 显存 |
| CPU (8核) | 16 | ~15 | ~4GB 内存 |
服务化:FastAPI 包装
from fastapi import FastAPI
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
import torch
app = FastAPI()
model = SentenceTransformer(
"BAAI/bge-m3",
device="cuda" if torch.cuda.is_available() else "cpu",
)
class EmbedRequest(BaseModel):
texts: list[str]
normalize: bool = True
class EmbedResponse(BaseModel):
embeddings: list[list[float]]
model: str
dimension: int
@app.post("/embed", response_model=EmbedResponse)
async def embed(request: EmbedRequest):
embeddings = model.encode(
request.texts,
normalize_embeddings=request.normalize,
convert_to_numpy=True,
)
return EmbedResponse(
embeddings=embeddings.tolist(),
model="BAAI/bge-m3",
dimension=embeddings.shape[1],
)
# 启动:uvicorn main:app --host 0.0.0.0 --port 8080 --workers 1
# 示例使用单 worker;多 worker 通常会各自加载模型,须评估显存与资源隔离,GPU 锁不会自动实现模型跨进程共享
warning 多进程问题
普通多 worker 部署不会自动共享同一份 GPU 模型,通常会重复加载并占用显存。如果需要高并发,可评估任务队列(如 Celery)或多实例负载均衡,但须明确各进程的模型加载、GPU 分配与并发上限;当前 async 路由直接执行同步推理,仍会阻塞事件循环。
Cohere Embed v3
Cohere 的 Embedding 模型提供 Input Type 参数,用于区分检索文档、检索查询等输入用途;任务区分机制并非 Cohere 独有。
Input Type:最容易被忽视的参数
import cohere
co = cohere.Client("your-api-key")
# 索引文档时:用 search_document
doc_embeddings = co.embed(
texts=["文档内容1", "文档内容2"],
model="embed-multilingual-v3.0",
input_type="search_document", # 关键参数!
)
# 查询时:用 search_query
query_embedding = co.embed(
texts=["用户的问题"],
model="embed-multilingual-v3.0",
input_type="search_query", # 关键参数!
)
为什么重要?查询和文档在语义空间中的分布不同:查询通常是短句、关键词;文档是完整段落。Cohere 用不同的 Input Type 分别优化两者的向量表示,让查询向量更容易找到对应的文档向量。
其他模型(如 bge)通过在查询前加前缀实现类似效果:
# 部分中文 BGE 检索模型的查询前缀示例;按具体模型说明使用,bge-m3 不要求添加该前缀
query = "为这个句子生成表示以用于检索相关文章:" + user_query
其他 Cohere 特性
- 多语言支持:embed-multilingual-v3.0 支持 100+ 语言
- int8 量化输出:向量从 float32 压缩为 int8,存储减少 75%,精度损失约 1-2%
- embedding_types 参数:一次请求同时返回 float、int8、binary 三种格式
response = co.embed(
texts=docs,
model="embed-multilingual-v3.0",
input_type="search_document",
embedding_types=["float", "int8"], # 同时获取两种格式
)
float_vecs = response.embeddings.float # 高精度,用于精排
int8_vecs = response.embeddings.int8 # 低精度,用于粗筛,节省存储
选型总结:直接给结论
以下按场景给出候选建议,仍需结合目标语料、成本、数据合规及部署条件验证:
| 场景 | 推荐模型 | 理由 |
|---|---|---|
| 中文生产环境,数据敏感 | BAAI/bge-m3 | 本地部署候选;原稿列出长文本上限(8192 token),具体配置需核对 |
| 纯中文,资源受限(< 4GB 显存) | BAAI/bge-base-zh-v1.5 | 102M 参数,CPU 可运行,效果仍然良好 |
| 中英混合,云端 API | OpenAI text-embedding-3-small | 按文中未核实价格,small 费用约为 large 的 1/6.5;是否够用需评测 |
| 英文为主,质量优先 | OpenAI text-embedding-3-large | 质量优先候选,需对照英文目标任务评测 |
| 多语言,需要 int8 量化 | Cohere embed-multilingual-v3.0 | 原生支持量化,多语言强 |
| 极轻量,边缘设备 | shaw/dmeta-embedding-zh | 102M 参数,纯 CPU 也快 |
| 垂直领域(医疗/法律/金融) | bge-m3 + Fine-tune | 见 [[【技术与工具】/Embedding工程/02-Embedding工程实践 |
tip 一个快速验证方法
在正式选型前,用你自己的数据做一个 10 分钟的快速测试:准备 50-100 个问答对,分别用候选模型计算 query 和 document 的向量,看 Recall@5(前 5 个检索结果中命中的相关文档数,占该查询全部已标注相关文档数的比例,再对查询求平均;每个查询只有一个相关文档时等于前 5 名命中率)。前 5 个结果中相关文档所占比例是 Precision@5。该小样本测试比仅看 MTEB 榜单更贴近自己的场景,但不能替代完整评测。
延伸阅读
- Embedding工程概览 - 方案全景与背景
- Embedding工程实践 - 切片、Pipeline、Fine-tune 落地
- 向量数据库选型指南 - 向量存储层
- RAG 检索增强生成实战 - Embedding 的主战场
#Embedding #模型选型 #BGE #OpenAI #MTEB #RAG #语义搜索