概述
在本文中,我将指导您构建一个使用 OpenAI 的 GPT-4o 模型的多模态 RAG 聊天应用程序。您将学习以下内容:
-
多模态 RAG 聊天应用程序:创建一个应用程序,通过从 PDF 文档中检索信息来实现视觉问答。
-
无缝解析:使用 Unstructured 库无缝解析文本、表格和图像。
-
性能评估:使用 DeepEval 库提供的各种指标评估聊天机器人的性能。
-
Streamlit UI:通过 Streamlit 应用程序演示该应用程序。
为什么要阅读这个?
你是否有兴趣利用像 GPT-4o 这样的先进基础模型的多模态能力来构建自己的 AI 应用程序?那么你来对地方了!
无论你是寻求市场研究报告见解的市场营销专业人士,分析多模态医疗文件的医疗从业者,还是处理复杂法律文件的法律专业人士,这篇文章都为你提供了宝贵的见解。
我将详细解释每个概念,并提供所有代码的详细说明。话虽如此,让我们开始吧! 🎬
多模态 RAG 的崛起
从基于文本的 RAG 模型过渡到多模态 RAG 系统标志着 AI 能力的重大飞跃。以下是快速概述:
-
起源:RAG 这个术语是在 2021 年 4 月提出的,通过基于文本的知识增强语言输出。
-
进展:随着 2024 年 5 月发布的 GPT-4o 等模型,我们现在可以整合视觉信息,允许同时处理图像、表格和文本。
-
新可能性:这一演变使得更全面和上下文丰富的 AI 应用成为可能。
在本文中,我将展示一个案例研究,使用多模态 RAG 框架对我在 Neurocomputing 上发表的研究文章进行问答。该文章包含文本、表格和图形,我们将探索 GPT-4o 的视觉能力如何回答复杂问题。
让我们开始编码吧!🎬
设置虚拟环境并安装 Python 库
首先,让我们使用以下命令设置虚拟环境:
python3.10 -m venv venv
现在,让我们安装必要的软件包。您可以在 GitHub 仓库的主目录中的“requirements.txt”中找到它们。
pip install -r requirements.txt
现在,打开一个 Jupyter Notebook,比如“your-project.ipynb”,开始编写您的代码。就这样!我们现在准备进入主要细节。
这是多模态 RAG 项目的工作 GitHub 仓库:
预处理非结构化数据
要构建一个 RAG 应用程序,我们的第一步是将上下文加载到数据库中,这里使用的是 PDF 文档。由于大型语言模型(LLM)的上下文窗口限制,我们无法将整个文档直接存储并传递到提示中。这样做很可能会导致错误,因为它超过了最大标记数。
为了解决这个问题,我们将首先从文档中提取不同的元素——图像、文本和表格。为此任务,我们将使用 Unstructured 库。
安装
首先让我们安装这个包(如果尚未通过 pip 安装的话)
#%brew install tesseract poppler
%pip install -q "unstructured[all-docs]"
请注意,我们还需要系统中的“tesseract”和“poppler”库,以便 unstructured 库能够处理文本提取以及从图像中提取文本。您可以使用 homebrew 安装这两个包(请参见注释行)。
分区和块划分
from unstructured.partition.pdf import partition_pdf
elements = partition_pdf(
filename="TAGIV.pdf", # mandatory
strategy="hi_res", # mandatory to use ``hi_res`` strategy
extract_images_in_pdf=True, # mandatory to set as ``True``
extract_image_block_types=["Image", "Table"], # optional
extract_image_block_to_payload=False, # optional
extract_image_block_output_dir="saved_images", # optional - only works when ``extract_image_block_to_payload=False``
)
我们将使用 partition_pdf
模块对文档进行分区,并从我们的文件‘TAGIV.pdf’中提取不同的元素。我们将设置 hi_res
策略以提取高质量的图像和表格。可选参数 extract_image_block_types
和 extract_image_block_output_dir
指定仅提取图像和表格,并将其保存到名为 “saved_images” 的目录中。
我们将使用 chunk_by_title
方法对元素进行块划分,该方法用于根据“标题或标题”将提取的元素划分为块。这适用于通常由不同部分和子部分组成的研究文章,例如引言、方法、结果等。
from unstructured.chunking.title import chunk_by_title # might be better for an article
from typing import Any
chunks = chunk_by_title(elements)
# different category in the document
category_counts = {}
for element in chunks:
category = str(type(element))
if category in category_counts:
category_counts[category] += 1
else:
category_counts[category] = 1
# Unique_categories will have unique elements
unique_categories = set(category_counts.keys())
category_counts
{"<class 'unstructured.documents.elements.CompositeElement'>": 200,
"<class 'unstructured.documents.elements.Table'>": 3,
"<class 'unstructured.documents.elements.TableChunk'>": 2}
块划分显示有三个独特的类别:
-
CompositeElements
-
Table
-
TableChunk
‘CompositeElements’ 是不同文本的集合,可能是段落、部分、页脚、公式等。还有三个 ‘Table’ 结构,以及两个 ‘TableChunk’,通常表示表格的一部分或片段。因此,可能一个表格跨页分割,只有一部分被划分。
_我在文档中确实有四个表格,但只有三个被完全解析。_🤔
过滤
接下来,我们将简化文档元素,以便分别处理文本和表格数据以进行进一步处理。为此,我们将定义一个 Pydantic 模型,以标准化文档元素,并根据其类型将其分类为“text”或“table”。
from pydantic import BaseModel
class Element(BaseModel):
type: str
text: Any
# 按类型分类
categorized_elements = []
for element in chunks:
if "unstructured.documents.elements.CompositeElement" in str(type(element)):
categorized_elements.append(Element(type="text", text=str(element)))
elif "unstructured.documents.elements.Table" in str(type(element)):
categorized_elements.append(Element(type="table", text=str(element)))
# 文本
text_elements = [e for e in categorized_elements if e.type == "text"]
# 表格
table_elements = [e for e in categorized_elements if e.type == "table"]
我们将遍历文档元素的块,识别每个元素的类型,并将其附加到分类列表中。最后,我们将此列表过滤为文本和表格元素的单独列表。至此,预处理步骤已完成。
文本、表格和图像摘要
为了为后面使用多向量检索器做准备,我们需要为文本、表格和图像元素创建摘要。这些摘要将存储在向量存储中,以便在我们将输入查询传递到提示中时实现语义搜索。
文本和表格摘要
让我们开始文本和表格摘要。首先,我们将设置一个提示模板,指示AI充当专家研究助理,负责总结表格和文本。接下来,我们将创建一个链,处理每个文本和表格元素,通过这个提示和GPT-4o模型,生成简洁的摘要。
为了提高效率,我们将同时批量处理五个文本或表格元素,使用max_concurrency
参数。
%pip install -q langchain langchain-chroma unstructured[all-docs] pydantic lxml langchainhub langchain-openai
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
## 检索器
# 提示
prompt_text = """You are an expert Research Assistant tasked with summarizing tables and texts from research articles. \
Give a concise summary of the text. text chunk: {element} """
prompt = ChatPromptTemplate.from_template(prompt_text)
# 摘要链
model = ChatOpenAI(temperature=0, model="gpt-4o")
summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser()
# 应用于文本
texts = [i.text for i in text_elements]
text_summaries = summarize_chain.batch(texts, {"max_concurrency": 5})
# 应用于表格
tables = [i.text for i in table_elements]
table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5})
图像摘要
接下来,我们将设置一些函数来帮助我们总结图像。我们将定义三个关键函数:encode_image
、image_summarize
和 generate_img_summaries
。
-
encode_image:此函数以二进制读取模式(‘rb’)打开图像文件,并返回其 base64 编码的字符串表示。
-
image_summarize:此函数使用一个包含提示的 HumanMessage 对象,指示模型如何总结图像。它还包括 base64 编码的图像数据,格式为数据 URL,以便直接在内容中嵌入图像。
-
generate_img_summaries:此函数处理给定目录中的所有 JPG 图像,为每个图像生成摘要,并返回 base64 编码的图像。
这些函数将使我们能够高效地总结和处理图像,将其无缝集成到我们的多模态 RAG 应用中。
以下是完整代码:
## getting image summaries
import base64
import os
from langchain_core.messages import HumanMessage
def encode_image(image_path):