LLM之RAG实战(二十三)| LlamaIndex高级检索(二):父文档检索

       在上一篇文章中,我们介绍了基本RAG的构建,也探讨了RAG管道中从小到大检索技术的两种主要技术:父文档检索和句子窗口检索。

       在本文,我们将深入探讨一下从小到大检索技术中的父文档检索

一、块引用:较小的子块引用较大的父块

      为了让您快速回顾一下什么是父文档检索或小孩块引用更大的父块,以下是我们在上一篇文章中谈到的内容:

首先检索与回答查询最相关的较小数据段,然后使用它们相关的父标识符访问并返回将作为上下文传递给LLM(大型语言模型)的较大父数据块。在LangChain中,这可以通过ParentDocumentRetriever来完成。

       让我们使用此技术构建一个管道。我们将构建一些子块来指向或引用较大父块。在查询期间,我们对较小的块执行搜索,并将他们的父块作为上下文传递给LLM。这样我们能够向LLM传递语音更大、更丰富的上下文,从而减少产生幻觉。

二、数据处理

       要将数据存储到矢量存储并永久保存,我们可以使用如下几个步骤来实现这一点。

2.1 项目目录设置

   基于我们在上一篇文章中创建的项目目录设置。移动到ParentDocumentRetrieval文件夹,并在其中添加一个main.py文件。

2.2 创建子节点或块

对于1024个字符长的每个文本段,我们将其进一步划分为更小的段:

  • 八个段,每个段128个字符;
  • 四个段,每个段256个字符;
  • 两个段各512个字符。此外,我们还将最初的1024个字符的文本段包含到我们的段集合中。

      最后,我们得到173个单独的节点。将chunk_overlap=0至关重要。下面是实现的代码:

from llama_index import (    Document,    StorageContext,    VectorStoreIndex,    SimpleDirectoryReader,    ServiceContext,    load_index_from_storage,)from llama_index.retrievers import RecursiveRetrieverfrom llama_index.node_parser import SimpleNodeParserfrom llama_index.embeddings import OpenAIEmbeddingfrom llama_index.schema import IndexNodefrom llama_index.llms import OpenAI# for loading environment variablesfrom decouple import configimport os# set env variablesos.environ["OPENAI_API_KEY"] = config("OPENAI_API_KEY")# create LLM and Embedding Modelembed_model = OpenAIEmbedding()llm = OpenAI(model="gpt-3.5-turbo")service_context = ServiceContext.from_defaults(    embed_model=embed_model, llm=llm)# load datadocuments = SimpleDirectoryReader(    input_dir="../dataFiles").load_data(show_progress=True)doc_text = "\n\n".join([d.get_content() for d in documents])docs = [Document(text=doc_text)]# create nodes parsernode_parser = SimpleNodeParser.from_defaults(chunk_size=1024)# split into nodesbase_nodes = node_parser.get_nodes_from_documents(documents=docs)# set document IDsfor idx, node in enumerate(base_nodes):    node.id_ = f"node-{idx}"# create parent child documentssub_chunk_sizes = [128, 256, 512]sub_node_parsers = [    SimpleNodeParser.from_defaults(chunk_size=c, chunk_overlap=0) for c in sub_chunk_sizes]all_nodes = []for base_node in base_nodes:    for n in sub_node_parsers:        sub_nodes = n.get_nodes_from_documents([base_node])        sub_inodes = [            IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes        ]        all_nodes.extend(sub_inodes)    # also add original node to node    original_node = IndexNode.from_text_node(base_node, base_node.node_id)    all_nodes.append(original_node)        all_nodes_dict = {n.node_id: n for n in all_nodes}print(all_nodes[0])print(len(all_nodes_dict))# creating indexindex = VectorStoreIndex(nodes=all_nodes, service_context=service_context)

       这是第一步,接下来怎么办?

2.3 创建检索器(递归检索器)

       现在我们已经创建了嵌入,让我们开始创建一个检索器。以下是执行此操作的代码:

