如果说 LLM 是大脑,那向量就是它的「神经信号编码」。人类用文字交流,计算机用数字计算,而 Embedding 是连接这两个世界的桥梁。不懂向量,就不懂 RAG;不懂 RAG,就没法让 LLM 真正落地到实际业务中。
📑 目录
Embedding(文本嵌入):文字变数字的魔法
一句话定义
把任意长度文本转换成固定长度的数字数组(向量)。语义相似的文本,向量在空间中的距离也更近。
本质大白话
Embedding = 文字的 GPS 坐标
想象一个多维空间(比如 1536 维):
「猫」 → [0.12, -0.34, 0.56, ...] ← 附近聚集着「小狗」「萌宠」
「狗」 → [0.15, -0.31, 0.52, ...] ← 跟「猫」很近!(都是宠物)
「汽车」→ [-0.45, 0.78, -0.23, ...] ← 离「猫」「狗」很远
「公交车」→ [-0.42, 0.75, -0.20, ...] ← 跟「汽车」很近!(交通工具)
关键洞察:
Embedding 不是简单的编码,而是把语义关系映射到几何空间!
语义近 = 空间距短 | 语义远 = 空间距长
Embedding 模型对比
| 模型 | 维度 | 语言 | 特点 | 场景 |
|---|---|---|---|---|
| text-embedding-3-small | 1536 | 多语 | 效果好,付费 | 通用首选 |
| BGE-large-zh | 1024 | 中英 | 开源免费效果好 | 中文首选 |
| M3E-base | 768 | 中文 | 轻量快速 | 中文轻量 |
| E5-mistral | 1024 | 多语 | 多语言强 | 国际化 |
| Cohere embed-v3 | 1024 | 多语 | 搜索专用 | 搜索引擎 |
python
from openai import OpenAI
import numpy as np
client = OpenAI()
def get_embedding(text, model="text-embedding-3-small"):
response = client.embeddings.create(model=model, input=text)
return response.data[0].embedding # 1536维向量
# 语义相似的文本,向量接近
texts = ["今天天气不错", "天气很好", "Python 编程教程"]
embs = [get_embedding(t) for t in texts]
def cosine(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(f"'今天天气不错' vs '天气很好': {cosine(embs[0], embs[1]):.4f}") # ~0.92
print(f"'今天天气不错' vs 'Python': {cosine(embs[0], embs[2]):.4f}") # ~0.15
❌ 常见误区
- ❌ Embedding 模型越大越好 --- text-embedding-3-small 很多场景优于大模型
- ❌ 同一 Embedding 通用所有任务 --- 搜索和分类需要不同的 Embedding
- ❌ 中文必须专用中文模型 --- 多语言模型在中文上已经很出色
余弦相似度:衡量「方向一致性」
一句话定义
计算两个向量夹角的余弦值,范围 -1, 1。越接近 1 表示方向一致(语义越相似)。
本质大白话
余弦相似度 = 忽略长度,只看方向
两个人朝同一个方向走:
A 走了100米,B 走了1000米
方向一样 → 余弦 ≈ 1.0
A 往东走,B 往西走
方向相反 → 余弦 ≈ -1.0
文本场景:方向相同 = 语义相近(不管文章长短)
→ 这就是为什么余弦成为文本相似度的默认选择!
cos_sim ( A , B ) = A ⋅ B ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ B ∣ ∣ \text{cos\_sim}(A,B) = \frac{A \cdot B}{||A|| \cdot ||B||} cos_sim(A,B)=∣∣A∣∣⋅∣∣B∣∣A⋅B
python
import numpy as np
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
vec_cat = get_embedding("可爱的小猫")
vec_dog = get_embedding("可爱的狗狗")
vec_car = get_embedding("一辆汽车")
print(f"猫 vs 狗: {cosine_similarity(vec_cat, vec_dog):.4f}") # ~0.85
print(f"猫 vs 车: {cosine_similarity(vec_cat, vec_car):.4f}") # ~0.20
❌ 常见误区
- ❌ 余弦 > 0.9 就意思一样 --- 不同模型阈值不同,不要迷信绝对数值
- ❌ 余弦适用于所有场景 --- 图像特征场景可能需要欧氏距离
欧氏距离:衡量「直线距离」
一句话定义
两个向量在多维空间的直线距离。越小越相似。
余弦 vs 欧氏
| 余弦 | 欧氏 | |
|---|---|---|
| 关注点 | 方向 | 实际距离 |
| 受长度影响 | 否 | 是 |
| 范围 | -1,1 | [0,+∞) |
| 文本场景 | 默认首选 | 少数 |
| 图像场景 | 可能失效 | 常用 |
python
import numpy as np
def euclidean(a, b):
return np.linalg.norm(a - b)
# 直观对比
short = get_embedding("你好")
long_text = get_embedding("你好" * 20)
print(f"余弦: {cosine_similarity(short, long_text):.4f}") # ~0.95 (方向一样)
print(f"欧氏: {euclidean(short, long_text):.4f}") # 可能很大(长度不同)
# 结论:文本用余弦,特征考虑欧氏
向量检索:从语义匹配到实际查询
一句话定义
给定查询文本转为向量后,在向量库中找到 Top-K 条最相似记录的过程。RAG 的核心操作。
检索流程
用户提问:「如何优化 MySQL 查询性能?」
↓
[Embedding 模型]
↓
查询向量: [0.21, -0.15, 0.33, ...]
↓
┌────────────────────────────────┐
│ 向量数据库 │
│ doc1 [0.20,-0.14,0.35,...] → 0.012 ★1 │
│ doc2 [0.55, 0.72,-0.11,...] → 0.892 │
│ doc3 [0.22,-0.13,0.31,...] → 0.025 ★2 │
│ doc4 [-0.81,0.33, 0.55,...] → 1.203 │
└────────────────────────────────┘
↓
返回 Top-K (doc1, doc3) 作为 LLM 上下文
python
from typing import List
import numpy as np
class VectorRetriever:
def __init__(self, embedding_func):
self.embed_fn = embedding_func
self.vectors = []
self.documents = []
def add_document(self, text, metadata=None):
vec = self.embed_fn(text)
self.vectors.append(vec)
self.documents.append({"text": text, "metadata": metadata or {}})
def search(self, query, top_k=5) -> List[dict]:
q_vec = self.embed_fn(query)
scores = []
for v in self.vectors:
scores.append(np.dot(q_vec, v) / (
np.linalg.norm(q_vec) * np.linalg.norm(v)))
top_idx = np.argsort(scores)[::-1][:top_k]
return [{"doc": self.documents[i], "score": scores[i]} for i in top_idx]
# 使用
retriever = VectorRetriever(get_embedding)
retriever.add_document("MySQL 索引优化策略:...")
retriever.add_document("Redis 缓存使用指南:...")
results = retriever.search("数据库太慢了怎么办", top_k=3)
❌ 常见误区
- ❌ 向量检索 = 完美语义搜索 --- 找不到精确关键词匹配(型号编号、专有名词)
- ❌ Top-K 越大越好 --- K 太大引入噪音,K 太小漏信息。一般 3-10 合理
- ❌ 只要向量就够了 --- 生产环境通常需要混合检索(详见 2.2)
什么时候用什么度量?
| 场景 | 推荐 | 原因 |
|---|---|---|
| 文本语义检索 | 余弦 | 忽略长度,关注方向 |
| 图像特征比对 | 欧氏 | 特征幅度有意义 |
| 归一化后的向量 | 两者等效 | 归一化后余弦=欧氏 |
| 异常检测 | 欧氏 | 远离聚类中心的点易识别 |
| 推荐系统 | 余弦 | 关注偏好方向而非强度 |
📊 本节知识地图
┌────────────────────────────────────────────────────┐
│ 向量基础 --- RAG 的地基 │
│ │
│ ┌────────────┐ ┌──────────────────────────┐ │
│ │ Embedding │──→ │ 相似度计算 │ │
│ │ 文本→向量 │ │ 余弦 ★ / 欧氏 ☆ │ │
│ └────────────┘ └──────────┬───────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────┐ │
│ │ 向量检索 │ │
│ │ Top-K 返回 │ │
│ └────────┬─────────┘ │
│ ↓ │
│ ┌──────────────────┐ │
│ │ 2.2 RAG体系 │ ← 下节详解 │
│ └──────────────────┘ │
│ │
│ 一句话: │
│ Embedding转数字 → 相似度找最相关 → 检索返回 │
└────────────────────────────────────────────────────┘
🔗 关联推荐
- 📖 1.1 模型本源 → Token 是 Embedding 的输入单元
- 📖 1.3 交互基础 → 检索结果是 Context 的重要来源
- 🔜 2.2 RAG 体系 → 向量检索是 RAG 的第一步
- 🔜 2.3 向量数据库 → 向量存在哪里?怎么选型?