介绍
从文件中提取结构化数据已成为各行各业的一项基本流程。无论是处理财务报告、合同、发票还是技术文档,企业都面临着将非结构化或半结构化格式的数据转换为易于分析和处理的结构化格式的持续挑战。文档通常采用 PDF、Microsoft Word 文件、Excel 表和扫描图像等格式,这些格式在布局上具有极大的灵活性,但对于自动数据提取而言却具有挑战性。
传统的数据提取方法,如基于规则的系统和基本的机器学习模型,很难处理这些文档布局的复杂性和多变性。文档的结构可能千差万别,缺乏一致性往往会导致错误或信息缺失。这就是大型语言模型(LLM)的进步之处。
LLM能够理解上下文并适应各种格式,为包括文档处理在内的许多领域带来了革命性的变化。它们可以解析复杂的文档布局,理解自然语言的细微差别,并以令人印象深刻的准确性提取结构化格式的数据。本篇文章将探讨如何利用 LLM 解决文档数据提取难题、它们与传统方法的比较,以及如何将它们用于满足自己的文档处理需求。
文档数据提取的挑战
文档数据提取的最大障碍之一是不同文档所使用的格式和结构范围很广。文档有多种形式,包括 PDF、Microsoft Word 文件、Excel 电子表格,甚至还有扫描文档等图像。在提取结构化数据时,这些格式都会带来各自独特的挑战。
文档格式和布局
文档通常以各种格式创建,例如:
每种格式都有自己的结构,都会带来挑战,但真正的困难在于这些格式中存在的各种变化。例如,PDF 可以是单栏或多栏,可以包含表格或图表,可以包含页眉和页脚,甚至可以嵌入图像和图表。这种巨大的可变性使得开发 “一刀切 ”的解决方案变得不切实际。
复杂布局和技术文档
有些文档类型,尤其是技术报告、财务报表或科学论文,由于结构复杂但布局严谨,会带来更大的挑战。以下是与这些文档相关的一些具体问题:
传统提取方法的局限性
从文档中提取数据的传统方法有很多局限性,尤其是在处理非结构化或半结构化数据时:
这些传统方法的局限性清楚地表明,我们需要更先进、更智能的方法来应对文档数据提取日益增长的复杂性。在下面,我们将探讨大型语言模型(LLM)如何克服这些挑战,为这项任务带来新的灵活性和理解力。
大型语言模型(LLM)为何能改变文档数据提取的游戏规则
大型语言模型(LLM)作为一种强大的工具迅速崛起,能够以传统方法无法企及的方式理解和处理语言。LLM 能够解释自然语言并适应各种格式,是从复杂文档中提取数据的绝佳解决方案。下面我们就来分析一下 LLM 为何在这项任务中如此有效。
理解上下文
LLM 的最大优势之一是其理解上下文的能力。传统方法通常依赖于根据固定规则或模式提取数据,当文档布局发生变化时,这种方法就会失效。而 LLM 是在大量文本的基础上训练出来的,这使它们能够理解单词、句子甚至整个章节的含义。它们可以区分页眉、脚注和主体内容中的文本,从而更容易提取相关信息。
例如,在一份财务报告中,LLM 可以识别出某些文本是标题(如 “财务摘要”),而不是需要提取的数据。它还能识别表格的起点和终点,而这正是传统提取工具难以做到的。
对不同格式和布局的适应性
LLM 具有很强的适应性。无论你处理的是 PDF、Word 文档,甚至是扫描图像,LLM 都能应对自如。现代 LLM 不受文档结构的限制--它们可以适应各种格式和布局,而无需进行大量的重新配置。
这种适应性在处理不一致的文档类型时尤为重要,例如来自不同供应商的发票或布局各异的研究论文。LLM 可以独立处理每份文档,根据上下文识别数据,而不是依赖于固定模板。
处理技术术语和复杂表格
LLM 擅长处理技术语言。由于它们接受过来自广泛领域(如金融、医学和工程)的数据培训,因此可以轻松理解特定行业的术语。这使得它们在从技术文档中提取结构化数据时特别有用,因为技术文档通常包含行话、公式和复杂的表格。
例如,在解析科学论文时,LLM 可以识别化学符号、方程式和特定行业术语,从而实现更准确的数据提取。同样,如果表格有合并单元格或嵌套标题,LLM 仍能以结构化格式捕获数据,而无需定制规则。
保留文档结构
LLM 的另一个主要优势是能够保持原始文档的结构。无论你是从多栏 PDF 还是复杂表格中提取数据,LLM 都能准确呈现文档的布局。例如,可以将多栏文档转换为文本,同时保持正确的阅读顺序,这对传统提取方法来说是一项具有挑战性的任务。
此外,在处理表格时,LLM 可以将输出格式化为 CSV 或 JSON 等结构化数据,使其更容易集成到数据库或分析工具中。这确保了原始数据的完整性,这在处理详细报告或技术文档时至关重要。
输出定制的灵活性
LLM 最强大的功能之一是可以自定义数据提取方式。通过使用提示,你可以指示 LLM 以特定方式格式化输出。例如,你可以告诉它以 markdown 格式提取表格、跳过页脚或排除页眉。
与需要严格规则或模板的传统方法相比,这种灵活性改变了游戏规则。有了 LLM,你就可以轻松适应不断变化的文档格式或提取需求,而无需对整个系统重新编程。
实践:使用开源 LLM 进行结构化数据提取
既然我们已经了解了 LLM 在文档提取方面的优势,下面就让我们通过一个实践案例来了解如何使用开源模型进行结构化数据提取。在本例中,我们将使用 MiniCPM-Llama3-V2.5 模型,该模型可同时处理语言和视觉任务,因此非常适合从复杂文档中提取数据。
安装所需的库
在开始之前,我们需要安装一些库来帮助进行文本提取、图像处理和模型加载。下面是所需 Python 库的列表:
pip install Pillow torch torchvision transformers sentencepiece pymupdf
如果要处理大型文档或多个文件,请确保你的系统有支持 CUDA 的 GPU,因为它将大大加快处理速度。
加载 LLM 和标记器
接下来,让我们加载开源 LLM 和标记器。在本例中,我们使用的是 MiniCPM-Llama3-V2.5,这是一个具有强大视觉功能的模型。下面的代码初始化了模型和标记器:
import torch
from transformers import AutoModel, AutoTokenizer
# Load the model and tokenizer
model = AutoModel.from_pretrained("openbmb/MiniCPM-Llama3-V-2_5", trust_remote_code=True, torch_dtype=torch.float16)
model = model.to(device="cuda")
tokenizer = AutoTokenizer.from_pretrained("openbmb/MiniCPM-Llama3-V-2_5", trust_remote_code=True)
model.eval()
在此设置中:
将 PDF 页面转换为图像
由于 LLM 擅长处理图像(除文本外),我们可以将 PDF 的页面转换为图像。这样,模型就能 “读取 ”文档,提取相关信息,并将其结构化以便进一步分析。
以下是将 PDF 的每一页转换成图像的代码:
import fitz # PyMuPDF
from PIL import Image
# Open the PDF file
pdf_path = "mypdf.pdf"
pdf_document = fitz.open(pdf_path)
# Store images
images = []
# Loop through each page and convert it to an image
for page_number in range(len(pdf_document)):
page = pdf_document.load_page(page_number)
pix = page.get_pixmap()
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
images.append(img)
pdf_document.close()
在这段代码中
将图像发送到 LLM 进行数据提取
现在,我们已经将文档页面转换为图像,可以将它们发送给 LLM 进行提取。我们还可以向模型提供一个特定的问题或指令,指导它如何提取信息。
下面是一个如何将图像发送到 LLM 并提取文本和表格的示例:
question = """Extract all the text in this image."""Extract all the text in this image.
If there is a header or a footer, just ignore it.
Extract tables as markdown tables.
Don't use the subtitles for the list items, just return the list as text."""
msgs = [{"role": "user", "content": question}]
res = model.chat(
image=images[0], # Using the first image as an example
msgs=msgs,
tokenizer=tokenizer,
sampling=True,
temperature=0.7
)
print(res)
在这段代码中:
处理输出和后处理
数据提取出来后,可能会包含前缀或其他不需要的信息。LLM 可能会返回带有 “标题 ”或 “副标题 ”等标签的结果。你可以使用简单的后处理技术(如正则表达式)来清理这些输出,删除不需要的文本。
即使是最复杂的文档,你也可以通过这一过程提取有意义的结构化数据,以便进一步分析或集成到其他系统中。
如何定制和优化用于文档数据提取的大型语言模型
既然我们已经探讨了如何将大型语言模型(LLM)用于文档提取,并看到了一个实际操作的例子,现在是时候深入研究如何定制和优化这一过程了。定制是实现最佳结果的关键,因为每个文档和用例都可能有独特的要求。此外,还可以对 LLM 进行性能优化,以便高效处理大型数据集和复杂文档。让我们将其分解为易于理解的步骤。
使用提示自定义提取流程
使用 LLM 进行文档提取的最大优势之一是可以自定义提取数据的方式。使用传统方法,你需要硬编码规则或模板。然而,使用 LLM,你只需提供结构良好的提示,就能获得所需的输出。这通常被称为提示工程。
举例说明: 如果想从文档中提取表格,但只需要某些列,可以按如下方式调整提示:
question = """Extract the table from this image but only include columns for Date and Amount."""Extract the table from this image but only include columns for Date and Amount.
Ignore any other columns."""
这种灵活性使 LLM 与众不同。你可以准确地指示模型如何构建数据,它也会相应地调整输出。
处理复杂文档
在发票、财务报表或学术论文等文档中,脚注或参考文献等元素可能会扰乱数据提取的流程。通过提示工程,你可以指示 LLM 专注于特定部分或排除不必要的细节。
例如,如果文档包含主要内容和脚注,你的提示可以告诉 LLM 跳过脚注:
question = """Extract the text from this document, but skip any footnotes or references at the bottom of the page."""
处理大型文件: 批处理和并发执行
在处理大量数据集或长篇文档(如多页 PDF)时,有效管理资源至关重要。一次处理大量页面或文档可能会耗费大量时间和计算成本。要解决这个问题,可以采用批处理和并发执行。
批处理
与单独处理每个页面不同,你可以将多个页面组合在一起,然后批量处理。这样,模型就能更有效地处理较大的数据集,并使整个处理过程更快。
举例说明: 下面是一种简单的批处理方法:
batch_size = 10 # Number of pages to process at a time10 # Number of pages to process at a time
for i in range(0, len(images), batch_size):
batch = images[i:i + batch_size]
# Process each batch
通过在一个批次中处理多个页面,可以减少在每个页面上运行模型的开销。
并发执行
如果你的系统有多个处理内核或 GPU,你可以通过并发运行多个批次来进一步优化流程。Python 的 concurrent.futures 或 Ray 等库可用于处理并行执行。
示例:
from concurrent.futures import ThreadPoolExecutor
def process_image(image):
# Function to process individual image using LLM
return model.chat(image=image, msgs=msgs, tokenizer=tokenizer)
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(process_image, images))
这种方法可以并行处理多个页面或文档,大大加快提取过程。
后处理和清理输出结果
LLM 提取数据后,下一步就是确保输出是干净、可用的格式。如前所述,LLM 可能会在输出中添加 “标题 ”或 “副标题 ”等前缀,这可能并不可取,具体取决于你的使用情况。
使用正则表达式清理输出
从提取的数据中删除不需要的前缀或标签的简单方法是使用正则表达式(regex)。Regex 可以让你高效地查找和替换文本模式。
举例说明: 如果 LLM 返回以下输出:
**title**: "Financial Report""Financial Report"
**text**: "This is the report for Q2 2023."
你可以使用 regex 删除前缀:
import re
def clean_output(text):
cleaned_text = re.sub(r"\*\*.*?\*\*: ", "", text) # Removes prefixes like **title**:
return cleaned_text
cleaned_result = clean_output(res)
print(cleaned_result)
这种方法有助于确保提取的数据是干净的,可以进一步处理或存储在数据库中。
优化性能
LLM 可能是资源密集型的,尤其是在处理大型文档或多个文件时。不过,有几种策略可以优化性能:
使用较小的模型处理较简单的任务
虽然较大的模型在处理复杂任务时通常会有更好的表现,但并不总是必要的。对于技术术语较少或复杂布局较少的简单文件,使用较小的 LLM 可以节省计算资源,而不会影响准确性。
调整模型参数
你可以通过调整温度和取样等参数来优化 LLM 处理文档的方式。这些参数会影响模型输出的随机性和多样性。
举例说明:
res = model.chat(
image=images[0],0],
msgs=msgs,
tokenizer=tokenizer,
sampling=False, # Disabling sampling for consistent results
temperature=0.5 # Reducing randomness
)
缓存结果
对于重复提取或处理类似文档时,缓存中间结果可以节省时间。例如,在处理多页文档时,缓存每一页的标记化输出结果可以避免提取过程中的冗余计算。
面向未来的提取管道
随着文档提取需求的增长,确保提取管道面向未来也很重要。使用 LLM,可以通过以下方式做到这一点:
保护数据隐私和安全
在处理财务记录或个人信息等敏感数据时,确保数据隐私至关重要。幸运的是,大多数 LLM 都可以在本地服务器上运行,无需将敏感文件发送到外部服务器。这可确保你的数据安全,并符合数据隐私法规(如 GDPR)。
你还可以将匿名化技术集成到提取管道中。例如,在提取过程中,姓名、地址或电话号码等个人标识符可以用虚拟数据代替,以保护隐私。
流程图和可视化表示
像文档数据提取这样复杂的任务,可以通过可视化表示来更容易地理解流程。流程图可以帮助说明 LLM 在从文档输入到最终输出的提取过程中是如何工作的。下面是我们开发的系统的细分,可以用流程图来表示。
文档数据提取流程图
复杂文档处理的表示方法
你可以扩展本流程图,以涵盖更复杂的情况,如一次处理多个文档或执行批处理。
这种表示法显示了 LLM 如何同时处理多个文件,优化大规模操作的工作流程。
结论
在这篇文章中,我们探讨了大型语言模型(LLM)在从复杂文档中提取结构化数据方面的显著优势。与传统方法不同,LLM 具有无与伦比的灵活性和适应性,可以处理布局各异、技术术语和表格错综复杂的文档。
完整代码:
import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer
import fitz # PyMuPDF
# Load the model and tokenizer
model = AutoModel.from_pretrained("openbmb/MiniCPM-Llama3-V-2_5", trust_remote_code=True, torch_dtype=torch.float16)
model = model.to(device="cuda")
tokenizer = AutoTokenizer.from_pretrained("openbmb/MiniCPM-Llama3-V-2_5", trust_remote_code=True)
model.eval()
# Convert PDF pages to images
pdf_path = "mypdf.pdf"
pdf_document = fitz.open(pdf_path)
images = []
for page_number in range(len(pdf_document)):
page = pdf_document.load_page(page_number)
pix = page.get_pixmap()
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
images.append(img)
pdf_document.close()
# Prompt for extraction
question = """Extract all the text in this image.
If there is a header or a footer, just ignore it.
Extract tables as markdown tables.
Don't use the subtitles for the list items, just return the list as text."""
msgs = [{"role": "user", "content": question}]
# Send the image to the LLM
res = model.chat(
image=images[0], # You can loop through the images here
msgs=msgs,
tokenizer=tokenizer,
sampling=True,
temperature=0.7
)
# Print the result
print(res)
# Example of post-processing the result
import re
def clean_output(text):
cleaned_text = re.sub(r"\*\*.*?\*\*: ", "", text) # Removes prefixes like **title**:
return cleaned_text
cleaned_result = clean_output(res)
print(cleaned_result)