基于 LangChain 框架,向量数据库如何创建、读取、更新、删除(CRUD)

RAG是目前大语言模型从工具走向生产力实践的最热门的方式,它可以实现从海量的文本数据中检索相关的信息,并用于生成高质量的文本输出。

而聊到RAG,我们就很难避开使用RAG的基础设施-向量数据库

图片

今天我将带领大家,以最为基础的CRUD入手来看看向量数据库应该如何使用。考虑到目前市面上的向量数据库众多,每个数据库的操作方式也无统一标准本文将基于LangChain提供的VectorStore类中的统一操作方法,以chroma向量数据库作为示例进行演示。

文章目录

    • 通俗易懂讲解大模型系列
    • 技术交流&资料
    • 向量数据库-新增
    • 向量数据库-删除
    • 向量数据库-更新
    • 向量数据库-查询
    • 结语

通俗易懂讲解大模型系列

  • 做大模型也有1年多了,聊聊这段时间的感悟!

  • 用通俗易懂的方式讲解:大模型算法工程师最全面试题汇总

  • 用通俗易懂的方式讲解:我的大模型岗位面试总结:共24家,9个offer

  • 用通俗易懂的方式讲解:大模型 RAG 在 LangChain 中的应用实战

  • 用通俗易懂的方式讲解:一文讲清大模型 RAG 技术全流程

  • 用通俗易懂的方式讲解:如何提升大模型 Agent 的能力?

  • 用通俗易懂的方式讲解:ChatGPT 开放的多模态的DALL-E 3功能,好玩到停不下来!

  • 用通俗易懂的方式讲解:基于扩散模型(Diffusion),文生图 AnyText 的效果太棒了

  • 用通俗易懂的方式讲解:在 CPU 服务器上部署 ChatGLM3-6B 模型

  • 用通俗易懂的方式讲解:使用 LangChain 和大模型生成海报文案

  • 用通俗易懂的方式讲解:ChatGLM3-6B 部署指南

  • 用通俗易懂的方式讲解:使用 LangChain 封装自定义的 LLM,太棒了

  • 用通俗易懂的方式讲解:基于 Langchain 和 ChatChat 部署本地知识库问答系统

  • 用通俗易懂的方式讲解:在 Ubuntu 22 上安装 CUDA、Nvidia 显卡驱动、PyTorch等大模型基础环境

  • 用通俗易懂的方式讲解:Llama2 部署讲解及试用方式

  • 用通俗易懂的方式讲解:基于 LangChain 和 ChatGLM2 打造自有知识库问答系统

  • 用通俗易懂的方式讲解:一份保姆级的 Stable Diffusion 部署教程,开启你的炼丹之路

  • 用通俗易懂的方式讲解:对 embedding 模型进行微调,我的大模型召回效果提升了太多了

  • 用通俗易懂的方式讲解:LlamaIndex 官方发布高清大图,纵览高级 RAG技术

  • 用通俗易懂的方式讲解:为什么大模型 Advanced RAG 方法对于AI的未来至关重要?

  • 用通俗易懂的方式讲解:使用 LlamaIndex 和 Eleasticsearch 进行大模型 RAG 检索增强生成

  • 用通俗易懂的方式讲解:基于 Langchain 框架,利用 MongoDB 矢量搜索实现大模型 RAG 高级检索方法

  • 用通俗易懂的方式讲解:使用Llama-2、PgVector和LlamaIndex,构建大模型 RAG 全流程

技术交流&资料

技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。

成立了大模型技术交流群,本文完整代码、相关资料、技术交流&答疑,均可加我们的交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、微信搜索公众号:机器学习社区,后台回复:加群
方式②、添加微信号:mlc2060,备注:来自CSDN + 技术交流

在这里插入图片描述

向量数据库-新增

在这里插入图片描述

LangChain的VectorStore类是一个通用的向量数据库的接口,它可以对接不同的底层向量数据库,如chroma、faiss、annoy等,实现统一的操作方法和API。VectorStore类还提供了一些高级的功能,如语义检索、最大边际相关性(MMR)等,可以帮助我们更好地利用向量数据库的能力。

要想向向量数据库中新增数据,我们首先需要创建一个VectorStore对象,并在创建时配置好embedding function,即用于将原始数据转换为向量的函数。如下所示:

# 通过HuggingFace创建embedding_function
embeddings = HuggingFaceEmbeddings(model_name=model)
# 创建VectorStore的具体实现类Chroma对象,并指定collection_name和持久化目录
vector = Chroma(collection_name = 'cname', embedding_function=embeddings,persist_directory='/vs')

创建好VectorStore对象后,我们就可以使用insert方法来向向量数据库中插入数据了。insert方法接受一个doc对象作为参数,doc对象可以是一个字符串或一个字典,如果是一个字典,那么必须包含一个名为text的键,其值为要插入的文本内容,同时,我们还可以在doc对象中添加一些其他的元数据,用于后续的查询或过滤。例如,我们可以向向量数据库中插入以下三个doc对象:

# 先将文本拆分并转化为doc
loader = TextLoader(url,autodetect_encoding = True)

docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
# 插入向量数据库
vector.add_documents(documents=splits)

