使用 Elasticsearch 和 LlamaIndex 进行高级文本检索:句子窗口检索

2023 年是检索增强生成 (RAG) 的一年,人们探索了许多用例,并使用该技术开发了数百种产品。 从 Q/A 聊天机器人到基于上下文的代理,RAG 的使用一直是 LLM 申请快速增长的主要因素。 支持不断发展的社区以及 Langchain 和 LlamaIndex 等强大框架的可用性,使开发人员可以更轻松地构建复杂的应用程序。

在本文中,我想讨论一种先进的 RAG 技术,它有助于向客户提供了一些高质量的输出,并被证明是一种高效且有效的文本检索方法,即句子窗口检索 (sentence window retrieval - SWR)。

什么事 LIama-index

LlamaIndex 是一个数据框架,供 LLM 应用程序摄取、构建和访问私有或特定领域的数据。

LlamaIndex 是开源的,可用于构建各种应用程序。 在 GitHub 上查看该项目。

什么是句子窗口检索 ?

句子窗口检索背后的核心思想是根据查询有选择地从自定义知识库中获取上下文,然后利用该上下文的更广泛版本来生成更强大的文本。 此过程涉及嵌入一组有限的句子以供检索,这些句子周围的附加上下文(称为 “窗口上下文 - window context”)单独存储并链接到它们。 一旦识别出最相似的句子,就会在将这些句子发送到大型语言模型 (LLM) 进行生成之前重新整合上下文,从而丰富整体上下文理解。 通过将焦点缩小到特定的句子窗口,SWR 旨在提高信息提取的准确性和相关性,从而促进文本的全面合成。

这种方法的一个重要考虑因素是上下文窗口的大小,决定嵌入句子之前和之后有多少句子被合并到 LLM 中进行生成。 该方法相对于传统的检索增强生成(RAG)带来了一些改进:

  • 提高精度:通过将搜索范围缩小到特定句子,可以提高信息检索的精度,过滤掉可能削弱结果相关性的不必要信息。
  • 效率:SWR 通过最大限度地减少信息检索过程中处理的文本量、避免筛选冗长的文档并提高整体效率来加速流程。
  • 灵活性:研究人员受益于该技术的灵活性,使他们能够调整关键字周围文本窗口的大小,从而完善他们的搜索策略。

虽然 SWR 通过关注特定句子来减少标记的使用,但需要权衡,因为关键的文本块可能会被遗漏并最终出现在周围的上下文中。 选择适当的上下文窗口超参数对于解决此问题至关重要。

让我们使用 Elasticsearch 和 LlamaIndex 设置我们自己的基于 SWR 的 RAG 管道。 我们将一步一步地实现每个组件并提供详细的解释。

向量数据库设置

在本文中,我选择 Elasticsearch 作为我们的 Vector 数据库,其背后的原因是:

  • 开放几免费:那些计划构建包含向量搜索的可扩展人工智能应用程序的人可以考虑在其专用服务器上建立向量数据库 
  • 不仅仅是向量数据库:Elasticsearch 是一个构建在 Apache Lucene 之上的开源搜索和分析引擎。 它旨在处理大量数据并提供近乎实时的搜索功能。

在 Docker 上设置 Elasticsearch

使用以下 docker 命令启动单节点 Elasticsearch 实例。我们可以参考之前的文章 “Elasticsearch:如何在 Docker 上运行 Elasticsearch 8.x 进行本地开发”。我选择不使用安全配置。直接使用 docker compose 来启动 Elasticsearch 及 Kibana:

.env

$ pwd
/Users/liuxg/data/docker8
$ ls -al
total 16
drwxr-xr-x    4 liuxg  staff   128 Jan 16 13:00 .
drwxr-xr-x  193 liuxg  staff  6176 Jan 12 08:31 ..
-rw-r--r--    1 liuxg  staff    21 Jan 16 13:00 .env
-rw-r--r--    1 liuxg  staff   733 Mar 14  2023 docker-compose.yml
$ cat .env
STACK_VERSION=8.11.3

docker-compose.yml

