LFM2.5-350M-Extract-ML-LoRA

A 350M model that fills the JSON schema you hand it, and says what kind of document it is looking at — in eight languages.

LoRA fine-tune of LiquidAI/LFM2.5-350M on andreagemelli/xfund-docai-xl. Two tasks share one prompt shape: information extraction (return the keys you were given, values copied verbatim from the page) and document classification (pick one of twelve classes). Italian, German, Spanish, French, Portuguese, Chinese, Japanese, English — keys, descriptions and class names all in the document's own language.

Successor to andreagemelli/LFM2.5-350M-IT-Extract (Italian, extraction only). It powers Scrivano, an offline desktop Document AI app.

The adapters are already merged into the weights — load it like any causal LM, peft not required.

Results

Measured on the 785-row val split of xfund-docai-xl, greedy decoding (do_sample=False, max_new_tokens=1024, bf16). Score is per-document field F1 averaged over documents; an output that is not valid JSON is scored 0 and kept in the average.

Model Avg F1 Non-parsing outputs
LiquidAI/LFM2.5-350M — base, zero-shot 0.2640 195 / 723
andreagemelli/LFM2.5-350M-IT-Extract — previous fine-tune, Italian + extraction only 0.2229 229 / 723
full fine-tune, same data and schedule 0.7364 6 / 785
this model — LoRA 0.7504 3 / 785

Per language

F1 (non-parsing outputs / rows)

base IT-Extract full fine-tune LoRA (this model)
it 0.2459 (17/111) 0.3135 (37/111) 0.7141 (0/111) 0.7269 (0/111)
de 0.2948 (27/91) 0.2574 (29/91) 0.7890 (0/91) 0.8143 (0/91)
es 0.2850 (21/104) 0.1508 (42/104) 0.7402 (0/104) 0.7424 (0/104)
fr 0.2529 (28/115) 0.2160 (35/115) 0.7669 (3/115) 0.8060 (0/115)
pt 0.2268 (21/103) 0.1575 (40/103) 0.6026 (1/103) 0.5948 (2/103)
zh 0.3049 (36/97) 0.2341 (23/97) 0.8763 (0/97) 0.8815 (0/97)
ja 0.2461 (45/102) 0.2306 (23/102) 0.7596 (0/102) 0.7714 (1/102)
en 0.6011 (2/62) 0.6281 (0/62)

Per task

base IT-Extract full fine-tune LoRA (this model)
information extraction 0.2517 (191/385) 0.3979 (74/385) 0.7410 (6/436) 0.7617 (3/436)
document classification 0.2781 (4/338) 0.0237 (155/338) 0.7307 (0/349) 0.7364 (0/349)
overall 0.2640 (195/723) 0.2229 (229/723) 0.7364 (6/785) 0.7504 (3/785)

Reading the numbers

  • LoRA beats the full fine-tune on both tasks and on seven of eight languages, on the same data and the same one-epoch budget. Portuguese is the single regression (−0.008), inside the noise of a 103-document split.
  • The base model's low score is mostly a formatting failure: 195 of 723 outputs were not JSON at all. It understands the documents better than 0.26 suggests; it does not obey the output contract.
  • The Italian-only predecessor is worse than the base model overall. It collapses on classification — 0.0237, with 155 of 338 outputs unparseable — because it never saw the task and answers it with an extraction-shaped object. It also drops below base in six of seven languages. It is better than base at what it was trained on (extraction 0.3979 vs 0.2517, Italian 0.3135 vs 0.2459), which is what overfitting looks like from the inside.
  • This model is the most reliable formatter of the four: 3 non-parsing outputs in 785 (0.4%).
  • English is the weakest language (0.6281). It comes from FUNSD — noisy business and laboratory paperwork whose pages rarely state a document type, so English classification leans almost entirely on the synthetic pages.

