利用LangChain实现RAG

  检索增强生成(Retrieval-Augmented Generation, RAG)结合了搜寻检索生成能力和自然语言处理架构,透过这个架构,模型可以从外部知识库搜寻相关信息,然后使用这些信息来生成response。要完成检索增强生成主要包含四个步骤:

1.TextLoad:

2.TextSplit:

3.Generate and save Embedding:

4.Search relavant context and get final anwser.

DocumentLoader

  Langchain的document_loader目录下提供多种Load不同类型文档的方法,文档大类型总体可以分为结构化文档和非结构化文档。不同Loader的API信息如下图所示,更多Loader相关的API信息可查看这里。

下面是Load csv格式文件数据的代码和结果,import CSVLoader,print load的结果。可以看到,结果是一个List,List中每一个item是一个Document对象,包含page_content 和metadata数据,metadata包含source和row两个信息。

除了Load csv格式文件,还可以load pdf文件,import PyPDFLoader,load后的数据也是一个List,list中的每个value是Document对象,具体结果如下所示:

  在Load PDF格式文档的时候,可以选择多种Loader,例如UnstructuredPDFLoader,MathpixPDFLoader,OnlinePDFLoader,PyPDFium2Loader,PDFMinerLoader,PyMuPDFLoader,PyPDFDirectoryLoader,PDFPlumberLoader。另外,在load PDF文档时还可以通过参数控制是否需要解析图片上的文字信息等。总之,PDF的loader是非常成熟和丰富的。

除了load PDF格式文档,当然还支持txt格式,markdown格式,html格式等文档,在load的时候可以通过参数选择是否显示load进度,是否使用多线程进行load,多线程load可提升load效率。代码如下所示:

loader = DirectoryLoader('testdata', glob="**/*.txt",show_progress=True, use_multithreading=True)docs = loader.load()print(docs)

TextSplitter

  langchain提供了多种TextSplitter,具体如下图所示,这些spliter中比较常用的有TextSplitter,CharacterTextSplitter,RecursiveCharacterTextSplitter等。

下面是调用CharacterTextSplitter对文本进行切割,需要指定separator,chunk_size,chunk_overlap等参数。chunk_size:定义文本应该被分割成的最大块的大小。在这个例子中,设置为 1000,所以每个块最多包含 1000 个字符。这只是一个最大字符数量控制,真正在分割的时候还要参考separator。chunk_overlap:块之间的最大重叠量。这里设置为 200,所以连续块之间最多可以有 200 个字符的重叠。length_function:用于计算块长度的函数。在这个例子中,使用内置的 len 函数,所以块的长度就是它的字符数.

from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(separator = "\n\n",chunk_size = 1000,chunk_overlap  = 200,length_function = len,is_separator_regex = False,
)

  上面是最简单的文本切割方式,在切割分本时,我们需要尽量保证文本的语句含义不要丢失,即相关性很强的内容最好能放到一个chunk内,上面的切割方式虽然简单,但是容易把文本含义丢失。所以除了上面最简单的切割方式外,langchain还提供了RecursiveCharacterTextSplitter 切割方式。该切割方式的默认separator是["\n\n", "\n", " ", ""]。文本分割器首先尝试在每个双换行符 ("\n\n") 处拆分文本,这通常用于分隔文本中的段落。如果生成的块过大,它接着尝试在每个换行符 ("\n") 处拆分,这通常用于分隔句子。如果块仍然过大,它最后尝试在每个空格 (" ") 处拆分,这用于分隔单词。如果块仍然过大,它会在每个字符 ("") 处拆分,尽管在大多数情况下,这种细粒度的拆分是不必要的。这种方法的优点是它尽量保留了语义上下文,通过保持段落、句子和单词的完整性。这些文本单元往往具有强烈的语义关系,其中的单词在意义上通常密切相关,这对于许多自然语言处理任务是有益的。

   根据经验,采用RecursiveCharacterTextSplitter进行文本切割时,chunk_size设置在500-1000分为内效果较好,chunk_overlap设置在chunk_size大小的10%-20%左右。

  如果文档内容包含格式信息,例如html,markdown等,langchain还提供了HTMLHeaderTextSplitter和MarkdownHeaderTextSplitter切割方式。即在切割的时候按标题头切割,如果是在一个标题下的内容就放在一起。这样能最大程度保证相关性强的内容被切割在一个chunk里面。除了对文本进行切割,Langchain还可以对Code进行切割。下面的代码中就是加载代码目录地址,对该目录下的代码进行切割。

