Embedding 与向量数据库完全指南:语义搜索的工程实现

Embedding 是把"语义"变成"数学"的桥梁,是几乎所有现代 AI 应用的基础设施。问"两段文字相似度"、做"语义搜索"、构建"推荐系统"、实现"RAG"—— 背后都是 Embedding。但这个概念被讲得很玄,什么"向量空间""高维语义",听得人云里雾里。这篇文章用最直接的方式把它讲透,并带你看一遍向量数据库(Pinecone、Milvus、pgvector)的工作机制。

Embedding 是什么:一句话定义

Embedding 就是把任意对象(单词、句子、图片、用户、商品)映射到一个固定维度的向量,且这个向量满足一个性质:语义相近的对象在向量空间里距离也近

embed("猫") = [0.12, -0.34, 0.56, ..., 0.78]    # 1024 维
embed("狗") = [0.11, -0.32, 0.55, ..., 0.76]    # 和"猫"很近
embed("航空母舰") = [-0.45, 0.67, -0.12, ..., 0.89]  # 和"猫"很远

有了这个性质,大量"语义相关"的工程问题立刻能用"数学距离"解决:

  • 语义搜索:输入查询 embed,在文档 embedding 库里找距离最近的。
  • 聚类:K-means 等聚类算法直接作用于 embedding。
  • 分类:用 KNN 找最近邻判别类别。
  • 异常检测:某个点距离所有其他点都远 → 异常。
  • 推荐:用户 embedding 和商品 embedding 内积高 → 推荐。

怎么算两个 embedding 的相似度

常见三种距离:

import numpy as np

a = np.array([1, 2, 3])
b = np.array([2, 4, 6])

# 1. 欧氏距离:几何上的"直线距离",对模长敏感
euclidean = np.linalg.norm(a - b)            # √14

# 2. 余弦相似度:只看方向不看模长,值域 [-1, 1],1 完全同向
cos = (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))   # 1.0

# 3. 点积(内积):对归一化的向量等价于余弦
dot = a @ b                                   # 28

文本 embedding 一般用余弦相似度,因为我们关心"语义方向"不关心"模长",而且现代 embedding 模型大多输出已归一化(模长 = 1)的向量,这时余弦和点积等价,点积计算更快。

Word Embedding 的演化简史

1. One-Hot:最朴素

# 词表 ["猫", "狗", "鱼"]
"猫" -> [1, 0, 0]
"狗" -> [0, 1, 0]
"鱼" -> [0, 0, 1]

问题:任意两词距离都一样,没有语义信息;维度 = 词表大小(几万到几十万),稀疏到爆。

2. Word2Vec(2013):密集向量的开端

核心假设:"一个词的语义由它的上下文决定"。用一个简单的两层神经网络,让"中心词预测上下文"(Skip-gram)或"上下文预测中心词"(CBOW),训练出来的中间隐层就是 embedding。

from gensim.models import Word2Vec

sentences = [["猫", "喜欢", "吃", "鱼"], ["狗", "喜欢", "吃", "肉"], ...]
model = Word2Vec(sentences, vector_size=300, window=5, min_count=1)

model.wv["猫"]                  # 300 维向量
model.wv.most_similar("猫")     # 找最相似的词:[("狗", 0.85), ("宠物", 0.79), ...]
model.wv.similarity("猫", "狗") # 0.85

Word2Vec 揭示了著名的"向量算术":vec(国王) - vec(男人) + vec(女人) ≈ vec(王后)。这是 embedding 第一次让大家直观感受到"语义被编码进了几何"。

3. 上下文敏感 embedding(2018 起)

Word2Vec 的缺陷:每个词只有一个 embedding,"苹果"在"我吃苹果"和"苹果发布新手机"里向量一样。ELMo、BERT 解决这个 —— 同一个词在不同上下文里有不同 embedding。BERT 的每一层输出都可以当 embedding 用。

4. 句子/文档 embedding

把一段文本变成单个向量。常见方法:

  • 取所有 token embedding 的平均(简单但有效)。
  • 取 [CLS] token 的 embedding(BERT 风格)。
  • 用专门训练的句向量模型:Sentence-BERT、SimCSE、E5、BGE、Cohere embed-v3、OpenAI text-embedding-3。

2024 年起,中文场景里 BGE-M3、Conan-embedding、Qwen3-embedding 等模型表现都很好,且支持中英双语和长文本(8k+)。

实战 1:用 OpenAI / 国内服务调 Embedding API

# OpenAI
from openai import OpenAI
client = OpenAI()

resp = client.embeddings.create(
    model="text-embedding-3-large",
    input=["猫喜欢吃鱼", "犬科动物以肉为食", "航空母舰是大型军舰"],
)
embeddings = [d.embedding for d in resp.data]   # 3 个 3072 维向量

import numpy as np
a, b, c = [np.array(e) for e in embeddings]
print(a @ b)    # 较高,语义相近
print(a @ c)    # 较低

