在前一篇文章中,我们实现了对本地文本做分片以及向量化的操作。
但是有个问题是,每次使用的时候都需要对整个文本进行分片和向量化,这样的操作是非常耗时的,而且也是需要消耗 token
数量的。
那有没有办法可以只做一次分片和向量化,然后保存下来,下次使用的时候直接调用呢?
答案是有的,我们只需要将分片和向量化的结果保存到数据库中,下次使用的时候直接从数据库中读取即可。
而保存这种向量化结果的数据库叫做向量数据库,本文中,我们会使用 Chroma
作为向量数据库来保存向量化结果。
同时,这种操作其实是很常见的操作,因此 langchain 也给我们封装了更好的接口,使我们可以更方便的来对本地文档做查询。
Chroma
向量数据库 Chroma
是一种专门设计用来高效管理和查询向量数据的数据库系统。
Chroma
通过其高效的数据结构和算法优化,能够快速处理和检索大量的向量数据。
以下是 Chroma 向量数据库的一些主要特点:
- 高效的向量索引:
Chroma
使用高效的索引结构,如倒排索引、KD-树或基于图的索引,以加快向量搜索速度; - 支持多种相似度度量:它支持多种向量相似度度量标准,包括欧氏距离、余弦相似度等,使其可以广泛应用于不同的应用场景;
- 可扩展性和弹性:
Chroma
能够支持水平扩展,适应大规模数据集的需要。同时,它也能有效处理数据的动态变化,适应快速发展的存储需求; - 易于集成和使用:
Chroma
设计有易于使用的API接口,支持多种编程语言接入,便于开发者在不同的系统和应用中集成使用; - 实时性能优化:
Chroma
优化了查询处理过程,支持实时的数据查询和更新,满足实时分析和决策的需求。
安装
我们可以使用如下命令进行安装:
pip install chroma pip install unstructured pip install pypdf
使用 langchain 结合本地文档做查询的流程
- 读取文件并进行分片
- 将分片后的文本向量化
- 使用用户的问题检索向量化的结果
- 将检索到的本地文本结合用户问题,发送给 LLM 进行回答
- 得到 LLM 的回答
Document Loader(读取文档)
DocumentLoader
是一个用来加载本地文档的类,它可以加载本地文档:
from langchain.document_loaders import DirectoryLoader loader = DirectoryLoader("./", glob='service_design.txt') docs = loader.load()
也可以加载 pdf 文档:
from langchain_community.document_loaders import PyPDFLoader loader = PyPDFLoader("Spotmax_intro_cn_2020.pdf") docs = loader.load()
Text Splitter(文档分片)
RecursiveCharacterTextSplitter
文本分割器是通用文本的推荐分割器。
它会按顺序尝试在这些字符上进行分割,直到分块足够小。
默认列表是 ["\n\n", "\n", " ", ""]
。这样可以尽可能地保持所有段落(然后是句子,然后是单词)在一起。
from langchain.text_splitter import RecursiveCharacterTextSplitter text_spliter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10) splits = text_spliter.split_documents(docs)
配置 key
import os os.environ['OPENAI_API_KEY'] = "your key" os.environ['OPENAI_BASE_URL'] = "https://api.openai-hk.com/v1"
将数据载入向量数据库
from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings embedding = OpenAIEmbeddings() vectordb = Chroma.from_documents( documents=splits, embedding=embedding, collection_name="spotmax", persist_directory=persist_directory, ) vectordb.persist() print(vectordb._collection.count())
这个时候,我们就可以对本地文档进行查询了。
docs = vectordb.similarity_search("如何提高可用性", k=2) print(docs[0])
利用向量数据库进行 QA
langchain 提供了一个 RetrievalQA
类,它可以将向量数据库和 LLM 结合起来,实现对本地文档的查询。
from langchain.chains.retrieval_qa.base import RetrievalQA from langchain_openai import ChatOpenAI llm = ChatOpenAI( model_name="gpt-3.5-turbo", temperature=0, max_tokens=200, api_key="your key", base_url="https://api.openai-hk.com/v1", ) retriever = vectordb.as_retriever( search_type="mmr", search_kwargs={"k": 3} ) qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever, return_source_documents=False, verbose=True) result = qa({"query": "什么是SpotMax?"}) print(result['result'])
工作过程如下:
- 根据用户问题从向量数据库查找相关地分片
- 将分片 + 用户问题传递给 LLM
- 得到 LLM 的回答
说明:
search_type
使用最大边际相关性(Maximal Marginal Relevance
)搜索类型,这是一种混合搜索策略,旨在平衡相关性和多样性。search_kwargs={"k": 3}
在搜索过程中返回3个最相关的文档。RetrievalQA.from_chain_type
创建一个RetrievalQA
实例,使用之前定义的llm
和retriever
chain_type="stuff"
使用stuff
类型的链,这意味着所有检索到的上下文将被组合成一个输入,然后传递给 LLM。return_source_documents
设置为False
,表示不返回源文档,只返回 LLM 的回答verbose
设置为True
,表示打印详细信息
chain type
stuff
:将相关地Chunks
全部放入Prompt
,调用大模型次数少,对于小地数据响应速度快。不适用于大的数据集,相关的chunks
合在一起会超出 token 限制。refine
:用下一个相关chunk
来优化前序的chunk
调用 LLM 后的返回,持续迭代。调用大模型次数多,适用于大的数集。map reduce
: 将所有相关的chunks
分别调用 LLM,然后将结果合并。最后将合并的结果再次调用 LLM。调用大模型次数多,适用于大的数据集。map re-rank
: 将每个chunk
调用 LLM,LLM 返回的结果还打了个分,将分值最高的答案返回给用户。
从现有的 chroma 向量数据库中加载
上面的例子中,我们是将文档分片后,再向量化,然后保存到数据库中。
如果我们已经有了向量化的结果,我们可以直接从数据库中加载,这样可以避免每次都要重新向量化。
唯一的区别是创建 Chroma
对象时,不是使用 from_documents
方法,而是直接指定 db
路径以及 collection_name
即可。
from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings embedding = OpenAIEmbeddings() vectordb = Chroma(persist_directory='data/', embedding_function=embedding, collection_name='spotmax')
注意事项
- 不同的
chain_type
查询结果不一样,有些chain_type
可能查不到你想要的结果。 - 分块大小和
chunk_overlap
也会影响查询结果,需要根据实际情况调整。
一些报错
MacOS 下的报错(本机是 13.1 版本):
pydantic.errors.PydanticImportError: `BaseSettings` has been moved to the `pydantic-settings` packag
解决办法,执行下面的 shell 命令:
SYSTEM_VERSION_COMPAT=0 pip install --no-cache-dir "onnxruntime>=1.14.1"
同时 chromadb
升级到最新版:
pip install chromadb==0.5.4
总结
本文介绍了如何使用 Chroma
向量数据库来保存向量化结果,以及如何使用 langchain 结合本地文档做查询。
具体来说,就是使用 RetrievalQA
结合 Chroma
向量数据库和 LLM,实现对本地文档的查询。