399 lines
13 KiB
Plaintext
399 lines
13 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 12 向量数据库\n",
|
||
"\n",
|
||
"## 学习目标\n",
|
||
"1. 理解向量数据库与传统数据库的区别\n",
|
||
"2. 掌握 ChromaDB 的基本使用方法\n",
|
||
"3. 学会向量数据库的持久化存储和加载\n",
|
||
"4. 理解元数据过滤在检索中的作用\n",
|
||
"5. 能够选择合适的向量数据库方案"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 1. 为什么需要向量数据库\n",
|
||
"\n",
|
||
"上一节课我们用 `InMemoryVectorStore` 把向量存在内存中,它适合学习和测试,但有几个明显缺点:\n",
|
||
"\n",
|
||
"- **数据无法持久化**:程序关闭后向量就消失了\n",
|
||
"- **无法增量更新**:不能方便地添加、删除文档\n",
|
||
"- **不支持复杂查询**:无法按元数据过滤\n",
|
||
"- **不适合大规模数据**:内存容量有限\n",
|
||
"\n",
|
||
"**向量数据库** 就是为解决这些问题而生的专用数据库。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 2. 向量数据库 vs 传统数据库\n",
|
||
"\n",
|
||
"| 特性 | 传统数据库(MySQL) | 向量数据库(ChromaDB) |\n",
|
||
"| --- | --- | --- |\n",
|
||
"| 存储内容 | 结构化数据 | 向量 + 原始文本 + 元数据 |\n",
|
||
"| 查询方式 | 精确匹配、范围查询 | 相似度搜索 |\n",
|
||
"| 索引类型 | B+ 树 | HNSW、IVF 等近似最近邻索引 |\n",
|
||
"| 典型应用 | 订单、用户管理 | 语义搜索、RAG、推荐 |\n",
|
||
"\n",
|
||
"向量数据库的核心能力:**根据语义相似度快速找到最相关的向量**。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 3. 核心概念\n",
|
||
"\n",
|
||
"### 3.1 向量(Vector)\n",
|
||
"文本经过 Embedding 模型编码后变成的高维数组。例如一个 2560 维的浮点数数组。\n",
|
||
"\n",
|
||
"### 3.2 相似度(Similarity)\n",
|
||
"两个向量之间的距离。常用度量方式:\n",
|
||
"- **余弦相似度(Cosine Similarity)**:衡量方向是否一致,范围 -1 到 1\n",
|
||
"- **欧氏距离(Euclidean Distance)**:衡量向量空间中的直线距离\n",
|
||
"\n",
|
||
"### 3.3 索引(Index)\n",
|
||
"为了加速海量向量的相似度搜索,向量数据库会构建专门的索引结构,如 HNSW。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 4. 常见向量数据库\n",
|
||
"\n",
|
||
"| 数据库 | 特点 | 适用场景 |\n",
|
||
"| --- | --- | --- |\n",
|
||
"| **ChromaDB** | 轻量、易用、支持持久化 | 本地开发、中小规模 |\n",
|
||
"| **FAISS** | Meta 开源、高性能 | 大规模向量检索 |\n",
|
||
"| **Milvus** | 企业级、分布式 | 生产环境、海量数据 |\n",
|
||
"| **Pinecone** | 云端托管、无需运维 | 快速上线、Serverless |\n",
|
||
"| **Weaviate** | 支持多模态、GraphQL | 复杂查询、多模态 |\n",
|
||
"\n",
|
||
"本节课以 **ChromaDB** 为例,因为它安装简单、接口友好,非常适合学习。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 5. 安装 ChromaDB\n",
|
||
"\n",
|
||
"在虚拟环境中执行:\n",
|
||
"\n",
|
||
"```powershell\n",
|
||
"pip install chromadb -i https://pypi.tuna.tsinghua.edu.cn/simple\n",
|
||
"```\n",
|
||
"\n",
|
||
"安装完成后需要**重启 Jupyter 内核**。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 6. 使用 ChromaDB 创建向量库\n",
|
||
"\n",
|
||
"下面演示如何把文档存入 ChromaDB 并进行检索。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import warnings\n",
|
||
"from dotenv import load_dotenv\n",
|
||
"\n",
|
||
"load_dotenv()\n",
|
||
"\n",
|
||
"warnings.filterwarnings('ignore', category=DeprecationWarning)\n",
|
||
"\n",
|
||
"from langchain_community.vectorstores import Chroma\n",
|
||
"from langchain_core.documents import Document\n",
|
||
"from langchain_openai import OpenAIEmbeddings\n",
|
||
"import os\n",
|
||
"\n",
|
||
"# 创建 embedding 模型\n",
|
||
"embeddings = OpenAIEmbeddings(\n",
|
||
" model='qwen3-embedding',\n",
|
||
" openai_api_base=os.environ.get('OPENAI_BASE_URL'),\n",
|
||
" openai_api_key=os.environ.get('OPENAI_API_KEY')\n",
|
||
")\n",
|
||
"\n",
|
||
"# 准备文档\n",
|
||
"documents = [\n",
|
||
" Document(page_content='人工智能是计算机科学的一个分支。', metadata={'source': 'ai_book', 'category': 'AI'}),\n",
|
||
" Document(page_content='深度学习使用多层神经网络。', metadata={'source': 'ai_book', 'category': 'AI'}),\n",
|
||
" Document(page_content='机器学习让计算机从数据中学习。', metadata={'source': 'ai_book', 'category': 'AI'}),\n",
|
||
" Document(page_content='香蕉是一种黄色的水果。', metadata={'source': 'food_book', 'category': 'food'}),\n",
|
||
" Document(page_content='苹果通常是红色或绿色的。', metadata={'source': 'food_book', 'category': 'food'})\n",
|
||
"]\n",
|
||
"\n",
|
||
"# 从文档创建 ChromaDB 向量库\n",
|
||
"# persist_directory 指定数据保存目录\n",
|
||
"vectorstore = Chroma.from_documents(\n",
|
||
" documents=documents,\n",
|
||
" embedding=embeddings,\n",
|
||
" persist_directory='./chroma_db_demo'\n",
|
||
")\n",
|
||
"\n",
|
||
"print('向量库创建成功!')\n",
|
||
"print(f'文档数量:{vectorstore._collection.count()}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 代码解释\n",
|
||
"\n",
|
||
"- `Chroma.from_documents()`:从 Document 列表直接创建向量库\n",
|
||
"- `embedding=embeddings`:指定向量化模型\n",
|
||
"- `persist_directory`:数据持久化目录,程序关闭后数据仍存在"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 7. 相似度检索\n",
|
||
"\n",
|
||
"创建检索器后,就可以根据用户问题进行语义搜索。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# 创建检索器\n",
|
||
"retriever = vectorstore.as_retriever(search_kwargs={'k': 3})\n",
|
||
"\n",
|
||
"# 检索与问题最相关的文档\n",
|
||
"query = '什么是人工智能?'\n",
|
||
"results = retriever.invoke(query)\n",
|
||
"\n",
|
||
"print(f'问题:{query}\\n')\n",
|
||
"for i, doc in enumerate(results):\n",
|
||
" print(f'--- 结果 {i+1} ---')\n",
|
||
" print(f'内容:{doc.page_content}')\n",
|
||
" print(f'元数据:{doc.metadata}\\n')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 8. 持久化加载\n",
|
||
"\n",
|
||
"ChromaDB 的数据已经保存在 `./chroma_db_demo` 目录中。下次启动程序时,可以直接加载,不需要重新向量化。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# 从已有目录加载向量库\n",
|
||
"loaded_vectorstore = Chroma(\n",
|
||
" persist_directory='./chroma_db_demo',\n",
|
||
" embedding_function=embeddings\n",
|
||
")\n",
|
||
"\n",
|
||
"loaded_retriever = loaded_vectorstore.as_retriever(search_kwargs={'k': 2})\n",
|
||
"results = loaded_retriever.invoke('深度学习和神经网络的关系')\n",
|
||
"\n",
|
||
"print('从持久化数据库加载后检索:')\n",
|
||
"for doc in results:\n",
|
||
" print(f'- {doc.page_content}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 9. 元数据过滤\n",
|
||
"\n",
|
||
"元数据过滤可以让我们在检索时只搜索特定类别的文档。例如只搜索 `category='AI'` 的文档。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# 只检索 category 为 AI 的文档\n",
|
||
"filtered_results = loaded_vectorstore.similarity_search(\n",
|
||
" '水果的颜色',\n",
|
||
" filter={'category': 'AI'},\n",
|
||
" k=3\n",
|
||
")\n",
|
||
"\n",
|
||
"print('过滤后只搜索 AI 类文档:')\n",
|
||
"for doc in filtered_results:\n",
|
||
" print(f'- [{doc.metadata[\"category\"]}] {doc.page_content}')\n",
|
||
"\n",
|
||
"# 只检索 category 为 food 的文档\n",
|
||
"food_results = loaded_vectorstore.similarity_search(\n",
|
||
" '人工智能',\n",
|
||
" filter={'category': 'food'},\n",
|
||
" k=3\n",
|
||
")\n",
|
||
"\n",
|
||
"print('\\n过滤后只搜索 food 类文档:')\n",
|
||
"for doc in food_results:\n",
|
||
" print(f'- [{doc.metadata[\"category\"]}] {doc.page_content}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 代码解释\n",
|
||
"\n",
|
||
"- `filter={'category': 'AI'}`:只返回 category 字段等于 AI 的文档\n",
|
||
"- 即使查询词是「水果的颜色」,由于过滤条件限制,也只会返回 AI 类文档\n",
|
||
"- 元数据过滤常用于多租户、多知识库场景"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 10. 增量更新:添加和删除文档\n",
|
||
"\n",
|
||
"向量数据库支持动态增删文档,不需要每次重新构建整个库。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# 添加新文档\n",
|
||
"new_docs = [\n",
|
||
" Document(page_content='自然语言处理让计算机理解人类语言。', metadata={'source': 'ai_book', 'category': 'AI'})\n",
|
||
"]\n",
|
||
"vectorstore.add_documents(new_docs)\n",
|
||
"print(f'添加后文档数量:{vectorstore._collection.count()}')\n",
|
||
"\n",
|
||
"# 查询刚添加的文档\n",
|
||
"results = vectorstore.similarity_search('NLP 是什么', k=2)\n",
|
||
"for doc in results:\n",
|
||
" print(f'- {doc.page_content}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 11. 完整示例:基于 ChromaDB 的本地知识库问答\n",
|
||
"\n",
|
||
"把第 11 节课的 RAG 链和 ChromaDB 结合起来,构建一个可持久化的本地知识库。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from langchain_openai import ChatOpenAI\n",
|
||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||
"from langchain_core.runnables import RunnablePassthrough\n",
|
||
"\n",
|
||
"llm = ChatOpenAI(model='qwen3.6-35b-A3b', temperature=0.1)\n",
|
||
"\n",
|
||
"prompt = ChatPromptTemplate.from_messages([\n",
|
||
" ('system', '你是知识库问答助手,请根据上下文回答问题。如果不知道,请说「我不知道」。'),\n",
|
||
" ('user', '''上下文:\\n{context}\\n\\n问题:{question}''')\n",
|
||
"])\n",
|
||
"\n",
|
||
"def format_docs(docs):\n",
|
||
" return '\\n\\n'.join([doc.page_content for doc in docs])\n",
|
||
"\n",
|
||
"# 使用 ChromaDB 检索器\n",
|
||
"chroma_retriever = loaded_vectorstore.as_retriever(search_kwargs={'k': 2})\n",
|
||
"\n",
|
||
"qa_chain = (\n",
|
||
" {'context': chroma_retriever | format_docs, 'question': RunnablePassthrough()}\n",
|
||
" | prompt\n",
|
||
" | llm\n",
|
||
" | StrOutputParser()\n",
|
||
")\n",
|
||
"\n",
|
||
"question = '深度学习是什么?'\n",
|
||
"answer = qa_chain.invoke(question)\n",
|
||
"print(f'Q: {question}')\n",
|
||
"print(f'A: {answer}')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 12. 向量数据库选型建议\n",
|
||
"\n",
|
||
"| 场景 | 推荐方案 |\n",
|
||
"| --- | --- |\n",
|
||
"| 本地学习、原型开发 | ChromaDB / InMemoryVectorStore |\n",
|
||
"| 大规模生产环境 | Milvus / FAISS |\n",
|
||
"| 不想自己运维 | Pinecone / Weaviate Cloud |\n",
|
||
"| 需要多模态检索 | Weaviate |\n",
|
||
"| 已有 Elasticsearch 集群 | Elasticsearch 向量检索 |\n",
|
||
"\n",
|
||
"选择向量数据库时主要考虑:数据规模、查询性能、运维成本、是否支持元数据过滤。"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 13. 本节课练习\n",
|
||
"\n",
|
||
"1. 创建 5 条以上不同类别的文档,存入 ChromaDB,并指定 source 和 category 元数据\n",
|
||
"2. 分别用无过滤和有过滤两种方式检索,对比结果差异\n",
|
||
"3. 关闭 Jupyter 后重新打开,加载已持久化的 ChromaDB,验证数据没有丢失\n",
|
||
"4. 尝试添加一条新文档,然后检索这条新文档相关的内容\n",
|
||
"5. 用 ChromaDB 替换第 11 节课中的 InMemoryVectorStore,重新构建 RAG 链"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": ".venv",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.14.4"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
}
|