embedding 是什么
把文本变成一个几百到几千维的向量。语义相近的文本 → 向量距离近。
"猫" → [0.1, 0.7, -0.2, ...] (1024 维)
"狗" → [0.15, 0.65, -0.18, ...] (向量很近)
"汽车" → [-0.4, 0.1, 0.8, ...] (距离远)
用 OpenAI embedding API
from openai import OpenAI
client = OpenAI()
resp = client.embeddings.create(
model="text-embedding-3-small", # 1536 维,便宜
input=["猫坐在垫子上", "狗趴在地板上", "汽车在路上跑"],
)
vectors = [d.embedding for d in resp.data] # 3 个向量
print(len(vectors[0])) # 1536
用本地中文 embedding(推荐)
OpenAI 对中文支持一般。中文场景用 bge-large-zh:
pip install sentence-transformers
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
texts = ["猫坐在垫子上", "狗趴在地板上", "汽车在路上跑"]
vectors = model.encode(texts, normalize_embeddings=True)
print(vectors.shape) # (3, 1024)
normalize_embeddings=True 之后,向量已归一化——后面用点积就等价余弦相似度。
算相似度
import numpy as np
def cosine(a, b):
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
cosine(vectors[0], vectors[1]) # 0.85 猫和狗很像
cosine(vectors[0], vectors[2]) # 0.30 猫和汽车不太像
或归一化后直接 np.dot(a, b)。
实战:语义搜索
docs = [
"Python 是一种解释型编程语言",
"天空今天很蓝,适合出去玩",
"机器学习是 AI 的子领域",
"我喜欢吃苹果",
"深度学习用神经网络处理复杂任务",
]
doc_vecs = model.encode(docs, normalize_embeddings=True)
query = "什么是神经网络?"
q_vec = model.encode([query], normalize_embeddings=True)[0]
scores = doc_vecs @ q_vec # 一个矩阵乘法搞定所有相似度
top_idx = scores.argsort()[::-1][:3]
for i in top_idx:
print(f"{scores[i]:.3f} - {docs[i]}")
输出会把"神经网络"相关的文档排前面——这就是语义搜索,比关键字搜索强得多。
切块:长文本要分段
def chunk_text(text, size=300, overlap=50):
chunks = []
i = 0
while i < len(text):
chunks.append(text[i:i+size])
i += size - overlap
return chunks
更智能的做法:按句子 / 段落 / Markdown 标题切。langchain 有现成的 RecursiveCharacterTextSplitter。
模型对比(2026 主流)
| 模型 | 维度 | 中英 | 备注 |
|---|---|---|---|
text-embedding-3-small |
1536 | 中等 | OpenAI,便宜 |
text-embedding-3-large |
3072 | 强 | OpenAI,贵 |
voyage-3 |
1024 | 强 | Anthropic 推荐配合 Claude |
bge-large-zh-v1.5 |
1024 | 中文最强 | 本地免费 |
bge-m3 |
1024 | 多语言+长文 | 长文档好 |
余弦距离 vs 欧氏距离 vs 点积
向量归一化后三者等价——大多数库默认归一化,只关心结果别纠结公式。
缓存:embedding 也要钱
embedding 不变就别重复算。用 lru_cache / Redis / 文件缓存:
from functools import lru_cache
@lru_cache(maxsize=10000)
def embed(text):
return tuple(model.encode([text], normalize_embeddings=True)[0])
下一篇讲向量数据库——把 embedding 存起来快速搜索。