Caveats

  • The schema handed to the model lists exactly the keys the page answers. Scores here are an upper bound on a run where the caller does not already know which fields are present.
  • The metric double-penalises a wrong value — it counts as a false positive and a false negative.
  • Base and IT-Extract were measured on this same val split before English was added (723 of the current 785 rows). Their per-language numbers are directly comparable; their overall averages are over seven languages, not eight.
  • Reproduce with main.py from the Scrivano repo: uv run main.py --model-id andreagemelli/LFM2.5-350M-Extract-ML-LoRA.

Usage

import json, torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "andreagemelli/LFM2.5-350M-Extract-ML-LoRA"
device = "cuda" if torch.cuda.is_available() else "cpu"

model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.bfloat16).to(device)
tokenizer = AutoTokenizer.from_pretrained(model_id)

schema = {
    "cognome": "il cognome della persona",
    "nome": "il nome di battesimo della persona",
    "data-nascita": "la data di nascita della persona",
}
system = (
    "You are an expert document analysis model.\n"
    "Task: information extraction\n"
    "Return a JSON object with exactly the keys listed below, in the same order. Every value "
    "must be copied verbatim from the document. Omit a key whose value is absent.\n\n"
    "Schema:\n" + "".join(f"{k}: {v}.\n" for k, v in schema.items())
)
page_text = "COMUNE DI TORINO\nCognome: VALLE\nNome: LUISA\nData di nascita: 22/12/1977\n..."

inputs = tokenizer.apply_chat_template(
    [{"role": "system", "content": system}, {"role": "user", "content": page_text}],
    add_generation_prompt=True, return_tensors="pt", return_dict=True,
).to(device)

out = model.generate(**inputs, max_new_tokens=1024, do_sample=False)
answer = tokenizer.decode(out[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True).strip()
print(json.loads(answer))
# {"cognome": "VALLE", "nome": "LUISA", "data-nascita": "22/12/1977"}

Prompt shape

Both tasks open the same way and diverge on one line. Write the keys, their descriptions and the class names in the document's language — that is how the model was trained.

Information extraction

You are an expert document analysis model.
Task: information extraction
Return a JSON object with exactly the keys listed below, in the same order. Every value must
be copied verbatim from the document. Omit a key whose value is absent.

Schema:
cognome: il cognome della persona.
nome: il nome di battesimo della persona.
data-nascita: la data di nascita della persona.

{"cognome": "VALLE", "nome": "LUISA", "data-nascita": "22/12/1977"}

Document classification

You are an expert document analysis model.
Task: document classification
Assign the document to exactly one of the classes listed below. Answer with a JSON object of
the form {"class": "<class>"}.

Classes:
領収書: 代金が支払われたこと、または物品を受領したことを示す書面.
委任状: 本人が代理人に手続きを委任することを示す書面.
… (all twelve, shuffled)

{"class": "委任状"}

The twelve classes and the per-language key vocabulary live in classes.json and schemas.json.

Training

LoRA via peft, SFT via trl, one epoch on 2 519 chat examples, on Colab. Adapters merged into the base weights before upload.

Base model LiquidAI/LFM2.5-350M (bf16)
Dataset andreagemelli/xfund-docai-xl — 2 519 train / 785 val rows
Rank / alpha / dropout r=128, lora_alpha=256, lora_dropout=0.1, bias="none"
Target modules GLU w1 w2 w3 · attention q_proj k_proj v_proj out_proj · conv in_proj out_proj
Epochs · batch size 1 · 4 per device
LR · schedule 5e-5 · linear, 100 warmup steps
Selection eval each epoch, load_best_model_at_end=True
from peft import LoraConfig, TaskType

lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    inference_mode=False,
    r=128,
    lora_alpha=256,
    lora_dropout=0.1,
    target_modules=["w1", "w2", "w3", "q_proj", "k_proj", "v_proj", "out_proj", "in_proj"],
    bias="none",
    modules_to_save=None,
)
from trl import SFTConfig

