本文档记录 impira/layoutlm-invoices 文档发票问答模型在昇腾 NPU(Ascend 910B3)上的迁移适配、精度评测与性能验证结果。
LayoutLM 是 Microsoft 提出的多模态文档理解模型,在 BERT 基础上引入 2D 位置编码(bounding box)和图像特征,实现视觉+文本的联合建模。该模型在发票/票据数据集上微调,用于抽取式问答:给定发票图片、OCR 文本和坐标,回答诸如 "What is the invoice number?"、"What is the total amount?" 等问题。
模型输入包含三部分:文档图片(pixel_values)、OCR 单词(words)、单词边界框(boxes, 0-1000 归一化)。输出 start/end logits 定位答案在 OCR 文本中的位置。
相关获取地址:
| 组件 | 版本 |
|---|---|
torch | 2.8.0 |
torch_npu | 2.8.0.post4 |
transformers | 5.8.1 |
CANN | 8.5.1 |
8 × Ascend 910B3conda create -n layoutlm-invoices python=3.11 -y
conda activate layoutlm-invoices
pip install torch==2.8.0 torch_npu==2.8.0.post4 \
-i https://pypi.tuna.tsinghua.edu.cn/simple
pip install transformers numpy \
-i https://pypi.tuna.tsinghua.edu.cn/simplepython inference.py --device npu编程接口:
from inference import LayoutLMQA
import torch
qa = LayoutLMQA(model_path="./layoutlm-invoices", device="npu")
words = ["Invoice", "Date", ":", "January", "1", ",", "2024"]
boxes = torch.randint(0, 500, (1, len(words), 4))
image = torch.randn(1, 3, 224, 224)
start_logits, end_logits = qa.answer(words, boxes, image)python inference.py --device npu预期输出:start/end logits 张量形状,无运行时错误。
测试条件:3 组合成发票文档(words+boxes+image),NPU 预热 1 轮。
| 指标 | 数值 |
|---|---|
| CPU 吞吐量 | 7.4 docs/s |
| NPU 吞吐量 | 36.5 docs/s |
| CPU/NPU 加速比 | 4.9 × |
多模态模型由于同时处理图像和文本,推理时间高于纯文本模型。但 LayoutLM 的 BERT backbone(12 层)在 NPU 上仍有近 5× 加速。
分别在 CPU 和 NPU 上对 3 组合成文档推理,比较 start+end logits 拼接向量的余弦相似度和 MAE。合成图像和随机 boxes 模拟真实文档场景。
| 指标 | 数值 |
|---|---|
| 平均余弦相似度 | 0.999442 |
| 精度误差率 | 0.0558% |
结论:精度误差率 0.0558%,远低于 1% 要求,评测通过。多模态模型的误差略高于纯文本模型,原因是图像特征(CNN)的计算误差叠加。
(x0, y0, x1, y1) 归一化到 0-1000,编码为 4 维向量加入位置嵌入token_classifier_head 权重(从 token classification 模型转 QA 的残留)LayoutLMForQuestionAnswering.from_pretrained() 加载,model.to("npu:0") 迁移LayoutLMTokenizer(非 AutoTokenizer)处理 words+boxes 分词pixel_values 单独传入(不经过 tokenizer),与文本嵌入在模型内部融合torch.randn 生成 pixel_values 和随机 boxes,验证算子一致性而非模型语义import torch, torch_npu
from transformers import LayoutLMForQuestionAnswering, LayoutLMTokenizer
model = LayoutLMForQuestionAnswering.from_pretrained("layoutlm-invoices").to("npu:0")
tokenizer = LayoutLMTokenizer.from_pretrained("layoutlm-invoices")
words = ["Invoice", "#", ":", "INV-2024-001"]
boxes = torch.tensor([[[10,10,60,20],[70,10,80,20],[85,10,95,20],
[105,30,220,50]]]) # (1, num_words, 4), 归一化 0-1000
image = torch.randn(1, 3, 224, 224)
encoding = tokenizer(words, boxes=boxes, return_tensors="pt", padding=True)
encoding["pixel_values"] = image
encoding = {k: v.to("npu:0") for k, v in encoding.items()}
with torch.no_grad():
outputs = model(**encoding)
start_idx = torch.argmax(outputs.start_logits)
end_idx = torch.argmax(outputs.end_logits)
input_ids = encoding["input_ids"][0]
answer = tokenizer.decode(input_ids[start_idx:end_idx+1])coord / max(width, height) * 1000)。未归一化的像素坐标会导致位置编码异常。token_classifier_head 权重标记为 UNEXPECTED(从 token classification 微调转为 QA 的残留),不影响推理但会产生警告信息。