version: "3.9"
services:elasticsearch:image: elasticsearch:${STACK_VERSION}container_name: elasticsearchenvironment:- discovery.type=single-node- ES_JAVA_OPTS=-Xms1g -Xmx1g- xpack.security.enabled=falsevolumes:- type: volumesource: es_datatarget: /usr/share/elasticsearch/dataports:- target: 9200published: 9200networks:- elastickibana:image: kibana:${STACK_VERSION}container_name: kibanaports:- target: 5601published: 5601depends_on:- elasticsearchnetworks:- elastic      volumes:es_data:driver: localnetworks:elastic:name: elasticdriver: bridge

我们使用如下的命令来启动:

docker-compose up

这样我们就完成了 Elasticsearch 及 Kibana 的安装了。我们的 Elasticsearch 及 Kibana 都没有安全的设置。这个在生产环境中不被推荐使用。

应用设计 -  组装管道

我们将使用 Jupyter notebook 来进行设计。我们在命令行中打入:

jupyter notebook

安装依赖

我们使用如下的命令来安装 Python 的依赖包:

pip3 install llama-index openai elasticsearch transformers load_dotenv pypdf

我们接下来在当前的工作目录中创建一个叫做 .env 的文件:

.env

OPENAI_API_KEY="YourOpenAIKey"

请在 .env 中创建如上所示的变量。你需要把自己的 openai key 写入到上面的文章里。

初始化 LLM

import openai,os
from dotenv import load_dotenv
from llama_index.llms import OpenAIload_dotenv()openai_api_key=os.getenv('OPENAI_API_KEY')openai.api_key = openai_api_key
llm = OpenAI(model="gpt-3.5-turbo", temperature=0.1)

在本示例中,我们将使用在 pdf_files 目录下的 sample2.pdf 文件做为示例来进行展示。我们的文档共有 5 页。我们将使用 LlamaIndex 加载、分块和摄取我们的文件。你也可以使用自己的文件来进行练习。

加载数据中

我们使用 Llamaindex 的 SimpleDirectoryReader 来加载我们的 pdf 文件。 你可以使用此阅读器加载目录中的所有内容,但我们指定文件名更精确。

from llama_index import VectorStoreIndex, SimpleDirectoryReader, Documentreader = SimpleDirectoryReader(input_files=['./pdf_files/sample2.pdf'])
docs = reader.load_data()document = Document(text="\n\n".join([doc.text for doc in docs]))

reader 将 pdf 中的所有页面加载到单独的文档中,并将它们添加到一个数组中,然后我们迭代所有文档并将它们连接到一个文档中。

将 Elasticsearch 初始化为向量存储

from llama_index.vector_stores import ElasticsearchStorevector_store = ElasticsearchStore(es_url="http://localhost:9200",index_name="books"  # If this index doesn't exist, a new one is created
)

现在我们已经有了数据和向量存储,让我们开始构建实际的句子窗口检索框架。我们将首先构建一个句子窗口索引,并使用它来创建一个句子窗口查询引擎。

以下是构建句子窗口索引所需的函数:

from llama_index import ServiceContext, VectorStoreIndex, StorageContext
from llama_index.node_parser import SentenceWindowNodeParser
from llama_index.indices.postprocessor import MetadataReplacementPostProcessor
from llama_index.indices.postprocessor import SentenceTransformerRerankdef build_sentence_window_index(document, llm, vector_store, embed_model="local:BAAI/bge-small-en-v1.5"
):node_parser = SentenceWindowNodeParser.from_defaults(window_size=3,window_metadata_key="window",original_text_metadata_key="original_text",)sentence_context = ServiceContext.from_defaults(llm=llm,embed_model=embed_model,node_parser=node_parser)storage_context = StorageContext.from_defaults(vector_store=vector_store)sentence_index = VectorStoreIndex.from_documents([document], service_context=sentence_context, storage_context=storage_context)return sentence_indexdef get_sentence_window_query_engine(sentence_index,similarity_top_k=6,rerank_top_n=2,
):postproc = MetadataReplacementPostProcessor(target_metadata_key="window")rerank = SentenceTransformerRerank(top_n=rerank_top_n, model="BAAI/bge-reranker-base")sentence_window_engine = sentence_index.as_query_engine(similarity_top_k=similarity_top_k, node_postprocessors=[postproc, rerank])return sentence_window_engine

