hung-k-nguyen commited on
Commit
63f2a8f
·
0 Parent(s):

document-classification-v1 — open-weight open-vocab document classifier (ONNX)

Browse files
.gitattributes ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ modules/bge-text/tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
+ modules/omni-image/tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ pipeline_tag: zero-shot-image-classification
4
+ language:
5
+ - en
6
+ tags:
7
+ - zero-shot-image-classification
8
+ - image-classification
9
+ - document-ai
10
+ - open-vocabulary
11
+ - open-weights
12
+ datasets:
13
+ - nutrientdocs/document-classification-benchmark
14
+ ---
15
+
16
+ # document-classification-v1 — open-weight
17
+
18
+ **An open-weight, open-vocabulary document classifier you can download and run.** Supply any set of text
19
+ labels at inference; the model scores a document image against them by calibrated cosine and returns a
20
+ per-label match probability. No fixed class list, no per-class training.
21
+
22
+ The **open-weight** sibling of the commercial flagship
23
+ [`document-classification-v2`](https://huggingface.co/nutrientdocs/document-classification-v2). It ships as
24
+ two self-contained **ONNX** graphs — an image tower and a text tower — that you run with `onnxruntime`.
25
+ `embed_dim: 1024`; classification `p = sigmoid(scale·cos + bias)` (calibration in
26
+ `modules/omni-image/config.json`).
27
+
28
+ - 🎯 **Try it:** [document-classification-demo](https://huggingface.co/spaces/nutrientdocs/document-classification-demo)
29
+ - 🏆 **Leaderboard:** [document-classification-leaderboard](https://huggingface.co/spaces/nutrientdocs/document-classification-leaderboard)
30
+ - 📊 **Benchmark:** [document-classification-benchmark](https://huggingface.co/datasets/nutrientdocs/document-classification-benchmark)
31
+ - 🏵️ **Flagship (commercial):** [document-classification-v2](https://huggingface.co/nutrientdocs/document-classification-v2)
32
+
33
+ ## Results (macro-F1, zero-shot)
34
+
35
+ | Benchmark | **v1 (open)** | v2 (commercial) | best cloud VLM |
36
+ | --- | ---: | ---: | ---: |
37
+ | DocLayNet | **0.89** | 0.88 | 0.83 |
38
+ | Forms | 0.80 | 1.00 | 1.00 |
39
+ | Tobacco | 0.62 | 0.69 | 0.85 |
40
+ | OOD (unseen types) | 0.87 | 0.97 | — |
41
+ | OOV (synonym wording) | 0.74 | 0.80 | — |
42
+
43
+ Every entry is scored by the same open scorer — full ranking, plus a **generalist zero-shot baseline** and
44
+ each cloud model, on the
45
+ [leaderboard](https://huggingface.co/spaces/nutrientdocs/document-classification-leaderboard). v1 leads on the
46
+ visual document-type track (DocLayNet) as a free download; like all embedding models it trails large VLMs on
47
+ Tobacco (a read-the-header task). ~**3–7 docs/s on an A40** (image branch).
48
+
49
+ ## Usage (ONNX)
50
+
51
+ ```python
52
+ import numpy as np, onnxruntime as ort, json
53
+ from transformers import AutoProcessor, AutoTokenizer
54
+ from huggingface_hub import hf_hub_download
55
+
56
+ R = "nutrientdocs/document-classification-v1"
57
+ img_sess = ort.InferenceSession(hf_hub_download(R, "modules/omni-image/image_model.onnx"))
58
+ txt_sess = ort.InferenceSession(hf_hub_download(R, "modules/omni-image/text_model.onnx"))
59
+ cal = json.load(open(hf_hub_download(R, "modules/omni-image/config.json")))["calibration"]
60
+ proc = AutoProcessor.from_pretrained(R, subfolder="modules/omni-image") # bundled preprocessor
61
+ tok = AutoTokenizer.from_pretrained(R, subfolder="modules/omni-image") # bundled tokenizer (right-pad + attention_mask)
62
+
63
+ from PIL import Image
64
+ labels = ["invoice", "letter", "memo", "form", "scientific article", "resume"]
65
+ pix = proc(images=[Image.open("doc.png").convert("RGB")], return_tensors="np")["pixel_values"].astype(np.float16)
66
+ ie = img_sess.run(["image_emb"], {"pixel_values": pix})[0] # [1, 1024] L2
67
+ enc = tok(labels, padding=True, truncation=True, max_length=64, return_tensors="np")
68
+ te = txt_sess.run(["text_emb"], {"input_ids": enc["input_ids"].astype(np.int64),
69
+ "attention_mask": enc["attention_mask"].astype(np.int64)})[0] # [N,1024] L2
70
+ cos = (ie @ te.T)[0]
71
+ probs = 1 / (1 + np.exp(-(cal["scale"] * cos + cal["bias"])))
72
+ print(dict(zip(labels, probs.round(3).tolist())))
73
+ ```
74
+
75
+ ## What's in this repo
76
+ - `modules/omni-image/{image_model.onnx, text_model.onnx}` — the image + text towers (fp16, `onnxruntime`).
77
+ - `modules/omni-image/{config.json, preprocessor_config.json, tokenizer.json}` — calibration + the
78
+ preprocessor and tokenizer needed to run them. That's it — nothing else required.
79
+
80
+ Open weights under **Apache-2.0** — free to download and run. For the higher-accuracy commercial flagship
81
+ (on-prem, calibrated), see [`document-classification-v2`](https://huggingface.co/nutrientdocs/document-classification-v2).
82
+
83
+ ## About the author
84
+
85
+ <a href="https://nutrient.io/">
86
+ <img src="https://avatars2.githubusercontent.com/u/1527679?v=3&s=200" height="80" />
87
+ </a>
88
+
89
+ This project is maintained and funded by [Nutrient](https://nutrient.io/) - The deterministic document infrastructure enterprises run their highest-stakes workflows on: replayable output, clear exceptions, and full audit trails on the messy, regulated documents where AI alone breaks.
modules/omni-image/config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "embed_dim": 1024,
3
+ "calibration": {
4
+ "scale": 39.22854489999006,
5
+ "bias": -7.550847457627121
6
+ },
7
+ "format": "opaque-merged",
8
+ "n_tensors": 1202
9
+ }
modules/omni-image/image_model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2a53c51167fe784f2a079d93f05ab14ab3e8663f4b65867fb81e8871d99b5793
3
+ size 867778973
modules/omni-image/omni-calibration.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "checkpoints/omni-stage2-merged",
3
+ "split": "test",
4
+ "label_set": "canonical",
5
+ "n": 5056,
6
+ "scale": 39.22854489999006,
7
+ "bias": -7.550847457627121,
8
+ "nll_pair_before": 0.7197994543067421,
9
+ "nll_pair_after": 0.08995073535632168,
10
+ "ece_pair_before": 0.3980247445107305,
11
+ "ece_pair_after": 0.0032327794736899413,
12
+ "ece_top1_before": 0.18871585317257003,
13
+ "ece_top1_after": 0.21049625066654812
14
+ }
modules/omni-image/preprocessor_config.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "do_convert_rgb": null,
3
+ "do_normalize": true,
4
+ "do_rescale": true,
5
+ "do_resize": true,
6
+ "image_mean": [
7
+ 0.5,
8
+ 0.5,
9
+ 0.5
10
+ ],
11
+ "image_processor_type": "SiglipImageProcessor",
12
+ "image_std": [
13
+ 0.5,
14
+ 0.5,
15
+ 0.5
16
+ ],
17
+ "resample": 2,
18
+ "rescale_factor": 0.00392156862745098,
19
+ "size": {
20
+ "height": 512,
21
+ "width": 512
22
+ }
23
+ }
modules/omni-image/text_model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5b039657d9e56b39ebec1517b834a5f2c3c4e4a75e3574fe071938b803fa6004
3
+ size 1310191414
modules/omni-image/tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:93623af029cdc69b87f2864d3b2cc2424fdf16684f15e139b5b9d08ec34ced91
3
+ size 11423701
modules/omni-image/tokenizer_config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|im_end|>",
7
+ "errors": "replace",
8
+ "is_local": false,
9
+ "local_files_only": false,
10
+ "model_max_length": 131072,
11
+ "pad_token": "<|endoftext|>",
12
+ "split_special_tokens": false,
13
+ "tokenizer_class": "Qwen2Tokenizer",
14
+ "unk_token": null
15
+ }