mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
293 字
1 分钟
Embedding 与向量搜索原理
2025-07-26

一、Embedding 概述#

1.1 什么是 Embedding#

flowchart LR subgraph Input[" 输入文本"] T["'人工智能改变世界'"] end subgraph Model[" Embedding 模型"] direction TB M1["Tokenizer 分词"] M2["Transformer 编码"] M3["Pooling 池化"] M4["归一化"] M1 --> M2 --> M3 --> M4 end subgraph Output[" 向量输出"] V["[0.12, -0.34, 0.56, ...]<br/>1536 维浮点数"] end subgraph Storage[" 向量存储"] direction TB S1["向量数据库"] S2["相似度索引"] S1 --> S2 end T --> M1 M4 --> V V --> S1 style Input fill:#e3f2fd style Model fill:#fff8e1 style Output fill:#f3e5f5 style Storage fill:#e8f5e9
graph LR A["文本"] --> B["模型"] B --> C[1536 维向量] C --> D[向量数据库] D --> E[相似文本检索]

Embedding 是将离散数据(文本、图像)映射到连续向量空间的技术:

  • 语义相似:语义相近的文本,向量距离更近
  • 维度固定:模型输出的向量维度固定(如 1536 维)
  • 可计算:余弦相似度、点积等度量

1.2 常见 Embedding 模型#

模型维度特点
text-embedding-ada-0021536OpenAI 最新模型
text-embedding-3-small1536更小更快
m3e1024中文优化
BGE1024中英文优化

二、Embedding 模型原理#

2.0 Embedding 创建流程详解#

flowchart TB subgraph Input[" 输入层"] direction LR T["原始文本:<br/>'机器学习很有趣'"] end subgraph Tokenize[" 分词层"] direction TB TK1["Tokenizer"] TK2["Token IDs<br/>[102, 4523, 1234, 3892]"] TK3["Attention Mask<br/>[1, 1, 1, 1]"] TK1 --> TK2 TK1 --> TK3 end subgraph Transformer[" Transformer 编码"] direction TB TR1["Token Embedding<br/>词嵌入层"] TR2["Position Embedding<br/>位置编码"] TR3["Multi-Head Attention<br/>多头注意力"] TR4["Feed Forward<br/>前馈网络"] TR1 --> TR2 --> TR3 --> TR4 end subgraph Pooling[" 池化层"] direction TB P1["Mean Pooling<br/>平均池化"] P2["或 CLS Pooling<br/>首 token 池化"] end subgraph Output[" 输出向量"] direction LR V["归一化向量<br/>[0.12, -0.34, ...]<br/>维度: 768/1536"] end T --> TK1 TK2 --> TR1 TK3 --> TR3 TR4 --> P1 TR4 --> P2 P1 --> V P2 --> V style Input fill:#e3f2fd style Tokenize fill:#fff8e1 style Transformer fill:#f3e5f5 style Pooling fill:#e8f5e9 style Output fill:#fce4ec

2.1 Transformer 结构#

# Transformer 简化的前向传播
class TransformerEmbedding:
def __init__(self, model_name):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModel.from_pretrained(model_name)
def encode(self, texts):
# 1. Tokenize
tokens = self.tokenizer(
texts,
padding=True,
truncation=True,
max_length=512
)
# 2. Forward
outputs = self.model(**tokens)
# 3. Pooling
embeddings = self.mean_pooling(outputs, tokens['attention_mask'])
return F.normalize(embeddings, p=2)

2.2 Pooling 策略#