让我们分解这些功能并看看每个组件的作用:

Sentence Window Index

def build_sentence_window_index(document, llm, vector_store, embed_model="local:BAAI/bge-small-en-v1.5"
):# create the sentence window node parser w/ default settingsnode_parser = SentenceWindowNodeParser.from_defaults(window_size=3,window_metadata_key="window",original_text_metadata_key="original_text",)sentence_context = ServiceContext.from_defaults(llm=llm,embed_model=embed_model,node_parser=node_parser)storage_context = StorageContext.from_defaults(vector_store=vector_store)sentence_index = VectorStoreIndex.from_documents([document], service_context=sentence_context, storage_context=storage_context)return sentence_index

build_sentence_window_index 函数用于从给定文档构建句子窗口的索引。 下面是它的作用的详细说明:

参数:该函数有四个参数:

  • document:构建索引的文档。
  • llm:要使用的语言模型。
  • vector_store:要使用的向量存储。 在本例中,它是 ElasticsearchStore 的一个实例,它使用 Elasticsearch 作为存储后端。
  • embed_model:要使用的嵌入模型。 默认值为 “local:BAAI/bge-small-en-v1.5”。

Node Parser:它使用默认设置创建一个 SentenceWindowNodeParser 对象。 该对象用于将文档解析为句子窗口,即句子序列。

Service Context:它使用提供的语言模型、嵌入模型和节点解析器创建 ServiceContext 对象。 该对象用于管理构建索引所需的服务。

Storage Context:它使用提供的向量存储创建一个 StorageContext 对象。 该对象用于管理索引的存储。

Index Creation:它使用服务上下文和存储上下文从文档创建 VectorStoreIndex。

Return Value: 返回创建的 VectorStoreIndex。

Query Engine

def get_sentence_window_query_engine(sentence_index,similarity_top_k=6,rerank_top_n=2,
):# define postprocessorspostproc = MetadataReplacementPostProcessor(target_metadata_key="window")rerank = SentenceTransformerRerank(top_n=rerank_top_n, model="BAAI/bge-reranker-base")sentence_window_engine = sentence_index.as_query_engine(similarity_top_k=similarity_top_k, node_postprocessors=[postproc, rerank])return sentence_window_engine

get_sentence_window_query_engine 函数用于根据给定的句子窗口索引创建查询引擎。 下面是它的作用的详细说明:

参数:该函数采用三个参数:

  • Sentence_index:用于创建查询引擎的句子窗口索引。
  • similarity_top_k:要返回的最相似结果的数量。 默认值为 6。
  • rerank_top_n:要重新排名的顶部结果的数量。 默认值为 2。

Postprocessors:它定义了两个后处理器:

  • MetadataReplacementPostProcessor:此后处理器将每个节点的文本替换为 “window” 元数据键的值。
  • SentenceTransformerRerank:此后处理器使用句子转换器模型对顶部 rerank_top_n 结果进行重新排名。

查询引擎创建:它使用指定数量的要返回的最相似结果和定义的后处理器从句子窗口索引创建查询引擎。

返回值:返回创建的查询引擎。

重新排名是一个用于细化初始搜索结果的过程。

该函数使用 SentenceTransformerRerank 后处理器进行重新排名。 该后处理器使用句子转换器模型对顶部 rerank_top_n 结果进行重新排名。 rerank_top_n 参数指定应重新排名的顶部结果的数量。

重新排名过程涉及使用句子转换器模型来计算排名靠前的 rerank_top_n 结果的新相似度分数,然后根据新分数对这些结果进行排序。 这可以通过考虑初始排名可能无法捕获的更复杂的语义相似性来帮助提高结果的相关性。

把它放在一起

sentence_index = build_sentence_window_index(document,llm,embed_model="local:BAAI/bge-small-en-v1.5",vector_store=vector_store
)query_engine = get_sentence_window_query_engine(sentence_index=sentence_index)

我们可以到 Kibana 里进行查看:

上面的向量的维度是384。我们可以在地址已进行查看。