repo_path = "/Users/taoli/study/Python/langchain/langchain"
loader = GenericLoader.from_filesystem(repo_path,glob="**/*",suffixes=[".py"],parser=LanguageParser(language=Language.PYTHON, parser_threshold=500),
)
documents = loader.load()
python_splitter = RecursiveCharacterTextSplitter.from_language(language=Language.PYTHON, chunk_size=1000, chunk_overlap=100
)
texts = python_splitter.split_documents(documents)

Embedding model

  介绍完了Splitter,接着来看看Embedding。langchain支持多种Embedding model的载入。部分Embedding model如下图所示,更多Embedding信息可查看这里。比较常用的有OpenAIEmbeddings和HuggingFaceEmbeddings。

  如下面代码所示,在调用向量数据库的classmethod from_document,就需要指定调用的Embedding方法,可以使用HuggingFace中某个特定的Embedding模型,也可以直接使用OpenAIEmbedding。设置Embedding后,调用from_document方法,就会把texts中的内容转换成向量并存放在向量数据库中,用于后面的检索获取。

db = Chroma.from_documents(texts, HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2"))
db = Chroma.from_documents(texts,OpenAIEmbeddings)

在调用from_document方法时,除了指定embedding function,texts内容外,还可以指定persist_directory,即持久化向量信息的目录等多个参数信息。

Search relavant context and get final anwser

  介绍完Embedding后,再来看看相关性检索和得到最终结果的部分。如下面代码所示,在获取到向量库对象后,需要转换成retriever。Retriever类可以根据输入的查询问题,从文档中检索相关性高的内容并返回,检索器不需要存储文档,只需要返回(或检索)文档。向量存储可以用作检索器的支撑,但还有其他类型的检索器。

retriever = db.as_retriever(search_type="mmr",search_kwargs={"k": 8})llm = ChatOpenAI(model_name="gpt-3.5-turbo")
qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever, chain_type_kwargs={"prompt": rag_prompt})
result = qa_chain({"query": "What classes are derived from the Chain class?"})
print(result)memory = ConversationSummaryMemory(llm=llm, memory_key="chat_history", return_messages=True
)
qa = ConversationalRetrievalChain.from_llm(llm, retriever=retriever, memory=memory)
questions = ["What is the class hierarchy?","What classes are derived from the Chain class?","What one improvement do you propose in code in relation to the class hierarchy for the Chain class?",
]
for question in questions:result = qa(question)print(f"-> **Question**: {question} \n")print(f"**Answer**: {result['answer']} \n")

在调用as_retriever()方法中可以指定search_type,search_type主要包括两种:query- Similarity Search and Max Marginal Relevance Search (MMR Search),其中MMR Search旨在提高搜索结果的多样性,常用于推荐系统。如果想检索更准确则推荐选择query- Similarity Search,默认的search_type也是query- Similarity Search。

search_kwargs中可以传入多个参数,常见的参数包括:

k 定义返回多少个文档;默认为 4。
如果search_type是 "similarity_score_threshold" ,可以设置score_threshold,即减少相关性的分数阀值。
fetch_k 确定传递给MMR算法的文档数量;默认为 20。
lambda_mult 控制MMR算法返回结果的多样性,1表示最小多样性,0表示最大多样性。默认为 0.5。
filter 允许您根据文档的元数据定义文档检索的筛选条件。如果Vectorstore不存储任何元数据,则此选项无效。

  获取到retriver后,在检索文档生成答案时,有多种方式,既可以通过RetrievalQA这个chain, 还可以通过ConversationalRetrievalChain来获取最终的答案。在调用chain的时候还可以指定chain_type,langchain提供了四种类型的chain_type.Stuff,Refine,Map Reduce和Map re-rank.默认是使用Stuff类型。