add_documents方法会返回一个id列表,这个id是doc的索引,用于唯一标识插入的doc对象。我们可以根据需要记录这个id,并与原始文件关联,一般来说,一个file对应多个doc,例如,我们可以将一个长文本拆分为多个段落,然后将每个段落作为一个doc插入到向量数据库中,这样可以提高检索的效率和精度。

向量数据库-删除

要想从向量数据库中删除数据,我们可以使用delete方法,delete方法接受一个id或一个id列表作为参数,然后根据id来删除对应的doc对象。例如,我们可以使用以下代码来删除id为1的doc对象:

# 删除id为1的doc对象
vector.delete('1')

如果我们不知道要删除的doc对象的id,但是知道它的一些元数据,我们可以先使用metadata中的字段来查询到id,然后再使用delete方法来批量删除。例如,我们可以使用以下代码来删除所有category为功能的doc对象:

# 根据file_id的条件,查询到所有符合的doc对象的id
reuslt = vector.get(where={"file_id":file_id}) 
# 使用delete方法,批量删除这些id对应的doc对象
if reuslt['ids'] :
    vector.delete(reuslt['ids'])

向量数据库-更新

VectorStore类没有提供专门的更新方法,因为更新一个doc对象相当于先删除它,然后再插入一个新的doc对象。因此,我们可以使用delete和insert方法来实现文档的更新。例如,我们可以使用以下代码来更新id为2的doc对象,将它的source从文档改为官网:

# 删除id为2的doc对象
vector.delete('2')

# 插入新的doc对象
vector.add_documents(new_doc)

向量数据库-查询

图片VectorStore类提供了多种查询方法,用于根据不同的需求和场景来检索向量数据库中的数据。查询方法主要分为两种类型:similarity和mmr。similarity类型的查询方法是基于向量之间的相似度来进行检索的,它可以接受一个字符串或一个向量作为查询,然后返回最相似的doc对象以及相似度。mmr类型的查询方法是基于最大边际相关性(MMR)来进行检索的,它可以接受一个字符串或一个向量作为查询,然后返回一个多样化的doc对象列表,这些doc对象既与查询相关,又尽量不相似。

接下来,我们将介绍以下四个查询方法:

  • similarity_search:这个方法是最基本的相似度查询方法,它接受一个字符串作为查询,以及一个可选的top_n参数,用于指定返回的doc对象的数量,默认为4,然后返回最相似的doc对象或它们的id。例如,我们可以使用以下代码来查询与“语义检索”最相似的doc对象:
# 查询与“语义检索”最相似的doc对象
docs = vector.similarity_search("语义检索")

# 打印查询结果
for doc in docs:
    print(doc)
  • similarity_search_with_score:这个方法与similarity_search类似,但是它会同时返回向量之间的距离,距离越小表示越相似。例如,我们可以使用以下代码来查询与“语义检索”最相似的doc对象,并打印它们的分数:
# 查询与“语义检索”最相似的doc对象,并返回分数
docs_with_score = vector.similarity_search_with_score("语义检索")

# 打印查询结果和分数
for doc_with_score in docs_with_score:
    print(doc_with_score[0], doc_with_score[1])
  • similarity_search_with_relevance_scores:这个方法与similarity_search_with_score类似,但是它会将分数转换为一个介于0和1之间的相关度评分,这个评分表示查询和doc对象之间的语义相关程度,评分越高越相似。代码与similarity_search_with_score类似,不再额外示例

  • max_marginal_relevance_search:这个方法是基于最大边际相关性(MMR)来进行查询的,最大边际相关性(MMR)是一种用于检索或摘要的方法,它既考虑了查询和文档之间的相似度,又考虑了文档之间的多样性,从而避免返回重复或冗余的结果。它接受一个字符串或一个向量作为查询,以及一个可选的top_n参数,用于指定返回的doc对象的数量,默认为10,然后返回一个多样化的doc对象列表,这些doc对象既与查询相关,又尽量不相似。这个方法可以用于生成摘要、提取关键信息、避免重复内容等场景。例如,我们可以使用以下代码来查询与“LangChain”相关的但不相似的doc对象:

# 查询与“LangChain”相关的但不相似的doc对象
docs = vector.max_marginal_relevance_search("LangChain")

# 打印查询结果
for doc in docs:
    print(doc)

结语

CRUD只是操作向量数据库的基础手段,想要用好RAG必须在CRUD的基础上掌握语义检索的相关原理。

比如文档拆分时需要按语义尽可能的拆分为小的单元,而在召回时,则需要基于召回的单元尽可能的补充完整的窗口上下文,才能在最终使用LLM时得到尽可能好的结果。这些都需要在crud的基础上执行一些额外操作,我将在下次给大家讲述,希望你持续关注。

档拆分时需要按语义尽可能的拆分为小的单元,而在召回时,则需要基于召回的单元尽可能的补充完整的窗口上下文,才能在最终使用LLM时得到尽可能好的结果。这些都需要在crud的基础上执行一些额外操作,我将在下次给大家讲述,希望你持续关注。

以上就是本文的全部内容,我希望你能从中学到一些有用的知识,也欢迎你在评论区留下你的反馈或问题。谢谢你的阅读,下次再见。