RAG(检索增强生成)的质量在很大程度上取决于流程中第一步的质量:检索。生成步骤的好坏取决于工作环境,而生成环境是检索步骤的结果。
然而,检索也依赖于它收到的query。检索有多种类型:基于关键字、基于语义搜索(嵌入)、混合搜索,甚至在某些情况下会基于对 API 的查询结果(例如,网络搜索结果等)。但归根结底,在大多数情况下,都是人类在键盘后面输入query,而人类并不能保证为他们想要的结果生成高质量的query。
在本文中,我们将向您介绍一种非常简单但有效的技术,该技术可以确保我们检索到更多与给定query更相关的上下文;它就是:查询扩展。
TL;DR:查询扩展增加了结果的数量,因此它提高了召回率(相对于精确度)。一般来说,BM25 有利于精确度,而嵌入检索有利于召回率。因此,在您想要依赖关键字搜索的情况下,使用 BM25+查询扩展来提高召回率是有意义的。
查询扩展
查询扩展是一种技术,我们利用用户查询并生成一定数量的类似查询。例如:
用户查询: “开源 NLP 框架”
查询扩展后: [”自然语言处理工具”,”免费 NLP 库”,”开源语言处理平台”,”带有开源代码的 NLP 软件”,”开源 NLP 框架”]
这有助于改善检索结果,进而在以下情况下提高 RAG 结果的质量:
-
用户查询不明确或者格式不正确。
-
在基于关键字的检索的情况下,它还允许使用具有相似含义或同义词的查询来覆盖您的问题。
以“全球变暖”为例,查询扩展可以让我们确保我们也对“气候变化”或类似查询进行关键词搜索。
让我们从构建一个简单的QueryExpander开始。此组件使用 OpenAI 模型(gpt-3.5-turbo在本例中)来生成number个附加查询:
@component``class QueryExpander:`` ` `def __init__(self, prompt: Optional[str] = None, model: str = "gpt-3.5-turbo"):`` ` `self.query_expansion_prompt = prompt` `self.model = model` `if prompt == None:` `self.query_expansion_prompt = """` `You are part of an information system that processes users queries.` `You expand a given query into {{ number }} queries that are similar in meaning.`` ` `Structure:` `Follow the structure shown below in examples to generate expanded queries.` `Examples:` `1. Example Query 1: "climate change effects"` `Example Expanded Queries: ["impact of climate change", "consequences of global warming", "effects of environmental changes"]`` ` `2. Example Query 2: ""machine learning algorithms""` `Example Expanded Queries: ["neural networks", "clustering", "supervised learning", "deep learning"]`` ` `Your Task:` `Query: "{{query}}"` `Example Expanded Queries:` `"""` `builder = PromptBuilder(self.query_expansion_prompt)` `llm = OpenAIGenerator(model = self.model)` `self.pipeline = Pipeline()` `self.pipeline.add_component(name="builder", instance=builder)` `self.pipeline.add_component(name="llm", instance=llm)` `self.pipeline.connect("builder", "llm")`` ` `@component.output_types(queries=List[str])` `def run(self, query: str, number: int = 5):` `result = self.pipeline.run({'builder': {'query': query, 'number': number}})` `expanded_query = json.loads(result['llm']['replies'][0]) + [query]` `return {"queries": list(expanded_query)}
要复制如上所示的示例用户查询和扩展查询,您可以按如下方式运行组件:
expander = QueryExpander()``expander.run(query="开源 NLP 框架", number=4)
这将导致返回queries包含原始查询 + 4 个扩展查询的组件:
{'queries': ['自然语言处理工具',` `'免费 NLP 库',` `'开源语言处理平台',` `'带有开源代码的 NLP 软件',` `'开源 NLP 框架']}
使用查询扩展进行检索
让我们看看如果我们使用查询扩展作为检索pipelines中的一个步骤会发生什么。让我们通过一个非常简单的小演示来看一下这一点。为此,我使用了一些虚拟数据。以下是我使用的documents列表:
documents = [` `Document(content="气候的影响有很多,包括生物多样性的丧失。"),` `Document(content="气候变化的影响在极地冰盖的融化中是显而易见的。"),` `Document(content="全球气候变化的后果变暖的包括的上升海平面。"),` `Document(content="环境变化的影响之一是天气模式的变化。"),` `Document(content="全球都在呼吁减少人们的航空旅行次数。"),` `Document(content="航空旅行是造成气候变化的主要因素之一。"),` `Document(content="预计土耳其夏季气候会变暖。"),``]
当要求使用(我们进行关键字搜索)检索查询“气候变化”的前 3 个文档时,通过InMemoryBM25Retriever,我们得到的前 3 个候选文档如下:
'航空旅行是造成气候变化的主要因素之一。'``'气候变化的影响在极地冰盖的融化中是显而易见的。'``'气候的影响有很多,包括生物多样性的丧失。'
这里有两点需要注意:
-
我们只要求 3 篇文档,并且我们得到了 3 篇与查询“气候变化”相关的文档。从这个意义上讲,这次检索是完全有效的,并且做得很好。
-
但是,由于我们将查询“气候变化”与关键字检索器结合使用,我们实际上遗漏了一些可能与查询更相关的文档。例如,包含“全球变暖”的文档被完全遗漏了。
现在了解当用户在搜索框中输入模糊的查询或关键字时这会对获得的结果产生什么影响。
现在,让我们将查询扩展添加到组合中。这次我们将使用一个可以自定义检索器的方法MultiQueryInMemoryBM25Retriever,它可以接受列表queries
而不是单个query。这是我们创建的检索管道:
query_expander = QueryExpander()``retriever = MultiQueryInMemoryBM25Retriever(InMemoryBM25Retriever(document_store=doc_store))`` ``expanded_retrieval_pipeline = Pipeline()``expanded_retrieval_pipeline.add_component("expander", query_expander)``expanded_retrieval_pipeline.add_component("keyword_retriever", retriever)`` ``expanded_retrieval_pipeline.connect("expander.queries", "keyword_retriever.queries")
现在,我们可以运行这个管道,再次使用相同的查询“气候变化”
`expanded_retrieval_pipeline.run({"expander": {"query": "climate change"}},`
`include_outputs_from=["expander"])`
我们得到以下结果。查询扩展器创建了以下内容queries
:
'expander': {'queries': ['全球变暖的后果',` `'气候变化对环境的影响',` `'气候变率的影响',` `'气候危机的影响',` `'温室气体排放的后果',` `'气候变化']}}
请注意,您可能会得到不同的结果,因为您QueryExpander可能会生成不同的queries
我们从检索管道收到了以下文件:
'全球气候变化的后果变暖的包括的上升海平面。'``'气候变化的影响在极地冰盖的融化中是显而易见的。'``'全球都在呼吁减少人们的航空旅行次数。'``'气候的影响有很多,包括生物多样性的丧失。'``'环境变化的影响之一是天气模式的变化。'``'航空旅行是造成气候变化的主要因素之一。'
请注意我们如何能够添加有关“全球变暖”和“环境变化的影响”的背景信息。
对 RAG 使用查询扩展
我们将以下 Wikipedia 页面编入索引InMemoryDocumentStore
:
"Electric_vehicle", "Dam", "Electric_battery", "Tree", "Solar_panel", "Nuclear_power",``"Wind_power", "Hydroelectricity", "Coal", "Natural_gas",` `"Greenhouse_gas", "Renewable_energy", "Fossil_fuel"
然后,我们构建一个 RAG Pipelines。对于我们最终的 LLM 提示,我们还指出了用户的原始查询是什么。
template = """``You are part of an information system that summarises related documents.``You answer a query using the textual content from the documents retrieved for the``following query.``You build the summary answer based only on quoting information from the documents.``You should reference the documents you used to support your answer.``###``Original Query: "{{query}}"``Retrieved Documents: {{documents}}``Summary Answer:``"""``query_expander = QueryExpander()``retriever = MultiQueryInMemoryBM25Retriever(InMemoryBM25Retriever(document_store=doc_store))``prompt_builder = PromptBuilder(template = template)``llm = OpenAIGenerator()`` ``query_expanded_rag_pipeline = Pipeline()``query_expanded_rag_pipeline.add_component("expander", query_expander)``query_expanded_rag_pipeline.add_component("keyword_retriever", retriever)``query_expanded_rag_pipeline.add_component("prompt", prompt_builder)``query_expanded_rag_pipeline.add_component("llm", llm)`` ``query_expanded_rag_pipeline.connect("expander.queries", "keyword_retriever.queries")``query_expanded_rag_pipeline.connect("keyword_retriever.documents", "prompt.documents")``query_expanded_rag_pipeline.connect("prompt", "llm")
使用查询扩展器运行此pipelines,使用简单查询“绿色能源”,我们能够获得由维基百科页面构建的响应,包括“电动汽车”、“风能”、“可再生能源”、“化石燃料”和“核能”。如果没有MultiQueryInMemoryBM25Retriever,我们依靠对查询“绿色能源”进行一次 BM25 检索的前 k 个结果,只能得到由页面“可再生能源”、“风能”和“化石燃料”构建的响应。
总结
查询扩展是一项很棒的技术,它可以让您在仍然使用关键字搜索的同时获得更广泛的相关资源。虽然语义搜索是一个很好的选择,但它需要使用嵌入模型,并且我们将执行搜索的数据源必须有嵌入。这使得基于关键字的搜索成为一种非常有吸引力的选择,可以实现更快、更便宜的检索。
然而,这意味着我们严重依赖所提供查询的质量。查询扩展允许您通过生成与用户查询类似的查询来解决此问题。
我认为,这种技术的主要优势之一是它允许您避免在每次更新时嵌入文档,同时设法在查询时提高检索到的文档的相关性。关键字检索不需要任何额外的嵌入步骤,因此在这种情况下,检索时发生的唯一推理是当我们要求 LLM 生成一定数量的类似查询时。
如果对内容有什么疑问和建议可以私信和留言,也可以添加我加入大模型交流群,一起讨论大模型在创作、RAG和agent中的应用。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。