Views
No views yet
| Domain | Fields | Description |
|---|---|---|
general | 13 scalar + line items | Standard business invoices |
receipt | 7 scalar + line items | POS / thermal receipts |
medical | 16 scalar + procedures | Hospital bills |
insurance | 22 scalar + items | Insurance EOB / claims |
logistics | 22 scalar + charges | Freight / shipping invoices |
inference_example.py:1pip install transformers torch easyocr huggingface_hub Pillow
2
3# Download inference_example.py from this repo, then:
4python inference_example.py invoice.png # auto-detect domain
5python inference_example.py invoice.png --domain general # force domain1from huggingface_hub import snapshot_download
2from transformers import AutoModelForTokenClassification, LayoutLMv3Processor
3import json, torch
4
5# Download all domains
6snapshot_download("rhlprj/invoice-layoutlmv3-multidomain", local_dir="models/")
7
8# Load one domain
9domain = "general"
10model = AutoModelForTokenClassification.from_pretrained(f"models/{domain}")
11processor = LayoutLMv3Processor.from_pretrained(f"models/{domain}", apply_ocr=False)
12
13with open(f"models/{domain}/label_maps.json") as f:
14 label_maps = json.load(f)
15id2label = {int(k): v for k, v in label_maps["id2label"].items()}
16
17# Encode (supply your own OCR words + bboxes normalised to 0-1000)
18encoding = processor(
19 images=pil_image,
20 text=ocr_words, # List[str]
21 boxes=boxes_0_1000, # List[List[int]], each [x0, y0, x1, y1] in 0-1000
22 truncation=True,
23 padding="max_length",
24 max_length=512,
25 return_tensors="pt",
26)
27
28# Run model
29with torch.no_grad():
30 outputs = model(**{k: v.to(model.device) for k, v in encoding.items()})
31token_logits = outputs.logits[0].cpu()
32
33# CRITICAL: map subword predictions back to word level using word_ids()
34# Do NOT use preds[1:len(words)+1] — that assumes 1 token per word and WILL break.
35word_ids = encoding.word_ids(0)
36first_subword = {}
37for tok_idx, w_id in enumerate(word_ids):
38 if w_id is not None and w_id not in first_subword:
39 first_subword[w_id] = tok_idx
40
41for w_idx in range(len(ocr_words)):
42 tok_idx = first_subword.get(w_idx)
43 if tok_idx is not None:
44 label = id2label[int(token_logits[tok_idx].argmax())]
45 print(f" {ocr_words[w_idx]:30s} -> {label}")INV-2025-00782 becomes 6+ subword tokens. The model predicts
one BIO label per subword, so you must use encoding.word_ids(0) to map
predictions back to word level. Taking predictions[1:len(words)+1] is
incorrect and will produce garbage labels.inference_example.py for the complete, tested implementation.microsoft/layoutlmv3-base (133M params)label_maps.json with the full BIO label set.
Labels follow the format: O, B-<field>, I-<field>.