轻松解析本地PDF表格,基于LlamaIndex和UnstructuredIO打造RAG
1 使用 LlamaIndex 和 UnstructuredIO 检索数据
在数据检索领域,LlamaIndex 以其强大的工具和技术,为用户带来了全新的检索体验。这个框架的亮点在于索引系统的灵活性,用户可以根据文档的具体内容,量身定制索引策略,以适应不同的文档结构。每种索引都设计得独具匠心,能够精准匹配各种文档结构,确保信息检索的准确性和高效性。
对于包含大量表格的 PDF 文件,建议使用 LlamaIndex 推荐的 RecursiveRetriever。这种递归检索技术的精髓在于,它不仅深入挖掘与信息直接相关的节点,还会追溯这些节点与其它检索器或查询引擎之间的关联,进而执行相应的检索操作。
例如,某个节点精炼地总结了某个结构化表格的关键信息,并链接到该表格的 SQL 或 Pandas 查询引擎。那么在检索到这个节点之后,我们就能够利用这些底层的查询工具深入挖掘,从而获取更详尽的数据。这种深入的检索方法,大大增强了我们从复杂数据集中提取有价值信息的能力。
为了有效实施这一策略,分步骤进行:
a. 首先,将 PDF 文件转换成 HTML 格式,这一步已经完成。
b. 接着,利用 UnstructuredIO 读取转换后的 HTML 文件。
c. 对于 UnstructuredIO 从 HTML 中识别出的每个元素,无论是文本还是表格,都将其存储到 LlamaIndex 的节点中。
d. 这样一来,就构建了一个包含文本和表格的节点列表。
e. (可选步骤)可以专门筛选出包含表格的节点,并将这些表格发送到语言模型(LLM)以生成摘要。
f. 然后,借助 LlamaIndex,LLM 代理将递归地检索与问题相关的信息。
g. 最后,将这些检索到的数据发送回 LLM,以生成最终的响应。
虽然这个过程听起来颇为复杂,但得益于 LlamaIndex 提供的封装良好的函数,我们执行这些步骤更加容易些。
1.1 读取和处理数据
from llama_index.readers.file.flat_reader import FlatReader
from llama_index.node_parser import UnstructuredElementNodeParser
import os
import pickle
from pathlib import Path
os.environ["OPENAI_API_KEY"] = "<your openai api key>"
# 读取数据
reader = FlatReader()
data = reader.load_data(Path('./The_Worlds_Billionaires.html'))
# 初始化 NodeParser
node_parser = UnstructuredElementNodeParser()
# 如果稍后想重用它
if not os.path.exists("qr_2023_nodes.pkl"):
raw_nodes = node_parser.get_nodes_from_documents(data)
pickle.dump(raw_nodes, open("the_world_billionaires_raw_nodes.pkl", "wb"))
# 基础节点和节点映射
base_nodes, node_mappings = node_parser.get_base_nodes_and_mappings(
raw_nodes
)
1.2 构建索引
from llama_index.retrievers import RecursiveRetriever
from llama_index.query_engine import RetrieverQueryEngine
from llama_index import VectorStoreIndex
vector_index = VectorStoreIndex(base_nodes_qr_2023)
vector_retriever = vector_index.as_retriever(similarity_top_k=3)
vector_query_engine = vector_index.as_query_engine(similarity_top_k=3)
recursive_retriever = RecursiveRetriever(
"vector",
retriever_dict={"vector": vector_retriever},
node_dict=node_mappings_qr_2023,
)
query_engine = RetrieverQueryEngine.from_args(recursive_retriever)
query_engine.query("Who is the richest billionaire in 2020?")
1.3 其他类型的查询索引
前面的例子已经展示了 UnstructuredElementNodeParser 如何无缝集成到 LlamaIndex + UnstructuredIO 的数据处理流程中,体现了其在提升数据处理效率和便捷性方面的强大能力。它采用了一种简化的方法论,让原本复杂的数据提取工作变得更加易于掌握。
鉴于 LlamaIndex 提供了多种索引类型和检索技术,探索不同的选项以找到最适合你特定场景的解决方案是非常有价值的。不妨尝试包括自动合并检索器、结果重排序以及混合搜索在内的多种策略。
每种策略都有其独到之处,而最终的效果也会随着数据的复杂性而有所不同。通过实际测试和评估,你可以优化检索流程,确保采用最合适的方法从数据集中提取关键信息。
2 如何从 PDF/HTML 中提取表格
这部分内容提供了一个可选的功能,它通过较低级别的 API 支持从 PDF 或 HTML 中提取表格,这可能对特定需求非常有用。虽然前面提到的方法在大多数情况下已经足够有效,但如果需要更精细的控制,比如直接操作底层数据,那么可能需要在数据处理流程中加入额外的步骤,例如利用语言模型(LLM)来生成数据摘要。这一环节值得你进一步探索。
2.1 从 PDF 中提取表格
完成从 PDF 中提取表格的任务,可以依赖多种光学字符识别(OCR)技术和库,同时也可以考虑使用云服务,但这涉及较高的成本。UnstructuredIO 提供了一个功能强大的 partition_pdf
方法,它通过多个参数让你能够灵活地在处理速度和识别准确性之间做出权衡,并且可以指定特定的深度学习模型来优化表格的提取效果。
from unstructured.partition.pdf import partition_pdf
from unstructured.staging.base import elements_to_json
import json
file_path = 'The_Worlds_Billionaires.pdf'
raw_pdf_elements = partition_pdf(
filename=file_path,
extract_images_in_pdf=False,
infer_table_structure=True,
chunking_strategy='by_title',
max_characters=4000,
new_after_n_chars=3800,
combine_text_under_n_chars=2000,
strategy = "hi_res"
)
# 将结果存储在 json 中
elements_to_json(raw_pdf_elements, filename=f"./The_Worlds_Billionaires_Converted.json")
no_tables = 0
def process_json_file(input_filename):
# 读取 JSON 文件
with open(f'./{input_filename}.json', 'r') as file:
data = json.load(file)
# 遍历 JSON 数据并提取所需的表格元素
extracted_elements = []
for entry in data:
if entry['type'] == 'CompositeElement':
extracted_elements.append(entry['text'])
if entry["type"] == "Table":
no_tables += 1
extracted_elements.append(entry["metadata"]["text_as_html"])
# 将提取的元素写入输出文件
with open(f"{input_filename}.txt", 'w') as output_file:
for element in extracted_elements:
output_file.write(element + "\n\n") # 添加两个换行符以分隔
process_json_file(f"The_Worlds_Billionaires_Converted") # ## with new_file_name 是上面的 JSON 文件
print(f"Number of tables: {no_tables}")
### 加载数据
```python
# documents = SimpleDirectoryReader("./<folder_name>",
# input_files=['./<new_file_name.txt>']).load_data()
该方法能够读取 PDF 文件,并提取出其中的元素,如文本和表格。表格元素会以 JSON 格式保存为“text_as_html”。你可以逐一读取和处理 JSON 文件中的每个元素,并将处理后的数据存储为 TXT 文件,以便后续的 RAG 读取。
需要注意的是,直接解析 PDF 的性能可能不尽如人意。目前,UnstructuredIO 提供了多种模型,例如 YOLOx,来帮助将 PDF 转换为可操作的元素。然而,这些深度神经网络模型在低配置计算机上表现不佳,建议在配备高性能 GPU 的机器上运行。唯一需要关注的问题是,当同时处理千份文档时,系统的性能表现如何。
3 总结
开源项目和云服务提供商在应对 PDF 处理复杂性方面展现了行业的协同努力。
在这个不断变化的环境中,没有一种通用的方法能够有效管理复杂的 PDF 文件。经验表明,结合使用 LlamaIndex、UnstructuredIO 以及 PDF 到 HTML 的转换,是一种简单而高效的解决方案,能够产生优异的结果。
此外,提高 RAG 准确性的关键策略之一是灵活地结合不同的索引和检索器。这种多元化的方法认识到没有一套固定的索引规则适用于所有情况,强调需要根据每种文档的具体特性和处理细节来定制策略。通过接受这种灵活性,并使用量身定制的索引和检索器组合,你可以构建一个更准确、更复杂的检索系统,以应对数据的复杂性。
本文转载自 AI科技论谈,作者: AI科技论谈