File size: 4,417 Bytes
1c0079f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | ---
license: mit
language:
- en
library_name: graf
tags:
- graf
- paragraph-segmentation
- token-classification
- news
- encoder
- modernbert
- on-device
pipeline_tag: token-classification
base_model: jhu-clsp/ettin-encoder-17m
metrics:
- f1
---
# Graf
Paragraphs will be stacked until morale improves.
A small encoder that recovers paragraph breaks in flattened news text.
| | |
| --- | --- |
| **Task** | Paragraph break recovery (token classification) |
| **Size** | ~19 MB weights (trimmed vocab, int8 embeddings, fp16 rest) |
| **Runtime** | CPU/GPU (PyTorch); browser (ONNX Runtime WASM) |
| **Weights** | [`main`](https://huggingface.co/localangle/graf/tree/main) · [`web/`](https://huggingface.co/localangle/graf/tree/main/web) for WASM |
| **Demo** | [localangle/graf-demo](https://huggingface.co/spaces/localangle/graf-demo) (in-browser) |
Graf does **not** rewrite the article. It scores eligible token ends as
`BREAK` / `NO_BREAK`, then splits the flattened string at predicted breaks.
> Flattened: *City council approved a library renovation… Construction starts in March…*
> → Graf inserts breaks between the recovered paragraphs.
## Try it
- **Live demo:** [localangle/graf-demo](https://huggingface.co/spaces/localangle/graf-demo) — paste flattened news, paragraphize in the browser.
- **Local UI:** from the [graf](https://github.com/cjdd3b/graf) repo, `graf-web --web-package checkpoints/graf-web`.
## Install
```bash
pip install "graf @ git+https://github.com/cjdd3b/graf.git"
# or, from a checkout:
pip install -e .
```
Python ≥ 3.11. Needs `torch`, `transformers`, `safetensors`, `huggingface_hub`.
## Usage
```python
from graf.infer import GrafParagraphizer
graf = GrafParagraphizer("localangle/graf") # thr from graf.json (0.41)
result = graf.paragraphize(
"City council approved a library renovation after public comment. "
"Paulsen and Chen voted against the measure. Construction starts in March."
)
print("\n\n".join(result.paragraphs))
```
Load a local package folder the same way:
```python
graf = GrafParagraphizer("checkpoints/graf")
```
## Files
| File | Contents |
| --- | --- |
| `graf.json` | Format, threshold, kept vocabulary, source metadata |
| `weights.safetensors` | Trimmed encoder + head (int8 token embeddings, fp16 elsewhere) |
| `encoder/` | Ettin/ModernBERT config for the trimmed vocabulary |
| `tokenizer/` | Full tokenizer (ids remapped at inference via `vocab_map`) |
| `web/` | Browser package: `model.onnx` (~17 MB int8), `vocab_map.bin`, `graf-web.json`, `tokenizer/` |
PyTorch weights expand to fp32 at load; only storage is compressed. The `web/`
ONNX graph is what the static demo runs in WASM (soft cap 2048 tokens).
## Accuracy
Held-out Extra structure v0.2 test set (publisher-disjoint), package threshold
0.41 (student before compression; packaging keeps break agreement ≈ Jaccard 0.98):
| Metric | Value |
| --- | --- |
| Boundary F1 | 0.871 |
| Precision / Recall | 0.845 / 0.900 |
| PR-AUC | 0.948 |
| Body–body precision / recall | 0.829 / 0.894 |
| Median \|Δ block count\| | 2.0 |
## How it works
Flattened article text is packed into overlapping 2048-token windows. Eligible
token ends (positions that can host a paragraph boundary) are scored for
`BREAK`. Probabilities are averaged across windows, thresholded, and used to
reconstruct paragraph blocks.
The published package is a compressed [Ettin](https://huggingface.co/jhu-clsp/ettin-encoder-17m)
student (~17M) distilled for on-device / in-browser use.
## Limitations
- English news-style prose; other domains are unmeasured.
- Training text / Extra labels are not redistributed here.
- Exact full-article reconstruction is intentionally hard; treat boundary F1 /
body–body metrics as the primary quality signal.
- Browser int8 ONNX drifts slightly vs the PyTorch package; the web meta
threshold is nudged for recall (see `web/graf-web.json`).
## Built on
- [Ettin encoder 17M](https://huggingface.co/jhu-clsp/ettin-encoder-17m) (MIT)
by JHU CLSP. Fine-tuned and compressed for Graf; see
`THIRD_PARTY_NOTICES.md`.
## License
MIT for the Graf code and this package's weights, subject to the Ettin MIT
notice above.
## Citation
```bibtex
@software{graf_2026,
title = {Graf: Paragraph break recovery for flattened news},
author = {Local Angle},
year = {2026},
url = {https://huggingface.co/localangle/graf},
}
```
|