国内服务(阿里通义、智谱、百度)API 类似,只是 endpoint 和模型名不同。

实战 2:本地跑 embedding 模型

不想依赖云服务,本地跑(适合处理敏感数据 / 大批量):

from sentence_transformers import SentenceTransformer

# 中文推荐 BGE-M3 或 BGE-large-zh
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')

sentences = ["猫喜欢吃鱼", "犬科动物以肉为食", "航空母舰是大型军舰"]
embeddings = model.encode(sentences, normalize_embeddings=True)
# normalize 后点积 = 余弦

import numpy as np
similarity = embeddings @ embeddings.T
print(similarity)
# [[1.    0.7   0.2]
#  [0.7   1.    0.3]
#  [0.2   0.3   1.0]]

向量数据库:百万向量里快速找最近邻

有了 embedding 还不够 —— 几百万、几亿个向量里找最近邻,逐个算余弦不现实(O(N×d))。需要专门的近似最近邻(ANN)索引。

常见 ANN 算法

  • HNSW(Hierarchical Navigable Small World):分层图结构,查询时从顶层向底层逐步靠近目标。是 Pinecone、Milvus、pgvector 默认算法之一。查询复杂度 O(log N)。
  • IVF(Inverted File):用 K-means 把向量分簇,查询时只看最相似的几个簇。
  • LSH(Locality-Sensitive Hashing):用哈希函数让相似向量碰撞概率高。简单但精度通常不如 HNSW。
  • PQ(Product Quantization):向量压缩,内存占用降到 1/10 ~ 1/100。常和 IVF 组合(IVF-PQ)。

向量数据库选型

# Pinecone (云服务,免运维)
import pinecone
pc = pinecone.Pinecone(api_key=...)
index = pc.Index("my-index")
index.upsert([("id1", embedding1, {"text": "..."})])
result = index.query(vector=query_emb, top_k=5, include_metadata=True)

# Milvus (开源,可自部署)
from pymilvus import MilvusClient
client = MilvusClient("./milvus.db")
client.insert(collection_name="docs", data=[
    {"id": 1, "vector": embedding1, "text": "..."}
])
result = client.search(collection_name="docs", data=[query_emb], limit=5)

# pgvector (PostgreSQL 扩展,适合已有 PG 的项目)
CREATE EXTENSION vector;
CREATE TABLE docs (id BIGINT, text TEXT, embedding vector(1024));
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);

INSERT INTO docs VALUES (1, '...', '[0.1, 0.2, ...]'::vector);

SELECT id, text, 1 - (embedding <=> '[query_emb]') AS similarity
FROM docs ORDER BY embedding <=> '[query_emb]' LIMIT 5;

# FAISS (Meta 出品的本地库,无服务器)
import faiss
index = faiss.IndexHNSWFlat(1024, 32)   # 1024 维,M=32
index.add(np.array(embeddings))
D, I = index.search(query_emb.reshape(1, -1), k=5)

选型经验:小数据量(几十万)+ 已用 PG → pgvector;百万级 + 想自建 → Milvus / Qdrant / Weaviate;免运维,云服务 → Pinecone;纯本地、嵌入式 → FAISS / Chroma

实战 3:语义搜索系统骨架

# 1. 索引阶段:把文档全部 embed 后存进向量库
def index_documents(docs):
    embeddings = model.encode([d.text for d in docs], normalize=True)
    for doc, emb in zip(docs, embeddings):
        vector_db.insert(doc.id, emb.tolist(), metadata={"text": doc.text})

# 2. 查询阶段
def semantic_search(query, top_k=10):
    query_emb = model.encode(query, normalize=True)
    results = vector_db.query(query_emb.tolist(), top_k=top_k)
    return [(r.text, r.score) for r in results]

# 用法
semantic_search("如何配置 nginx 缓存?")
# [(关于 nginx cache 模块的文档, 0.89), (Cache-Control 头详解, 0.85), ...]

这套机制比传统关键词搜索(Elasticsearch BM25)在同义词、模糊匹配、跨语言上优势明显。生产环境通常混合检索:向量检索 + BM25 + 重排序(reranker),取三者的并集再用 reranker 排序,准确率和召回率都更好。

非文本 Embedding

图像 Embedding

import clip
import torch

model, preprocess = clip.load("ViT-B/32")
image = preprocess(Image.open("cat.jpg")).unsqueeze(0)
with torch.no_grad():
    img_emb = model.encode_image(image)        # 512 维
    txt_emb = model.encode_text(clip.tokenize(["a cat", "a dog"]))
    similarity = (img_emb @ txt_emb.T).softmax(dim=-1)
# CLIP 把图和文本编到同一空间,可以"用文字搜图"

CLIP / ALIGN / SigLIP 这类多模态模型是图文搜索的基础。淘宝拍图搜同款、Pinterest 的视觉搜索都用类似技术。