Stuff:stuff文件链("stuff "是指 "to stuff "或 "to fill")是最直接的文件链。它接收一个文件列表,将它们全部插入一个提示,并将该提示传递给一个LLM。这条链非常适合于文件数量较少且大多数调用只传递几个文件的应用。

Refine:完善文档链通过循环输入文档和迭代更新其答案来构建一个响应。对于每个文档,它将所有非文档输入、当前文档和最新的中间答案传递给LLM链,以获得一个新的答案。由于Refine链每次只向LLM传递一个文档,所以它很适合需要分析更多文档的任务,这条链会比Stuff文档链等调用更多的LLM。当然,也有一些任务是很难迭代完成的。例如,当文档之间经常相互参照,或者当一个任务需要许多文档的详细信息时,Refine链的表现就很差。下图是Refine的过程源代码中Refine背后的Prompt信息。

Map_Reduce: map reduce文档链首先对每个文档单独应用LLM链(map步骤),将链的输出视为一个新的文档。然后,它将所有的新文档传递给一个单独的组合文档链,以获得一个单一的输出(Reduce步骤)。

Map re_rank:map re-rank文件链对每个文件进行初始提示,不仅试图完成一项任务,而且对其答案的确定程度进行评分。得分最高的回答会被返回。

  在使用chain生成最终答案时,如果调用ConversationalRetrievalChain,可以在from_llm中传入如下的参数,包括chain_type,自定义的prompt等。例如要传入自定义的prompt,那么可以通过condense_question_prompt传入,或者通过combine_docs_chain_kwargs参数传入:

  combine_docs_chain_kwargs={"prompt": TEST_PROMPT},

  在使用chain生成最终答案时,如果调用RetrievalQA的from_chain_type方法,可以通过chain_type_kwargs传入自定义prompt等参数。

   以上就是RAG的过程,总结而言包含Document Loader,Document splitter,Embedding,Search and get final answer四个步骤。步骤示意图如下所示:

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

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

相关文章

2023亚太杯数学建模A题思路

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

【中间件篇-Redis缓存数据库08】Redis设计、实现、redisobject对象设计、多线程、缓存淘汰算法

Redis的设计、实现 数据结构和内部编码 type命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)hash(哈希)、list(列表)、set(集合)、zset (有序集合),但这些只是Redis对外的数据结构。 实际上每种数据结构都有自己底层的…

【DP】背包问题全解

一.简介 DP(动态规划)背包问题是一个经典的组合优化问题,通常用来解决资源分配的问题,如货物装载、投资组合优化等。问题的核心思想是在有限的资源约束下,选择一组物品以最大化某种价值指标,通常是总价值或…

【Java 进阶篇】Java与JQuery选择器:解锁前端开发的魔法大门

在前端开发的世界中,选择器是我们与HTML文档进行互动的钥匙,而Java和JQuery则为我们提供了强大的工具,使得前端开发不再是一个艰深的谜题。本篇博客将围绕Java与JQuery选择器展开,深入解析选择器的奥秘,为你打开前端开…

Qt文档阅读笔记-Fetch More Example解析

Fetch More Example这个例子说明了如何在视图模型上添加记录。 这个例子由一个对话框组成,在Directory的输入框中,可输入路径信息。应用程序会载入路径信息的文件信息等。不需要按回车键就能搜索。 当有大量数据时,需要对视图模型进行批量增…

宝塔开心版hostcli的广告去除

首先感谢hostcli把宝塔7.6剥离了,直接安装我这里是缺少pyenv的包。 直接进入正题吧。 定位到页面左下方的广告位于 /www/server/panel/BTPanel/templates/default/layout.html “退出”按钮下方有条线开始去掉 去掉之前的忘了截图了,就这样吧&#xff…

《QT从基础到进阶·十七》QCursor鼠标的不同位置坐标获取

