gemmaExtract

Pull typed fields out of a document in one forward pass. No text generation, no broken JSON.

You give gemmaExtract a document and a list of fields you want. It returns each field's value, where that value sits in the text, whether the field is absent, and how confident it is. Values are read straight from the document (or computed from numbers in it), so the model cannot invent a string that is not there.

document  โ”€โ”€โ–บ  one forward pass  โ”€โ”€โ–บ  {field: value, span, p_null, confidence}

Text input only. gemmaExtract currently reads plain text (state must be a string). For scanned documents, photos or PDFs, run OCR first and pass the recognized text. Image input is not supported yet.

Quick start

import sys
from huggingface_hub import snapshot_download

path = snapshot_download("beyoru/gemmaExtract")
sys.path.insert(0, path)
from gemma_extract import GemmaExtract

model = GemmaExtract.from_pretrained(path)   # needs a CUDA GPU, ~18 GB in bf16

doc = """INVOICE No: INV-2026-0042      Date: 15/01/2026
Seller: Northwind Office Supply Ltd.
1. A4 paper (box)      x10   45.00   450.00
2. Toner cartridge     x2    120.00  240.00
Subtotal: 690.00   Tax 10%: 69.00   TOTAL DUE: 759.00
Paid: 800.00"""

out = model.extract(doc, {
    "invoice_no": {"type": "text",   "instructions": "Invoice number"},
    "date":       {"type": "date",   "instructions": "Invoice date"},
    "items":      {"type": "list",   "instructions": "Names of the items bought"},
    "total":      {"type": "number", "instructions": "Total amount due"},
    "po_number":  {"type": "text",   "instructions": "Purchase order number"},
})
{
  "invoice_no": {"value": "INV-2026-0042", "spans": [{"start": 12, "end": 25, "text": "INV-2026-0042"}], "null": false, "p_null": 0.0001},
  "date":       {"value": "2026-01-15", "guess": "15/01/2026", "null": false},
  "items":      {"value": ["A4 paper (box)", "Toner cartridge"], "null": false},
  "total":      {"value": 759.0, "guess": "759.00", "op": "none", "null": false},
  "po_number":  {"value": null, "null": true, "p_null": 1.0}
}

Vietnamese documents work the same way:

model.extract("Hร“A ฤฦ N GIร TRแปŠ GIA Tฤ‚NG ... Ngร y 20 thรกng 10 nฤƒm 2026 ... Tแป•ng cแป™ng thanh toรกn: 3.685.000",
              {"ngay": {"type": "date", "instructions": "Ngร y lแบญp hoรก ฤ‘ฦกn"},
               "tong": {"type": "number", "instructions": "Tแป•ng cแป™ng thanh toรกn"}})
# {"ngay": {"value": "2026-10-20"}, "tong": {"value": 3685000.0}}

Field types

type returns notes
text string + character span copied verbatim from the document
list list of strings + spans every item that matches
number float read from the document, or computed from numbers in it (sum, diff, avg, count, div, mul; operands are returned)
date YYYY-MM-DD day-first by default, Vietnamese and English formats

Every field also gets p_null (probability the field is absent), null, confidence and guess (the best candidate, returned even when the field is judged absent).

Options:

  • null_threshold: per request or per field. The default depends on the detected language (en 0.40, vi 0.80, any other language 0.80) and leans conservative: it rarely reports a present value as missing. Raise recall of absent fields by lowering it.
  • number_style: "en" (1,234.5) or "vi" (1.234,5). By default it is detected from the document.

Results

Measured on half of an H200, bf16, batch 1, default null threshold.

Reading comprehension with unanswerable questions (1,000 held-out questions each, natural mix of answerable and unanswerable):

accuracy absent field reported as present present field reported as absent F1 on answerable
SQuAD 2.0 validation (EN) 79.3% 147 / 495 9 / 505 0.93
UIT-ViQuAD 2.0 validation (VI) 81.4% 63 / 318 16 / 682 0.91

Other languages (XQuAD, 300 questions per language, all answerable). The extraction head was not tuned for these languages; they come from the multilingual Gemma 4 backbone:

language EM F1 present field reported as absent
English 70.3 86.6 1 / 300
German 60.3 80.4 2 / 300
Spanish 58.0 80.2 6 / 300
Chinese 64.7 84.1 4 / 300
Russian 61.3 79.5 7 / 300
Thai 63.3 84.1 5 / 300
Arabic 56.7 77.3 9 / 300
Hindi 59.3 77.0 2 / 300

(Absent-field counts use the threshold applied to each language: 0.40 for English, 0.80 otherwise.)

Receipt fields (SROIE test, 361 receipts, exact match):

company date total address (F1)
96.4 98.9 93.4 67.5

Speed: the document is read once for all fields, so latency barely moves with the number of fields. Model pass for 1 / 5 / 10 fields: 52 / 53 / 54 ms (16 fields per pass; 20 fields take two passes, ~110 ms). A full extract() call on a 5-7 field invoice, including decoding and a small HTTP wrapper, takes ~150-180 ms.

Limitations

  • Arithmetic over the document (e.g. change = paid โˆ’ total) is partial: about 7 in 10 computed answers are right on English financial tables and about 1 in 2 on English narrative text; on Vietnamese documents it is not reliable yet and may return one of the operands instead. Copy-type numbers (totals, amounts printed on the document) are much more reliable.
  • Span-based: answers that must be paraphrased or merged from scattered places (long free-form answers, multi-line addresses) are weaker than with a generative model.
  • Up to 2,048 tokens per pass; longer documents are truncated (usage.truncated tells you).
  • Languages other than English and Vietnamese work (see the XQuAD table) but absent-field detection has only been checked in English and Vietnamese.
  • Text input only: images, scans and PDFs need an OCR step first.
  • Not supported yet: time, duration, estimate, enum fields and image input.
  • Runs with transformers (โ‰ฅ 5.17); there is no serving-engine integration yet.

How it works, briefly

A Gemma 4 E4B backbone whose global-attention layers attend in both directions, plus a small extraction head. The document comes first and the field schema after it; each field's description becomes a query that points to the start and end of its value in the document, decides whether the field is absent, and, for numbers, picks an operation and its operands. Nothing is decoded token by token.

License

CC BY-NC 4.0: free for non-commercial use with attribution; commercial use needs prior written permission from the author (beyoru). gemmaExtract is derived from Gemma 4 E4B (Apache 2.0); see NOTICE and LICENSE-APACHE-2.0.

Downloads last month
35
Safetensors
Model size
8B params
Tensor type
BF16
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for beyoru/gemmaExtract

Finetuned
(444)
this model
Quantizations
1 model