from llama_index import (    Document,    StorageContext,    VectorStoreIndex,    SimpleDirectoryReader,    ServiceContext,    load_index_from_storage,)from llama_index.retrievers import RecursiveRetrieverfrom llama_index.node_parser import SimpleNodeParserfrom llama_index.embeddings import OpenAIEmbeddingfrom llama_index.schema import IndexNodefrom llama_index.llms import OpenAI# for loading environment variablesfrom decouple import configimport os# set env variablesos.environ["OPENAI_API_KEY"] = config("OPENAI_API_KEY")# create LLM and Embedding Modelembed_model = OpenAIEmbedding()llm = OpenAI(model="gpt-3.5-turbo")service_context = ServiceContext.from_defaults(    embed_model=embed_model, llm=llm)# load datadocuments = SimpleDirectoryReader(    input_dir="../dataFiles").load_data(show_progress=True)doc_text = "\n\n".join([d.get_content() for d in documents])docs = [Document(text=doc_text)]# create nodes parsernode_parser = SimpleNodeParser.from_defaults(chunk_size=1024)# split into nodesbase_nodes = node_parser.get_nodes_from_documents(documents=docs)# set document IDsfor idx, node in enumerate(base_nodes):    node.id_ = f"node-{idx}"# create parent child documentssub_chunk_sizes = [128, 256, 512]sub_node_parsers = [    SimpleNodeParser.from_defaults(chunk_size=c, chunk_overlap=0) for c in sub_chunk_sizes]all_nodes = []for base_node in base_nodes:    for n in sub_node_parsers:        sub_nodes = n.get_nodes_from_documents([base_node])        sub_inodes = [            IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes        ]        all_nodes.extend(sub_inodes)    # also add original node to node    original_node = IndexNode.from_text_node(base_node, base_node.node_id)    all_nodes.append(original_node)        all_nodes_dict = {n.node_id: n for n in all_nodes}# creating indexindex = VectorStoreIndex(nodes=all_nodes, service_context=service_context)# creating a chunk retrievervector_retriever_chunk = index.as_retriever(similarity_top_k=2)retriever_chunk = RecursiveRetriever(    "vector",    retriever_dict={"vector": vector_retriever_chunk},    node_dict=all_nodes_dict,    verbose=True,)# retrieve needed nodesnodes = retriever_chunk.retrieve(    "Can you tell me about the key concepts for safety finetuning")for node in nodes:    print(node)

       上面的代码只检索节点并将其打印到屏幕上,以下是代码的输出:

       现在,让我们实现RetrieveQueryEngine

from llama_index import (    Document,    StorageContext,    VectorStoreIndex,    SimpleDirectoryReader,    ServiceContext,    load_index_from_storage,)from llama_index.retrievers import RecursiveRetrieverfrom llama_index.query_engine import RetrieverQueryEnginefrom llama_index.node_parser import SimpleNodeParserfrom llama_index.embeddings import OpenAIEmbeddingfrom llama_index.schema import IndexNodefrom llama_index.llms import OpenAI# for loading environment variablesfrom decouple import configimport os# set env variablesos.environ["OPENAI_API_KEY"] = config("OPENAI_API_KEY")# create LLM and Embedding Modelembed_model = OpenAIEmbedding()llm = OpenAI(model="gpt-3.5-turbo")service_context = ServiceContext.from_defaults(    embed_model=embed_model, llm=llm)# load datadocuments = SimpleDirectoryReader(    input_dir="../dataFiles").load_data(show_progress=True)doc_text = "\n\n".join([d.get_content() for d in documents])docs = [Document(text=doc_text)]# create nodes parsernode_parser = SimpleNodeParser.from_defaults(chunk_size=1024)# split into nodesbase_nodes = node_parser.get_nodes_from_documents(documents=docs)# set document IDsfor idx, node in enumerate(base_nodes):    node.id_ = f"node-{idx}"# create parent child documentssub_chunk_sizes = [128, 256, 512]sub_node_parsers = [    SimpleNodeParser.from_defaults(chunk_size=c, chunk_overlap=0) for c in sub_chunk_sizes]all_nodes = []for base_node in base_nodes:    for n in sub_node_parsers:        sub_nodes = n.get_nodes_from_documents([base_node])        sub_inodes = [            IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes        ]        all_nodes.extend(sub_inodes)    # also add original node to node    original_node = IndexNode.from_text_node(base_node, base_node.node_id)    all_nodes.append(original_node)        all_nodes_dict = {n.node_id: n for n in all_nodes}# creating indexindex = VectorStoreIndex(nodes=all_nodes, service_context=service_context)# creating a chunk retrievervector_retriever_chunk = index.as_retriever(similarity_top_k=2)retriever_chunk = RecursiveRetriever(    "vector",    retriever_dict={"vector": vector_retriever_chunk},    node_dict=all_nodes_dict,    verbose=True,)query_engine_chunk = RetrieverQueryEngine.from_args(    retriever_chunk, service_context=service_context)response = query_engine_chunk.query(    "What did the president say about Covid-19")print(str(response))

         在上面的代码中,我去掉了这几行代码:

retriever_chunk = RecursiveRetriever(    "vector",    retriever_dict={"vector": vector_retriever_chunk},    node_dict=all_nodes_dict,    verbose=True,)# retrieve needed nodesnodes = retriever_chunk.retrieve(    "Can you tell me about the key concepts for safety finetuning")for node in nodes:    print(node)

          运行代码,这是输出:

三、评估

现在,让我们继续评估管道的性能。

    我要做的第一件事是将Sqlite文件从basicRAG文件夹复制到ParentDocumentRetrieval文件夹中,这样做的目的是为了保留我们从上一篇文章中构建的基本RAG管道的度量,方便进行性能比较,如果您不想这样做,则不必这样做。还有一种方法可以解决此问题:指向Sqlite数据库的路径,这可以通过以下方式完成:

# RAG pipeline evalstru = Tru(database_file="<file_path_to_database_you_want_to_use>")

      您可以使用下面的代码来评估管道的性能。复制完default.sqlite文件后,您的项目目录应该如下所示:

       您还可以针对一系列问题评估您的最终RAG管道。在这个文件的旁边创建一个名为eval_questions.txt的文件,输入以下问题来进行评估:

  1. What measures did the speaker announce to support Ukraine in the conflict mentioned?

  2. How does the speaker propose to address the challenges faced by the United States in the face of global conflicts, specifically mentioning Russia’s actions?

  3. What is the speaker’s plan to combat inflation and its impact on American families?

  4. How does the speaker suggest the United States will support the Ukrainian people beyond just military assistance?

  5. What is the significance of the speaker’s reference to the NATO alliance in the context of recent global events?

  6. Can you detail the economic sanctions mentioned by the speaker that are being enforced against Russia?

  7. What actions have been taken by the U.S. Department of Justice in response to the crimes of Russian oligarchs as mentioned in the speech?

  8. How does the speaker describe the American response to COVID-19 and the current state of the pandemic in the country?

  9. What are the four common-sense steps the speaker mentions for moving forward safely in the context of COVID-19?

  10. How does the speaker address the economic issues such as job creation, infrastructure, and the manufacturing sector in the United States?

       eval_questions.txt文件应与main.py文件位于同一目录中。这样做的目的是使我们能够针对多个问题而不是像上一篇文章中那样仅针对一个问题来评估RAG管道。