一些常用鼠标图形: 鼠标光标相对于整个电脑屏幕的位置:QCursor::pos() 当前光标相对于当前窗口的位置:this->mapFromGlobal(QCursor::pos()) void MainWindow::mouseReleaseEvent(QMouseEvent* event) {QPoint pos event->pos(); …

06-解决Spirng中的循环依赖问题

Bean的循环依赖问题 循环依赖: A对象中有B属性 , B对象中有A属性(丈夫类Husband中有Wife的引用, 妻子类Wife中有Husband的引用) toString()方法重写时直接输出wife/husband会出现递归导致的栈内存溢出错误 直接输出wife/husband会调用它们的toString()方法, 在toString()方法…

Spring的Redis客户端

如何在Spring中操作redis 在创建springboot项目的时候引入redis的依赖. 在配置文件里指定redis主机的地址和端口,此处我们配置了ssh隧道,所以连接的就是本机的8888端口. 创建一个controller类,注入操作redis的对象. 前面使用jedis,是通过jedis对象里的各种方法来操作redis的,此…

在任何机器人上实施 ROS 导航堆栈的指南

文章目录 路径规划参考 路径规划 路径规划是导航的最终目标。这允许用户向机器人给出目标姿势,并让它在给定的环境中自主地从当前位置导航到目标位置。这是我们迄今为止所做的一切(地图绘制和本地化)的汇集点。ROS 导航堆栈已经为我们完成了…

通讯协议学习之路(实践部分):SPI开发实践

通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374) 本文…

【PG】PostgreSQL 预写日志(WAL)、checkpoint、LSN

目录 预写式日志(WAL) WAL概念 WAL的作用 WAL日志存放路径 WAL日志文件数量 WAL日志文件存储形式 WAL日志文件命名 WAL内容 检查点(checkpoint) 1 检查点概念 2 检查点作用 触发检查点 触发检查点之后数据库操作 设置合…

四入进博会,优衣库围绕科技可持续演绎“服装进化论”

11月5日,第六届中国国际进口博览会在上海拉开帷幕。这些年来,进博巨大的平台效应,使其成为各个行业头部品牌的秀场,也持续为消费者、产业链带来惊喜。 今年,也是全球服装界科技知名品牌——优衣库的第四次进博之旅。从…

Python爬虫爬取家纺数据并分析

因为时间的原因,没法写一个详细的教程,但是我可以提供一个基本的框架。你需要根据实际情况进行修改和扩展。以下是使用Python的requests库和BeautifulSoup库来爬取网页内容的基本步骤: # 导入所需的库 import requests from bs4 import Beaut…

2023/11/13JAVA学习

字节数组增大的同时,运行速度也会加快,但是大到一定程度就不行了 要想追加数据,要在低级流后面加true,高级流后面加不了 不是乱码,不是让人看的 保持数据一一对应 否则会报错 下载后,拷贝到一个包里,再 comment是你想添加的注释 txt文本也可

[算法训练营] 贪心算法专题(二)

🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的…

Day02_《MySQL索引与性能优化》

文章目录 一、SQL执行顺序二、索引简介1、关于索引2、索引的类型Btree 索引Btree 索引 三、Explain简介四、Explain 详解1、id2、select_type3、table4、type5、possible_keys6、key7、key_len8、ref9、rows10、Extra11、小案例 五、索引优化1、单表索引优化2、两表索引优化3、…

RT-DETR算法优化改进:一种新颖的动态稀疏注意力(BiLevelRoutingAttention) | CVPR2023

💡💡💡本文独家改进: 提出了一种新颖的动态稀疏注意力(BiLevelRoutingAttention),以实现更灵活的计算分配和内容感知,使其具备动态的查询感知稀疏性 1)代替RepC3进行使用; 2)BiLevelRoutingAttention直接作为注意力进行使用; 推荐指数:五星 RT-DETR魔术师专栏介…

leetcode刷题日记:118.Pascal‘s Triangle(杨辉三角)

118.Pascal’s Triangle(杨辉三角) 题目给我们一个整数numRows表示杨辉三角形的行数,返回杨辉三角形的前numRows行,下面给出一个杨辉三角形看看它有哪些规律; 可以看出杨辉三角形的每一行的最左侧和最右侧的值都为1. 其余的第…

Marin说PCB之 PCB封装和原理图封装的藕断丝连

最近天气开始降温了,小编我不得不拿出珍藏多年的秋裤穿上了,就是走路不太方便,有点紧啊,可能是当时衣服尺码买小了吧,不可能是我吃胖了,这个绝对不可能。 话说小编我今年属实有点走霉运啊,下班和…