OCR + LLM 复合系统:票据解析高精度字段结构化
大家好,今天我们来深入探讨如何利用 OCR(光学字符识别)和 LLM(大型语言模型)构建复合系统,实现票据解析的高精度字段结构化。在数字化转型的浪潮下,票据电子化已经成为必然趋势。然而,大量的历史票据和现实场景中,票据格式复杂多样,人工处理效率低下且容易出错。因此,构建一个能够自动、准确地解析票据并提取关键信息的系统至关重要。
1. 问题定义与挑战
票据解析的目标是从图像或 PDF 形式的票据中提取关键字段,例如发票号码、日期、金额、供应商信息等,并将这些信息结构化地存储,以便后续的业务处理和分析。这个过程面临以下挑战:
- 版面复杂性: 票据的版面设计千差万别,字段位置不固定,表格结构复杂。
- 图像质量: 扫描质量、光照条件、倾斜角度等因素都会影响 OCR 的识别精度。
- 噪声干扰: 票据上可能存在水印、盖章、手写批注等噪声,干扰字段提取。
- 语义理解: 有些字段的识别需要结合上下文信息进行语义理解,例如判断 "总计" 后面跟随的数字为金额。
- 多语言支持: 票据可能包含多种语言,需要支持多语言 OCR 和 NLP 处理。
2. 系统架构设计
为了应对上述挑战,我们设计如下的 OCR + LLM 复合系统架构:
graph LR
A[输入图像/PDF] --> B(图像预处理)
B --> C(OCR引擎)
C --> D(文本块及位置信息)
D --> E(版面分析)
E --> F(LLM字段定位)
F --> G(LLM字段内容提取与校正)
G --> H(结构化数据)
style A fill:#f9f,stroke:#333,stroke-width:2px
style H fill:#f9f,stroke:#333,stroke-width:2px
该架构主要包含以下几个模块:
- 图像预处理: 负责对输入图像进行清洗、增强和校正,提高 OCR 的识别精度。
- OCR 引擎: 将图像中的文本转换为可编辑的文本数据,并提供每个文本块的位置信息。
- 版面分析: 分析文本块之间的位置关系,识别表格、标题、段落等结构化元素。
- LLM 字段定位: 利用 LLM 理解票据的语义信息,确定目标字段在版面中的位置。
- LLM 字段内容提取与校正: 提取目标字段的内容,并利用 LLM 对提取结果进行校正和补全。
- 结构化数据: 将提取的字段信息按照预定义的格式进行结构化存储。
3. 关键技术实现
接下来,我们将详细介绍每个模块的关键技术实现,并提供相应的代码示例。
3.1 图像预处理
图像预处理的目标是改善图像质量,提高 OCR 的识别精度。常用的预处理技术包括:
- 灰度化: 将彩色图像转换为灰度图像,减少颜色信息对 OCR 的干扰。
- 二值化: 将灰度图像转换为黑白图像,突出文本信息。
- 降噪: 消除图像中的噪声,例如高斯噪声、椒盐噪声等。
- 倾斜校正: 校正图像的倾斜角度,使文本水平对齐。
- 对比度增强: 增强图像的对比度,使文本更加清晰。
import cv2
import numpy as np
def preprocess_image(image_path):
"""
图像预处理函数
"""
image = cv2.imread(image_path)
# 灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化 (使用 Otsu's method)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# 降噪 (使用中值滤波)
denoised = cv2.medianBlur(thresh, 3)
# 倾斜校正 (使用霍夫变换)
coords = np.column_stack(np.where(denoised > 0))
angle = cv2.minAreaRect(coords)[-1]
if angle < -45:
angle = -(90 + angle)
else:
angle = -angle
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
return rotated
# 示例
preprocessed_image = preprocess_image("invoice.jpg")
cv2.imwrite("preprocessed_invoice.jpg", preprocessed_image)
3.2 OCR 引擎
OCR 引擎负责将图像中的文本转换为可编辑的文本数据,并提供每个文本块的位置信息。常用的 OCR 引擎包括:
- Tesseract OCR: 开源 OCR 引擎,支持多种语言。
- Google Cloud Vision API: 谷歌云提供的 OCR 服务,具有较高的识别精度。
- Azure Computer Vision API: 微软云提供的 OCR 服务,功能丰富。
- PaddleOCR: 百度飞桨提供的 OCR 工具包,支持多种模型和语言。
这里我们以 Tesseract OCR 为例,演示如何使用 Python 进行 OCR 识别。
import pytesseract
from PIL import Image
def ocr_image(image_path):
"""
使用 Tesseract OCR 识别图像中的文本
"""
image = Image.open(image_path)
text = pytesseract.image_to_string(image, lang='chi_sim') # 可更改lang参数支持其他语言
return text
# 获取文本块及其位置信息
def get_bounding_boxes(image_path):
"""
获取文本块及其位置信息
"""
image = Image.open(image_path)
data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT, lang='chi_sim')
return data
# 示例
text = ocr_image("preprocessed_invoice.jpg")
print(text)
bounding_boxes = get_bounding_boxes("preprocessed_invoice.jpg")
print(bounding_boxes)
get_bounding_boxes 函数返回一个字典,包含文本块的 level(层级,如 paragraph, word, symbol)、page_num、block_num、par_num、line_num、word_num、left、top、width、height、conf(置信度)和 text 等信息。
3.3 版面分析
版面分析的目标是分析文本块之间的位置关系,识别表格、标题、段落等结构化元素。常用的版面分析技术包括:
- 基于规则的方法: 根据文本块的位置、大小和对齐方式等特征,定义一系列规则来识别版面结构。
- 基于聚类的方法: 将文本块按照位置关系进行聚类,例如将相邻的文本块聚类成行或列。
- 基于深度学习的方法: 使用深度学习模型学习版面结构,例如使用 CNN 或 Transformer 模型进行版面分割和识别。
这里我们以基于规则的方法为例,演示如何识别表格结构。
def detect_tables(bounding_boxes, threshold_x=10, threshold_y=5):
"""
基于规则检测表格
"""
words = []
n_boxes = len(bounding_boxes['level'])
for i in range(n_boxes):
if bounding_boxes['level'][i] == 5: # Word level
words.append({'text': bounding_boxes['text'][i],
'left': bounding_boxes['left'][i],
'top': bounding_boxes['top'][i],
'width': bounding_boxes['width'][i],
'height': bounding_boxes['height'][i]})
# 按行分组
rows = {}
for word in words:
row_id = word['top'] // threshold_y # Use integer division to group words into rows
if row_id not in rows:
rows[row_id] = []
rows[row_id].append(word)
# 对每一行中的词按列排序
for row_id in rows:
rows[row_id].sort(key=lambda x: x['left'])
# 检测列
columns = []
for row_id in rows:
for i, word in enumerate(rows[row_id]):
if i == 0:
columns.append({'left': word['left'], 'right': word['left'] + word['width']})
else:
merged = False
for j, col in enumerate(columns):
if abs(word['left'] - col['right']) < threshold_x:
columns[j]['right'] = word['left'] + word['width']
merged = True
break
if not merged:
columns.append({'left': word['left'], 'right': word['left'] + word['width']})
columns.sort(key=lambda x: x['left'])
# 组装表格数据
table = []
for row_id in sorted(rows.keys()):
row_data = []
for col in columns:
cell_text = ""
for word in rows[row_id]:
if col['left'] <= word['left'] and word['left'] + word['width'] <= col['right']:
cell_text += word['text'] + " "
row_data.append(cell_text.strip())
table.append(row_data)
return table
# 示例
table = detect_tables(bounding_boxes)
for row in table:
print(row)
3.4 LLM 字段定位
LLM 字段定位的目标是利用 LLM 理解票据的语义信息,确定目标字段在版面中的位置。具体实现步骤如下:
- 构建提示词 (Prompt): 根据目标字段的语义信息,构建一个提示词,例如 "请找出发票号码" 或 "提取总金额"。
- 输入 LLM: 将 OCR 识别结果和提示词输入 LLM。
- 获取字段位置: LLM 根据提示词,分析 OCR 识别结果,输出目标字段在版面中的位置信息,例如文本块的坐标或文本内容。
我们可以使用 OpenAI 的 GPT 模型或开源的 LLM 模型,例如 Llama 2 或 ChatGLM。这里我们以 OpenAI 的 GPT-3.5 模型为例,演示如何使用 Python 进行字段定位。
import openai
import os
# 设置 OpenAI API 密钥
openai.api_key = os.getenv("OPENAI_API_KEY")
def locate_field_with_llm(ocr_text, field_name):
"""
使用 LLM 定位字段
"""
prompt = f"从以下文本中提取 {field_name}:n{ocr_text}n{field_name}:"
response = openai.Completion.create(
engine="text-davinci-003", # 或者选择其他的模型,比如"gpt-3.5-turbo-instruct"
prompt=prompt,
max_tokens=50, # 根据需要调整
n=1,
stop=None,
temperature=0.2, # 降低temperature可以提高一致性
)
extracted_text = response.choices[0].text.strip()
return extracted_text
# 示例
ocr_text = ocr_image("preprocessed_invoice.jpg")
invoice_number = locate_field_with_llm(ocr_text, "发票号码")
total_amount = locate_field_with_llm(ocr_text, "总金额")
print(f"发票号码: {invoice_number}")
print(f"总金额: {total_amount}")
使用 GPT-3.5 模型需要付费,你可以根据自己的需求选择合适的模型。同时,为了提高字段定位的精度,可以对提示词进行优化,例如添加更多的上下文信息或约束条件。
3.5 LLM 字段内容提取与校正
LLM 字段内容提取与校正的目标是提取目标字段的内容,并利用 LLM 对提取结果进行校正和补全。具体实现步骤如下:
- 提取字段内容: 根据字段位置信息,从 OCR 识别结果中提取字段内容。
- 输入 LLM: 将提取的字段内容输入 LLM,并提供上下文信息,例如字段类型、单位等。
- 校正和补全: LLM 根据上下文信息,对提取结果进行校正和补全,例如将 "123.4" 校正为 "123.40",或将 "2023/12/3" 补全为 "2023年12月03日"。
def correct_and_complete_field(field_text, field_type, context=""):
"""
使用 LLM 校正和补全字段内容
"""
prompt = f"请校正和补全以下 {field_type}:n{field_text}n上下文:{context}n校正后的{field_type}:"
response = openai.Completion.create(
engine="text-davinci-003",
prompt=prompt,
max_tokens=50,
n=1,
stop=None,
temperature=0.2,
)
corrected_text = response.choices[0].text.strip()
return corrected_text
# 示例
invoice_number = "123456789"
corrected_invoice_number = correct_and_complete_field(invoice_number, "发票号码")
total_amount = "123.4"
corrected_total_amount = correct_and_complete_field(total_amount, "总金额", "单位:元")
print(f"校正后的发票号码: {corrected_invoice_number}")
print(f"校正后的总金额: {corrected_total_amount}")
3.6 结构化数据
结构化数据的目标是将提取的字段信息按照预定义的格式进行存储,例如 JSON、CSV 或数据库。可以根据具体的业务需求选择合适的存储格式。
import json
def save_to_json(data, output_path="output.json"):
"""
将数据保存为 JSON 文件
"""
with open(output_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
# 示例
data = {
"invoice_number": corrected_invoice_number,
"total_amount": corrected_total_amount,
# 其他字段...
}
save_to_json(data)
4. 优化策略
为了进一步提高票据解析的精度和效率,可以采用以下优化策略:
- 模型微调: 使用特定领域的票据数据对 OCR 模型和 LLM 模型进行微调,提高模型在特定场景下的性能。
- 数据增强: 通过对图像进行旋转、缩放、裁剪等操作,增加训练数据的多样性,提高模型的鲁棒性。
- 集成学习: 将多个 OCR 引擎和 LLM 模型进行集成,利用集体的智慧提高整体性能。
- 规则引擎: 结合规则引擎和 LLM,利用规则引擎处理简单的场景,利用 LLM 处理复杂的场景,提高整体效率。
5. 一些思考
通过结合 OCR 技术和 LLM 的强大能力,我们能够构建高精度的票据解析系统。不过,这仍然是一个不断发展的领域。以下是一些值得思考的方向:
- 少样本学习: 如何在只有少量标注数据的情况下,快速构建高精度的票据解析系统?
- 零样本学习: 如何在没有标注数据的情况下,利用 LLM 的知识迁移能力,实现票据解析?
- 多模态学习: 如何结合图像、文本和布局信息,提高票据解析的精度和鲁棒性?
| 优化策略 | 描述 | 适用场景 |
|---|---|---|
| 模型微调 | 使用特定领域的票据数据对 OCR 模型和 LLM 模型进行微调,提高模型在特定场景下的性能。 | 票据类型固定,有一定量的标注数据。 |
| 数据增强 | 通过对图像进行旋转、缩放、裁剪等操作,增加训练数据的多样性,提高模型的鲁棒性。 | 图像质量不稳定,存在倾斜、模糊等问题。 |
| 集成学习 | 将多个 OCR 引擎和 LLM 模型进行集成,利用集体的智慧提高整体性能。 | 需要更高的识别精度,对计算资源要求不高。 |
| 规则引擎 | 结合规则引擎和 LLM,利用规则引擎处理简单的场景,利用 LLM 处理复杂的场景,提高整体效率。 | 票据结构相对固定,但存在一些复杂的语义信息需要处理。 |
| Prompt优化 | 精心设计提示词,提供足够的上下文信息,引导 LLM 更好地理解和提取信息。 | 提升LLM的效果,不需要额外训练数据,成本较低。 |
未来,随着技术的不断发展,我们可以期待更加智能、高效的票据解析系统的出现,为企业的数字化转型提供强有力的支持。
提升系统的鲁棒性和泛化性
构建一个鲁棒性强、泛化性好的系统,需要考虑以下几个方面:
- 多样化的训练数据: 使用来自不同来源、不同格式、不同质量的票据数据进行训练,使模型能够适应各种复杂的场景。
- 领域自适应: 采用领域自适应技术,将模型从一个领域迁移到另一个领域,例如从发票解析迁移到银行账单解析。
- 对抗训练: 使用对抗训练技术,生成对抗样本,提高模型的鲁棒性。
总而言之,构建一个高精度的票据解析系统是一个复杂而富有挑战性的任务,需要综合运用 OCR、LLM 和其他相关技术。希望今天的分享能够帮助大家更好地理解和应用这些技术,构建出更加智能、高效的票据解析系统。
代码的模块化与可维护性
为了保证代码的可维护性和可扩展性,我们需要对代码进行模块化设计,将不同的功能封装成独立的模块,并提供清晰的接口。例如,可以将图像预处理、OCR 识别、版面分析、LLM 字段定位和结构化数据存储等功能分别封装成独立的模块。
结论:迈向智能票据处理的下一步
通过结合 OCR 与 LLM 技术,我们能够显著提升票据解析的精度。模型微调、数据增强以及集成学习等策略能够进一步优化系统性能,应对各种复杂场景。未来,随着技术的持续演进,智能票据处理将更加高效、准确,为企业数字化转型提供有力支撑。