Tutorial 5: 向量数据库
什么是向量数据库?
向量数据库 是专门用于存储和检索向量的数据库,支持高效的相似性搜索。
传统数据库 vs 向量数据库
传统数据库:
SELECT * FROM documents WHERE title = '机器学习'
→ 精确匹配
向量数据库:
SEARCH vectors SIMILAR TO query_vector LIMIT 5
→ 语义相似匹配
为什么需要向量数据库?
高效搜索: 支持亿级向量的毫秒级检索
近似最近邻: ANN 算法实现快速相似搜索
可扩展性: 支持分布式部署
元数据过滤: 结合向量搜索和传统过滤
主流向量数据库
数据库 |
类型 |
特点 |
适用场景 |
|---|---|---|---|
Chroma |
嵌入式 |
轻量、易用、开源 |
开发测试、小规模 |
FAISS |
库 |
Meta开源、高性能 |
大规模检索 |
Pinecone |
云服务 |
全托管、易扩展 |
生产环境 |
Milvus |
分布式 |
开源、高可用 |
企业级部署 |
Weaviate |
云/自托管 |
支持多模态 |
复杂应用 |
Qdrant |
云/自托管 |
Rust实现、高性能 |
生产环境 |
使用 Chroma
Chroma 是最简单易用的向量数据库,适合学习和小规模应用。
# pip install chromadb
import chromadb
from chromadb.utils import embedding_functions
# 1. 创建客户端
# 内存模式(数据不持久化)
client = chromadb.Client()
# 持久化模式
client = chromadb.PersistentClient(path="./chroma_db")
# 2. 创建/获取集合
# 使用默认嵌入函数
collection = client.create_collection(
name="my_collection",
metadata={"description": "测试集合"}
)
# 使用自定义嵌入函数
sentence_transformer_ef = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name="all-MiniLM-L6-v2"
)
collection = client.create_collection(
name="custom_embedding_collection",
embedding_function=sentence_transformer_ef
)
# 3. 添加文档
collection.add(
documents=[
"机器学习是人工智能的分支",
"深度学习使用神经网络",
"自然语言处理处理文本数据"
],
metadatas=[
{"category": "AI", "source": "教材"},
{"category": "AI", "source": "论文"},
{"category": "NLP", "source": "教材"}
],
ids=["doc1", "doc2", "doc3"]
)
print(f"集合中有 {collection.count()} 个文档")
# 4. 查询
results = collection.query(
query_texts=["什么是神经网络?"],
n_results=2
)
print("\n查询结果:")
for i, (doc, metadata, distance) in enumerate(zip(
results['documents'][0],
results['metadatas'][0],
results['distances'][0]
)):
print(f" {i+1}. [{distance:.4f}] {doc}")
print(f" 元数据: {metadata}")
# 5. 带过滤的查询
results = collection.query(
query_texts=["AI技术"],
n_results=2,
where={"category": "AI"} # 只搜索 AI 类别
)
# 6. 更新文档
collection.update(
ids=["doc1"],
documents=["机器学习是人工智能的重要分支,让计算机从数据中学习"],
metadatas=[{"category": "AI", "source": "教材", "updated": True}]
)
# 7. 删除文档
collection.delete(ids=["doc3"])
使用 FAISS
FAISS 是 Meta 开源的高性能向量检索库。
# pip install faiss-cpu # 或 faiss-gpu
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
# 1. 准备数据
model = SentenceTransformer('all-MiniLM-L6-v2')
documents = [
"Python是一种编程语言",
"机器学习是AI的分支",
"深度学习使用神经网络",
"自然语言处理理解文本",
"计算机视觉处理图像"
]
# 生成嵌入
embeddings = model.encode(documents)
embeddings = np.array(embeddings).astype('float32')
# 2. 创建索引
dimension = embeddings.shape[1] # 向量维度
# Flat 索引(精确搜索,适合小数据集)
index = faiss.IndexFlatL2(dimension)
# 添加向量
index.add(embeddings)
print(f"索引中有 {index.ntotal} 个向量")
# 3. 搜索
query = "什么是神经网络?"
query_embedding = model.encode([query]).astype('float32')
k = 3 # 返回 top 3
distances, indices = index.search(query_embedding, k)
print(f"\n查询: {query}")
for i, (idx, dist) in enumerate(zip(indices[0], distances[0])):
print(f" {i+1}. [{dist:.4f}] {documents[idx]}")
# 4. 使用 IVF 索引(适合大数据集)
nlist = 2 # 聚类数量
quantizer = faiss.IndexFlatL2(dimension)
index_ivf = faiss.IndexIVFFlat(quantizer, dimension, nlist)
# 训练索引
index_ivf.train(embeddings)
index_ivf.add(embeddings)
# 设置搜索参数
index_ivf.nprobe = 1 # 搜索的聚类数量
distances, indices = index_ivf.search(query_embedding, k)
# 5. 保存和加载索引
faiss.write_index(index, "my_index.faiss")
loaded_index = faiss.read_index("my_index.faiss")
使用 LangChain 集成
LangChain 提供了统一的向量数据库接口。
from langchain_community.vectorstores import Chroma, FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.schema import Document
# 准备嵌入模型
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
# 准备文档
documents = [
Document(page_content="机器学习是AI的分支", metadata={"source": "教材"}),
Document(page_content="深度学习使用神经网络", metadata={"source": "论文"}),
Document(page_content="NLP处理自然语言", metadata={"source": "教材"}),
]
# 使用 Chroma
vectorstore_chroma = Chroma.from_documents(
documents=documents,
embedding=embeddings,
persist_directory="./chroma_langchain"
)
# 使用 FAISS
vectorstore_faiss = FAISS.from_documents(
documents=documents,
embedding=embeddings
)
# 统一的搜索接口
query = "什么是神经网络?"
# 相似度搜索
results = vectorstore_chroma.similarity_search(query, k=2)
for doc in results:
print(f"内容: {doc.page_content}")
print(f"元数据: {doc.metadata}")
# 带分数的搜索
results_with_scores = vectorstore_chroma.similarity_search_with_score(query, k=2)
for doc, score in results_with_scores:
print(f"[{score:.4f}] {doc.page_content}")
# 转换为检索器
retriever = vectorstore_chroma.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
docs = retriever.get_relevant_documents(query)
实战:构建知识库
import chromadb
from sentence_transformers import SentenceTransformer
from typing import List, Dict, Optional
import uuid
class KnowledgeBase:
"""知识库管理器"""
def __init__(self, persist_path: str = "./knowledge_db"):
self.client = chromadb.PersistentClient(path=persist_path)
self.model = SentenceTransformer('all-MiniLM-L6-v2')
self.collection = self.client.get_or_create_collection(
name="knowledge_base",
metadata={"description": "RAG知识库"}
)
def add_documents(
self,
documents: List[str],
metadatas: Optional[List[Dict]] = None,
ids: Optional[List[str]] = None
):
"""添加文档"""
if ids is None:
ids = [str(uuid.uuid4()) for _ in documents]
if metadatas is None:
metadatas = [{} for _ in documents]
# 生成嵌入
embeddings = self.model.encode(documents).tolist()
# 添加到集合
self.collection.add(
documents=documents,
embeddings=embeddings,
metadatas=metadatas,
ids=ids
)
print(f"添加了 {len(documents)} 个文档")
def search(
self,
query: str,
top_k: int = 5,
filter_dict: Optional[Dict] = None
) -> List[Dict]:
"""搜索相关文档"""
query_embedding = self.model.encode([query]).tolist()
results = self.collection.query(
query_embeddings=query_embedding,
n_results=top_k,
where=filter_dict
)
# 格式化结果
formatted_results = []
for i in range(len(results['documents'][0])):
formatted_results.append({
'document': results['documents'][0][i],
'metadata': results['metadatas'][0][i],
'distance': results['distances'][0][i],
'id': results['ids'][0][i]
})
return formatted_results
def delete(self, ids: List[str]):
"""删除文档"""
self.collection.delete(ids=ids)
print(f"删除了 {len(ids)} 个文档")
def get_stats(self) -> Dict:
"""获取统计信息"""
return {
"total_documents": self.collection.count(),
"collection_name": self.collection.name
}
# 使用知识库
kb = KnowledgeBase()
# 添加文档
documents = [
"RAG是检索增强生成的缩写,结合了检索和生成技术。",
"向量数据库用于存储和检索向量嵌入。",
"Chroma是一个轻量级的向量数据库。",
"FAISS是Meta开发的高性能向量检索库。",
"嵌入模型将文本转换为向量表示。"
]
metadatas = [
{"topic": "RAG", "level": "基础"},
{"topic": "向量数据库", "level": "基础"},
{"topic": "向量数据库", "level": "入门"},
{"topic": "向量数据库", "level": "进阶"},
{"topic": "嵌入", "level": "基础"}
]
kb.add_documents(documents, metadatas)
# 搜索
print("\n搜索: '什么是向量数据库?'")
results = kb.search("什么是向量数据库?", top_k=3)
for r in results:
print(f" [{r['distance']:.4f}] {r['document']}")
print(f" 元数据: {r['metadata']}")
# 带过滤的搜索
print("\n搜索(只看基础级别):")
results = kb.search(
"向量数据库",
top_k=3,
filter_dict={"level": "基础"}
)
for r in results:
print(f" [{r['distance']:.4f}] {r['document']}")
# 统计
print(f"\n知识库统计: {kb.get_stats()}")
索引优化
import faiss
import numpy as np
# 不同索引类型的选择
dimension = 384
n_vectors = 100000
# 1. Flat(精确搜索)- 小数据集
index_flat = faiss.IndexFlatL2(dimension)
# 2. IVF(倒排索引)- 中等数据集
nlist = 100 # 聚类数
quantizer = faiss.IndexFlatL2(dimension)
index_ivf = faiss.IndexIVFFlat(quantizer, dimension, nlist)
# 3. HNSW(图索引)- 高召回率
index_hnsw = faiss.IndexHNSWFlat(dimension, 32) # 32是连接数
# 4. PQ(乘积量化)- 大数据集,节省内存
m = 8 # 子向量数
index_pq = faiss.IndexPQ(dimension, m, 8)
# 5. IVF + PQ - 大规模数据集
index_ivfpq = faiss.IndexIVFPQ(quantizer, dimension, nlist, m, 8)
关键概念总结
概念 |
解释 |
|---|---|
向量数据库 |
专门存储和检索向量的数据库 |
ANN |
近似最近邻搜索算法 |
索引 |
加速搜索的数据结构 |
Collection |
向量数据库中的集合/表 |
元数据过滤 |
结合向量搜索和属性过滤 |
下一步
在下一个教程中,我们将学习各种检索策略。