from typing import Listfrom llama_index import (    Document,    VectorStoreIndex,    SimpleDirectoryReader,    ServiceContext,)from llama_index.retrievers import RecursiveRetrieverfrom llama_index.query_engine import RetrieverQueryEnginefrom llama_index.node_parser import SimpleNodeParserfrom llama_index.embeddings import OpenAIEmbeddingfrom llama_index.schema import IndexNodefrom llama_index.llms import OpenAI# for loading environment variablesfrom decouple import configimport osfrom trulens_eval import Feedback, Tru, TruLlamafrom trulens_eval.feedback import Groundednessfrom trulens_eval.feedback.provider.openai import OpenAI as OpenAITruLensimport numpy as np# set env variablesos.environ["OPENAI_API_KEY"] = config("OPENAI_API_KEY")# create LLM and Embedding Modelembed_model = OpenAIEmbedding()llm = OpenAI(model="gpt-3.5-turbo")service_context = ServiceContext.from_defaults(    embed_model=embed_model, llm=llm)# load datadocuments = SimpleDirectoryReader(    input_dir="../dataFiles").load_data(show_progress=True)doc_text = "\n\n".join([d.get_content() for d in documents])docs = [Document(text=doc_text)]# create nodes parsernode_parser = SimpleNodeParser.from_defaults(chunk_size=1024)# split into nodesbase_nodes = node_parser.get_nodes_from_documents(documents=docs)# set document IDsfor idx, node in enumerate(base_nodes):    node.id_ = f"node-{idx}"# create parent child documentssub_chunk_sizes = [128, 256, 512]sub_node_parsers = [    SimpleNodeParser.from_defaults(chunk_size=c, chunk_overlap=0) for c in sub_chunk_sizes]all_nodes = []for base_node in base_nodes:    for n in sub_node_parsers:        sub_nodes = n.get_nodes_from_documents([base_node])        sub_inodes = [            IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes        ]        all_nodes.extend(sub_inodes)    # also add original node to node    original_node = IndexNode.from_text_node(base_node, base_node.node_id)    all_nodes.append(original_node)        all_nodes_dict = {n.node_id: n for n in all_nodes}# creating indexindex = VectorStoreIndex(nodes=all_nodes, service_context=service_context)# creating a chunk retrievervector_retriever_chunk = index.as_retriever(similarity_top_k=2)retriever_chunk = RecursiveRetriever(    "vector",    retriever_dict={"vector": vector_retriever_chunk},    node_dict=all_nodes_dict,    verbose=True,)query_engine_chunk = RetrieverQueryEngine.from_args(    retriever_chunk, service_context=service_context)# RAG pipeline evalstru = Tru()openai = OpenAITruLens()grounded = Groundedness(groundedness_provider=OpenAITruLens())# Define a groundedness feedback functionf_groundedness = Feedback(grounded.groundedness_measure_with_cot_reasons).on(    TruLlama.select_source_nodes().node.text    ).on_output(    ).aggregate(grounded.grounded_statements_aggregator)# Question/answer relevance between overall question and answer.f_qa_relevance = Feedback(openai.relevance).on_input_output()# Question/statement relevance between question and each context chunk.f_qs_relevance = Feedback(openai.qs_relevance).on_input().on(    TruLlama.select_source_nodes().node.text    ).aggregate(np.mean)tru_query_engine_recorder = TruLlama(query_engine_chunk,    app_id='Parent_document_retrieval',    feedbacks=[f_groundedness, f_qa_relevance, f_qs_relevance])eval_questions = []with open("./eval_questions.txt", "r") as eval_qn:    for qn in eval_qn:        qn_stripped = qn.strip()        eval_questions.append(qn_stripped)def run_eval(eval_questions: List[str]):    for qn in eval_questions:        # eval using context window        with tru_query_engine_recorder as recording:            query_engine_chunk.query(qn)run_eval(eval_questions=eval_questions)# run dashboardtru.run_dashboard()

       一旦运行了代码,您就可以点击终端中的链接

      这将打开一个Streamlit web应用程序,界面如下所示:

       从上图中,我们可以看到Parent Document Retrieval管道比基本的RAG管道LlamaIndex_App1应用程序好很多。

参考文献:

[1] https://ai.gopubby.com/advanced-retrieval-techniques-in-rag-part-02-parent-document-retrieval-334cd924a36e

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/372494.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

