輕松解析本地PDF表格,基于LlamaIndex和UnstructuredIO打造RAG
1 使用 LlamaIndex 和 UnstructuredIO 檢索數(shù)據(jù)
在數(shù)據(jù)檢索領(lǐng)域,LlamaIndex 以其強(qiáng)大的工具和技術(shù),為用戶帶來(lái)了全新的檢索體驗(yàn)。這個(gè)框架的亮點(diǎn)在于索引系統(tǒng)的靈活性,用戶可以根據(jù)文檔的具體內(nèi)容,量身定制索引策略,以適應(yīng)不同的文檔結(jié)構(gòu)。每種索引都設(shè)計(jì)得獨(dú)具匠心,能夠精準(zhǔn)匹配各種文檔結(jié)構(gòu),確保信息檢索的準(zhǔn)確性和高效性。
對(duì)于包含大量表格的 PDF 文件,建議使用 LlamaIndex 推薦的 RecursiveRetriever。這種遞歸檢索技術(shù)的精髓在于,它不僅深入挖掘與信息直接相關(guān)的節(jié)點(diǎn),還會(huì)追溯這些節(jié)點(diǎn)與其它檢索器或查詢引擎之間的關(guān)聯(lián),進(jìn)而執(zhí)行相應(yīng)的檢索操作。
例如,某個(gè)節(jié)點(diǎn)精煉地總結(jié)了某個(gè)結(jié)構(gòu)化表格的關(guān)鍵信息,并鏈接到該表格的 SQL 或 Pandas 查詢引擎。那么在檢索到這個(gè)節(jié)點(diǎn)之后,我們就能夠利用這些底層的查詢工具深入挖掘,從而獲取更詳盡的數(shù)據(jù)。這種深入的檢索方法,大大增強(qiáng)了我們從復(fù)雜數(shù)據(jù)集中提取有價(jià)值信息的能力。
為了有效實(shí)施這一策略,分步驟進(jìn)行:
a. 首先,將 PDF 文件轉(zhuǎn)換成 HTML 格式,這一步已經(jīng)完成。
b. 接著,利用 UnstructuredIO 讀取轉(zhuǎn)換后的 HTML 文件。
c. 對(duì)于 UnstructuredIO 從 HTML 中識(shí)別出的每個(gè)元素,無(wú)論是文本還是表格,都將其存儲(chǔ)到 LlamaIndex 的節(jié)點(diǎn)中。
d. 這樣一來(lái),就構(gòu)建了一個(gè)包含文本和表格的節(jié)點(diǎn)列表。
e. (可選步驟)可以專門篩選出包含表格的節(jié)點(diǎn),并將這些表格發(fā)送到語(yǔ)言模型(LLM)以生成摘要。
f. 然后,借助 LlamaIndex,LLM 代理將遞歸地檢索與問(wèn)題相關(guān)的信息。
g. 最后,將這些檢索到的數(shù)據(jù)發(fā)送回 LLM,以生成最終的響應(yīng)。
雖然這個(gè)過(guò)程聽起來(lái)頗為復(fù)雜,但得益于 LlamaIndex 提供的封裝良好的函數(shù),我們執(zhí)行這些步驟更加容易些。
1.1 讀取和處理數(shù)據(jù)
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>"
# 讀取數(shù)據(jù)
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"))
# 基礎(chǔ)節(jié)點(diǎn)和節(jié)點(diǎn)映射
base_nodes, node_mappings = node_parser.get_base_nodes_and_mappings(
raw_nodes
)
1.2 構(gòu)建索引
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 其他類型的查詢索引
前面的例子已經(jīng)展示了 UnstructuredElementNodeParser 如何無(wú)縫集成到 LlamaIndex + UnstructuredIO 的數(shù)據(jù)處理流程中,體現(xiàn)了其在提升數(shù)據(jù)處理效率和便捷性方面的強(qiáng)大能力。它采用了一種簡(jiǎn)化的方法論,讓原本復(fù)雜的數(shù)據(jù)提取工作變得更加易于掌握。
鑒于 LlamaIndex 提供了多種索引類型和檢索技術(shù),探索不同的選項(xiàng)以找到最適合你特定場(chǎng)景的解決方案是非常有價(jià)值的。不妨嘗試包括自動(dòng)合并檢索器、結(jié)果重排序以及混合搜索在內(nèi)的多種策略。
每種策略都有其獨(dú)到之處,而最終的效果也會(huì)隨著數(shù)據(jù)的復(fù)雜性而有所不同。通過(guò)實(shí)際測(cè)試和評(píng)估,你可以優(yōu)化檢索流程,確保采用最合適的方法從數(shù)據(jù)集中提取關(guān)鍵信息。
2 如何從 PDF/HTML 中提取表格
這部分內(nèi)容提供了一個(gè)可選的功能,它通過(guò)較低級(jí)別的 API 支持從 PDF 或 HTML 中提取表格,這可能對(duì)特定需求非常有用。雖然前面提到的方法在大多數(shù)情況下已經(jīng)足夠有效,但如果需要更精細(xì)的控制,比如直接操作底層數(shù)據(jù),那么可能需要在數(shù)據(jù)處理流程中加入額外的步驟,例如利用語(yǔ)言模型(LLM)來(lái)生成數(shù)據(jù)摘要。這一環(huán)節(jié)值得你進(jìn)一步探索。
2.1 從 PDF 中提取表格
完成從 PDF 中提取表格的任務(wù),可以依賴多種光學(xué)字符識(shí)別(OCR)技術(shù)和庫(kù),同時(shí)也可以考慮使用云服務(wù),但這涉及較高的成本。UnstructuredIO 提供了一個(gè)功能強(qiáng)大的 ??partition_pdf?
? 方法,它通過(guò)多個(gè)參數(shù)讓你能夠靈活地在處理速度和識(shí)別準(zhǔn)確性之間做出權(quán)衡,并且可以指定特定的深度學(xué)習(xí)模型來(lái)優(yōu)化表格的提取效果。
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"
)
# 將結(jié)果存儲(chǔ)在 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 數(shù)據(jù)并提取所需的表格元素
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") # 添加兩個(gè)換行符以分隔
process_json_file(f"The_Worlds_Billionaires_Converted") # ## with new_file_name 是上面的 JSON 文件
print(f"Number of tables: {no_tables}")
### 加載數(shù)據(jù)
```python
# documents = SimpleDirectoryReader("./<folder_name>",
# input_files=['./<new_file_name.txt>']).load_data()
該方法能夠讀取 PDF 文件,并提取出其中的元素,如文本和表格。表格元素會(huì)以 JSON 格式保存為“text_as_html”。你可以逐一讀取和處理 JSON 文件中的每個(gè)元素,并將處理后的數(shù)據(jù)存儲(chǔ)為 TXT 文件,以便后續(xù)的 RAG 讀取。
需要注意的是,直接解析 PDF 的性能可能不盡如人意。目前,UnstructuredIO 提供了多種模型,例如 YOLOx,來(lái)幫助將 PDF 轉(zhuǎn)換為可操作的元素。然而,這些深度神經(jīng)網(wǎng)絡(luò)模型在低配置計(jì)算機(jī)上表現(xiàn)不佳,建議在配備高性能 GPU 的機(jī)器上運(yùn)行。唯一需要關(guān)注的問(wèn)題是,當(dāng)同時(shí)處理千份文檔時(shí),系統(tǒng)的性能表現(xiàn)如何。
3 總結(jié)
開源項(xiàng)目和云服務(wù)提供商在應(yīng)對(duì) PDF 處理復(fù)雜性方面展現(xiàn)了行業(yè)的協(xié)同努力。
在這個(gè)不斷變化的環(huán)境中,沒(méi)有一種通用的方法能夠有效管理復(fù)雜的 PDF 文件。經(jīng)驗(yàn)表明,結(jié)合使用 LlamaIndex、UnstructuredIO 以及 PDF 到 HTML 的轉(zhuǎn)換,是一種簡(jiǎn)單而高效的解決方案,能夠產(chǎn)生優(yōu)異的結(jié)果。
此外,提高 RAG 準(zhǔn)確性的關(guān)鍵策略之一是靈活地結(jié)合不同的索引和檢索器。這種多元化的方法認(rèn)識(shí)到?jīng)]有一套固定的索引規(guī)則適用于所有情況,強(qiáng)調(diào)需要根據(jù)每種文檔的具體特性和處理細(xì)節(jié)來(lái)定制策略。通過(guò)接受這種靈活性,并使用量身定制的索引和檢索器組合,你可以構(gòu)建一個(gè)更準(zhǔn)確、更復(fù)雜的檢索系統(tǒng),以應(yīng)對(duì)數(shù)據(jù)的復(fù)雜性。
本文轉(zhuǎn)載自 ??AI科技論談??,作者: AI科技論談
