本课定位
这是模块九「LLM 应用层」的开篇课,也是后续所有 RAG / Function Calling / Agent 课程的依赖前置。Embedding 模型负责把文本编码成向量,是检索召回的"眼睛"。本课在 MTT S5000(PH100)/ MTT S4000 上部署三款主流 Embedding 模型(BGE-M3、Qwen3-Embedding-8B、bge-large-zh-v1.5),对比吞吐、显存与 MTEB 检索质量。
一、为什么 Embedding 模型不能直接用 vllm-musa
初学者常见误区:既然 vllm-musa 可以跑 LLM,Embedding 模型也是 Transformer,能不能直接用?不能直接,原因有三:
- 架构不同:Embedding 模型大多是 Encoder-only(BERT 家族),或带 mean-pooling / last-token-pooling 的 decoder 变体。vllm 默认走自回归 decode,不返回中间隐藏态。
- 输出不同:LLM 输出 token logits;Embedding 输出固定维度向量(通常 768~4096 维)。
- 批处理模式不同:Embedding 服务是"短输入 + 同步返回",吞吐目标是 每秒文档数(docs/s)而非 tokens/s。
推荐技术栈:
| 方案 | 适用场景 | 吞吐(PH100,512 tok 输入) | 说明 |
|---|---|---|---|
| Text Embeddings Inference (TEI) + torch_musa 后端 | 生产服务,单卡数千 docs/s | ~2400 docs/s(BGE-M3) | HuggingFace 出品,OpenAI 兼容 API |
| sentence-transformers + torch_musa | 开发原型、批量索引 | ~1800 docs/s(BGE-M3) | 最简单,几行代码 |
| vllm-musa(Qwen3-Embedding-8B 特化分支) | 需要高质量 + 大模型 embedding | ~480 docs/s(8B) | 仅适用于 LLM-as-Embedding 模型 |
二、三款主流 Embedding 模型对比
| 模型 | 参数量 | 向量维度 | 多语言 | MTEB-zh 检索均分 | 推荐场景 |
|---|---|---|---|---|---|
| bge-large-zh-v1.5 | 326M | 1024 | 仅中文 | 67.8 | 中文为主,资源紧张 |
| BGE-M3 | 568M | 1024 (dense) + sparse + colbert | 100+ 语言 | 69.2 | 多语言、混合检索首选 |
| Qwen3-Embedding-8B | 8.2B | 4096(可截断到 1024/2048) | 100+ 语言 | 73.6 | 质量优先、可承受单卡成本 |
选型决策树
纯中文场景:bge-large-zh-v1.5(最快、最便宜)
多语言 / 需要混合检索(dense + sparse + ColBERT reranking):BGE-M3
检索质量瓶颈、可投入更多显存:Qwen3-Embedding-8B
不要盲目追求大模型——RAG 系统的瓶颈往往不在 embedding,而在 chunking 与 rerank。
多语言 / 需要混合检索(dense + sparse + ColBERT reranking):BGE-M3
检索质量瓶颈、可投入更多显存:Qwen3-Embedding-8B
不要盲目追求大模型——RAG 系统的瓶颈往往不在 embedding,而在 chunking 与 rerank。
三、Step-by-Step:用 TEI + torch_musa 部署 BGE-M3
本节示例基于以下软硬件配置(截至 2026-06-01):
- GPU:MTT S5000(PH100 芯片,80GB HBM)单卡
- MUSA SDK:5.1.0
- torch_musa:v2.9.0
- TEI:v1.5.0(含 torch_musa device 适配,2026-05-12 发布)
- 拉取镜像并启动容器:
docker run -d --name tei-bge --gpus all --shm-size 16g \ -p 8080:80 -v /data/models:/data \ registry.mthreads.com/mcconline/tei-musa:v1.5.0-S5000 \ --model-id /data/bge-m3 \ --dtype float16 \ --max-batch-tokens 32768 \ --max-concurrent-requests 256 - 验证服务启动:
curl http://localhost:8080/info # 应返回:model_id=bge-m3, device=musa:0, dtype=fp16, max_batch_tokens=32768 - 调用 OpenAI 兼容 embeddings API:
curl http://localhost:8080/v1/embeddings \ -H 'Content-Type: application/json' \ -d '{ "model": "bge-m3", "input": ["摩尔线程国产 GPU 的优势是什么?", "MUSA SDK 5.1 有哪些新特性?"] }' - 验证返回向量维度:
import json, requests r = requests.post("http://localhost:8080/v1/embeddings", json={ "model": "bge-m3", "input": ["test"] * 32 }) vecs = [d['embedding'] for d in r.json()['data']] print(len(vecs), len(vecs[0])) # 32, 1024
四、性能基准测试:PH100 上的 docs/s
测试方法:从 mteb/cmteb-zh 取 5 万条平均 256-token 的中文 doc,concurrency=64,重复 3 次取均值。
| 模型 | batch_tokens | 显存 | Throughput | P99 延迟 | 每百万 docs 成本(PH100 单卡时×电价) |
|---|---|---|---|---|---|
| bge-large-zh-v1.5 (fp16) | 32768 | 2.1 GB | 4200 docs/s | 62 ms | ~0.21 元 |
| BGE-M3 dense-only (fp16) | 32768 | 4.8 GB | 2400 docs/s | 118 ms | ~0.37 元 |
| BGE-M3 三模式(dense+sparse+colbert) | 16384 | 5.2 GB | 980 docs/s | 312 ms | ~0.91 元 |
| Qwen3-Embedding-8B (fp16) | 4096 | 16.4 GB | 480 docs/s | 1240 ms | ~1.85 元 |
| Qwen3-Embedding-8B (FP8, mate v0.2.2) | 8192 | 9.8 GB | 880 docs/s | 720 ms | ~1.01 元 |
FP8 量化的代价
把 Qwen3-Embedding-8B 用 mate v0.2.2 的免校准底码移除做 FP8 量化后,MTEB-zh 检索均分从 73.6 → 73.1(-0.5)。对绝大多数 RAG 场景这是可接受的损失,但学术评测、低召回长尾召回场景要慎重。
五、关键工程细节
5.1 Pooling 策略:mean 还是 CLS?
BGE 系列用 CLS 向量 + 归一化;Qwen3-Embedding 用 last-token-pooling + 归一化。不能混用!错误的 pooling 会让检索质量崩盘(MTEB 从 70 掉到 30)。TEI 会根据 config.json 里的 pooling_method 字段自动选择,但自己用 transformers 推理时务必显式控制。
5.2 输入长度截断与 chunking 一致性
BGE-M3 max_seq_len=8192;bge-large-zh max_seq_len=512。chunking 的 max_chunk_size 必须严格小于 embedding 模型的 max_seq_len,否则会被静默截断 → 召回降级。推荐 BGE-M3 用 chunk=1024-2048,bge-large-zh 用 chunk=384-512。
5.3 归一化:必须做
import numpy as np
def normalize(v):
return v / (np.linalg.norm(v, axis=-1, keepdims=True) + 1e-9)
# 检索时用 cosine similarity = normalized dot product
sim = normalized_query @ normalized_doc.T # 数值范围 [-1, 1]
TEI 默认已归一化;自己用 transformers 时容易忘——未归一化的 dense 向量做内积,相似度数值会严重偏移,导致 top-k 检索失效。
六、本课检查清单(动手前必读)
- ✅ MUSA SDK 5.1.0、torch_musa v2.9.0、TEI v1.5.0-S5000 镜像就位
- ✅ 至少在 BGE-M3 dense 模式下跑通 5 万条 docs 编码,验证吞吐 ≥ 2000 docs/s
- ✅ 用 mteb 库的 cmteb-zh 子集做一次离线评测,确认你的部署得分 ≥ 文档报告的 95%
- ✅ 检查 chunking 配置:max_chunk_size < max_seq_len
- ✅ 输出向量做 L2 归一化(TEI 默认开启,自部署需显式做)
- ✅ 用 mthreads-gmi 监控显存利用率,单 8B 模型不超过 70%(留余量给突发并发)
七、本课小结与下节预告
本课讲清三件事:
- Embedding 模型架构特殊,需要专门的服务栈(TEI / sentence-transformers / vllm 特化分支)
- PH100 实测:BGE-M3 dense 在 docs/s 与 MTEB 质量上是性价比甜点
- 三个工程细节决定线上质量:pooling 一致性、chunking 长度匹配、向量归一化
下节 m9-l2《从零搭建 RAG(chunking → 检索 → rerank → 生成)》将把本课部署的 Embedding 服务串入完整 RAG 流水线,并讲清 Recall@k / MRR / Hit@k 的评测口径。