一、什么是Embedding?
聊向量数据库之前,得先把这事儿讲清楚。不少朋友第一次接触这个概念时,可能会觉得“Embedding”是个挺抽象的术语。其实,它没那么复杂。
1.1 通俗理解
简单来说,Embedding 就是给文字配上一组数字——专业的说法叫“向量”。好让计算机能够理解不同文字之间在语义上是亲近还是疏远。

人类理解语义: "猫" 和 "狗" 很相似(都是宠物)
"猫" 和 "汽车" 很不同
计算机怎么理解?→ 把文字变成数字!
"猫" → [0.2, 0.8, 0.1, 0.9, ...] ← 一组数字(向量)
"狗" → [0.3, 0.7, 0.2, 0.8, ...] ← 和"猫"的数字很接近
"汽车" → [0.9, 0.1, 0.8, 0.2, ...] ← 和"猫"的数字差很远
1.2 一个直观的比喻
想象一下二维地图(实际的 Embedding 是几百维,但原理是一样的):
↑ "动物属性"
│
1.0│?猫?狗
0.8│ ?兔子
0.5│
│✈️飞机
0.2│ ?汽车
│ ?火车
0.0├───────────────────→ "交通工具属性"
0.0 0.2 0.5 0.8 1.0
"猫"和"狗"在地图上离得很近 → 语义相似
"猫"和"汽车"离得很远 → 语义不同
1.3 Embedding 的维度
实际场景中的 Embedding 不是二维,而是几百到几千维不等。维度越高,能表达更细微的语义差异,计算量自然也更大:
┌─────────────────────┬──────────────────────┐
│Embedding 模型 │向量维度 │
├─────────────────────┼──────────────────────┤
│OpenAI text-ada-002 │1536 维 │
│OpenAI text-3-small │1536 维 │
│OpenAI text-3-large │3072 维 │
│BGE-large-zh │1024 维 │
│Jina Embeddings v3 │1024 维 │
│Cohere Embed v3 │1024 维 │
└─────────────────────┴──────────────────────┘
二、如何生成Embedding?
知道了概念,怎么动手拿到这个向量呢?下面介绍两种最典型的路径。
2.1 使用 OpenAI Embedding API
最省事的办法,直接用 OpenAI 的接口:
from openai import OpenAI
client = OpenAI(api_key="sk-xxx")
# 生成单个文本的 Embedding
response = client.embeddings.create(
model="text-embedding-3-small",
input="今天天气真好"
)
vector = response.data[0].embedding
print(f"维度:{len(vector)}") # 1536
print(f"前5个数字:{vector[:5]}")
# [0.012, -0.034, 0.056, ...]
2.2 使用开源 Embedding 模型
如果你的数据不能外传,或者想省点 API 费用,完全可以用开源方案本地跑:
# 使用 sentence-transformers 库(免费、可本地运行)
# pip install sentence-transformers
from sentence_transformers import SentenceTransformer
# 加载模型(首次运行会自动下载)
model = SentenceTransformer('BAAI/bge-large-zh-v1.5') # 中文最佳
# 生成 Embedding
sentences = ["今天天气真好", "天气不错适合出门", "我要买一辆汽车"]
embeddings = model.encode(sentences)
print(f"形状:{embeddings.shape}") # (3, 1024) → 3个句子,每个1024维
三、向量相似度计算
有了向量的肉身,还得知道怎么度量向量的“亲疏远近”。最常见的手段是余弦相似度。
3.1 余弦相似度(最常用)
衡量两个向量的方向是否一致,不关心长度:
余弦相似度的直觉理解:
两个向量方向完全相同 → 相似度 = 1(完全相似)
两个向量方向垂直 → 相似度 = 0(不相关)
两个向量方向相反 → 相似度 = -1(完全相反)
↑ B
╱
╱ θ = 小角度 → cos(θ) 接近 1 → 很相似
╱
──────→ A
import numpy as np
def cosine_similarity(a, b):
"""计算两个向量的余弦相似度"""
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# 示例
emb_cat = model.encode("一只可爱的猫咪")
emb_dog = model.encode("一只可爱的狗狗")
emb_car = model.encode("一辆红色的汽车")
print(f"猫 vs 狗:{cosine_similarity(emb_cat, emb_dog):.3f}") # ~0.85 高相似
print(f"猫 vs 车:{cosine_similarity(emb_cat, emb_car):.3f}") # ~0.25 低相似
3.2 其他距离度量
除了余弦相似度,还有一些其他选择:
┌──────────────────┬──────────────────────────────────────────┐
│度量方式 │适用场景 │
├──────────────────┼──────────────────────────────────────────┤
│余弦相似度 │最通用,推荐默认使用 │
│(Cosine) │不受向量长度影响 │
├──────────────────┼──────────────────────────────────────────┤
│欧氏距离 │需要考虑"绝对距离"时 │
│(Euclidean) │值越小越相似 │
├──────────────────┼──────────────────────────────────────────┤
│内积 │已归一化的向量 │
│(Inner Product) │等价于余弦相似度 │
└──────────────────┴──────────────────────────────────────────┘
四、向量数据库
单个向量、少量向量还好处理,当你有成千上万的文档要做语义检索时,就轮到向量数据库出场了。
4.1 为什么需要向量数据库?
想象一下:你准备了 100 万篇文档的 Embedding,用户提了个问题。需要用最快的时间找出最相关的 10 篇文档。
- 暴力搜索:逐一比较 100 万次 → 几分钟,太慢了。
- 向量数据库:用特殊的数据结构 → 毫秒级返回!
4.2 主流向量数据库对比
┌──────────────┬──────────┬──────────┬──────────┬─────────────────┐
│数据库 │类型 │特点 │难度 │适合场景 │
├──────────────┼──────────┼──────────┼──────────┼─────────────────┤
│Chroma │嵌入式 │最简单 │★☆☆☆☆ │学习、原型验证 │
│FAISS │库(Meta) │最快 │★★★☆☆ │研究、单机大规模 │
│Milvus │分布式 │功能全 │★★★★☆ │企业级生产环境 │
│Qdrant │服务端 │Rust写的 │★★★☆☆ │性能敏感场景 │
│Wea viate │服务端 │多模态 │★★★☆☆ │图+文混合搜索 │
│Pinecone │全托管 │免运维 │★★☆☆☆ │不想管运维 │
│PgVector │PG扩展 │复用PG │★★☆☆☆ │已有PostgreSQL │
└──────────────┴──────────┴──────────┴──────────┴─────────────────┘
4.3 Chroma 快速上手(最简单)
如果你只想快速跑通、做原型验证,Chroma 绝对是最好的敲门砖:
# pip install chromadb
import chromadb
# 1. 创建客户端和集合
client = chromadb.Client()
collection = client.create_collection("my_docs")
# 2. 添加文档(Chroma 自动生成 Embedding!)
collection.add(
documents=[
"Python 是一种简单易学的编程语言",
"Ja va 是一种面向对象的编程语言",
"今天股市大涨,上证指数突破3500点",
"机器学习是人工智能的一个子领域",
],
ids=["doc1", "doc2", "doc3", "doc4"]
)
# 3. 搜索最相关的文档
results = collection.query(
query_texts=["什么编程语言适合初学者?"],
n_results=2 # 返回最相关的2条
)
print(results["documents"])
# [['Python 是一种简单易学的编程语言',
# 'Ja va 是一种面向对象的编程语言']]
# → 准确找到了编程相关的文档,没有返回股市新闻!
4.4 Milvus 生产级使用
而如果你要考虑生产环境,Milvus 是更成熟的选择:
# pip install pymilvus
from pymilvus import MilvusClient
# 1. 连接 Milvus
client = MilvusClient("https://localhost:19530")
# 2. 创建集合
client.create_collection(
collection_name="articles",
dimension=1024, # 向量维度,需要与 Embedding 模型匹配
)
# 3. 插入数据
data = [
{"id": 1, "vector": [0.1, 0.2, ...], "text": "Python 入门教程"},
{"id": 2, "vector": [0.3, 0.4, ...], "text": "Ja va 设计模式"},
# ...
]
client.insert(collection_name="articles", data=data)
# 4. 搜索
results = client.search(
collection_name="articles",
data=[query_embedding], # 查询向量
limit=5, # 返回 top 5
output_fields=["text"] # 同时返回文本
)
五、向量索引类型
5.1 为什么需要索引?
没有索引(暴力搜索 Flat):
┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ... ┌──┐
│ │ │ │ │ │ │ │ │ │ │ │
└──┘ └──┘ └──┘ └──┘ └──┘ └──┘
逐一比较所有向量 → 准确但慢
有索引(近似搜索):
先把向量分组/建树,搜索时只看相关的组 → 快但可能遗漏
5.2 常见索引类型
┌───────────┬──────────────────────────────────────────────┐
│索引类型 │通俗解释 │
├───────────┼──────────────────────────────────────────────┤
│Flat │暴力搜索,逐一比较 │
│(平坦) │最准确但最慢。数据少时可以用 │
│ │ │
│IVF │先把向量分成若干个"簇" │
│(倒排) │搜索时只在最相近的几个簇中找 │
│ │类比:先确定在哪个书架,再在书架上找书 │
│ │ │
│HNSW │建一个多层图结构(像跳表) │
│(层次图) │从粗到细逐层搜索 │
│ │类比:先看世界地图,再看国家地图,再看城市地图│
│ │目前最流行的索引类型 │
│ │ │
│PQ │把向量压缩(量化),用更少的空间存储 │
│(乘积量化) │牺牲一点精度换取大幅减少内存占用 │
└───────────┴──────────────────────────────────────────────┘
HNSW 索引示意图:
Layer 2 (最顶层,最稀疏):
A ─────────── D
│ │
Layer 1 (中间层):
A ── B ──── C ── D
│ │ │ │
Layer 0 (最底层,最密集):
A─B─E─F─C─G─H─D─I
搜索过程:
1. 从顶层开始,找到大致方向(A → D)
2. 下一层细化(A → B → C)
3. 底层精确搜索(找到最近邻)
六、混合检索
6.1 向量检索的局限
纯粹依赖向量检索并非万能,尤其在需要精确匹配关键词的场景下,它的短板也很明显。
问题:用户搜索 "Python 3.12 新特性"
纯向量搜索可能返回:
1. "Python 最新版本的功能介绍" ← 相关但不精确
2. "编程语言的新特性总结" ← 相关但太宽泛
✗ 可能没有精确匹配 "3.12" 这个版本号
纯关键词搜索(BM25)可能返回:
1. "Python 3.12 发布说明" ← 精确匹配 ✓
2. "Python 3.12 变更日志" ← 精确匹配 ✓
✗ 但如果用户搜 "最新Python有啥变化",关键词匹配不到
6.2 混合检索 = 向量 + 关键词
所以,一个很自然的改进方向,就是把两者结合起来:
┌────────────────────────────────────────────────────────┐
│ 混合检索流程 │
│ │
│ 用户查询:"Python 3.12 新特性" │
│ │ │
│ ┌────┴────┐ │
│ ▼ ▼ │
│ 向量检索 BM25 关键词检索 │
│ (语义理解) (精确匹配) │
│ │ │ │
│ ▼ ▼ │
│ 结果集A 结果集B │
│ │ │ │
│ └────┬────┘ │
│ ▼ │
│ 融合排序(RRF / 加权融合) │
│ │ │
│ ▼ │
│ 最终排序结果 │
└────────────────────────────────────────────────────────┘
# 混合检索的简化实现
def hybrid_search(query, documents, alpha=0.7):
"""alpha: 向量检索的权重(0-1)
1-alpha: 关键词检索的权重"""
# 向量检索得分
vector_scores = vector_search(query, documents)
# BM25 关键词检索得分
bm25_scores = bm25_search(query, documents)
# 融合得分
final_scores = {}
for doc_id in set(vector_scores) | set(bm25_scores):
v_score = vector_scores.get(doc_id, 0)
b_score = bm25_scores.get(doc_id, 0)
final_scores[doc_id] = alpha * v_score + (1 - alpha) * b_score
return sorted(final_scores.items(), key=lambda x: x[1], reverse=True)
七、Reranker 重排序
7.1 为什么需要重排序?
向量检索本质上是“粗筛”。对于前 10 名或者前 20 名的结果,它给出的排序未必完全精准。Reranker 就是用来做“精排”的,在候选集上重新打分:
检索流程:
百万文档 ──向量检索──→ Top 20 候选 ──Reranker──→ Top 5 精排结果
(粗筛:快但粗略) (精排:慢但精准)
7.2 使用 Reranker
# 使用 Cohere Reranker(API 方式)
import cohere
co = cohere.Client(api_key="xxx")
results = co.rerank(
query="什么是向量数据库?",
documents=[
"向量数据库用于存储和检索高维向量数据",
"关系数据库使用SQL进行查询",
"向量搜索引擎可以进行语义搜索",
"NoSQL数据库包括MongoDB等"
],
top_n=2
)
for r in results.results:
print(f"排名 {r.index}: 分数 {r.relevance_score:.3f}")
# 排名 0: 分数 0.95 → "向量数据库用于存储..."
# 排名 2: 分数 0.82 → "向量搜索引擎可以..."
# 使用开源 Reranker(本地运行,免费)
from sentence_transformers import CrossEncoder
reranker = CrossEncoder('BAAI/bge-reranker-v2-m3')
pairs = [
["什么是向量数据库?", "向量数据库用于存储和检索高维向量数据"],
["什么是向量数据库?", "关系数据库使用SQL进行查询"],
]
scores = reranker.predict(pairs)
# [0.95, 0.12] → 第一个文档和查询更相关
八、Embedding 模型选择指南
选模型本质上是个“决策树”问题,看你的数据和场景来定:
┌──────────────────────────────────────────────────────────────┐
│ Embedding 模型选择决策树 │
│ │
│ 你的数据是什么语言? │
│ │ │
│ ├─ 中文为主 → BGE-large-zh / Jina Embeddings v3 │
│ │ │
│ ├─ 英文为主 → OpenAI text-embedding-3-small │
│ │ (付费但效果好) │
│ │ 或 BGE-large-en (免费开源) │
│ │ │
│ └─ 多语言 → Jina Embeddings v3 / Cohere Embed v3 │
│ │
│ 数据能否发送到云端? │
│ │ │
│ ├─ 可以 → OpenAI / Cohere API(简单省事) │
│ └─ 不行 → BGE / Jina(本地部署) │
│ │
│ 数据规模多大? │
│ │ │
│ ├─ < 10万条 → 维度大一点没关系(1024-3072) │
│ └─ > 100万条 → 考虑较小维度(512-768)节省存储 │
└──────────────────────────────────────────────────────────────┘
九、实战练习
完整示例:构建一个简易语义搜索引擎
"""
用 Chroma + Sentence Transformers 构建语义搜索引擎
无需 API Key,完全本地运行
"""
import chromadb
from sentence_transformers import SentenceTransformer
# 1. 初始化
embedding_model = SentenceTransformer('BAAI/bge-small-zh-v1.5')
chroma_client = chromadb.Client()
# 2. 创建集合,使用自定义 Embedding 函数
collection = chroma_client.create_collection(
name="knowledge_base",
metadata={"hnsw:space": "cosine"} # 使用余弦相似度
)
# 3. 准备文档
documents = [
"Python 是一种解释型、面向对象的高级编程语言",
"Ja va 是一种广泛使用的编程语言,具有跨平台特性",
"Ja vaScript 是网页开发的核心语言,可以在浏览器中运行",
"机器学习是人工智能的一个分支,通过数据来学习规律",
"深度学习使用多层神经网络来处理复杂的模式识别任务",
"Docker 是一种容器化技术,可以将应用打包成轻量级容器",
"Kubernetes 用于自动化部署、扩展和管理容器化应用",
"Git 是一种分布式版本控制系统,用于跟踪代码变更",
"REST API 是一种基于 HTTP 协议的接口设计风格",
"微服务架构将应用拆分为多个独立的小服务",
]
# 4. 生成 Embedding 并存入数据库
embeddings = embedding_model.encode(documents).tolist()
collection.add(
documents=documents,
embeddings=embeddings,
ids=[f"doc_{i}" for i in range(len(documents))]
)
# 5. 搜索
def search(query: str, top_k: int = 3):
query_embedding = embedding_model.encode([query]).tolist()
results = collection.query(
query_embeddings=query_embedding,
n_results=top_k
)
print(f"\n查询:{query}")
print("=" * 50)
for i, (doc, distance) in enumerate(zip(results["documents"][0], results["distances"][0])):
similarity = 1 - distance # Chroma 返回的是距离,转为相似度
print(f"{i+1}. [{similarity:.2f}] {doc}")
# 测试
search("什么编程语言好学")
# 1. [0.78] Python 是一种解释型、面向对象的高级编程语言
# 2. [0.65] Ja va 是一种广泛使用的编程语言...
# 3. [0.61] Ja vaScript 是网页开发的核心语言...
search("如何部署应用")
# 1. [0.82] Docker 是一种容器化技术...
# 2. [0.75] Kubernetes 用于自动化部署...
# 3. [0.52] 微服务架构将应用拆分为...
search("怎么管理代码")
# 1. [0.80] Git 是一种分布式版本控制系统...
# 2. [0.45] ...
十、本章小结
┌────────────────────────────────────────────────────────┐
│ 本章知识地图 │
│ │
│ Embedding:把文字变成数字向量 │
│ ├── 语义相似的文字 → 向量距离近 │
│ ├── 余弦相似度 = 最常用的相似度计算方式 │
│ └── 主流模型:OpenAI / BGE / Jina │
│ │
│ 向量数据库:高效存储和搜索向量 │
│ ├── 入门首选:Chroma(嵌入式,零配置) │
│ ├── 生产首选:Milvus / Qdrant │
│ └── 索引类型:Flat / IVF / HNSW / PQ │
│ │
│ 检索增强: │
│ ├── 混合检索 = 向量 + 关键词(互补) │
│ └── Reranker = 对初步结果精排 │
└────────────────────────────────────────────────────────┘
十一、扩展学习资源
必读
- Chroma 文档 —— 最简单的向量数据库入门
- Sentence Transformers 文档 —— 开源 Embedding 模型库
- MTEB 排行榜 —— Embedding 模型评测排行
推荐
- Milvus 文档 —— 生产级向量数据库
- Pinecone 学习中心 —— 优秀的向量数据库教程
- What Are Embeddings?(Vicki Boykis) —— 深入理解 Embedding 的好文章
动手实践
- 用 Chroma 构建一个简单的文档搜索系统
- 对比不同 Embedding 模型在中文搜索上的效果
- 试试在同一数据集上,纯向量检索 vs 混合检索的效果差异