# Mean Pooling
def mean_pooling(output, attention_mask):
# 掩盖 padding token
input_mask_expanded = attention_mask.unsqueeze(-1).expand(output.size()).float()
return torch.sum(output * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
# CLS Pooling(使用 [CLS] 向量)
def cls_pooling(output):
return output[:, 0] # 取第一个 token

三、向量索引#

3.1 暴力检索 vs 索引#

# 暴力检索
def brute_search(query_vector, vectors, top_k):
scores = cosine_similarity(query_vector, vectors)
return top_k_indices(scores, top_k)
# 时间复杂度 O(n*d)
# IVF 索引
def ivf_search(query_vector, vectors, top_k):
# 1. 找到最近的聚类中心
center = find_nearest_center(query_vector)
# 2. 在中心内搜索
candidates = vectors[center_points[center]]
scores = cosine_similarity(query_vector, candidates)
return top_k(scores, top_k)
# 时间复杂度 O(sqrt(n))

3.2 HNSW 图索引#

flowchart TB subgraph L2[" Layer 2 - 快速跳转"] direction LR A2["Node A"] --- B2["Node B"] B2 --- C2["Node C"] end subgraph L1[" Layer 1 - 中间层"] direction LR A1["Node A"] --- B1["Node D"] B1 --- C1["Node E"] C1 --- D1["Node F"] end subgraph L0[" Layer 0 - 精确搜索"] direction LR A0["Node A"] --- B0["Node G"] B0 --- C0["Node H"] C0 --- D0["Node I"] D0 --- E0["Node J"] E0 --- F0["Node K"] end L2 --> L1 L1 --> L0 style L2 fill:#e3f2fd,stroke:#1976d2 style L1 fill:#fff8e1,stroke:#f57c00 style L0 fill:#e8f5e9,stroke:#388e3c
flowchart LR subgraph Search[" HNSW 搜索过程"] direction TB S1["入口点<br/>顶层开始"] --> S2["贪心搜索<br/>找最近邻"] S2 --> S3["逐层下沉<br/>缩小范围"] S3 --> S4["底层精确<br/>返回结果"] end style Search fill:#f3e5f5
graph TB A["Layer 2"] --> B["稀疏连接"] B --> C["Layer 1"] C --> D["Layer 0"] D --> E["密集连接"] style A fill:#87CEEB style E fill:#90EE90
# HNSW 参数
class HNSWIndex:
def __init__(self):
self.ef_construction = 200 # 建图精度
self.M = 16 # 每个节点连接数
self.ef_search = 100 # 搜索精度

四、向量数据库#

4.1 Milvus 配置#

# Milvus Collection 配置
collection_schema:
name: documents
fields:
- name: id
type: int64
is_primary: true
- name: embedding
type: FLOAT32
dim: 1536
index:
type: HNSW
params:
M: 16
efConstruction: 200
- name: text
type: varchar
max_length: 1000

4.2 Pinecone 操作#

# Pinecone SDK
import pinecone
# 连接
pinecone.init(api_key="...", environment="us-east1")
index = pinecone.Index("documents")
# 插入
index.upsert([
("1", [0.1] * 1536, {"text": "文档1"}),
("2", [0.2] * 1536, {"text": "文档2"})
])
# 检索
results = index.query(
vector=[0.1] * 1536,
top_k=10,
include_metadata=True
)

五、相似度计算#

5.0 相似度搜索流程#

flowchart TB subgraph Query[" 查询向量"] Q["Query: '深度学习教程'<br/>[0.15, 0.32, -0.08, ...]"] end subgraph Database[" 向量数据库"] direction TB D1["Doc 1: '机器学习入门'<br/>相似度: 0.72"] D2["Doc 2: '神经网络教程'<br/>相似度: 0.89"] D3["Doc 3: 'Python 编程'<br/>相似度: 0.45"] D4["Doc 4: '深度学习指南'<br/>相似度: 0.95"] D5["Doc 5: '数据结构'<br/>相似度: 0.31"] end subgraph Compute[" 相似度计算"] direction TB C1["余弦相似度<br/>Cosine Similarity"] C2["点积<br/>Dot Product"] C3["欧氏距离<br/>Euclidean Distance"] end subgraph Ranking[" 排序输出"] R1["#1 Doc 4: 0.95 "] R2["#2 Doc 2: 0.89 "] R3["#3 Doc 1: 0.72 "] end Q --> Compute Database --> Compute Compute --> Ranking style Query fill:#e3f2fd style Database fill:#fff8e1 style Compute fill:#f3e5f5 style Ranking fill:#e8f5e9
graph TB subgraph VectorSpace["向量空间可视化"] direction TB V1["(相似文本聚类)"] V2["同义词 → 距离近"] V3["无关词 → 距离远"] end A["'猫'"] ---|"距离近"| B["'小猫'"] A ---|"距离近"| C["'宠物'"] A ---|"距离远"| D["'汽车'"] A ---|"距离远"| E["'编程'"] style VectorSpace fill:#f5f5f5

5.1 余弦相似度#

def cosine_similarity(a, b):
"""余弦相似度:-1 到 1,1 表示完全相同"""
dot_product = sum(x * y for x, y in zip(a, b))
norm_a = sqrt(sum(x * x for x in a))
norm_b = sqrt(sum(x * x for x in b))
return dot_product / (norm_a * norm_b)

5.2 点积#

def dot_product(a, b):
"""点积:值域无限,需要归一化"""
return sum(x * y for x, y in zip(a, b))
# 归一化后点积等价于余弦相似度
normalized_a = a / norm(a)
normalized_b = b / norm(b)
dot(normalized_a, normalized_b) == cosine_similarity(a, b)

5.3 距离度量#

def euclidean_distance(a, b):
"""欧几里得距离:值域 0 到 +∞"""
return sqrt(sum((x - y) ** 2 for x, y in zip(a, b)))
def manhattan_distance(a, b):
"""曼哈顿距离"""
return sum(abs(x - y) for x, y in zip(a, b))

六、工程实践#

6.1 批量处理#

class BatchEmbedding:
def __init__(self, batch_size=32):
self.batch_size = batch_size
def encode_batch(self, texts):
results = []
for i in range(0, len(texts), self.batch_size):
batch = texts[i:i+self.batch_size]
embeddings = self.model.encode(batch)
results.extend(embeddings)
return results

6.2 增量索引#

# 增量添加到向量数据库
def add_documents(new_docs):
embeddings = model.encode(new_docs)
index.upsert(
zip(ids, embeddings, metadatas)
)
# 异步增量索引
async def add_async(new_docs):
embeddings = await model.encode_async(new_docs)
await index.upsert_async(embeddings)

七、总结#

mindmap root((Embedding 系统)) 模型 OpenAI ada-002 BGE/m3e 中文优化 维度选择 768/1024/1536 处理流程 Tokenize 分词 Transformer 编码 Pooling 池化 归一化 索引结构 HNSW 高效 IVF 聚类 PQ 压缩 相似度度量 余弦相似度 点积 欧氏距离 工程 批量处理 增量索引 缓存优化
组件关键指标
Embedding 模型维度、语义理解能力
索引结构HNSW > IVF > 暴力
向量数据库QPS、延迟、召回率
相似度余弦相似度、点积、欧氏距离

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Embedding 与向量搜索原理
https://blog.souloss.com/posts/ai-engineering/embedding-vectors/
作者
Souloss
发布于
2025-07-26
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时