共计 5709 个字符,预计需要花费 15 分钟才能阅读完成。
文档了解是文档解决和提取中最重要的步骤。这是从非结构化或半结构化文档中提取信息并将其转换为结构化模式的过程。提取后的结构化示意能够反对各种上游工作,例如信息检索,汇总,分类等。有许多不同的办法能够了解文档,但它们都有一个独特的指标: 创立文档内容的结构化示意,以便用于进一步的解决。
对于半结构化文档,例如发票,收款或合同,Microsoft 的 Layoutlm 模型能够良好的进行工作。
在本文中,咱们将在微软的最新 Layoutlm V3 上进行微调,并将其性能与 Layoutlm V2 模型进行比拟。
LayoutLM v3
LayoutLM v3 绝对于其前两个版本的次要劣势是多模态 transformer 架构,它以对立的形式将文本和图像嵌入联合起来。文档图像不依赖 CNN 进行解决,而是将图像补丁块示意为线性投影,而后线性嵌入与文本标记对齐,如下图所示。这种办法的次要长处是缩小了所需的参数和整体计算量。
论文的作者示意,“LayoutLMv3 不仅在以文本为核心的工作 (包含表单了解、票据了解和文档视觉问题答复) 中实现了最先进的性能,而且还在以图像为核心的工作 (如文档图像分类和文档布局剖析) 中实现了最先进的性能。”
微调 LayoutLM v3
咱们将应用雷同的 220 个带正文的发票数据集来微调 layoutLM v3 模型。为了进行标注,我应用了 UBIAI 文本正文工具,因为它反对 OCR 解析,原生 PDF/ 图像正文,并能够用 LayoutLM 模型兼容的格局导出,这样就能够节俭前期解决的工作。
从 UBIAI 导出正文文件后,咱们将应用谷歌 colab 进行模型训练和推理。源代码地址在最初提供,咱们这里简述工作的流程
第一步是关上 colab,装置相应的库。与 layoutLMv2 不同,咱们没有应用 detectron 2 包对实体提取的模型进行微调。然而对于布局检测(不在本文探讨范畴内),须要应用 detectorn 2 包:
from google.colab import drive | |
drive.mount('/content/drive') | |
!pip install -q git+https://github.com/huggingface/transformers.git | |
!pip install -q git+https://github.com/huggingface/datasets.git "dill<0.3.5" seqeval |
接下来,应用 preprocess.py 脚本来解决从 UBIAI 导出的 ZIP 文件:
! rm -r layoutlmv3FineTuning | |
! git clone -b main https://github.com/UBIAI/layoutlmv3FineTuning.git | |
#!/bin/bash | |
IOB_DATA_PATH = "/content/drive/MyDrive/LayoutLM_data/Invoice_Project_mkWSi4Z.zip" | |
! cd /content/ | |
! rm -r data! mkdir data | |
! cp "$IOB_DATA_PATH" data/dataset.zip | |
! cd data && unzip -q dataset && rm dataset.zip | |
! cd .. |
运行预处理脚本:
#!/bin/bash | |
TEST_SIZE = 0.33 | |
DATA_OUTPUT_PATH = "/content/" | |
! python3 layoutlmv3FineTuning/preprocess.py --valid_size $TEST_SIZE --output_path $DATA_OUTPUT_PATH |
加载解决后数据集:
from datasets import load_metric | |
from transformers import TrainingArguments, Trainer | |
from transformers import LayoutLMv3ForTokenClassification,AutoProcessor | |
from transformers.data.data_collator import default_data_collator | |
import torch | |
from datasets import load_from_disk | |
train_dataset = load_from_disk(f'/content/train_split') | |
eval_dataset = load_from_disk(f'/content/eval_split') | |
label_list = train_dataset.features["labels"].feature.names | |
num_labels = len(label_list) | |
label2id, id2label = dict(), dict() | |
for i, label in enumerate(label_list): | |
label2id[label] = i | |
id2label[i] = label |
定义评估指标:
metric = load_metric("seqeval") | |
import numpy as np | |
return_entity_level_metrics = False | |
def compute_metrics(p): | |
predictions, labels = p | |
predictions = np.argmax(predictions, axis=2) | |
true_predictions = [[label_list[p] for (p, l) in zip(prediction, label) if l != -100] | |
for prediction, label in zip(predictions, labels) | |
] | |
true_labels = [[label_list[l] for (p, l) in zip(prediction, label) if l != -100] | |
for prediction, label in zip(predictions, labels) | |
] | |
results = metric.compute(predictions=true_predictions, references=true_labels,zero_division='0') | |
if return_entity_level_metrics: | |
# Unpack nested dictionaries | |
final_results = {} | |
for key, value in results.items(): | |
if isinstance(value, dict): | |
for n, v in value.items(): | |
final_results[f"{key}_{n}"] = v | |
else: | |
final_results[key] = value | |
return final_results | |
else: | |
return {"precision": results["overall_precision"], | |
"recall": results["overall_recall"], | |
"f1": results["overall_f1"], | |
"accuracy": results["overall_accuracy"], | |
} |
对模型进行训练和评估:
model = LayoutLMv3ForTokenClassification.from_pretrained("microsoft/layoutlmv3-base", | |
id2label=id2label, | |
label2id=label2id) | |
processor = AutoProcessor.from_pretrained("microsoft/layoutlmv3-base", apply_ocr=False) | |
NUM_TRAIN_EPOCHS = 50 | |
PER_DEVICE_TRAIN_BATCH_SIZE = 1 | |
PER_DEVICE_EVAL_BATCH_SIZE = 1 | |
LEARNING_RATE = 4e-5 | |
training_args = TrainingArguments(output_dir="test", | |
# max_steps=1500, | |
num_train_epochs=NUM_TRAIN_EPOCHS, | |
logging_strategy="epoch", | |
save_total_limit=1, | |
per_device_train_batch_size=PER_DEVICE_TRAIN_BATCH_SIZE, | |
per_device_eval_batch_size=PER_DEVICE_EVAL_BATCH_SIZE, | |
learning_rate=LEARNING_RATE, | |
evaluation_strategy="epoch", | |
save_strategy="epoch", | |
# eval_steps=100, | |
load_best_model_at_end=True, | |
metric_for_best_model="f1") | |
trainer = Trainer( | |
model=model, | |
args=training_args, | |
train_dataset=train_dataset, | |
eval_dataset=eval_dataset, | |
tokenizer=processor, | |
data_collator=default_data_collator, | |
compute_metrics=compute_metrics, | |
) | |
trainer.train() | |
trainer.evaluate() |
训练实现后,对测试数据集进行评估。以下为评估后的模型得分:
{'epoch': 50.0, | |
'eval_accuracy': 0.9521988527724665, | |
'eval_f1': 0.6913439635535308, | |
'eval_loss': 0.41490793228149414, | |
'eval_precision': 0.6362683438155137, | |
'eval_recall': 0.756857855361596, | |
'eval_runtime': 9.7501, | |
'eval_samples_per_second': 9.846, | |
'eval_steps_per_second': 9.846} |
该模型 f1 得分为 0.69,召回率为 0.75,准确率为 0.63。
让咱们在不属于训练数据集的新发票上运行模型。
应用 LayoutLM v3 进行预测
为了进行预测,咱们将应用 Tesseract 对发票进行 OCR,并将信息输出到训练好的模型中进行预测。为了简化这一过程,我创立了一个自定义脚本,其中只蕴含几行代码,容许接管 OCR 输入并应用模型运行预测。
第一步,让咱们导入一些重要的库并加载模型:
from google.colab import drive | |
drive.mount('/content/drive') | |
!pip install -q git+https://github.com/huggingface/transformers.git | |
! sudo apt install tesseract-ocr | |
! sudo apt install libtesseract-dev | |
! pip install pytesseract | |
! git clone https://github.com/salmenhsairi/layoutlmv3FineTuning.git | |
import os | |
import torch | |
import warnings | |
from PIL import Image | |
warnings.filterwarnings('ignore') | |
os.makedirs('/content/images',exist_ok=True) | |
for image in os.listdir(): | |
try: | |
img = Image.open(f'{os.curdir}/{image}') | |
os.system(f'mv"{image}""images/{image}"') | |
except: | |
pass | |
model_path = "/content/drive/MyDrive/LayoutLM_data/layoutlmv3.pth" # path to Layoutlmv3 model | |
imag_path = "/content/images" # images folder | |
if model_path.endswith('.pth'): | |
layoutlmv3_model = torch.load(model_path) | |
model_path = '/content/pre_trained_layoutlmv3' | |
layoutlmv3_model.save_pretrained(model_path) |
应用模型进行预测
咱们应用了 220 张带标注的发票进行训练,该模型可能正确预测卖方名称、日期、发票编号和总价(TTC)!
如果仔细观察,就会发现把笔记本电脑总价当作发票总价的做法是谬误的(上图)。这并不奇怪,咱们能够用更多的训练数据来解决这个问题。
比拟 LayoutLM v2 和 LayoutLM v3
除了计算量更少之外,layoutLM V3 是否比它的 v2 版本提供了性能晋升? 为了答复这个问题,咱们比拟了雷同发票的两个模型输入。上面雷同数据下 layoutLM v2 输入:
v3 模型可能正确地检测到大多数的我的项目,而 v2 不能检测 invoice_ID、发票 number_ID 和 Total_ID
v2 型号谬误地将 Total price $ 1445 .00 标为 MONTANT_HT(法语中是税前总价),而 v3 正确地预测了总价。
两个模型都谬误地将笔记本电脑的价格标为 Total。
基于这个例子,layoutLM V3 显示了更好的整体性能,但咱们须要在更大的数据集上进行测试。
总结
本文中展现了如何在发票数据提取的特定用例上微调 layoutLM V3。而后将其性能与 layoutLM V2 进行了比拟,发现它的性能略有进步,但仍须要在更大的数据集上验证。
基于性能和计算收益,我强烈建议应用新的 layoutLM v3。
本文的一些有用的材料:
https://avoid.overfit.cn/post/be399d8f17f542929155b8b2481ecaaa
作者:Walid Amamou