cjdd3b commited on
Commit
1c0079f
·
verified ·
1 Parent(s): ed256e2

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,35 +1,5 @@
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
 
1
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
 
2
  *.bin filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
3
  *.onnx filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
4
  *.pt filter=lfs diff=lfs merge=lfs -text
5
  *.pth filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
README.md ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language:
4
+ - en
5
+ library_name: graf
6
+ tags:
7
+ - graf
8
+ - paragraph-segmentation
9
+ - token-classification
10
+ - news
11
+ - encoder
12
+ - modernbert
13
+ - on-device
14
+ pipeline_tag: token-classification
15
+ base_model: jhu-clsp/ettin-encoder-17m
16
+ metrics:
17
+ - f1
18
+ ---
19
+
20
+ # Graf
21
+
22
+ Paragraphs will be stacked until morale improves.
23
+
24
+ A small encoder that recovers paragraph breaks in flattened news text.
25
+
26
+ | | |
27
+ | --- | --- |
28
+ | **Task** | Paragraph break recovery (token classification) |
29
+ | **Size** | ~19 MB weights (trimmed vocab, int8 embeddings, fp16 rest) |
30
+ | **Runtime** | CPU/GPU (PyTorch); browser (ONNX Runtime WASM) |
31
+ | **Weights** | [`main`](https://huggingface.co/localangle/graf/tree/main) · [`web/`](https://huggingface.co/localangle/graf/tree/main/web) for WASM |
32
+ | **Demo** | [localangle/graf-demo](https://huggingface.co/spaces/localangle/graf-demo) (in-browser) |
33
+
34
+ Graf does **not** rewrite the article. It scores eligible token ends as
35
+ `BREAK` / `NO_BREAK`, then splits the flattened string at predicted breaks.
36
+
37
+ > Flattened: *City council approved a library renovation… Construction starts in March…*
38
+ > → Graf inserts breaks between the recovered paragraphs.
39
+
40
+ ## Try it
41
+
42
+ - **Live demo:** [localangle/graf-demo](https://huggingface.co/spaces/localangle/graf-demo) — paste flattened news, paragraphize in the browser.
43
+ - **Local UI:** from the [graf](https://github.com/cjdd3b/graf) repo, `graf-web --web-package checkpoints/graf-web`.
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install "graf @ git+https://github.com/cjdd3b/graf.git"
49
+ # or, from a checkout:
50
+ pip install -e .
51
+ ```
52
+
53
+ Python ≥ 3.11. Needs `torch`, `transformers`, `safetensors`, `huggingface_hub`.
54
+
55
+ ## Usage
56
+
57
+ ```python
58
+ from graf.infer import GrafParagraphizer
59
+
60
+ graf = GrafParagraphizer("localangle/graf") # thr from graf.json (0.41)
61
+ result = graf.paragraphize(
62
+ "City council approved a library renovation after public comment. "
63
+ "Paulsen and Chen voted against the measure. Construction starts in March."
64
+ )
65
+ print("\n\n".join(result.paragraphs))
66
+ ```
67
+
68
+ Load a local package folder the same way:
69
+
70
+ ```python
71
+ graf = GrafParagraphizer("checkpoints/graf")
72
+ ```
73
+
74
+ ## Files
75
+
76
+ | File | Contents |
77
+ | --- | --- |
78
+ | `graf.json` | Format, threshold, kept vocabulary, source metadata |
79
+ | `weights.safetensors` | Trimmed encoder + head (int8 token embeddings, fp16 elsewhere) |
80
+ | `encoder/` | Ettin/ModernBERT config for the trimmed vocabulary |
81
+ | `tokenizer/` | Full tokenizer (ids remapped at inference via `vocab_map`) |
82
+ | `web/` | Browser package: `model.onnx` (~17 MB int8), `vocab_map.bin`, `graf-web.json`, `tokenizer/` |
83
+
84
+ PyTorch weights expand to fp32 at load; only storage is compressed. The `web/`
85
+ ONNX graph is what the static demo runs in WASM (soft cap 2048 tokens).
86
+
87
+ ## Accuracy
88
+
89
+ Held-out Extra structure v0.2 test set (publisher-disjoint), package threshold
90
+ 0.41 (student before compression; packaging keeps break agreement ≈ Jaccard 0.98):
91
+
92
+ | Metric | Value |
93
+ | --- | --- |
94
+ | Boundary F1 | 0.871 |
95
+ | Precision / Recall | 0.845 / 0.900 |
96
+ | PR-AUC | 0.948 |
97
+ | Body–body precision / recall | 0.829 / 0.894 |
98
+ | Median \|Δ block count\| | 2.0 |
99
+
100
+ ## How it works
101
+
102
+ Flattened article text is packed into overlapping 2048-token windows. Eligible
103
+ token ends (positions that can host a paragraph boundary) are scored for
104
+ `BREAK`. Probabilities are averaged across windows, thresholded, and used to
105
+ reconstruct paragraph blocks.
106
+
107
+ The published package is a compressed [Ettin](https://huggingface.co/jhu-clsp/ettin-encoder-17m)
108
+ student (~17M) distilled for on-device / in-browser use.
109
+
110
+ ## Limitations
111
+
112
+ - English news-style prose; other domains are unmeasured.
113
+ - Training text / Extra labels are not redistributed here.
114
+ - Exact full-article reconstruction is intentionally hard; treat boundary F1 /
115
+ body–body metrics as the primary quality signal.
116
+ - Browser int8 ONNX drifts slightly vs the PyTorch package; the web meta
117
+ threshold is nudged for recall (see `web/graf-web.json`).
118
+
119
+ ## Built on
120
+
121
+ - [Ettin encoder 17M](https://huggingface.co/jhu-clsp/ettin-encoder-17m) (MIT)
122
+ by JHU CLSP. Fine-tuned and compressed for Graf; see
123
+ `THIRD_PARTY_NOTICES.md`.
124
+
125
+ ## License
126
+
127
+ MIT for the Graf code and this package's weights, subject to the Ettin MIT
128
+ notice above.
129
+
130
+ ## Citation
131
+
132
+ ```bibtex
133
+ @software{graf_2026,
134
+ title = {Graf: Paragraph break recovery for flattened news},
135
+ author = {Local Angle},
136
+ year = {2026},
137
+ url = {https://huggingface.co/localangle/graf},
138
+ }
139
+ ```
THIRD_PARTY_NOTICES.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Third-party notices
2
+
3
+ ## Ettin / ModernBERT backbone
4
+
5
+ This package's encoder is derived from
6
+ [`jhu-clsp/ettin-encoder-17m`](https://huggingface.co/jhu-clsp/ettin-encoder-17m)
7
+ (MIT License). Graf fine-tunes and compresses those weights; the original
8
+ license terms continue to apply to the Ettin contribution.
9
+
10
+ Paper: Weller et al., "Ettin: an Open Suite of Paired Encoders and Decoders"
11
+ (arXiv:2507.11412).
encoder/config.json ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "ModernBertForTokenClassification"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "bos_token_id": 42889,
8
+ "causal_mask": false,
9
+ "classifier_activation": "gelu",
10
+ "classifier_bias": false,
11
+ "classifier_dropout": 0.0,
12
+ "classifier_pooling": "mean",
13
+ "cls_token_id": 42889,
14
+ "decoder_bias": true,
15
+ "deterministic_flash_attn": false,
16
+ "dtype": "float32",
17
+ "embedding_dropout": 0.0,
18
+ "eos_token_id": 42890,
19
+ "global_attn_every_n_layers": 3,
20
+ "gradient_checkpointing": false,
21
+ "hidden_activation": "gelu",
22
+ "hidden_size": 256,
23
+ "id2label": {
24
+ "0": "NO_BREAK",
25
+ "1": "BREAK"
26
+ },
27
+ "initializer_cutoff_factor": 2.0,
28
+ "initializer_range": 0.02,
29
+ "intermediate_size": 384,
30
+ "is_causal": false,
31
+ "label2id": {
32
+ "BREAK": 1,
33
+ "NO_BREAK": 0
34
+ },
35
+ "layer_norm_eps": 1e-05,
36
+ "layer_types": [
37
+ "full_attention",
38
+ "sliding_attention",
39
+ "sliding_attention",
40
+ "full_attention",
41
+ "sliding_attention",
42
+ "sliding_attention",
43
+ "full_attention"
44
+ ],
45
+ "local_attention": 128,
46
+ "max_position_embeddings": 7999,
47
+ "mlp_bias": false,
48
+ "mlp_dropout": 0.0,
49
+ "model_type": "modernbert",
50
+ "norm_bias": false,
51
+ "norm_eps": 1e-05,
52
+ "num_attention_heads": 4,
53
+ "num_hidden_layers": 7,
54
+ "pad_token_id": 42891,
55
+ "position_embedding_type": "sans_pos",
56
+ "rope_parameters": {
57
+ "full_attention": {
58
+ "rope_theta": 160000.0,
59
+ "rope_type": "default"
60
+ },
61
+ "sliding_attention": {
62
+ "rope_theta": 160000.0,
63
+ "rope_type": "default"
64
+ }
65
+ },
66
+ "sep_token_id": 42890,
67
+ "sparse_pred_ignore_index": -100,
68
+ "sparse_prediction": false,
69
+ "tie_word_embeddings": true,
70
+ "transformers_version": "5.17.0",
71
+ "vocab_size": 42893
72
+ }
graf.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "embedding_key": "model.embeddings.tok_embeddings.weight",
3
+ "format": "graf-package-1",
4
+ "kept": 42893,
5
+ "max_length": 2048,
6
+ "name": "graf",
7
+ "source_model": "cjdd3b/graf-4b",
8
+ "storage": "token embeddings int8 per row; other weights fp16; computed in fp32",
9
+ "stride": 512,
10
+ "threshold": 0.41,
11
+ "vocabulary_full": 50368
12
+ }
tokenizer/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer/tokenizer_config.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "clean_up_tokenization_spaces": true,
4
+ "cls_token": "[CLS]",
5
+ "is_local": false,
6
+ "local_files_only": false,
7
+ "mask_token": "[MASK]",
8
+ "model_input_names": [
9
+ "input_ids",
10
+ "attention_mask"
11
+ ],
12
+ "model_max_length": 8192,
13
+ "pad_token": "[PAD]",
14
+ "sep_token": "[SEP]",
15
+ "tokenizer_class": "TokenizersBackend",
16
+ "unk_token": "[UNK]"
17
+ }
web/graf-web.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "break_label_index": 1,
3
+ "check": {
4
+ "max_abs_drift_break": 0.6738,
5
+ "window_tokens": 26
6
+ },
7
+ "cls_token_id": 50281,
8
+ "embedding_key": "model.embeddings.tok_embeddings.weight",
9
+ "format": "graf-web-1",
10
+ "max_length": 2048,
11
+ "model": "model.onnx",
12
+ "name": "graf",
13
+ "pad_token_id": 42891,
14
+ "quantization": "int8-mixed-rr",
15
+ "sep_token_id": 50282,
16
+ "size_mb": 17.4,
17
+ "soft_cap": 2048,
18
+ "source_package": {
19
+ "path": "checkpoints/graf",
20
+ "source_model": "cjdd3b/graf-4b",
21
+ "threshold": 0.41
22
+ },
23
+ "stride": 512,
24
+ "threshold": 0.37,
25
+ "threshold_note": "retuned on ORT int8 vs gold val (24 arts); package thr was 0.41",
26
+ "tokenizer": "tokenizer",
27
+ "unk_token_id": 50280,
28
+ "vocab_full": 50368,
29
+ "vocab_kept": 42893,
30
+ "vocab_map": "vocab_map.bin"
31
+ }
web/model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a0d09d22cf8864d7a30b1ec7b88f3b17785565bc9c18eb19fc57be9165d4bdd1
3
+ size 17396401
web/tokenizer/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
web/tokenizer/tokenizer_config.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "clean_up_tokenization_spaces": true,
4
+ "cls_token": "[CLS]",
5
+ "is_local": false,
6
+ "local_files_only": false,
7
+ "mask_token": "[MASK]",
8
+ "model_input_names": [
9
+ "input_ids",
10
+ "attention_mask"
11
+ ],
12
+ "model_max_length": 8192,
13
+ "pad_token": "[PAD]",
14
+ "sep_token": "[SEP]",
15
+ "tokenizer_class": "TokenizersBackend",
16
+ "unk_token": "[UNK]"
17
+ }
web/vocab_map.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aa7c3905b15ad8155c25dd1755b45b1d4ee54a3416c933f2d8dba0ed30ce50ff
3
+ size 201472
weights.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c37723b0405c487dcdf020cbeaafbe014dedbc48e7219c1e7f9edb1a7e597a77
3
+ size 19297568