sft_config = SFTConfig(
    output_dir="./lfm2-sft-lora",
    num_train_epochs=1,
    per_device_train_batch_size=4,
    learning_rate=5e-5,
    lr_scheduler_type="linear",
    warmup_steps=100,
    logging_steps=10,
    save_strategy="epoch",
    eval_strategy="epoch",
    load_best_model_at_end=True,
)

Limitations

  • 350M parameters. Read the output. It is a small model doing a copying task; it will occasionally copy the wrong box, and a right-looking value in the wrong field is not flagged.
  • Text in, text out. No vision — you supply the page text, from a PDF text layer or your own OCR. In Scrivano, OCR costs roughly 0.10 F1 against a clean text layer.
  • The schema is your job. The model mirrors what it is handed; a vague description or a key the page never answers degrades the result. Keep the schema tight and in the document's language.
  • JSON is not guaranteed. 3 of 785 val outputs did not parse. Wrap json.loads and have a fallback.
  • Classification is twelve heuristic classes, read off page titles rather than human-annotated, and the English half of it rests mostly on synthetic pages.
  • Single-page documents only; no OCR-noise augmentation on the synthetic pages.
  • Portuguese and English are the weakest languages (~0.60); Chinese, German and French the strongest.

Related

Base model LiquidAI/LFM2.5-350M
Dataset andreagemelli/xfund-docai-xl
Previous model (Italian, extraction only) andreagemelli/LFM2.5-350M-IT-Extract
…its GGUF build andreagemelli/LFM2.5-350M-IT-Extract-GGUF
Previous dataset (Italian, extraction only) andreagemelli/xfund-kie-it
App / evaluation code andreagemelli/scrivano

The full fine-tune referenced in the tables (andreagemelli/LFM2.5-350M-Extract-ML) is not published — this LoRA outscores it.

Licence

CC BY-NC-SA 4.0 — NonCommercial, ShareAlike. Inherited from the training data: XFUND is CC BY-NC-SA 4.0 and FUNSD is non-commercial research use only, so the strictest terms apply. Stacked on top of the base model's LFM Open License v1.0; the NonCommercial term governs.

Citation

@misc{gemelli2026lfm25extractml,
  title        = {LFM2.5-350M-Extract-ML-LoRA: a tiny multilingual model for document
                  information extraction and classification},
  author       = {Gemelli, Andrea},
  year         = {2026},
  howpublished = {\url{https://huggingface.co/andreagemelli/LFM2.5-350M-Extract-ML-LoRA}}
}

@misc{gemelli2026xfunddocaixl,
  title        = {xfund-docai-xl: eight languages, two tasks, one prompt shape},
  author       = {Gemelli, Andrea},
  year         = {2026},
  howpublished = {\url{https://huggingface.co/datasets/andreagemelli/xfund-docai-xl}}
}

Please also cite the upstream corpora this model is trained on:

@inproceedings{xu2022xfund,
  title     = {{XFUND}: A Benchmark Dataset for Multilingual Visually Rich Form Understanding},
  author    = {Xu, Yiheng and Lv, Tengchao and Cui, Lei and Wang, Guoxin and Lu, Yijuan and
               Florencio, Dinei and Zhang, Cha and Wei, Furu},
  booktitle = {Findings of the Association for Computational Linguistics: ACL 2022},
  year      = {2022}
}

@inproceedings{jaume2019funsd,
  title     = {{FUNSD}: A Dataset for Form Understanding in Noisy Scanned Documents},
  author    = {Jaume, Guillaume and Kemal Ekenel, Hazim and Thiran, Jean-Philippe},
  booktitle = {2019 International Conference on Document Analysis and Recognition Workshops
               (ICDARW)},
  year      = {2019}
}
Downloads last month
209
Safetensors
Model size
0.4B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for andreagemelli/LFM2.5-350M-Extract-ML-LoRA

Adapter
(34)
this model

Dataset used to train andreagemelli/LFM2.5-350M-Extract-ML-LoRA