css浮动

CSS浮动 1. 浮动的简介 在最初&#xff0c;浮动是用来实现文字环绕图片效果的&#xff0c;现在浮动是主流的页面布局方式之一。 2. 元素浮动后的特点 脱离文档流。不管浮动前是什么元素&#xff0c;浮动后&#xff1a;默认宽与高都是被内容撑开&#xff08;尽可能小&#x…

AI专题:冬渐去、春将来,待看,AI 开花,数据挂果,可控链潮起

今天分享的是AI 系列深度研究报告&#xff1a;《AI专题&#xff1a;冬渐去、春将来&#xff0c;待看&#xff0c;AI 开花&#xff0c;数据挂果&#xff0c;可控链潮起》。 &#xff08;报告出品方&#xff1a;AVIC&#xff09; 报告共计&#xff1a;36页 行业概览:2023年呈稳…

哪种安全数据交换系统,可以满足信创环境要求?

安全数据交换系统是一种专门设计用于在不同网络环境之间安全传输数据的技术解决方案。这类系统确保数据在传输过程中的完整性、机密性和可用性&#xff0c;同时遵守相关的数据保护法规和行业标准。 使用安全数据交换系统的原因主要包括以下几点&#xff1a; 1、数据保护&#…

光隔离探头

一、前言。 光隔离探头的CMRR比高压差分探头要高很多,在一些共模电压较高的测量领域用的比较多,如:开关电源、逆变器等。但是市面上介绍光隔离探头的方案比较少,这里简要说明一下我的个人想法。 二、数字光和模拟光。 数字光就是通信上常用的光模块,传的是数字信号,带…

【学网络安全】kali linux入门及常用简单工具介绍(附工具包)

前言 相信很多同学了解到和学习网络安全的时候都听过kali系统&#xff0c;大家都称之为黑客最喜爱的系统&#xff0c;那么什么是kali&#xff0c;初学者用kali能做些什么&#xff0c;我将在本文中做简单的介绍 一、kali linux是什么&#xff1f; Kali Linux 是专门用于渗透测…

linux服务器springboot或tomcat项目启动,进行jvm参数调优设置

简介 在实验环境或生产环境中&#xff0c;往往一台linux服务器需要添加启动n个项目&#xff0c;但是项目启动占用的jvm内存默认值基本上都是很大的&#xff0c;800m到2G都有&#xff0c;这样很容易将服务器的内存吃垮&#xff0c;从而导致系统强制oom&#xff08;内存泄露&…

(7)医学图像配准综述:SimpleITK + SimpleElastix + Elastix + ITKElastix + PyElastix

文章目录 前言一、常见的图像配准工具1.0、ITK VTK —— 科学界最大与最早的开源免费项目之一1.1、ITK系列&#xff1a;ITK SimpleITK SimpleElastix1.2、Elastix系列&#xff1a;Elastix ITKElastix PyElastix 二、图像配准2.1、SimpleITK图像配准2.2、SimpleElastix图像…

ROS从入门到精通4-1:Docker安装与常用命令总结

目录 0 专栏介绍1 Docker与机器人应用2 Docker安装步骤3 Docker常用命令3.1 创建与启动容器3.2 暂停与删除容器3.3 容器文件拷贝3.4 构建镜像与上下文 0 专栏介绍 本专栏旨在通过对ROS的系统学习&#xff0c;掌握ROS底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS进…

(2021|ICLR,LoRA,秩分解矩阵,更少的可训练参数)LoRA:大语言模型的低秩自适应

LoRA: Low-Rank Adaptation of Large Language Models 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 问题陈述 3. 现有的解决方案不够好吗&#xff1f; 4. 我们的方法 …

stack和queue及优先级队列和适配器(包括deque)的介绍

stack stack的介绍 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作为其底层的容器&#xff0c;并提供一组…

Coil:Android上基于Kotlin协程的超级图片加载库

