Embedding 与向量化:从 Token 到语义搜索
📄摘要: Embedding(嵌入)是将离散符号映射至连续向量空间的核心技术,其「语义相近则向量相近」的基本性质使其成为语义搜索、RAG 检索与向量数据库的数学基石。本文首先严格区分了 Token Embedding(上下文无关的查表向量)与 Sentence Embedding(上下文感知的句级表示)两种易混淆的嵌入类型及其工程误区,随后系统对比了 LLM 自回归训练与专用 Embedding 模型对比学习范式之间的本质差异,并梳理了主流嵌入模型的选型参考。在应用层面,本文深入解析了语义搜索的三步流程、三种向量相似度度量、ANN 近似最近邻搜索的核心算法(HNSW、IVF、PQ)及其精度与速度权衡,以及混合搜索作为 RAG 系统最稳健检索方案的技术原理。
1. Embedding(嵌入)基本概念
Embedding(嵌入) 是将离散的符号(文字、图片、用户 ID)映射为连续向量空间中的点。其核心思想是:语义相近的东西,在向量空间中距离也近。
使用类比理解 Embedding:
想象你有一张巨幅世界地图——
传统表示: "巴黎" = 一个离散符号,计算机只知道这是字符串 "巴黎"
Embedding: "巴黎" = [0.82, -0.31, 0.45, ..., 0.17] (768 维向量)
"上海" = [0.79, -0.28, 0.41, ..., 0.15]
"狗" = [-0.12, 0.88, -0.33, ..., -0.56]
向量空间中:
巴黎 vs 上海: cosine_sim ≈ 0.92 → 都是大城市!
巴黎 vs 狗: cosine_sim ≈ 0.03 → 完全无关2
3
4
5
6
7
8
9
10
11
12
1.1 Token Embedding vs Sentence Embedding
两个关键维度区分:
这两种 Embedding 服务于完全不同的场景,核心区别在于:粒度不同、上下文感知不同、用途不同。
| 维度 | Token Embedding(词元嵌入) | Sentence Embedding(句嵌入) |
|---|---|---|
| 粒度 | 一个 Token → 一个向量 | 一整句话/段落 → 一个向量 |
| 上下文感知 | 上下文无关:同一个「苹果」无论出现在科技新闻还是水果食谱,Embedding 层的向量都一样 | 上下文相关:「苹果很好吃」和「苹果发布了新品」得到完全不同的整体向量 |
| 上下文来源 | 自身不带上下文,需通过后续 Attention 层融入 | 向量本身已经编码了整个句子的语义 |
| 获取位置 | LLM 的第一层(Embedding 层),训练前就已存在 | 从模型中间层或专用模型提取,是计算后的结果 |
| 典型维度 | 768 ~ 12288(与模型 d_model 一致) | 768 / 1024 / 4096(专用 Embedding 模型) |
| 主要用途 | LLM 内部表示——作为 Attention/FFN 的输入 | 语义搜索、文本聚类、相似度计算、RAG 检索 |
| 典型模型 | GPT、LLaMA 的 Embedding 层 | text-embedding-3、bge-large、GTE-Qwen2 |
Token Embedding 工作方式:
"苹果很好吃" → Tokenizer → ["苹果", "很", "好吃"]
│ │ │
▼ ▼ ▼
查 Embedding 表,各自独立得到向量(此时三个向量互不感知)
│ │ │
└────────────┴────────────┘
│
▼
进入 Attention 层后才开始互相"交流"2
3
4
5
6
7
8
9
Sentence Embedding 工作方式:
"苹果很好吃" ──→ Embedding 模型 ──→ [0.15, 0.83, -0.42, ...] ← 一个向量代表整句
"苹果发布了新品" ──→ 同一模型 ──→ [0.72, -0.31, 0.58, ...] ← 截然不同的向量
虽然都包含"苹果",但两句的整体向量差异很大:
cosine_sim("苹果很好吃", "苹果发布了新品") ≈ 0.32 ← 语义完全不同2
3
4
5
为什么这个区分很重要?
很多人混淆这两者,导致实际工程中用错工具。常见误区:想用 LLM 的 Token Embedding 做语义搜索——但 Token Embedding 是上下文无关的,同一个「苹果」永远是同一个向量,无法区分「吃苹果」和「买苹果手机」。
正确的做法是用专门的 Sentence Embedding 模型(如 text-embedding-3、bge-large)来生成句子级向量。
Embedding 维度与语义空间
Embedding 向量的维度(如 768、1024、4096)决定了语义空间的"分辨率"。类比理解如下:维度太低(如 2 维):
只能表达"正面-负面"这一个维度,无法区分"愤怒"与"悲伤"
维度适中(如 768 维):
可以同时表达"情绪""主题""风格""语言""正式度"等数十个语义维度
维度太高(如 4096 维):
表达力更强,但存储和计算成本更大;向量检索时维度灾难现象加剧2
3
4
5
6
7
8
常见模型的 Embedding 维度:OpenAI text-embedding-3-small(512/1536 可选)、BGE-M3(1024)、GTE-Qwen2-7B(3584)。
2. Embedding Model
Embedding 不是"大模型顺手做的事"——有专门的嵌入模型(Embedding Model)针对"把文本变成好向量"这个目标做专门训练。
LLM vs 专用 Embedding Model 的本质差异:
| LLM(如 GPT-4) | 专用 Embedding Model | |
|---|---|---|
| 目标 | 预测下一个 token(生成) | 让相似文本的向量近,不相似文本的向量远(对比学习) |
| 输出 | 概率分布(词表大小) | 固定长度向量(如 1536 维) |
| 训练范式 | 自回归(Next-Token Prediction) | 对比学习(Contrastive Learning) |
| 架构 | Decoder-Only Transformer | Encoder-Only 或 Decoder 池化 |
| 典型任务 | 聊天、写作、编程 | 搜索、聚类、分类、RAG 检索 |
主流专用 Embedding Model 一览:
| 模型 | 维度 | 多语言 | 特点 |
|---|---|---|---|
| OpenAI text-embedding-3-small | 512/1536 | 多语言 | API 调用,无需部署 |
| OpenAI text-embedding-3-large | 256/1024/3072 | 多语言 | 更强,更贵 |
| BGE-M3(BAAI) | 1024 | 中英为主 | 开源 SOTA,支持稠密+稀疏混合检索 |
| GTE-Qwen2-7B(阿里) | 3584 | 多语言 | 基于 Qwen2,中文表现顶尖 |
| Jina Embeddings v3 | 1024 | 多语言 | 支持任务特定 LoRA adapters |
| Cohere Embed v3 | 1024 | 多语言 | 压缩表示,适合大规模检索 |
| Nomic Embed Text | 768/1376 | 英文 | 全开源(数据+代码+权重) |
通用 LLM 做 Embedding:可行但"大材小用"。 你可以取 LLM 最后一层隐状态的均值作为文本 Embedding,但专用模型用更少的参数达到更好效果——7B 参数的 GTE-Qwen2 在 MTEB 检索基准上超越了多数通用 LLM。
3. 语义搜索(Semantic Search)
3.1 语义搜索的完整流程
语义搜索(Semantic Search) 是基于向量相似度匹配的搜索方式,核心流程只有三步:
语义搜索的完整流程:
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1. 向量化 │ │ 2. 相似计算 │ │ 3. 结果排序 │
│ │ │ │ │ │
│ 查询: "怎么 │ │ cos(查询, 文档1) │ │ 文档3: 0.92 │
│ 部署模型?" │ → │ cos(查询, 文档2) │ → │ 文档1: 0.87 │
│ ↓ │ │ cos(查询, 文档3) │ │ 文档2: 0.34 │
│ [0.1, -0.3] │ │ ... │ │ ... │
│ │ │ │ │ │
│ 文档库: │ │ │ │ │
│ 文档1 → [..]│ │ │ │ │
│ 文档2 → [..]│ │ │ │ │
│ 文档3 → [..]│ │ │ │ │
└─────────────┘ └──────────────┘ └──────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
三种向量相似度度量对比:
| 度量方式 | 公式 | 值域 | 适用场景 |
|---|---|---|---|
| 余弦相似度 | (A·B) / ( | A | · |
| 欧氏距离 | sqrt(Σ(Aᵢ-Bᵢ)²) | [0, ∞) | 关心绝对距离(长度+方向) |
| 点积 | A·B = Σ AᵢBᵢ | (-∞, ∞) | 向量已归一化时等价于余弦相似度 |
余弦相似度的几何直觉(二维示意):
查询向量 Q
↗
/ 夹角 ≈ 15°
/
* 文档向量 D₁(相关)
查询向量 Q
↗
/
/
←──→ 夹角 ≈ 130°
*
文档向量 D₂(不相关)
cos(15°) ≈ 0.97 → 高度相关!
cos(130°) ≈ -0.64 → 几乎无关2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ANN(Approximate Nearest Neighbor,近似最近邻搜索): 当文档库达到百万甚至亿级时,逐个计算余弦相似度不可行。ANN 算法(如 HNSW、IVF、PQ)通过牺牲一点精度换取巨大速度提升——检索 1 亿条向量从"几分钟"变成"几毫秒"。主流向量数据库(Milvus、Qdrant、Pinecone)在内部都使用了 ANN 索引。
3.2 ANN (Approximate Nearest Neighbor,近似最近邻搜索)
ANN 是一种在百万甚至亿级向量中快速找到与查询向量最相似的 Top-K 个向量的算法。它的核心取舍是:用少量精度换取数量级的搜索速度提升。
为什么需要 ANN?
暴力搜索(逐一对比所有向量)在数据量小时完全可行,但向量数据库的规模往往是百万到十亿级:
暴力搜索(Brute Force):
1 亿条向量 × 768 维浮点数 × 两次浮点运算 = 每秒约 5000 次比对
→ 检索一次需要 20000 秒 ≈ 5.5 小时 ❌
ANN 索引:
先在索引中快速定位候选区域(几毫秒),再对候选集做精确比对(几毫秒)
→ 检索一次只需 1~10 毫秒 ✅2
3
4
5
6
7
ANN 的基本原理——用索引代替全量对比:
ANN 的核心思路是在建库时花时间构建索引结构,把相似向量预组织在一起;查询时用索引快速缩小搜索范围,只在小范围内做精确计算:
| 阶段 | 操作 | 耗时 |
|---|---|---|
| 建库(离线) | 对所有向量建索引,相当于给整个向量空间画一张「地图」 | 数分钟到数小时(只做一次) |
| 查询(在线) | 根据查询向量在索引中定位候选区域,只计算候选区域内的向量 | 1~10 毫秒 |
主流 ANN 算法:
| 算法 | 原理 | 特点 |
|---|---|---|
| HNSW(分层可导航小世界图) | 构建多层图结构,上层做「高速公路」快速跳到目标区域,下层做精确查找 | 当前综合性能最强,查询快、召回率高,但内存开销较大 |
| IVF(倒排文件索引) | 用 K-Means 把所有向量聚成 N 个簇,查询时只在最近几个簇内搜索 | 简单高效,内存友好,适合大规模数据 |
| PQ(乘积量化) | 将高维向量切分成多段,每段独立做量化压缩,用近似距离代替精确距离 | 内存极度节省,但召回率略低,常与 IVF 组合使用 |
| IVF-PQ | IVF 做粗定位 + PQ 做压缩存储 | 工业界最常用的组合,兼顾速度与内存 |
| DiskANN | 将索引存在 SSD 上,只按需加载需要的部分到内存 | 超大规模(十亿级)、内存有限时的首选 |
HNSW 直观理解:
层级 2(稀疏,长距离跳跃):
● ──────────── ●
层级 1(中等密度):
● ───● ───● ───●
层级 0(最密,精确搜索):
●─●─●─●─●─●─●─●
查询路径:从顶层快速跳到目标区域 → 逐层下降 → 底层精确比对 → 返回结果2
3
4
5
6
7
8
9
10
11
精度 vs 速度的权衡:
ANN 索引通常有一个可调节的参数来控制这个权衡。以 HNSW 为例,ef_search 越大(搜索时探索更多候选),召回率越高但越慢:
| 配置 | 召回率 | 查询延迟 | 适用场景 |
|---|---|---|---|
| ef_search=16 | ~90% | ~1ms | 对延迟敏感的在线服务 |
| ef_search=64 | ~97% | ~3ms | 一般推荐场景 |
| ef_search=256 | ~99.5% | ~10ms | 对精度要求极高的场景 |
主流向量数据库的 ANN 实现:
| 向量数据库 | ANN 算法 | 特点 |
|---|---|---|
| Milvus | HNSW, IVF-PQ, DiskANN | 功能最全,支持十亿级向量 |
| Qdrant | HNSW | Rust 实现,性能优异,API 友好 |
| Pinecone | 自研(不公开) | 全托管,零运维,按量付费 |
| Weaviate | HNSW, Flat | 自带向量化模块 |
| Chroma | HNSW | 轻量级,适合原型开发 |
为什么语义搜索是 RAG 的基石?
传统关键词搜索: 语义搜索(向量搜索):
用户: "怎么保持健康?" 用户: "怎么保持健康?"
↓ ↓
匹配"保持""健康" 向量化 query
↓ ↓
找到含这些词的文档 找到向量最接近的文档
↓ ↓
❌ 漏掉 "养生秘诀" ✅ "养生秘诀" 高相似度!(语义匹配)
❌ 漏掉 "锻炼身体" ✅ "锻炼身体" 也高相似度2
3
4
5
6
7
8
9
这就是为什么 RAG 系统中,检索通常使用混合搜索(Hybrid Search):BM25 关键词匹配 + 向量语义匹配,再通过 Reranker 做最后排序,是最稳健的方案。