AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


利用 RAG 处理文本、表格和图像:综合指南(含代码)
发布日期:2024-04-05 11:27:48 浏览次数: 3435 来源:二师兄talks


在信息检索领域,检索增强生成 (RAG) 已成为从大量文本数据中提取知识的强大工具。这种通用技术利用检索和生成策略的组合来有效地总结和综合相关文档中的信息。然而,尽管 RAG 获得了相当大的吸引力,但它在更广泛的内容类型(包括文本、表格和图像)中的应用仍然相对未被探索。

多模态内容的挑战

大多数现实世界的文档都包含丰富的信息,通常将文本、表格和图像组合在一起,以传达复杂的想法和见解。虽然传统的 RAG 模型擅长处理文本,但它们难以有效地集成和理解多模态内容。这种局限性阻碍了RAG充分捕捉这些文件本质的能力,可能导致不完整或不准确的表述。

在这篇文章中,我们将探讨如何创建可以处理这些类型文档的多模态 RAG。

这是一个图表,我们将使用它作为处理此类文档的指南。


多模式RAG

第1步:将文件拆分为原始元素。

首先,让我们将所有必要的库导入到我们的环境中

    import osimport openaiimport ioimport uuidimport base64import timefrom base64 import b64decodeimport numpy as npfrom PIL import Image
    from unstructured.partition.pdf import partition_pdf
    from langchain.chat_models import ChatOpenAIfrom langchain.schema.messages import HumanMessage, SystemMessagefrom langchain.vectorstores import Chromafrom langchain.storage import InMemoryStorefrom langchain.schema.document import Documentfrom langchain.embeddings import OpenAIEmbeddingsfrom langchain.retrievers.multi_vector import MultiVectorRetrieverfrom langchain.chat_models import ChatOpenAIfrom langchain.prompts import ChatPromptTemplatefrom langchain.schema.output_parser import StrOutputParserfrom langchain.schema.runnable import RunnablePassthrough, RunnableLambda
    from operator import itemgetter

    我们将使用 unstructured 化来解析文档(PDF)中的图像、文本和表格,您可以直接在这个 google colab 中运行此代码,或下载pdf文件,将其上传到会话存储。然后按照以下步骤操作。

    • google colab:https://colab.research.google.com/drive/1I9-JMGL76XXzUel7XTjO8pWK7jV-V8WD?usp=sharing

    • pdf文件:https://sgp.fas.org/crs/misc/IF10244.pdf

    • unstructured:https://unstructured.io/

    (在执行代码之前,请参阅google colab中的安装说明来设置您的venv)

      # load the pdf file to drive# split the file to text, table and imagesdef doc_partition(path,file_name):raw_pdf_elements = partition_pdf(filename=path + file_name,extract_images_in_pdf=True,infer_table_structure=True,chunking_strategy="by_title",max_characters=4000,new_after_n_chars=3800,combine_text_under_n_chars=2000,image_output_dir_path=path)
      return raw_pdf_elementspath = "/content/"file_name = "wildfire_stats.pdf"raw_pdf_elements = doc_partition(path,file_name)

      运行上述代码后,文件中包含的所有图像将被提取并保存在您的路径中。在我们的例子中 (path = “/content/”)

      接下来,我们将每个原始元素附加到其类别中,(文本到文本,表格到表格,对于图像,unstructed 已经处理好了......

        # appending texts and tables from the pdf filedef data_category(raw_pdf_elements): # we may use decorator heretables = []texts = []for element in raw_pdf_elements:if "unstructured.documents.elements.Table" in str(type(element)): tables.append(str(element))elif "unstructured.documents.elements.CompositeElement" in str(type(element)): texts.append(str(element))data_category = [texts,tables]return data_categorytexts = data_category(raw_pdf_elements)[0]tables = data_category(raw_pdf_elements)[1]

        第 2 步:图像标题和表格摘要

        为了总结表格,我们将使用 Langchain 和 GPT-4。为了生成图像标题,我们将使用 GPT-4-Vision-Preview。这是因为它是目前唯一可以同时处理多个图像的模型,这对于包含多个图像的文档非常重要。对于文本元素,在将它们放入嵌入之前,我们将保持它们不变。

        准备好您的 OpenAI API 密钥


          os.environ["OPENAI_API_KEY"] = 'sk-xxxxxxxxxxxxxxx'openai.api_key = os.environ["OPENAI_API_KEY"]
          # function to take tables as input and then summarize themdef tables_summarize(data_category):prompt_text = """You are an assistant tasked with summarizing tables. \\Give a concise summary of the table. Table chunk: {element} """
          prompt = ChatPromptTemplate.from_template(prompt_text)model = ChatOpenAI(temperature=0, model="gpt-4")summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser()table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5})
          return table_summariestable_summaries = tables_summarize(data_category)text_summaries = texts

          对于图像,我们需要将它们编码为 base64 格式,然后再将它们提供给我们的模型进行字幕

            def encode_image(image_path):''' Getting the base64 string '''with open(image_path, "rb") as image_file:return base64.b64encode(image_file.read()).decode('utf-8')
            def image_captioning(img_base64,prompt):''' Image summary '''chat = ChatOpenAI(model="gpt-4-vision-preview",max_tokens=1024)
            msg = chat.invoke([HumanMessage(content=[{"type": "text", "text":prompt},{"type": "image_url","image_url": {"url": f"data:image/jpeg;base64,{img_base64}"},},])])return msg.content

            现在我们可以附加我们的images_base64列表并总结它们,然后我们拆分 base64 编码的图像及其相关文本。

            运行下面的代码时,您可能会收到RateLimitError错误代码429。当您超过gpt-4-vision-preview组织中每分钟请求 (RPM) 的速率限制时,就会出现此错误,在我的例子中,我的使用限制为 3 RPM,因此在每个图像字幕之后,我将 60 秒设置为安全措施并等待速率限制重置。


              # Store base64 encoded imagesimg_base64_list = []
              # Store image summariesimage_summaries = []
              # Prompt : Our prompt here is customized to the type of images we have which is chart in our caseprompt = "Describe the image in detail. Be specific about graphs, such as bar plots."
              # Read images, encode to base64 stringsfor img_file in sorted(os.listdir(path)):if img_file.endswith('.jpg'):img_path = os.path.join(path, img_file)base64_image = encode_image(img_path)img_base64_list.append(base64_image)img_capt = image_captioning(base64_image,prompt)time.sleep(60)image_summaries.append(image_captioning(img_capt,prompt))
              def split_image_text_types(docs):''' Split base64-encoded images and texts '''b64 = []text = []for doc in docs:try:b64decode(doc)b64.append(doc)except Exception as e:text.append(doc)return {"images": b64,"texts": text}

              第 3 步:创建一个多向量检索器并将文本、表格、图像及其索引存储在 Vectore Base 中

              我们已经完成了第一部分,包括将文档划分为原始元素,汇总表格和图像,现在我们准备好进行第二部分,我们将创建一个多向量检索器,并将第一部分的输出与它们的 ID 一起存储在 chromadb 中。

              1. 我们创建一个 vectorestore 来索引子块(summary_texts、summary_tables、summary_img),并使用 OpenAIEmbeddings() 进行嵌入,

              2 .用于存储父文档(doc_ids、文本)、(table_ids、表)和(img_ids、img_base64_list)的文档存储

                # The vectorstore to use to index the child chunksvectorstore = Chroma(collection_name="multi_modal_rag", embedding_function=OpenAIEmbeddings())
                # The storage layer for the parent documentsstore = InMemoryStore()id_key = "doc_id"
                # The retriever (empty to start)retriever = MultiVectorRetriever(vectorstore=vectorstore,docstore=store,id_key=id_key,)
                # Add textsdoc_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)))
                # Add tablestable_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)))
                # Add image summariesimg_ids = [str(uuid.uuid4()) for _ in img_base64_list]summary_img = [Document(page_content=s, metadata={id_key: img_ids[i]})for i, s in enumerate(image_summaries)]retriever.vectorstore.add_documents(summary_img)retriever.docstore.mset(list(zip(img_ids, img_base64_list)))

                步骤4:使用langchain RunnableLambda包装上述所有内容

                • 我们首先计算上下文(在本例中为“文本”和“图像”)和问题(此处只是一个 RunnablePassthrough)

                • 然后我们将其传递到我们的提示模板中,这是一个自定义函数,用于格式化 gpt-4-vision-preview 模型的消息。

                • 最后,我们将输出解析为字符串。


                  from operator import itemgetterfrom langchain.schema.runnable import RunnablePassthrough, RunnableLambda

                  def prompt_func(dict):format_texts = "\\n".join(dict["context"]["texts"])return [HumanMessage(content=[{"type": "text", "text": f"""Answer the question based only on the following context, which can include text, tables, and the below image:Question: {dict["question"]}

                  Text and tables:{format_texts}"""},{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{dict['context']['images'][0]}"}},])]

                  model = ChatOpenAI(temperature=0, model="gpt-4-vision-preview", max_tokens=1024)

                  # RAG pipelinechain = ({"context": retriever | RunnableLambda(split_image_text_types), "question": RunnablePassthrough()}| RunnableLambda(prompt_func)| model| StrOutputParser())


                  现在,我们准备测试我们的多重检索 Rag

                    chain.invoke("What is the change in wild fires from 1993 to 2022?")

                    答案如下

                    根据提供的图表,从 1993 年到 2022 年,野火数量有所增加。该图表显示了一个以千为单位的火灾数量的折线图,该曲线似乎从 1993 年的较低点开始,到 2022 年的较高点结束。1993年的确切数字没有在文本中提供,也没有在图表上看到,但视觉趋势表明有所增加。

                    同样,从1993年到2022年,由图表中的阴影区域表示的烧毁面积也有所增加。1993 年阴影区域的起点低于 2022 年的终点,这表明与 1993 年相比,2022 年被烧毁的英亩数更多。同样,没有提供1993年的具体数字,但图表上的视觉趋势表明,在这段时间里,烧毁的英亩数有所增加,period.to 这样做

                    参考资料 :

                    • google colab:https://colab.research.google.com/drive/1I9-JMGL76XXzUel7XTjO8pWK7jV-V8WD?usp=sharing

                    • pdf文件:https://sgp.fas.org/crs/misc/IF10244.pdf

                    • unstructured:https://unstructured.io/

                    • multi_vector:https://python.langchain.com/docs/modules/data_connection/retrievers/multi_vector

                    • semi-structured-multi-modal-rag:https://blog.langchain.dev/semi-structured-multi-modal-rag/



                    53AI,企业落地应用大模型首选服务商

                    产品:大模型应用平台+智能体定制开发+落地咨询服务

                    承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

                    联系我们

                    售前咨询
                    186 6662 7370
                    预约演示
                    185 8882 0121

                    微信扫码

                    与创始人交个朋友

                    回到顶部

                     
                    扫码咨询