Coil&#xff1a;Android上基于Kotlin协程的超级图片加载库 1. coil简介 在当今移动应用程序的世界中&#xff0c;图片加载是一个不可或缺的功能。为了让应用程序能够高效地加载和显示图片&#xff0c;开发人员需要依赖于强大的图片加载库。而今天&#xff0c;我将向大家介绍…

小程序插件测试

1、下载微信小程序开发者工具&#xff0c;下载地址&#xff1a; 微信开发者工具下载地址与更新日志 | 微信开放文档 选择稳定版下载&#xff1a; 2、下载并解压测试项目&#xff08;此处根据公司的项目来&#xff0c;可问开发要&#xff09; 3、导入解压后的文件夹&#xff…

Flink cdc3.0动态变更表结构——源码解析

文章目录 前言源码解析1. 接收schema变更事件2. 发起schema变更请求3. schema变更请求具体处理4. 广播刷新事件并阻塞5. 处理FlushEvent6. 修改sink端schema 结尾 前言 上一篇Flink cdc3.0同步实例 介绍了最新的一些功能和问题&#xff0c;本篇来看下新功能之一的动态变更表结…

通过Navicat for MySQL排查sql语句错误

开发的软件用到MySQL数据库&#xff0c;但在进行某个sql操作时执行失败了&#xff1a; 我们可以用Navicat for MySQL来排查sql语句是否存在语法错误等问题。将该sql语句复制 打开Navicat for MySQL&#xff0c;连接该软件所用到的MySQL数据库&#xff0c;点击“新建查询”。将刚…

AI嵌入式K210项目(26)-二维码识别

文章目录 前言一、什么是二维码&#xff1f;二、实验准备三、实验过程四、API接口总结 前言 本章介绍基于机器视觉实现二维码识别&#xff0c;主要包含两个过程&#xff0c;首先检测图像中是否有二维码&#xff0c;如果有则框出并打印二维码信息&#xff1b; 一、什么是二维码…

Re-understanding of data storytelling tools from a narrative perspective

作者&#xff1a;任芃锟, 王轶 & 赵凡 发表&#xff1a;Visual Intelligence&#xff0c;新刊&#xff0c;实行单盲同行评议制度。由施普林格以开放获取 (Open Access) 模式出版。获2022“中国科技期刊卓越行动计划高起点新刊”项目资助&#xff0c;目前出版不收取文章处理…

瑞幸CNY营销又整了哪些花活?媒介盒子分享

还有不到一周时间&#xff0c;龙年春节就要来了&#xff0c;在这场龙年营销大战中&#xff0c;咖啡界的“显眼包”瑞幸又又又凭借花式营销成功出圈&#xff0c;从产品到物料包装&#xff0c;从联名合作到粉丝营销&#xff0c;海陆空全方位上线&#xff0c;那瑞幸具体怎么整的活…

深入探索CoT有效性和推理步长对于LLM性能的影响

思想链&#xff08;CoT&#xff09;对于提高大型语言模型&#xff08;LLM&#xff09;的推理能力具有重要意义。 然而&#xff0c;CoT 的有效性与提示中推理步骤的长度之间的相关性仍然很大程度上未知。 为了阐明这一点&#xff0c;多家研究机构&#xff08;西北大学、罗格斯大…

【UE 材质】扇形材质

目录 效果 步骤 &#xff08;1&#xff09;控制扇形的弧宽度 &#xff08;2&#xff09;控制扇形的角度 &#xff08;3&#xff09;完整节点 效果 步骤 &#xff08;1&#xff09;控制扇形的弧宽度 创建一个材质&#xff0c;混合模式设置为“Additive”&#xff0c;着色…

【Java 数据结构】String进阶

字符串常量池 1. 创建对象的思考2. 字符串常量池(StringTable)3. 再谈String对象创建 1. 创建对象的思考 下面两种创建String对象的方式相同吗&#xff1f; public static void main(String[] args) {String s1 "hello";String s2 "hello";String s3 …
最新文章