摘要
本文探讨了信息检索与文本生成领域的最新进展,特别关注了OpenAI的RAG模型及其在文本内容搜索上的应用。文章详细介绍了gpt-4-vision-preview模型,这一模型标志着从结构化搜索向非结构化搜索的重大转变,能够有效处理和解释多模态信息,如图片、表格和文本。通过实际案例分析,文章展示了如何利用这些技术进行企业文档管理、学术研究和媒体内容分析,为读者提供了关于如何运用这些先进技术进行多模态数据处理的深入见解。
开篇
在人工智能的领域内,信息检索与文本生成一直是两个重要的研究方向。OpenAI的RAG(Retrieval-Augmented Generation)模型,作为这一领域的突破性成果,成功地将神经网络的文本生成能力与大规模数据集的检索功能结合起来。这一创新不仅提升了文本生成模型的准确性和信息丰富度,而且解决了传统模型在应对复杂查询时的局限性。
RAG模型的主流应用体现在文本内容的搜索上,例如企业知识库的检索。通过文本加载、切割、嵌入、索引等方法建立输入与目标的相关性,最终将搜索结果呈现给用户。然而,随着信息类型的多样化,传统的文本搜索已经不能满足所有的需求。
此时,OpenAI推出了gpt-4-vision-preview模型,这不仅是技术上的一大跃进,更标志着从结构化搜索走向非结构化搜索的重要转变。该模型具备处理和解释多模态信息的能力,无论是图片、表格还是文本,都能够被有效地摘要和搜索。这一进步极大地扩展了RAG功能的外延,为多模态数据处理开辟了新的道路。
例如,在企业文档管理方面,gpt-4-vision-preview可以分析含有图表和文本的PDF格式文档,如合同和报告,提供精准的摘要和关键信息提取。在学术研究领域,这一技术能自动整理和分析学术论文中的数据和图像,极大提升研究效率。而在媒体内容分析上,新闻报道中的图片与文本内容可以被整合分析,为媒体从业者提供更深入的洞察。
今天我会通过这篇文章,手把手带大家如何实现对一个多模态PDF的分析和搜索。
场景分析
那么今天的主角就要登场了,如下图所示,这个PDF文件是一个典型的财经市场分析报告,包含了丰富的文字、图表和数据。
财经市场分析报告
报告不仅包含了详细的文本描述,如市场趋势的分析和预测,还包括了大量的图表和数据,如股票市场的指数、固定收益产品的收益率和关键利率等。这些图表和数据在理解整个市场情况中起着至关重要的作用。
然而,人工处理这类多模态的PDF文件常常是耗时且劳力密集的。分析师需要仔细阅读文本,解读图表和数据,并将这些信息综合起来以形成完整的市场观点。这个过程不仅耗时,还容易出错,特别是在处理大量复杂数据时。
传统的RAG模型在处理这类多模态PDF文件时显得力不从心。尽管RAG在处理和生成基于文本的信息方面表现出色,但它主要针对文字内容进行搜索和生成,对于非文本元素,如图表和数据,其处理能力有限。这就意味着,在使用RAG模型进行信息检索时,对于包含非文本元素的复杂PDF文件,它可能无法充分理解和利用文件中的所有信息。
因此,针对这种业务场景就需要使用OpenAI推出的多模态的处理方式,利用GPT-4-Vision-Preview模型处理文本、图表和数据,以提供更加全面和准确的分析。从而提高分析效率,还能减少由于人工处理的错误而造成的风险。
技术分析
在多模态PDF文档处理中,首要挑战是识别文档中的非结构化信息,如图片、表格和文字。这里我们需要使用unstructured库,它提供了用于摄取和预处理图像和文本文档的开源组件,如PDF、HTML、Word文档等。它的主要用途是简化和优化大型语言模型(LLMs)的数据处理工作流程。unstructured的模块化功能和连接器形成了一个简化数据摄取和预处理的一致系统,使其适应不同平台,有效地将非结构化数据转换为结构化输出。
除此之外,为了更好处理PDF文档,我们还引入了poppler-utils工具, 它被用来提取图片和文本。特别是其中的pdfimages和pdftotext工具,分别用于从PDF中提取嵌入的图像和全部文本,这对于多模态PDF的分析至关重要。
备注:Poppler是一个PDF文档渲染库,可以使用两种后端进行绘制,分别是Cairo和Splash。这两种后端的特性有所不同,Poppler的功能也可能依赖于它使用的后端。此外,还有一个基于Qt4的绘图框架“Arthur”的后端,但这个后端不完整且已停止开发。Poppler为Glib和Qt5提供绑定,这些绑定提供了对Poppler后端的接口,尽管Qt5绑定只支持Splash和Arthur后端。Cairo后端支持矢量图形的抗锯齿和透明对象,但不支持平滑位图图像如扫描文档,并且不依赖于X Window系统,因此Poppler可以在Wayland、Windows或macOS等平台上运行。Splash后端支持位图的缩小过滤。Poppler还附带一个文本渲染后端,可通过命令行工具pdftotext调用,用于在命令行中搜索PDF中的字符串,例如使用grep工具。
解决了图片、表格和问题的提取问题之后,在将图片和表格内容转换为可分析的文本格式方面,Tesseract-OCR工具发挥了关键作用。尽管Tesseract不能直接处理PDF文件,但它可以将转换为.tiff格式的PDF文件中的图像转换为文本。这个过程是处理多模态PDF的关键环节,使得原本以图像或表格形式存在的信息转化为可供进一步分析的文本形式。
当然除了上面提到的识别和分离PDF元素的技术之外,例如:向量存储、向量索引器等技术也会用到,由于在传统的RAG 中经常用到,这里就不赘述了。
代码实现
经过场景和技术分析之后,我们了解到如果需要对PDF进行多模态的分析和查询,首先是要对其中的图片、表格、文字进行识别。这里我们会引入unstructured 、Poppler库以及Tesseract-OCR工具。有了这些工具的加持让我们的编码过程更加如虎添翼。
安装库和工具
!pip install langchain unstructured[all-docs] pydantic lxml openai chromadb tiktoken -q -U !apt-get install poppler-utils tesseract-ocr1.2.
上面的库有几个之前没有提到的,这里做一下说明。
pydantic:用于数据验证和设置管理。
lxml: XML和HTML处理库。
openai:与OpenAI的API进行交互。
chromadb:用于向量数据库管理或数据存储。
提取PDF信息
既然工具都准备好了,接下来就是处理PDF 的内容了,代码如下:
from typing import Any from pydantic import BaseModel from unstructured.partition.pdf import partition_pdf # 设置存放图像的路径 images_path = "./images" # 调用 partition_pdf 函数处理PDF文件,提取其中的元素 raw_pdf_elements = partition_pdf( filename="weekly-market-recap.pdf", # 指定要处理的PDF文件名 extract_images_in_pdf=True, # 设置为True以从PDF中提取图像 infer_table_structure=True, # 设置为True以推断PDF中的表格结构 chunking_strategy="by_title", # 设置文档切分策略为按标题切分 max_characters=4000, # 设置每个块的最大字符数为4000 new_after_n_chars=3800, # 在达到3800字符后开始新的块 combine_text_under_n_chars=2000, # 将少于2000字符的文本组合在一起 image_output_dir_path=images_path, # 指定输出图像的目录路径 )1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.
这段Python代码使用了pydantic和unstructured库来处理PDF文件。下面是代码的逐行解释:
images_path = "./images":设置一个变量images_path,用于存放从PDF中提取的图像。
接下来调用partition_pdf函数,这个函数用于处理PDF文件并提取其中的元素:
filename="weekly-market-recap.pdf":指定要处理的PDF文件名。
extract_images_in_pdf=True:设置为True以从PDF中提取图像。
infer_table_structure=True:设置为True以推断PDF中的表格结构。
chunking_strategy="by_title":设置文档切分策略为按标题切分。
max_characters=4000:设置每个块的最大字符数为4000。
new_after_n_chars=3800:在达到3800字符后开始新的块。
combine_text_under_n_chars=2000:将少于2000字符的文本组合在一起。
image_output_dir_path=images_path:指定输出图像的目录路径。
可以通过命令获取PDF图片的信息,由于通过上面代码将图片保存到images目录下面了,所以通过如下代码展示图片。
from IPython.display import Image Image('images/figure-1-1.jpg')1.2.
从PDF 中抽取的图片信息
为图片信息产生摘要
下面这段代码定义了一个名为ImageSummarizer的类,用于生成图像内容的摘要信息。
# 引入所需的库 import base64 import os # 从langchain包导入ChatOpenAI类和HumanMessage模块 from langchain.chat_models import ChatOpenAI from langchain.schema.messages import HumanMessage # 定义图像摘要类 class ImageSummarizer: # 初始化函数,设置图像路径 def __init__(self, image_path) -> None: self.image_path = image_path self.prompt = """你的任务是将图像内容生成摘要信息,以便检索。 这些摘要将被嵌入并用于检索原始图像。请给出一个简洁的图像摘要,以便于检索优化。 """ # 定义函数将图像转换为base64编码 def base64_encode_image(self): with open(self.image_path, "rb") as image_file: return base64.b64encode(image_file.read()).decode("utf-8") # 定义摘要函数 def summarize(self, prompt = None): # 获取图像的base64编码数据 base64_image_data = self.base64_encode_image() # 创建ChatOpenAI对象,使用gpt-4-vision预览模型,最大token数为1000 chat = ChatOpenAI(model="gpt-4-vision-preview", max_tokens=1000) # 调用chat对象的invoke方法,发送包含文本和图像的消息 response = chat.invoke( [ #人类提示语的输入 HumanMessage( content=[ { "type": "text", "text": prompt if prompt else self.prompt }, { "type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image_data}"}, }, ] ) ] ) # 返回base64编码的图像数据和响应内容 return base64_image_data, response.content1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.
以下是代码的详细解释:
1.导入库
导入base64和os库,用于图像编码和操作系统相关功能。
从Langchain包中导入ChatOpenAI类和HumanMessage模块,这些用于与OpenAI的聊天模型交互。
2.定义图像摘要类 ImageSummarizer
__init__方法初始化图像路径和摘要生成的提示语。
base64_encode_image方法将图像文件读取并转换为base64编码,这种编码格式适用于在网络上传输图像,便于图片信息与GPT模型交互。
3. 定义摘要函数 summarize
使用base64_encode_image方法获取图像的base64编码数据。
创建ChatOpenAI对象,指定使用gpt-4-vision-preview模型,最大token数设为1000,用于处理图像摘要任务。
使用chat.invoke方法,向模型发送包含文本提示和图像数据的消息。文本提示用于指导模型生成图像的摘要。
创建summarize函数的目的就是为了给图片生成摘要,接下来就是调用该函数。下面这段代码创建图像数据和图像摘要的列表,并遍历指定路径下的所有文件以生成这些信息。
# 创建图像数据和图像摘要的列表 image_data_list = [] image_summary_list = [] # 遍历指定路径下的所有文件 for img_file in sorted(os.listdir(images_path)): # 检查文件扩展名是否为.jpg if img_file.endswith(".jpg"): # 创建ImageSummarizer对象 summarizer = ImageSummarizer(os.path.join(images_path, img_file)) # 调用summarize方法生成摘要 data, summary = summarizer.summarize() # 将base64编码的图像数据添加到列表 image_data_list.append(data) # 将图像摘要添加到列表 image_summary_list.append(summary)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.
代码解释:
1. 初始化列表
image_data_list和image_summary_list两个列表被创建用于存储图像的base64编码数据和对应的图像摘要。
2.遍历指定路径下的所有文件
使用os.listdir(images_path)列出指定路径(images_path)下的所有文件,并使用sorted函数对文件名进行排序。
3. 检查和处理每个文件
通过if img_file.endswith(".jpg")检查文件扩展名是否为.jpg,以确保只处理图像文件。
对于每个.jpg文件,创建ImageSummarizer对象,传入图像的完整路径。
调用summarize方法生成该图像的摘要。
将返回的base64编码图像数据添加到image_data_list列表。
将返回的图像摘要添加到image_summary_list列表。
最终会生成两个列表:一个包含了图像文件的base64编码数据,另一个包含了相应的图像摘要,这些信息可以用于图像检索或其他处理。
我们通过如下代码来查看图片的摘要信息
image_summary_list1.
输出结果如下:
['这个图像包含了三个不同的图表,提供了有关假日销售增长、各行业一周和年迄今(YTD)的表现对比的数据。\n\n第一个图表是一个柱状图,标题为“Holiday sales growth is set to return to its pre-pandemic pace”,展示了2010年至2023年间11月和12月销售同比增长的实际数据和预测数据。蓝色柱子代表实际数据,条纹柱子代表预测数据,蓝色虚线代表2010-2019年平均增长率,绿色虚线代表2020-2022年平均增长率。\n\n第二个图表是一个色块图,分为两部分,展示了“V”, “B”, 和 “G”在不同的尺度(大、中、小)下的1周和年迄今(YTD)的表现。数值在色块内以白色文字显示。\n\n第三个图表是两个并列的柱状图,分别显示了不同行业在1周和年迄今(YTD)的表现。左侧的蓝色柱状图展示了一周的变化,右侧的绿色柱状图展示了年迄今的变化。图表的Y轴代表了百分比变化,X轴列出了不同的行业。\n\n摘要:三个图表显示假日销售增长预计将恢复至疫情前水平,不同市场尺度(大、中、小)的1周和年迄今表现,以及不同行业的1周和年迄今表现数据。']
从结果可以看出GPT-4-Vision-Preview 模型对图片是能够识别,并且对一些细节也能够把控。
识别表格和文本信息
图片、表格和文本信息组成了PDF的完整内容, 在识别了图片信息之后,就需要对表格和文本信息下手了。
下面这段代码定义了处理PDF文档元素的过程。
# 定义一个基本模型Element,用于存储文档元素的类型和文本内容 class Element(BaseModel): type: str text: Any # 创建表格元素和文本元素的空列表 #表格信息 table_elements = [] #文本信息 text_elements = [] # 遍历从PDF中分割出的所有元素 for element in raw_pdf_elements: # 判断元素是否为表格:非结构化-文档-元素-表格 if "unstructured.documents.elements.Table" in str(type(element)): # 如果是表格,将其添加到表格元素列表 table_elements.append(Element(type="table", text=str(element))) # 判断元素是否为复合元素 elif "unstructured.documents.elements.CompositeElement" in str(type(element)): # 如果是复合元素,将其添加到文本元素列表 text_elements.append(Element(type="text", text=str(element)))1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.
代码解释如下:
1.定义Element基本模型
使用pydantic的BaseModel定义了一个名为Element的类,用于存储文档元素的类型(如表格或文本)和文本内容。这个类使用类型注解str和Any定义了两个属性:type(元素类型)和text(元素内容)。
2. 创建空列表以存储表格和文本元素
table_elements和text_elements分别用于存储表格信息和文本信息。
3. 遍历PDF中的元素
对于从PDF文件中分割出的每个元素(在raw_pdf_elements中),代码通过判断元素的类型来分类处理:
如果元素是表格(unstructured.documents.elements.Table),则创建Element对象,并将其类型设为"table",文本内容设为元素的字符串表示,然后将此对象添加到table_elements列表。
如果元素是复合元素(unstructured.documents.elements.CompositeElement),则以相似的方式处理,将其类型设为"text",并添加到text_elements列表。
在对PDF文档中的不同元素进行分类和存储,接下来就需要对其进行处理,对这些表格和文字进行总结。代码如下:
# 导入所需模块 from langchain.chat_models import ChatOpenAI from langchain.prompts import ChatPromptTemplate from langchain.schema.output_parser import StrOutputParser # 定义用于生成总结的提示文本 prompt_text = """ 您负责简洁地总结表格或文本块,中文输出。: {element} """ # 从模板创建提示 prompt = ChatPromptTemplate.from_template(prompt_text) # 创建总结链,结合提示和GPT-3.5模型 summarize_chain = {"element": lambda x: x} | prompt | ChatOpenAI(temperature=0, model="gpt-3.5-turbo") | StrOutputParser() # 从表格元素中提取文本 tables = [i.text for i in table_elements] # 使用总结链批量处理表格文本,设置最大并发数为5 table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5}) # 从文本元素中提取文本 texts = [i.text for i in text_elements] # 使用总结链批量处理文本,设置最大并发数为5 text_summaries = summarize_chain.batch(texts, {"max_concurrency": 5})1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.
这段代码用于生成表格和文本块的总结,利用LangChain和GPT-3.5模型:
1.导入模块
导入LangChain的ChatOpenAI、ChatPromptTemplate和StrOutputParser模块。
2.定义生成总结的提示文本
定义了一个用于指导模型总结表格或文本块的提示文本模板。
3.创建提示和总结链
使用ChatPromptTemplate.from_template根据定义的提示文本创建提示。
创建一个名为summarize_chain的总结链,该链结合了提示、GPT-3.5模型(由于生成总结的能力不需要用到GPT-4,为了节省Token使用GPT-3.5),和字符串输出解析器(StrOutputParser)。
4.处理表格和文本元素
从table_elements中提取表格文本,使用summarize_chain.batch方法批量处理这些文本,最大并发数设为5。
从text_elements中提取文本块文本,同样使用summarize_chain.batch批量处理,最大并发数设为5。
我们将总结的内容打印如下:
表格和文字摘要信息
创建索引器
好了,目前为止我们将PDF中的图片、表格和文本信息都逐一抽取出来了,并针对这些信息生成了摘要,其目的是方便后续搜索,在做好这一系列准备之后就需要安排上索引器了。索引器顾名思义就是对被搜索信息添加上索引,方便搜索之用。
下面这段代码创建了一个多向量检索器,用于存储和检索文本、表格和图像数据:
import uuid # 导入uuid库,用于生成唯一标识符 # 导入所需的langchain模块 from langchain.embeddings import OpenAIEmbeddings from langchain.retrievers.multi_vector import MultiVectorRetriever from langchain.schema.document import Document from langchain.storage import InMemoryStore from langchain.vectorstores import Chroma id_key = "doc_id" # 设置文档的唯一标识键 # 初始化多向量检索器,初始为空,文字,图片,表格 retriever = MultiVectorRetriever( vectorstore=Chroma(collection_name="summaries", embedding_function=OpenAIEmbeddings()), # 使用OpenAI的嵌入方法 docstore=InMemoryStore(), # 使用内存存储文档 id_key=id_key, # 设置文档的唯一标识键 ) # 为文本添加唯一标识 doc_ids = [str(uuid.uuid4()) for _ in texts] # 为每个文本生成一个唯一的UUID # 创建文本的文档对象并添加到检索器 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))) # 将文本文档的ID和内容存储在内存存储中 # 为表格添加唯一标识 table_ids = [str(uuid.uuid4()) for _ in tables] # 为每个表格生成一个唯一的UUID # 创建表格的文档对象并添加到检索器 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))) # 将表格文档的ID和内容存储在内存存储中 # 为图像添加唯一标识 doc_ids = [str(uuid.uuid4()) for _ in image_data_list] # 为每个图像生成一个唯一的UUID # 创建图像的文档对象并添加到检索器 summary_images = [ Document(page_content=s, metadata={id_key: doc_ids[i]}) # 将图像封装为文档对象 for i, s in enumerate(image_summary_list) ] retriever.vectorstore.add_documents(summary_images) # 将图像文档添加到向量存储中 retriever.docstore.mset(list(zip(doc_ids, image_data_list))) # 将图像文档的ID和内容存储在内存存储中1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.
代码解释:
1. 初始化多向量检索器
使用Chroma作为向量存储,它利用OpenAI嵌入方法将文档转换为向量形式。
使用InMemoryStore作为文档存储,这是一种将文档存储在内存中的方法。
设置id_key为文档的唯一标识键。
2. 处理文本数据
为每个文本创建唯一标识符(UUID)。
将文本总结封装为Document对象,并添加到向量存储和文档存储中。
3. 处理表格数据
类似地,为每个表格创建UUID。
将表格总结封装为Document对象,并添加到向量存储和文档存储中。
4. 处理图像数据
为每个图像创建UUID。
将图像总结封装为Document对象,并添加到向量存储和文档存储中。
这段代码建立了一个系统,可以存储和检索多种类型的文档数据,包括文本、表格和图像,利用唯一标识符来管理每个文档。这对于处理和组织大量复杂数据非常有用。
生成多模态查询提示语
下面这段代码包含多个函数,用来处理和展示图像与文本数据。
from PIL import Image from IPython.display import HTML, display import io import re # 显示基于base64编码的图片 def plt_img_base64(img_base64): display(HTML(f'')) # 检查base64数据是否为图片 def is_image_data(b64data): """ 通过查看数据开头来检查base64数据是否为图片 """ image_signatures = { b"\xFF\xD8\xFF": "jpg", b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A": "png", b"\x47\x49\x46\x38": "gif", b"\x52\x49\x46\x46": "webp", } try: header = base64.b64decode(b64data)[:8] # 解码并获取前8个字节 for sig, format in image_signatures.items(): if header.startswith(sig): return True return False except Exception: return False # 分离base64编码的图片和文本 def split_image_text_types(docs): """ 分离base64编码的图片和文本 """ b64_images = [] texts = [] for doc in docs: # 检查文档是否为Document类型并提取page_content if isinstance(doc, Document): doc = doc.page_content #是图片 if is_image_data(doc): b64_images.append(doc) else: texts.append(doc) return {"images": b64_images, "texts": texts} # 根据数据生成提示信息 def img_prompt_func(data_dict): messages = [] # 如果存在图片,则添加到消息中 #图片的信息单独拿出来, 需要和提示语一起传给大模型的。 if data_dict["context"]["images"]: for image in data_dict["context"]["images"]: image_message = { "type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image}"}, } messages.append(image_message) # 添加文本到消息 formatted_texts = "\n".join(data_dict["context"]["texts"]) text_message = { "type": "text", "text": ( "You are financial analyst.\n" "You will be given a mixed of text, tables, and image(s) usually of charts or graphs.\n" "Use this information to answer the user question in the finance. \n" f"Question: {data_dict['question']}\n\n" "Text and / or tables:\n" f"{formatted_texts}" ), } messages.append(text_message) print(messages) return [HumanMessage(content=messages)]1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.
代码解释:
1. 显示基于base64编码的图片 (plt_img_base64)
这个函数的目的是显示base64编码的图片。
2. 检查base64数据是否为图片 (is_image_data)
通过检查base64编码数据的开头是否符合常见图像格式(如JPEG, PNG, GIF, WEBP)的签名来判断数据是否为图片。
3. 分离base64编码的图片和文本 (split_image_text_types)
此函数遍历文档集合,使用is_image_data函数来区分哪些是图像数据,哪些是文本,然后将它们分别存储在两个列表中。
4. 根据数据生成提示信息 (img_prompt_func)
此函数生成用于向LangChain的大模型传递的消息。对于每个图像,创建包含图像URL的消息;对于文本数据,创建包含提示文本和问题的消息。
需要对"text"进行特别说明,它定义消息的具体文本内容。
首先说明了用户角色:"You are financial analyst."(你是一名财务分析师)。
紧接着说明任务类型和数据格式:"You will be given a mixed of text, tables, and image(s) usually of charts or graphs."(你将会得到包含文本、表格和图像(通常是图表或图形)的混合数据)。
提示使用这些信息回答财务问题:"Use this information to answer the user question in the finance."(使用这些信息来回答财务方面的用户问题)。
{data_dict['question']}:这里{}内的部分是Python字符串格式化的占位符,用于插入变量data_dict中的'question'键所对应的值,即用户提出的问题。
"Text and / or tables:\n":提示文本,说明接下来的内容是文本和/或表格。
f"{formatted_texts}":再次使用字符串格式化将变量formatted_texts(格式化后的文本数据)插入到消息中。
实现金融分析查询
万事俱备只欠东风,现在开始利用GPT-4-Vision-Preview模型进行推理了, 我们尝试对索引器提问,看看结果如何。
下面这段代码实现了一个基于RAG(Retrieval Augmented Generation)模型的查询处理管道。
from langchain.schema.runnable import RunnableLambda, RunnablePassthrough # 创建ChatOpenAI模型实例 model = ChatOpenAI(temperature=0, model="gpt-4-vision-preview", max_tokens=1024) # 构建RAG(Retrieval Augmented Generation)管道 chain = ( { "context": retriever | RunnableLambda(split_image_text_types), # 使用检索器获取内容并分离图片和文本 "question": RunnablePassthrough(), # 传递问题 } | RunnableLambda(img_prompt_func) # 根据图片和文本生成提示信息 | model # 使用ChatOpenAI模型生成回答 | StrOutputParser() # 解析模型的字符串输出 ) # 定义查询问题 query = "Which year had the highest holiday sales growth?" # 调用chain的invoke方法执行查询 chain.invoke(query)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.
代码解释:
1.创建ChatOpenAI模型实例
使用ChatOpenAI类创建模型实例,配置了温度参数为0、使用的模型为gpt-4-vision-preview,最大token数设为1024。
2.构建RAG管道 (chain)
retriever | RunnableLambda(split_image_text_types):首先使用retriever检索内容,然后通过RunnableLambda调用split_image_text_types函数分离图片和文本。
RunnablePassthrough():用于直接传递查询问题。
RunnableLambda(img_prompt_func):根据检索到的图片和文本生成提示信息。
model:使用配置好的ChatOpenAI模型根据生成的提示信息生成回答。
StrOutputParser():解析模型输出的字符串。
3. 定义查询问题 (query)
定义了一个字符串query作为查询问题,例如:“Which year had the highest holiday sales growth?”(哪一年的假日销售增长最高?)。
4. 执行查询 (chain.invoke(query))
调用chain的invoke方法执行查询,根据问题和检索到的信息生成答案。
总的来说,这个代码片段建立了一个复杂的查询处理流程,结合了内容检索、数据分离、提示生成和模型回答,用于处理和回答基于文本和图像数据的复杂问题。
来查看输出的结果:
Based on the chart provided, the year with the highest holiday sales growth is 2021, with a growth rate of 12.7%. This is indicated by the tallest bar in the bar chart under the "Actual" section, which represents the actual year-over-year growth rate for November and December sales.
我们将其翻译成中文如下:
根据提供的图表,2021年的假日销售增长率最高,为12.7%。这一点从“实心”部分的柱状图中最高的柱子可以看出,该柱子代表了11月和12月的年度同比增长率。
PDF中图表部分与大模型回应对比
通过大模型的回应,再对比PDF中描述假日销售增长率的柱状图,可以明显发现2021年的增长率是最高的,为12.7%,并且对于柱子“实心”的描述也是正确的。看来OpenAI提供的多模态功能起到了作用。
总结
文章通过分析和实例演示,清晰地展示了OpenAI的最新技术在多模态数据处理方面的潜力和应用前景。通过对unstructured库、Poppler工具和Tesseract-OCR工具的细致介绍和代码实现的解读,文章不仅为读者提供了理论知识,也指导了如何实际操作和应用这些工具。文章的结论强调了gpt-4-vision-preview模型在提高分析效率和减少人工错误方面的价值。
作者介绍
崔皓,51CTO社区编辑,资深架构师,拥有18年的软件开发和架构经验,10年分布式架构经验。
还没有评论,来说两句吧...