我们已经有了引擎,让我们尝试从知识库中向它询问一个非常具体的问题:

resp = query_engine.query("what is the article about"
)
print(resp)

说的很详细了!

我鼓励您利用你的知识库进行尝试,并将性能与现有的 RAG 实施进行比较。你可以在地址 https://github.com/liu-xiao-guo/semantic_search_es 下载源码。相关文件:

  • https://github.com/liu-xiao-guo/semantic_search_es/tree/main/pdf_files
  • https://github.com/liu-xiao-guo/semantic_search_es/blob/main/Elasticsearch%20and%20LlamaIndex%20-%20Sentence%20Window%20Retrieval.ipynb

更多阅读:https://docs.llamaindex.ai/en/stable/examples/vector_stores/Elasticsearch_demo.html#basic-example

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java SE】类和对象详解

文章目录 1.什么是面向对象2. 类的定义和使用2.1 简单认识类2.2 类的定义格式 3. 类的实例化3.1 什么是实例化3.1.1 练习(定义一学生类) 3.2 类和对象的说明 4. this 引用5. 构造方法6. 对象的初始化6.1 默认初始化6.2 就地初始化 7. 封装7.1 封装的概念…

spingboot 集成identityserver4身份验证

一、新建项目&#xff1a;com.saas.swaggerdemo 详情见&#xff1a;spring-boot2.7.8添加swagger-CSDN博客 在之前项目基础上添加如下依赖 <dependency><groupId>com.nimbusds</groupId><artifactId>nimbus-jose-jwt</artifactId><version&…

MATLAB - 为机械臂路径选择轨迹

系列文章目录 前言 本示例概述了机器人系统工具箱™中可用的轨迹类型。在机械手运动、规划和控制应用中&#xff0c;必须为机器人选择轨迹。本示例有三个主要部分。第一部分展示了机械手使用的轨迹类型&#xff0c;第二部分演示了生成轨迹的功能&#xff0c;最后一部分则展示了…

hadoop-common: CMake failed with error code 1

问题 在编译hadoop源码时遇到如下错误 hadoop-common: CMake failed with error code 1 看了这个错误表示一脸懵逼 排查 在mvn 的命令中增加 -X 和 -e mvn clean package -e -X -Pdist,native -DskipTests -Dmaven.javadoc.skip -Dopenssl.prefix/usr/local/bin/openssl 在…

CentOs7.8安装原生Jenkins2.38教程

CentOs7.8安装Jenkins教程 前提&#xff1a;1、下载安装包2、安装&#xff1a;3、检验是否安装成功&#xff1a;4、查询java路径5、修改配置文件6、创建jenkins用户7、给jenkins用户授权8、开放防火墙端口9、运行和其他jenkins相关命令10、异常情况10.1 提示超时&#xff0c;查…

Linux之kernel(20)kdump支持

Linux之kernel(21)kdump介绍 Author&#xff1a;OnceDay Date&#xff1a;2024年1月14日 漫漫长路&#xff0c;有人对你微笑过嘛… 参考文档&#xff1a; 3.3.2 内核态调测工具&#xff1a;kdump&crash——kdump - 知乎 (zhihu.com)Kernel crash dump | Ubuntu[Linux]K…

工业企业能源管理平台,可以帮助企业解决哪些方面的能源问题?

随着全球工业化进程的加快&#xff0c;工业企业在生产经营过程中消耗的能源也越来越庞大。能源成本的上升和环境保护的压力使得工业企业对能源管理的重要性有了深刻的认识。为了提高能源利用效率、降低能源消耗、减少环境污染&#xff0c;工业企业在能源管理方面迫切需要一套规…

计算机毕业设计 基于SSM的历史/博物馆藏系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

浅谈SQL优化

避免使用子查询 例&#xff1a; select * from t1 where id in (select id from t2 where name lolly1023);其子查询在MySQL5.5版本里&#xff0c;内部执行计划是&#xff1a;先查询外表再匹配内表&#xff0c;而不是先查内表t2&#xff0c;当外表的数据很大时&#xff0c;查…

10.9.2 std::function 代替函数指针 Page182~183