音频 Embedding

Whisper、Wav2Vec、CLAP 可以把音频片段映射到向量,用于"哼唱搜歌""声纹识别""音频分类"。

用户 / 商品 Embedding

推荐系统训练时,用户和商品都映射到同一个空间,内积大 → 推荐。这就是 YouTube、TikTok、Netflix 的推荐核心。

常见坑

坑 1:不同模型的 embedding 不能混用

OpenAI 的 text-embedding-3 和 BGE-large 输出维度不同,即使维度对得上,语义空间也完全不同。一旦换模型,所有历史 embedding 要重新算。

坑 2:文本太长被截断

大多数 embedding 模型有 512 / 8192 token 的上限,超长文本被截断。长文档要先分段(chunking),通常按段落 + 重叠窗口(200-500 token 一段,50 token 重叠)。

坑 3:语言不匹配

纯英文模型对中文效果差,反之亦然。多语言场景用 BGE-M3、E5-multilingual 这类多语言模型。

坑 4:维度太高慢/省错维度

3072 维比 768 维更准但占 4 倍存储和算力。常见折中:用大维度模型 + Matryoshka 风格降维到 256 / 512,精度损失极小但速度大幅提升。OpenAI text-embedding-3 原生支持 truncate dimensions。

坑 5:相似度阈值不能跨数据集复用

"相似度 > 0.8 算匹配"这个阈值,在 A 数据集上可能合理,B 数据集上完全错。每个新场景都要重新校准阈值。

评估 embedding 质量

常用基准:

  • MTEB(Massive Text Embedding Benchmark):覆盖 58 个数据集,包括分类、聚类、检索、语义相似度等任务。所有主流模型都报这个分。
  • C-MTEB:中文版本,选模型时务必看。
  • BEIR:专注信息检索基准。

Embedding 的训练目标:对比学习

现代文本 embedding 几乎都用对比学习训练:让"语义相近的对"靠近,"不相关的对"拉远。

# InfoNCE 损失(对比学习经典损失)
# anchor: 锚点句子
# positive: 和 anchor 语义相近的句子(同义、改写、问答对)
# negatives: 一批不相关的句子

similarities = anchor_emb @ all_emb.T / temperature       # [1, N+1]
labels = torch.tensor([0])    # positive 在第 0 位
loss = F.cross_entropy(similarities, labels)

训练数据通常来自:

  • 问答对:Stack Overflow 问题 vs 高赞答案。
  • 检索点击日志:用户搜索 query → 点击文档。
  • 同义改写:用 LLM 生成同一句话的不同表达。
  • 翻译对:英 - 中翻译对(用于多语言 embedding)。

BGE / E5 / Cohere embed 都在大规模问答数据上做了"有监督对比学习",效果远超早期的 Sentence-BERT。这是它们在 MTEB 排行榜上长期霸榜的原因。

Embedding 微调:让通用模型贴合你的领域

通用 embedding 模型在医疗 / 法律 / 编程等垂直领域表现往往一般。可以用领域数据微调:

from sentence_transformers import SentenceTransformer, losses
from sentence_transformers.readers import InputExample
from torch.utils.data import DataLoader

# 准备领域问答对
train_examples = [
    InputExample(texts=["如何配置 nginx HTTPS?", "在 server 块加 listen 443 ssl..."], label=1.0),
    InputExample(texts=["什么是 systemd?", "systemd 是 Linux init 系统..."], label=1.0),
    # ...
]

model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
loader = DataLoader(train_examples, batch_size=32, shuffle=True)
loss = losses.MultipleNegativesRankingLoss(model)

model.fit(
    train_objectives=[(loader, loss)],
    epochs=3,
    warmup_steps=100,
    output_path="./bge-finetune",
)

哪怕只有几千对领域数据,微调后在该领域的检索准确率通常能涨 10-20%。这是中型企业搭建专业 RAG 系统时最有杠杆的一步。

写在最后

Embedding 是 AI 工程里少数"概念简单,应用极广"的工具。理解了它,语义搜索、推荐、RAG、聚类、异常检测、多模态匹配这些看似不同的应用,在你眼里都是"把对象映射到向量,然后做近邻查询"的同一件事。

给一个工程心得:在做任何"内容相关"的功能时,先问"能不能用 embedding 表示这两个对象的相似度"。能的话,工程方案立刻清晰:选模型 → embed → 向量库 → top-k 检索。这套组合在 2025 年成本已经低到几乎为零(开源模型 + pgvector 几乎零成本部署),但能力上限非常高。下一篇 RAG 就是 embedding 的最重要应用,会进一步把这个工具发挥到极致。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

Transformer 完全指南:从注意力机制到 GPT 的工作原理

2026-5-15 15:54:02

技术教程

RAG 检索增强生成完全指南:从 Naive 到生产级 Advanced RAG

2026-5-15 15:54:03

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索