Embedding 与向量数据库:语义理解的基础设施
AI 核心技能系列 · 第 4 篇
导语
大模型很强,但它有一个致命弱点:记忆有限。你公司的内部文档、产品手册、客户历史记录------这些数据模型统统不知道。怎么让模型在海量私有数据中快速找到相关信息?
答案是两个关键技术:Embedding(嵌入) 和 向量数据库。Embedding 负责把文本转换成数字向量(语义相似 = 向量接近),向量数据库负责在百万级向量中毫秒级找到最相似的。
这两者是 RAG、语义搜索、推荐系统的底层基石,也是下一篇 RAG 文章的前置知识。
一、什么是 Embedding:把万物变成向量
1.1 直觉理解
Embedding 的核心思想:语义相似 = 向量接近。
arduino
"猫在沙发上睡觉" → [0.23, 0.87, -0.12, 0.45, ...] ─┐ 距离近
"小猫躺在沙发上" → [0.25, 0.85, -0.10, 0.43, ...] ─┘ (语义相似)
"今天股市大涨" → [-0.67, 0.12, 0.89, -0.34, ...] 距离远(语义不相关)
每个文本被转换成一个固定长度的数字向量(比如 1536 维),向量在高维空间中的位置关系反映了语义关系。
1.2 从 Word2Vec 到 Sentence Embedding
| 阶段 | 年份 | 代表 | 关键突破 |
|---|---|---|---|
| 静态词向量 | 2013 | Word2Vec, GloVe | "king - man + woman ≈ queen" |
| 上下文词向量 | 2018 | ELMo, BERT | 同一个词在不同上下文有不同向量 |
| 句子向量 | 2019 | Sentence-BERT | 直接生成句子级别的语义向量 |
| 现代 Embedding | 2023+ | BGE-M3, text-embedding-3 | 高质量、多语言、多粒度 |
1.3 稠密 vs 稀疏
- 稠密向量 (Dense):每个维度都有值,如
[0.23, 0.87, -0.12, ...],捕获语义信息 - 稀疏向量(Sparse):大部分维度为 0,如 TF-IDF/BM25,捕获关键词匹配
- 混合方式:两者结合效果最好(后面会讲)
二、主流 Embedding 模型对比
| 模型 | 厂商 | 维度 | 多语言 | 定价/1M tokens | 特点 |
|---|---|---|---|---|---|
| text-embedding-3-large | OpenAI | 3072 | 好 | $0.13 | 维度可调,性价比高 |
| text-embedding-3-small | OpenAI | 1536 | 好 | $0.02 | 极便宜 |
| Embed v3 | Cohere | 1024 | 好 | $0.10 | 支持搜索/分类不同用途 |
| Voyage AI 3 | Voyage | 1024 | 好 | $0.06 | 代码 Embedding 最强 |
| BGE-M3 | BAAI | 1024 | 优秀 | 免费(开源) | 多语言多粒度多功能 |
| GTE-Qwen2 | 阿里 | 1536 | 优秀 | 免费(开源) | 中文最强梯队 |
| E5-Mistral | 微软 | 4096 | 好 | 免费(开源) | 基于 LLM 的 Embedding |
代码示例:调用 OpenAI Embedding
python
from openai import OpenAI
import numpy as np
client = OpenAI()
def get_embedding(text, model="text-embedding-3-small"):
response = client.embeddings.create(input=text, model=model)
return response.data[0].embedding
# 生成 Embedding
emb1 = get_embedding("猫在沙发上睡觉")
emb2 = get_embedding("小猫躺在沙发上打盹")
emb3 = get_embedding("今天股市大涨")
# 计算余弦相似度
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(f"猫睡觉 vs 猫打盹: {cosine_similarity(emb1, emb2):.4f}") # ~0.95 高相似
print(f"猫睡觉 vs 股市大涨: {cosine_similarity(emb1, emb3):.4f}") # ~0.15 低相似
三、向量相似度搜索:原理与算法
3.1 距离度量
| 度量方式 | 公式 | 适用场景 | 值域 |
|---|---|---|---|
| 余弦相似度 | <math xmlns="http://www.w3.org/1998/Math/MathML"> cos ( A , B ) = A ⋅ B ∥ A ∥ ⋅ ∥ B ∥ \cos(A,B) = \frac{A \cdot B}{\|A\| \cdot \|B\|} </math>cos(A,B)=∥A∥⋅∥B∥A⋅B | 文本语义搜索(最常用) | <math xmlns="http://www.w3.org/1998/Math/MathML"> [ − 1 , 1 ] [-1, 1] </math>[−1,1] |
| 欧氏距离 | <math xmlns="http://www.w3.org/1998/Math/MathML"> d ( A , B ) = ∥ A − B ∥ 2 d(A,B) = \|A - B\|_2 </math>d(A,B)=∥A−B∥2 | 需要考虑向量大小的场景 | <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 0 , + ∞ ) [0, +\infty) </math>[0,+∞) |
| 点积 | <math xmlns="http://www.w3.org/1998/Math/MathML"> A ⋅ B A \cdot B </math>A⋅B | 归一化后的向量 | <math xmlns="http://www.w3.org/1998/Math/MathML"> ( − ∞ , + ∞ ) (-\infty, +\infty) </math>(−∞,+∞) |
实践中余弦相似度用得最多------它只关心方向不关心大小,更符合"语义相似"的直觉。
3.2 精确搜索 vs 近似最近邻(ANN)
精确搜索(暴力遍历)在数据量大时不可行------100 万条向量,每条 1536 维,每次查询都要计算 100 万次距离。
ANN(Approximate Nearest Neighbor) 用空间换时间,牺牲一点精度换取几个数量级的速度提升。
| 算法 | 核心思想 | 时间复杂度 | 特点 |
|---|---|---|---|
| HNSW | 多层跳表图,从高层粗搜到底层精搜 | O(log n) | 最流行,查询快,内存大 |
| IVF | 先聚类再只搜最近的几个簇 | O(n/k) | 适合大数据集,需要训练 |
| PQ (Product Quantization) | 向量压缩,减少内存和计算 | O(n) 但常数小 | 内存效率极高 |
| HNSW + PQ | 组合使用 | O(log n) | 兼顾速度和内存 |
HNSW 直觉理解:
markdown
想象一个城市地图:
- 第 0 层(底层):所有建筑(所有向量)
- 第 1 层:主要建筑
- 第 2 层:地标建筑
- 第 3 层(顶层):只有几个核心地标
搜索过程:
1. 从顶层的某个地标出发
2. 在当前层找最近的邻居
3. 下到下一层,继续搜索
4. 一路到底层,找到精确的最近邻
四、向量数据库选型指南
4.1 主流向量数据库对比
| 数据库 | 类型 | 语言 | 部署方式 | 性能 | 特点 |
|---|---|---|---|---|---|
| Milvus | 专用向量库 | Go/C++ | 自部署/云服务 | 极高 | 分布式、功能全面、生产级 |
| Pinecone | 专用向量库 | - | 纯云服务 | 高 | 开箱即用、免运维 |
| Qdrant | 专用向量库 | Rust | 自部署/云 | 极高 | Rust 实现、性能优秀 |
| Weaviate | 专用向量库 | Go | 自部署/云 | 高 | 内置混合搜索 |
| Chroma | 轻量级 | Python | 本地/嵌入式 | 中 | 极简、适合原型 |
| FAISS | 索引库 | C++/Python | 嵌入式 | 极高 | Meta 出品,纯索引 |
| pgvector | PG 扩展 | C | 跟 PostgreSQL | 中 | 已有 PG 可直接用 |
4.2 选型决策
markdown
你的场景是什么?
│
├── 原型开发 / PoC
│ └── Chroma(Python 原生,5 行代码搞定)
│
├── 已有 PostgreSQL
│ └── pgvector(零运维成本,直接加扩展)
│
├── 不想运维
│ └── Pinecone(纯托管,按量付费)
│
├── 百万~千万级数据,需要高性能
│ ├── 需要分布式 → Milvus
│ └── 单机够用 → Qdrant
│
└── 纯研究 / Benchmark
└── FAISS(纯索引库,最灵活)
五、实战:构建一个语义搜索系统
python
import chromadb
from openai import OpenAI
client = OpenAI()
# 1. 初始化 Chroma
chroma_client = chromadb.Client()
collection = chroma_client.create_collection(
name="knowledge_base",
metadata={"hnsw:space": "cosine"} # 使用余弦相似度
)
# 2. 准备数据
documents = [
"Python 是一种解释型、面向对象的高级编程语言",
"机器学习是人工智能的一个分支,通过数据训练模型",
"Docker 是一个开源的容器化平台,用于自动化部署应用",
"React 是 Facebook 开发的 JavaScript 前端框架",
"深度学习使用多层神经网络来学习数据的层次表示",
"Kubernetes 是一个开源的容器编排平台",
"自然语言处理是计算机科学和 AI 的交叉领域",
"PostgreSQL 是一个强大的开源关系型数据库",
]
# 3. 生成 Embedding 并存入
def get_embeddings(texts):
response = client.embeddings.create(
input=texts, model="text-embedding-3-small"
)
return [item.embedding for item in response.data]
embeddings = get_embeddings(documents)
collection.add(
documents=documents,
embeddings=embeddings,
ids=[f"doc_{i}" for i in range(len(documents))],
)
# 4. 语义搜索
query = "怎么用 AI 处理文本数据?"
query_embedding = get_embeddings([query])[0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=3,
)
print("查询:", query)
print("最相关的文档:")
for i, doc in enumerate(results["documents"][0]):
dist = results["distances"][0][i]
print(f" {i+1}. [{dist:.4f}] {doc}")
# 输出:
# 1. [0.1823] 自然语言处理是计算机科学和 AI 的交叉领域
# 2. [0.2156] 机器学习是人工智能的一个分支,通过数据训练模型
# 3. [0.2891] 深度学习使用多层神经网络来学习数据的层次表示
六、最佳实践与避坑指南
6.1 Embedding 模型选型
| 场景 | 推荐模型 | 理由 |
|---|---|---|
| 英文为主 | text-embedding-3-small | 便宜够用 |
| 中文为主 | GTE-Qwen2 / BGE-M3 | 中文优化 |
| 代码搜索 | Voyage Code 3 | 代码 Embedding 最强 |
| 数据不能出境 | BGE-M3(自部署) | 开源免费 |
| 需要极致性能 | text-embedding-3-large | 维度更高,精度更好 |
6.2 关键避坑点
- 查询和文档用同一个模型:不同模型的向量空间不兼容
- 文本预处理影响大:去除 HTML 标签、特殊字符、过长文本截断
- 分块大小很重要:太大语义不精确,太小丢失上下文(下一篇 RAG 详讲)
- Embedding 模型更换需全量重建:换模型 = 重新生成所有向量
- 不要忽略混合搜索:向量搜索 + 关键词搜索(BM25)组合效果通常更好
6.3 混合搜索示例
python
# 混合搜索:结合向量检索和关键词检索
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(list(vector_scores) + list(bm25_scores)):
v_score = normalize(vector_scores.get(doc_id, 0))
k_score = normalize(bm25_scores.get(doc_id, 0))
final_scores[doc_id] = alpha * v_score + (1 - alpha) * k_score
return sorted(final_scores.items(), key=lambda x: x[1], reverse=True)
七、职业视角
7.1 面试高频问题
| 问题 | 核心答案要点 |
|---|---|
| Embedding 是什么? | 将文本映射到高维向量空间,语义相似的文本距离接近 |
| 向量数据库和传统数据库的区别? | 传统 DB 精确匹配,向量 DB 相似性搜索;索引结构不同(B-tree vs HNSW) |
| HNSW 算法的原理? | 多层跳表图结构,从高层粗搜到底层精搜,O(log n) 时间复杂度 |
| 为什么需要混合搜索? | 向量搜索捕获语义,关键词搜索捕获精确匹配,组合更全面 |
7.2 岗位价值
Embedding + 向量数据库是 RAG、搜索、推荐等方向的必备基础。掌握这个技能,你就具备了构建语义搜索系统的核心能力------这正是大量 AI 应用岗位的核心要求。
总结
- Embedding 本质:将文本映射到向量空间,语义相似 = 向量接近
- 模型选择:OpenAI 方便,开源(BGE-M3/GTE)免费可自部署,按场景选
- ANN 算法:HNSW 最流行,IVF + PQ 适合超大规模数据
- 向量数据库:原型用 Chroma,生产用 Milvus/Qdrant,已有 PG 用 pgvector
- 混合搜索:向量 + BM25 关键词,效果通常优于单一方式
本文是 AI 核心技能系列 第 4 篇,共 12 篇。上一篇:Prompt Engineering | 下一篇:RAG 从零到一
关注公众号「coft」,获取完整系列更新、配套代码和学习路线图。一起交流 AI 转行经验,助力职业跃升,迈向高薪岗位。