Embedding 与向量基础
Embedding(嵌入向量)是 RAG 系统的核心技术。理解 Embedding 的原理,是掌握 RAG 的基础。
什么是 Embedding?
Embedding 是将文本、图片等非结构化数据转换为固定长度的数值向量的过程。转换后的向量能够捕捉数据的语义信息,语义相近的内容在向量空间中距离更近。
graph LR
subgraph 文本
T1["我喜欢吃苹果"]
T2["我爱吃水果"]
T3["今天股市大跌"]
end
subgraph 向量空间
V1["[0.82, 0.15, 0.93, ...]"]
V2["[0.80, 0.18, 0.91, ...]"]
V3["[0.12, 0.87, 0.23, ...]"]
end
T1 -->|Embedding 模型| V1
T2 -->|Embedding 模型| V2
T3 -->|Embedding 模型| V3
V1 -.->|相似度 0.95| V2
V1 -.->|相似度 0.12| V3
style V1 fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
style V2 fill:#c8e6c9,stroke:#388e3c,stroke-width:2px
style V3 fill:#ffcdd2,stroke:#c62828,stroke-width:2px
核心概念
| 概念 | 说明 |
|---|---|
| 维度 | 向量的长度,如 1536 维 |
| 语义相似度 | 向量之间的距离,越近语义越相似 |
| 余弦相似度 | 最常用的相似度计算方法 |
| 向量空间 | 所有向量所在的高维空间 |
相似度计算
余弦相似度
余弦相似度衡量两个向量的方向是否一致,是 RAG 中最常用的相似度指标。
"""
三种常用的相似度计算方法
"""
import numpy as np
def cosine_similarity(a: list[float], b: list[float]) -> float:
"""
余弦相似度
- 值域: [-1, 1]
- 1 = 完全相同, 0 = 无关, -1 = 完全相反
- 最常用于文本相似度
"""
a, b = np.array(a), np.array(b)
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def euclidean_distance(a: list[float], b: list[float]) -> float:
"""
欧氏距离
- 值域: [0, +inf)
- 越小越相似
- 适合低维空间
"""
a, b = np.array(a), np.array(b)
return np.linalg.norm(a - b)
def dot_product(a: list[float], b: list[float]) -> float:
"""
点积
- 值域: (-inf, +inf)
- 越大越相似
- 需要归一化后使用
"""
a, b = np.array(a), np.array(b)
return np.dot(a, b)
# 演示
vec1 = [0.82, 0.15, 0.93, 0.41] # "我喜欢吃苹果"
vec2 = [0.80, 0.18, 0.91, 0.39] # "我爱吃水果"
vec3 = [0.12, 0.87, 0.23, 0.66] # "今天股市大跌"
print(f"苹果 vs 水果: {cosine_similarity(vec1, vec2):.4f}") # ~0.999
print(f"苹果 vs 股市: {cosine_similarity(vec1, vec3):.4f}") # ~0.573
Embedding 模型
主流 Embedding 模型对比
| 模型 | 提供方 | 维度 | 最大 Token | 中文支持 | 价格 |
|---|---|---|---|---|---|
| text-embedding-3-small | OpenAI | 1536 | 8191 | 好 | $0.02/1M tokens |
| text-embedding-3-large | OpenAI | 3072 | 8191 | 好 | $0.13/1M tokens |
| embed-v3 | Cohere | 1024 | 512 | 好 | $0.10/1M tokens |
| BGE-large-zh | BAAI | 1024 | 512 | 优秀 | 免费(开源) |
| E5-large-v2 | Microsoft | 1024 | 512 | 中等 | 免费(开源) |
| GTE-large | Alibaba | 1024 | 8192 | 优秀 | 免费(开源) |
如何选择?
graph TB
A{你的场景?} --> B{预算充足?}
A --> C{主要语言?}
B -->|是| D[OpenAI text-embedding-3-large]
B -->|否| E{需要私有部署?}
E -->|是| F{主要语言?}
E -->|否| G[OpenAI text-embedding-3-small]
F -->|中文| H[BGE-large-zh]
F -->|英文| I[E5-large-v2]
F -->|多语言| J[GTE-large]
C -->|中文为主| K[BGE 或 GTE 系列]
C -->|英文为主| L[E5 或 OpenAI]
style D fill:#fff3e0,stroke:#f57c00,stroke-width:2px
style G fill:#c8e6c9,stroke:#388e3c,stroke-width:3px
style H fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
使用 OpenAI Embedding
"""
使用 OpenAI Embedding API
"""
from openai import OpenAI
client = OpenAI()
def get_embeddings(texts: list[str], model: str = "text-embedding-3-small") -> list[list[float]]:
"""批量获取文本向量"""
response = client.embeddings.create(
model=model,
input=texts
)
return [item.embedding for item in response.data]
# 单条文本
embedding = get_embeddings(["你好,世界"])[0]
print(f"向量维度: {len(embedding)}") # 1536
print(f"前5个值: {embedding[:5]}")
# 批量处理
texts = [
"Python 是一门优秀的编程语言",
"Java 也是流行的编程语言",
"今天天气很好",
]
embeddings = get_embeddings(texts)
print(f"处理了 {len(embeddings)} 条文本")
使用开源 Embedding(本地部署)
"""
使用 sentence-transformers 本地部署 Embedding 模型
适合私有化部署场景
"""
from sentence_transformers import SentenceTransformer
# 加载模型(首次会下载)
model = SentenceTransformer("BAAI/bge-large-zh-v1.5")
# 编码文本
texts = [
"Python 设计模式是软件工程的重要基础",
"设计模式帮助编写更优雅的代码",
"今天的晚餐吃什么好呢",
]
embeddings = model.encode(texts, normalize_embeddings=True)
print(f"向量维度: {embeddings.shape[1]}") # 1024
print(f"处理了 {len(embeddings)} 条文本")
# 计算相似度
from numpy import dot
similarity = dot(embeddings[0], embeddings[1])
print(f"设计模式 vs 优雅代码: {similarity:.4f}") # 高相似度
similarity = dot(embeddings[0], embeddings[2])
print(f"设计模式 vs 晚餐: {similarity:.4f}") # 低相似度
向量数据库基础
向量数据库专门用于存储和检索高维向量数据,是 RAG 系统的核心基础设施。
向量数据库的核心能力
graph TB
A[向量数据库] --> B[存储]
A --> C[检索]
A --> D[过滤]
A --> E[管理]
B --> B1[高维向量存储]
B --> B2[元数据存储]
C --> C1[相似度搜索 ANN]
C --> C2[精确搜索 KNN]
D --> D1[元数据过滤]
D --> D2[混合查询]
E --> E1[CRUD 操作]
E --> E2[索引管理]
style A fill:#e3f2fd,stroke:#1976d2,stroke-width:3px
ChromaDB 快速上手
ChromaDB 是最轻量的向量数据库,适合学习和原型开发:
"""
使用 ChromaDB 构建向量存储
pip install chromadb
"""
import chromadb
# 创建客户端(内存模式)
client = chromadb.Client()
# 创建集合
collection = client.create_collection(
name="knowledge_base",
metadata={"hnsw:space": "cosine"} # 使用余弦相似度
)
# 添加文档
collection.add(
documents=[
"Python 是一门解释型编程语言",
"JavaScript 主要用于前端开发",
"Rust 以内存安全著称",
"Go 语言适合构建高并发服务",
],
metadatas=[
{"language": "Python", "type": "backend"},
{"language": "JavaScript", "type": "frontend"},
{"language": "Rust", "type": "system"},
{"language": "Go", "type": "backend"},
],
ids=["doc1", "doc2", "doc3", "doc4"]
)
# 查询
results = collection.query(
query_texts=["哪种语言适合做后端?"],
n_results=2,
where={"type": "backend"} # 元数据过滤
)
print("查询结果:")
for doc, meta, dist in zip(
results["documents"][0],
results["metadatas"][0],
results["distances"][0]
):
print(f" [{meta['language']}] {doc} (距离: {dist:.4f})")
向量搜索算法
| 算法 | 全称 | 特点 | 适用场景 |
|---|---|---|---|
| Flat | 暴力搜索 | 精确但慢 | 小数据集<10K |
| IVF | Inverted File Index | 分区检索 | 中等数据集 |
| HNSW | Hierarchical Navigable Small World | 最快最准 | 大多数场景 |
| PQ | Product Quantization | 压缩存储 | 超大数据集 |
本章小结
- Embedding 将文本转换为数值向量,捕捉语义信息
- 余弦相似度是最常用的相似度计算方法
- OpenAI text-embedding-3-small 是通用场景的最佳性价比选择
- 向量数据库提供高效的向量存储和检索能力
- HNSW 是当前最主流的近似最近邻搜索算法
下一章:我们将学习如何加载和解析不同格式的文档。