用LLama指数建立一条RAG管道
美丽时尚的LLama指数:构建RAG管道的秘诀' (The LLama Index of Beauty and Fashion Secrets to Building a RAG Pipeline)
介绍
大语言模型(LLMs)最受欢迎的应用之一是回答关于自定义数据集的问题。像ChatGPT和Bard这样的LLMs是出色的沟通者,几乎可以回答它们在模型训练中看到过的任何问题。这也是LLMs面临的最大瓶颈之一,它们只能回答它们在训练中看到过的问题。语言模型对世界的知识有限。例如,Chatgpt是基于2021年之前的数据进行训练的。而且,GPT无法了解您的私人文件。那么,如何让模型了解它尚未了解的知识呢?答案是使用检索增强生成管道(RAG)。在本文中,我们将学习有关检索增强生成(RAG)管道以及如何使用Llama索引构建它的内容。
学习目标
- 探索何时应该使用检索增强生成(RAG)。
- 简要了解RAG的不同组成部分。
- 了解Llama索引以及如何使用它构建用于PDF文档的简单RAG管道。
- 了解嵌入和向量数据库以及如何使用Llama索引的内置模块从PDF文档构建知识库。
- 发现基于RAG的应用程序的实际用例。
本文是数据科学博文马拉松比赛(Data Science Blogathon)的一部分。
什么是RAG?
LLMs是迄今最高效和强大的NLP模型。我们已经看到了LLMs在翻译、作文写作和常规问答方面的潜力。但是,当涉及特定领域的问答时,它们往往存在错误信息。此外,在特定领域的问答应用程序中,每个查询只有少量相关上下文的文档。因此,我们需要一个统一的系统,将文档提取、答案生成以及它们之间的所有过程整合在一起。这个过程称为检索增强生成。
了解更多:检索增强生成(RAG)在人工智能中的应用
因此,让我们了解为什么RAG对于构建实际领域特定的问答应用程序最有效。
为什么应该使用RAG?
LLM可以学习新数据的方法有三种。
- 训练:通过数万亿个标记和数十亿个参数训练一组大规模神经网络,创建大型语言模型。深度学习模型的参数是所有关于特定模型的信息的系数或权重。训练一个像GPT-4这样的模型成本数千万美元,这超出了任何人的能力。我们无法对新数据重新训练如此庞大的模型,这是不可行的。
- 微调:另一种选择是在现有数据上对模型进行微调。微调包括使用预训练模型作为训练的起点。我们利用预训练模型的知识在不同的数据集上训练新模型。尽管这种方法非常强大,但在时间和金钱方面都很昂贵。除非有特定的需求,否则微调没有意义。
- 提示:提示是将新信息嵌入到LLM的上下文窗口中,并使其根据提示中给定的信息回答查询的方法。虽然这种方法可能不像在训练或微调期间学到的知识那样有效,但对于许多现实生活的用例(例如文档问答)已经足够。
从文本文档中提取答案的提示是有效的,但是这些文档通常远大于大语言模型(LLMs)的上下文窗口,这是一个挑战。检索增强生成(RAG)管道通过处理、存储和检索相关的文档片段来解决这个问题,从而使LLMs能够高效地回答查询。因此,让我们讨论RAG管道的关键组成部分。
什么是RAG组件?
在典型的RAG过程中,我们有几个组成部分。
- 文本分割器:将文档分割为适应LLM上下文窗口的部分。
- 嵌入模型:用于获取文档嵌入的深度学习模型。
- 向量存储:存储和查询文档嵌入及其元数据的数据库。
- LLM:负责从查询中生成答案的大型语言模型。
- 实用函数:包括Webretriver和文档解析器等额外的实用函数,用于检索和预处理文件。
上图是典型的RAG过程。我们有文档(PDF,网页,文档),还有一个工具,可以将大文本分割成较小的块,嵌入模型可以获取文本块的向量表示,向量存储作为知识库,还有一个LLM可以从文本块中获取答案。
什么是Llama Index?
Llama Index(GPTIndex)是一个用Python编写的框架,用于构建LLM应用程序。它是一个简单灵活的数据框架,将自定义数据源与大型语言模型连接起来。它提供适当的工具来支持从各种来源摄取数据,用于数据索引的向量数据库以及用于查询大型文档的查询接口。简而言之,Llama Index是建立检索增强生成应用程序的一站式解决方案。它可以方便地与Langchain、Flask、Docker等其他应用程序集成。请查看官方GitHub存储库了解更多信息:https://github.com/run-llama/llama_index。
另请阅读:如何使用Llama Index
现在我们已经了解了RAG和Llama Index。那么,让我们建立我们的RAG流水线来处理PDF文档,并在我们继续讨论的过程中讨论个别概念。
设置开发环境
构建任何Python项目的第一步是创建虚拟环境。完成后,安装以下库。
llama-indexopenaitiktoken
现在,导入以下函数。
import osfrom llama_index import ServiceContext, LLMPredictor, OpenAIEmbedding, PromptHelperfrom llama_index.llms import OpenAIfrom llama_index.text_splitter import TokenTextSplitterfrom llama_index.node_parser import SimpleNodeParserfrom llama_index import VectorStoreIndex, SimpleDirectoryReaderfrom llama_index import set_global_service_context
现在,设置Open AI API密钥。
import osos.environ['OPENAI_API_KEY'] = "YOUR API KEY"
加载文档
正如我们所知,LLM并没有掌握世界的最新知识,也没有关于您内部文档的知识。为了帮助LLM,我们需要向它们提供来自知识源的相关信息。这些知识源可以是结构化数据,如CSV、电子表格或SQL表,非结构化数据,如文本、Word文档、Google文档、PDF或PPT,以及半结构化数据,如Notion、Slack、Salesforce等。
在本文中,我们将使用PDF。Llama Index包括一个名为SimpleDirectoryReader的类,它可以从指定目录读取保存的文档。它会根据文件扩展名自动选择解析器。
documents = SimpleDirectoryReader(input_dir='data').load_data()
您可以使用PyMuPDF或PyPDF2等软件包自定义实现PDF阅读器。
创建文本块
通常,从知识源中提取的数据往往很长,超过了LLM的上下文窗口。如果我们发送超过上下文窗口的文本,Chatgpt API将缩小数据,遗漏关键信息。解决此问题的一种方法是进行文本分块。在文本分块中,较长的文本根据分隔符分割成较小的块。
除了使文本能够适应大型语言模型的上下文窗口之外,文本分块还有其他好处。
- 较小的文本块可以提高嵌入准确性,从而提高检索准确性。
- 精确的上下文:缩小信息范围有助于获取更好的信息。
羊驼索引内置了用于分块文本的工具。所以,这就是我们可以做到的。
text_splitter = TokenTextSplitter( separator=" ", chunk_size=1024, chunk_overlap=20, backup_separators=["\n"], tokenizer=tiktoken.encoding_for_model("gpt-3.5-turbo").encode)node_parser = SimpleNodeParser.from_defaults( text_splitter = TokenTextSplitter ))
SimpleNodeParser将文本块转化为节点,而文本块是使用Llama Index的TokenTextSplitter创建的。我们也可以使用SentenceSplitter。
text_splitter = SentenceSplitter( separator=" ", chunk_size=1024, chunk_overlap=20, paragraph_separator="\n\n\n", secondary_chunking_regex="[^,.;。]+[,.;。]?", tokenizer=tiktoken.encoding_for_model("gpt-3.5-turbo").encode)
构建知识库
从知识来源中提取的文本需要存储在某个地方。但在基于RAG的应用中,我们需要数据的嵌入。这些嵌入是表示高维向量空间中的数据的浮点数。为了存储和操作它们,我们需要向量数据库。向量数据库是专门用于存储和查询向量的数据存储。
在本节中,我们将了解嵌入和向量数据库,并使用Llama Index为我们的RAG管道实现它们。
嵌入
我们可以通过简单的超市示例来理解嵌入。在超市里,你总是能在同一个角落找到苹果和橙子。要找到肥皂,你必须离开水果区向日常服装区走得更远,但你会很容易在同一区域内找到香水,只需走几步的距离。
这就是嵌入的工作原理。两个语义相关的文本在向量空间中是相邻的,而不相似的文本则相距较远。嵌入具有在不同数据点之间建立类比关系的非凡能力。以下是一个简单的说明。
那么,为什么我们需要嵌入呢?
使用能力强大的深度学习模型生成的嵌入能够高效地捕捉文本块的语义含义。当用户发送文本查询时,我们使用相同的模型将其转换为嵌入,比较向量数据库中存储的文本嵌入的距离,并检索最接近的“n”个文本块。这些块是与查询文本最相似的语义块。
对于嵌入模型,我们不需要特别做什么。Llama Index具有流行嵌入模型的自定义实现,例如OpenAI的Ada、Cohere、Sentence transformers等。
要自定义嵌入模型,我们需要使用ServiceContext和PromptHelper。
llm = OpenAI(model='gpt-3.5-turbo', temperature=0, max_tokens=256)embed_model = OpenAIEmbedding()prompt_helper = PromptHelper( context_window=4096, num_output=256, chunk_overlap_ratio=0.1, chunk_size_limit=None)service_context = ServiceContext.from_defaults( llm=llm, embed_model=embed_model, node_parser=node_parser, prompt_helper=prompt_helper)
向量数据库
向量数据库是专门用于存储和组织嵌入和相关元数据以提供最大查询效率的数据存储。它们提供语义检索数据的功能,有助于通过新信息增强LLMs。具体工作原理如下。
现在,您可能会问,为什么我们不能使用传统数据库?技术上讲,我们可以使用像SQLite或MySQL这样的数据库存储向量,并将查询文本的嵌入与所有其他嵌入进行线性比较。但问题是,你可能已经猜到了,这种线性搜索具有O(n)时间复杂度。虽然带有GPU的机器可以很好地处理几千个数据点,但在任何真实应用中处理数亿个嵌入时,性能将极差。
那么,我们该如何解决这个问题呢?答案是使用不同的ANN算法如HNSW对嵌入进行索引。HNSW是一种基于图的算法,可以高效地处理数十亿个嵌入。HNSW的平均查询复杂度为O(log n)。
除了HNSW之外,还有一些其他的索引技术,比如产品量化,标量量化和倒排文件索引。然而,大多数向量数据库默认使用HNSW作为索引算法。
我们学习了嵌入和向量存储。现在,我们将在代码中实现它们。我们将使用Llama Index的默认向量存储。它是一个内存中的向量数据库。您也可以选择其他向量存储,如Chroma、Weaviate、Qdrant、Milvus等。
index = VectorStoreIndex.from_documents( documents, service_context = service_context )
使用我们目录中的文档和之前定义的服务上下文的默认值创建索引。
查询索引
最后一步是从索引中查询并从LLM获取响应。Llama Index提供了一个查询引擎用于查询和一个类似聊天的对话引擎。两者之间的区别在于聊天引擎保留了对话的历史记录,而查询引擎则没有。
query_engine = index.as_query_engine(service_context=service_context)response = query_engine.query("什么是HNSW?")print(response)
图像和代码的GitHub存储库: sunilkumardash9/llama_rag
完整代码:
from llama_index import ServiceContext, LLMPredictor, OpenAIEmbedding, PromptHelperfrom llama_index.llms import OpenAIfrom llama_index.text_splitter import TokenTextSplitterfrom llama_index.node_parser import SimpleNodeParserimport tiktokenllm = OpenAI(model='gpt-3.5-turbo', temperature=0, max_tokens=256)embed_model = OpenAIEmbedding()text_splitter = TokenTextSplitter( separator=" ", chunk_size=1024, chunk_overlap=20, backup_separators=["\n"], tokenizer=tiktoken.encoding_for_model("gpt-3.5-turbo").encode)node_parser = SimpleNodeParser.from_defaults( text_splitter=text_splitter)prompt_helper = PromptHelper( context_window=4096, num_output=256, chunk_overlap_ratio=0.1, chunk_size_limit=None)service_context = ServiceContext.from_defaults( llm=llm, embed_model=embed_model, node_parser=node_parser, prompt_helper=prompt_helper)documents = SimpleDirectoryReader(input_dir='data').load_data()index = VectorStoreIndex.from_documents( documents, service_context = service_context )index.storage_context.persist()query_engine = index.as_query_engine(service_context=service_context)response = query_engine.query("什么是HNSW?")print(response)
真实生活应用案例
基于RAG的应用在许多真实生活应用场景中都很有帮助。
- 学术研究:研究人员经常处理大量的研究论文和PDF格式的文章。基于RAG的流水线可以帮助他们提取相关信息,创建参考文献,并高效地组织他们的引用。
- 律师事务所:律师事务所经常处理大量的法律文件。基于RAG的问答聊天机器人可以简化文件检索过程。这将节省大量时间。
- 教育机构:教师和教育工作者可以从教育资源中提取内容,创建定制化的学习材料或准备课程内容。学生可以在短时间内从大型PDF中提取适用信息。
- 行政部门:政府和私人行政部门经常处理大量的文件、申请和报告。使用基于RAG的聊天机器人可以简化繁琐的文件检索流程。
- 客户服务:基于RAG的问答聊天机器人结合现有的知识库可用于回答客户查询。
结论
正如我们所见,RAG(检索增强生成)简化了检索和生成的过程。它能够在极短的时间内从大量文档中查询到正确的上下文。然后,LLMs根据查询的正确上下文进行回答生成。附加的上下文基本上使LLM的回答与上下文保持一致。这样可以防止LLM产生妄想,同时保持其出色的措辞和写作能力。
所以,以下是这篇文章的要点。
要点
- 使LLMs了解个人文档并减少妄想的理想方式是利用来自知识库的检索文档增强LLMs。
- RAG是检索增强生成(Retrieval Augmented Generation)的缩写。RAG用于利用来自自定义文档的知识库信息来增强LLMs。
- 嵌入是文本数据在高维向量空间中的数值表示。嵌入捕捉文本的语义含义。
- 用户使用向量数据库作为知识库,利用各种索引算法组织高维向量,实现快速且稳定的查询能力。
- Llama Index提供了内置工具和方法,用于构建生产级的基于RAG的应用程序。
常见问题
本文中显示的媒体不是Analytics Vidhya所有,只是作者根据自己的判断使用。