std::function是一个模板类&#xff0c;基本可作为函数指针的代替品&#xff0c;具备更多功能&#xff0c;特别是与函数对象及bind配合使用。使用std::function时&#xff0c;需要添加头文件 #include <functional> 1.定义函数指针 18行&#xff0c;定义了一个函数指针类…

SpringBoot的yml多环境配置3种方法

文章目录 SpringBoot的yml多环境配置3种方法1、多个yml文件1.1、创建多个配置文件applicaiton.yml中指定配置 2、单个yml文件3、在pom.xml中指定环境配置3.1、创建多个配置文件3.2、在application.yml中添加多环境配置属性3.3、在pom.xml中指定使用的配置3.4、问题&#xff1a;…

Mysql root 密码重置详解

文章目录 1 概述1.1 前言1.2 mysql 版本查询 2 windows 操作系统2.1 mysql 8 及以上版本2.1.1 关闭 mysql 服务2.1.2 通过无认证方式启动 mysql2.1.3 新开窗口&#xff0c;登录 mysql&#xff0c;重置密码 1 概述 1.1 前言 不同的操作系统&#xff08;如&#xff1a;windows、…

Android PendingIntent 闪退

先来给大家推荐一个我日常会使用到的图片高清处理在线工具&#xff0c;主要是免费&#xff0c;直接白嫖 。 有时候我看到一张图片感觉很不错&#xff0c;但是图片清晰度不合我意&#xff0c;就想有没有什么工具可以处理让其更清晰&#xff0c; 网上随便搜下就能找到&#xff…

Spring Boot 3 + Vue 3实战:实现用户登录功能

文章目录 一、实战概述二、实战步骤​&#xff08;一&#xff09;创建前端项目 - login-vue1、创建Vue项目2、安装axios模块3、安装vue-router模块4、安装less和less-loader模块5、运行Vue项目6、在浏览器里访问首页7、在IDEA里打开Vue项目8、创建登录Vue组件9、创建首页Vue组件…

自动驾驶中的坐标系

自动驾驶中的坐标系 自动驾驶中的坐标系 0.引言1.相机传感器坐标系2.激光雷达坐标系3.车体坐标系4.世界坐标系4.1.地理坐标系4.2.投影坐标系4.2.1.投影方式4.2.2.墨卡托(Mercator)投影4.2.3.高斯-克吕格(Gauss-Kruger)投影4.2.4.通用横轴墨卡托UTM&#xff08;UniversalTransve…

MySQL视图索引基础练习

表定义 学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, C…

Android 布局菜鸟 android中的布局类型和特点?

一、LinearLayout(线性布局) 1、 特点: 主要以水平或垂直方式来排列界面中的控件。并将控件排列到一条直线上。在线性布局中,如果水平排列,垂直方向上只能放一个控件,如果垂直排列,水平方向上也只能放一个控件。 2、适⽤场景: Android开发中最常见的 ⼀种布局⽅式,排列…

CUDA tips

命令行查看核函数消耗的寄存器和共享内存数量 nvcc --ptxas-options-v reduce_sum.cu nvprof 使用 由于 8.0 及以上计算能力的显卡用不了 nvprof&#xff0c;官方建议用 nsight system 和 ncu&#xff0c;但是如果只想命令行打印表格查看 kernel 概况感觉还是 nvprof 方便&am…

Android 系统启动过程纪要(基于Android 10)

前言 看过源码的都知道&#xff0c;Launcher系统启动都会经过这三个进程 init ->zygote -> system_server。今天我们就来讲解一下这三个进程以及Launcher系统启动。 init进程 准备Android虚拟机环境&#xff1a;创建和挂载系统文件目录&#xff1b;初始化属性服务&…

SAP银企直联报错排查方法与步骤-F110

银企直联的报错排查经常需要利用F110来查询。方法步骤如下&#xff1a; 1、首先要确定报错是哪天的&#xff0c;并且当天那一次跑的付款建议。需要通过表 REGUH来确认(跟据供应商编码、日期) 2、通过REGUH表的信息知道了是2024年1月16号第5个标识&#xff08;也就是第五次跑付…