介绍
检索增强生成(Retrieval Augmented Generation)已经存在一段时间了。许多工具和应用程序围绕这个概念进行了构建,比如向量存储、检索框架和LLMs,使得处理自定义文档尤其是具有Langchain的半结构化数据变得方便。处理长、密集的文本从未如此轻松而有趣。传统的RAG对于不结构化的文本重型文件(如DOC、PDF等)效果良好。然而,这种方法对于嵌入在PDF中的半结构化数据(如嵌入式表格)效果不佳。
在处理半结构化数据时,通常有两个问题。
- 传统的提取和文本分割方法无法处理PDF中的表格。它们通常会破坏表格,从而导致信息的丢失。
- 嵌入表格可能无法转化为精确的语义搜索。
因此,在本文中,我们将使用Langchain构建一个用于处理半结构化数据的检索生成(Retrieval Generation)流水线,以解决这两个半结构化数据的问题。
学习目标
- 了解结构化、非结构化和半结构化数据之间的区别。
- 对检索增强生成和Langchain进行简要回顾。
- 学习如何使用Langchain构建一个用于处理半结构化数据的多向量检索器。
本文作为数据科学博文马拉松的一部分发表。
数据类型
通常有三种类型的数据:结构化数据、半结构化数据和非结构化数据。
- 结构化数据:结构化数据是标准化的数据,遵循预定义的模式,例如行和列。SQL数据库、电子表格、数据帧等。
- 非结构化数据:与结构化数据不同,非结构化数据没有数据模型。数据是随机的,例如PDF、文本、图像等。
- 半结构化数据:它是前两种数据类型的结合。与结构化数据不同,它没有严格的预定义模式。然而,数据仍然基于某些标记保持着分层次的顺序,这与非结构化类型形成了对比。例如CSV、HTML、嵌入式PDF中的表格、XML等。
![]()
什么是RAG?
RAG代表检索增强生成(Retrieval Augmented Generation)。这是为大型语言模型提供新信息的最简单方法。现在,让我们对RAG进行一个快速介绍。
在典型的RAG流程中,我们有知识来源,如本地文件、网页、数据库等,一个嵌入模型,一个向量数据库和一个LLM。我们从各种来源收集数据,拆分文档,获取文本块的嵌入并将它们存储在向量数据库中。现在,我们将查询的嵌入传递给向量存储,从向量存储中检索文档,最后使用LLM生成答案。
![]()
这是传统RAG的工作流程,适用于如文本等不结构化数据。然而,当涉及到半结构化数据时,例如嵌入在PDF中的表格,它通常无法表现良好。在本文中,我们将学习如何处理这些嵌入式表格。
什么是Langchain?
Langchain是一个用于构建基于LLM的应用程序的开源框架。自推出以来,该项目在软件开发人员中获得了广泛的应用。它提供了一系列统一的工具和技术,可以更快地构建AI应用程序。Langchain拥有向量存储、文档加载器、检索器、嵌入模型、文本拆分器等工具。它是构建AI应用程序的一站式解决方案。但是,它与众不同的核心价值主张有两个。
- LLM链条: Langchain提供多个链条。这些链条将多个工具链在一起,完成单一的任务。例如,ConversationalRetrievalChain将LLM、向量存储检索器、嵌入模型和聊天历史对象链在一起,以生成查询的响应。这些工具被硬编码并必须明确定义。
- LLM代理: 与LLM链条不同,AI代理没有硬编码的工具。我们不是将一个工具链在另一个后面,而是让LLM根据工具的文本描述来决定选择哪个工具和何时选择。这使得它非常适合构建涉及推理和决策的复杂LLM应用程序。
构建RAG管道
现在我们对这些概念有了初步了解。让我们讨论构建管道的方法。处理半结构化数据可能会有些棘手,因为它不遵循常规的存储信息模式。为了处理非结构化数据,我们需要专门的工具来提取信息。因此,在这个项目中,我们将使用一种称为“unstructured”的工具;它是一种用于从不同的非结构化数据格式(如PDF、HTML、XML等)中提取信息的开源工具。Unstructured在内部使用Tesseract和Poppler来处理文件中的多种数据格式。所以,在我们开始编码之前,让我们先设置好环境并安装所需的依赖。
![]()
设置开发环境
像任何其他Python项目一样,打开一个Python环境并安装Poppler和Tesseract。
!sudo apt install tesseract-ocr!sudo apt-get install poppler-utils
现在,安装我们项目所需的依赖项。
!pip install“unstructured [all-docs]”Langchain openai
使用Unstructured提取
现在我们已经安装了依赖项,我们将从一个PDF文件中提取数据。
from unstructured.partition.pdf import partition_pdfpdf_elements = partition_pdf( "mistral7b.pdf", chunking_strategy="by_title", extract_images_in_pdf=True, max_characters=3000, new_after_n_chars=2800, combine_text_under_n_chars=2000, image_output_dir_path="./" )
运行此代码将安装多个依赖项,如YOLOx,该依赖项用于OCR,并根据提取的数据返回对象类型。启用extract_images_in_pdf将允许unstructured从文件中提取嵌入的图像。这可以帮助实现多模态解决方案。
现在,让我们探索一下我们的PDF中的元素类别。
#创建一个字典来存储每种类型的计数category_counts = {}for element in pdf_elements: category = str(type(element)) if category in category_counts: category_counts[category] += 1 else: category_counts[category] = 1#Unique_categories将有唯一的元素unique_categories = set(category_counts.keys())category_counts
运行此代码将输出元素类别及其计数。
现在,我们将元素分开以便于处理。我们创建一个继承自Langchain的Document类型,名为Element类型。这样可以确保更有组织的数据,更容易处理。
from unstructured.documents.elements import CompositeElement, Tablefrom langchain.schema import Documentclass Element(Document): type: str#按类型分类categorized_elements = []for element in pdf_elements: if isinstance(element, Table): categorized_elements.append(Element(type="table", page_content=str(element))) elif isinstance(element, CompositeElement): categorized_elements.append(Element(type="text", page_content=str(element)))#Tablesstable_elements = [e for e in categorized_elements if e.type == "table"]#Texttext_elements = [e for e in categorized_elements if e.type == "text"]
多向量检索器
我们有表格和文本元素。现在,我们有两种处理它们的方式。我们可以将原始元素存储在文档存储中,或者存储文本的摘要。表格可能对语义搜索构成挑战,这种情况下,我们创建表格的摘要并将其与原始表格一起存储在文档存储中。为了实现这一点,我们将使用MultiVectorRetriever。这个检索器将管理一个向量存储,我们在其中存储摘要文本的嵌入,并且使用一个简单的内存中的文档存储来存储原始文档。
首先,构建一个总结链,以总结我们之前提取的表格和文本数据。
from langchain.chat_models import coherefrom langchain.prompts import ChatPromptTemplatefrom langchain.schema.output_parser import StrOutputParserprompt_text = """您是一个助手,负责总结表格和文本数据。给出关于表格或文本的简洁摘要。表格或文本块: {element} """prompt = ChatPromptTemplate.from_template(prompt_text)model = cohere.ChatCohere(cohere_api_key="your_key")summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser()tables = [i.page_content for i in table_elements]table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5})texts = [i.page_content for i in text_elements]text_summaries = summarize_chain.batch(texts, {"max_concurrency": 5})
我使用了Cohere LLM来总结数据;您可以使用像GPT-4这样的OpenAI模型。更好的模型会产生更好的结果。有时,模型可能无法完美捕捉表格的细节。因此,最好使用功能强大的模型。
现在,我们创建MultivectorRetriever。
from langchain.retrievers import MultiVectorRetrieverfrom langchain.prompts import ChatPromptTemplateimport uuidfrom langchain.embeddings import OpenAIEmbeddingsfrom langchain.schema.document import Documentfrom langchain.storage import InMemoryStorefrom langchain.vectorstores import Chroma# 用于索引子块的向量存储库vectorstore = Chroma(collection_name="collection", embedding_function=OpenAIEmbeddings(openai_api_key="api_key"))# 用于存储父文档的存储层store = InMemoryStore()id_key = ""id"# 检索器retriever = MultiVectorRetriever( vectorstore=vectorstore, docstore=store, id_key=id_key,)# 添加文本doc_ids = [str(uuid.uuid4()) for _ in texts]summary_texts = [ Document(page_content=s, metadata={id_key: doc_ids[i]}) for i, s in enumerate(text_summaries)]retriever.vectorstore.add_documents(summary_texts)retriever.docstore.mset(list(zip(doc_ids, texts)))# 添加表格table_ids = [str(uuid.uuid4()) for _ in tables]summary_tables = [ Document(page_content=s, metadata={id_key: table_ids[i]}) for i, s in enumerate(table_summaries)]retriever.vectorstore.add_documents(summary_tables)retriever.docstore.mset(list(zip(table_ids, tables)))
我们使用Chroma向量存储库存储文本和表格的摘要嵌入向量,使用内存中的文档存储库存储原始数据。
RAG
现在我们的检索器已经准备好了,我们可以使用Langchain Expression语言构建一个RAG流水线。
from langchain.schema.runnable import RunnablePassthrough# Prompt模板template = """根据以下内容回答问题,包括文本和表格:{context}问题:{question}"""prompt = ChatPromptTemplate.from_template(template)# LLM模型model = ChatOpenAI(temperature=0.0, openai_api_key="api_key")# RAG流水线chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | model | StrOutputParser())
现在,我们可以根据从向量存储库检索的嵌入向量提出问题并获得答案。
chain.invoke(input = "Llama 2和Mistral 7B Instruct的MT评分是多少?")
结论
半结构化数据格式中隐藏了大量的信息。而且从这些数据中提取和执行常规的RAG是具有挑战性的。在本文中,我们从PDF中提取文本和嵌入式表格,然后使用Langchain构建了一个多向量检索器和RAG流水线。因此,以下是本文的要点。
要点
- 常规的RAG在处理半结构化数据时经常面临挑战,例如在文本拆分过程中分割表格和语义搜索不准确。
- Unstructured是一个开源工具,用于半结构化数据,可以从PDF或类似的半结构化数据中提取嵌入式表格。
- 使用Langchain,我们可以构建一个多向量检索器,将表格、文本和摘要存储在文档存储中,以实现更好的语义搜索。
常见问题
本文中显示的媒体内容并非Analytics Vidhya所有,而是根据作者的自行判断使用。