--- license: apache-2.0 language: - en base_model: google-bert/bert-base-cased pipeline_tag: token-classification library_name: onnx tags: - grammatical-error-correction - gector - onnx - on-device datasets: - wikimedia/wikipedia --- # Proofbird Grammar — English `gector-bert-en` · on-device English grammar correction [![Try it in your browser](https://huggingface.co/datasets/huggingface/badges/resolve/main/open-in-hf-spaces-lg.svg)](https://huggingface.co/spaces/proofbird/try-grammar) **Try it in your browser:** type a sentence and see the corrections. The model runs on your own computer; nothing to install, and your text never leaves the page. **Version 1.0.0** · run `2026-11-06-0801` · tag [`v1.0.0`](https://huggingface.co/proofbird/gector-bert-en/tree/v1.0.0), the files the Proofbird app downloads. Pin it with `revision="v1.0.0"`. Versions: a new training run raises the first number, changed guards or rules the second, card or metadata fixes the third. A GECToR-style sequence tagger for English grammatical error correction, exported to ONNX (fp32) to run on the CPU. It is the on-device grammar model of [Proofbird](https://proofbird.app), a writing assistant for Windows, and it is published here so anyone can inspect what runs on their machine. GECToR does not rewrite text. For each token it predicts one edit tag (keep, delete, replace with a word, append a word, change case, change a verb form), and the edits are applied and the sentence re-tagged up to five times. The tagger only proposes local edits; it never paraphrases. ## Quick start [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/#fileId=https%3A//huggingface.co/proofbird/gector-bert-en/blob/main/quickstart.ipynb) No API key, no GPU. One file, [`quickstart.py`](quickstart.py), downloads the model and runs it with ONNX Runtime. ```bash pip install onnxruntime tokenizers huggingface_hub ``` ```python import importlib.util from huggingface_hub import hf_hub_download spec = importlib.util.spec_from_file_location("quickstart", hf_hub_download("proofbird/gector-bert-en", "quickstart.py")) quickstart = importlib.util.module_from_spec(spec) spec.loader.exec_module(quickstart) model = quickstart.Gector("proofbird/gector-bert-en") corrected, suggestions = model.correct("My brother have two cat.") print(corrected) print(suggestions) ``` ```text My brother has two cats. [{"start": 11, "length": 4, "original": "have", "replacement": "has"}, {"start": 20, "length": 3, "original": "cat", "replacement": "cats"}] ``` Each suggestion gives the character position in **your** text (`start`, `length`), what is there now (`original`) and what to put instead (`replacement`), so you can underline it and offer the fix. From the command line: `python quickstart.py "My brother have two cat."` prints every step below. ## What it corrects Subject-verb agreement (`The results shows` → `show`), tense and participle forms (`have went` → `have gone`), `a`/`an`, irregular plurals, confusables (`their`/`there`/`they're`, `its`/`it's`, `then`/`than`), doubled words, capitalisation at the start of a sentence, and everyday-register errors in questions, imperatives and the first and second person (`Does the machines work` → `Do`, `Opens the window` → `Open`). ## How it was trained - **Base encoder:** [`google-bert/bert-base-cased`](https://huggingface.co/google-bert/bert-base-cased) (Apache-2.0). A tagging head (5 000 edit labels) and an error-detection head sit on top. - **Data: synthetic only.** 3 000 000 sentences from English Wikipedia ([`wikimedia/wikipedia`](https://huggingface.co/datasets/wikimedia/wikipedia), `20231101.en`) and 250 000 sentences from [Tatoeba](https://tatoeba.org) were corrupted by hand-written English error rules; about 10% stay clean, so the model learns to leave correct text alone. 150 000 additional template sentences teach patterns Wikipedia rarely contains (questions, imperatives, first and second person, group and mass nouns, irregular plurals, errors in a later clause). - **No learner corpora** and no non-commercial data of any kind. No user text was used. - One epoch (100 344 steps, batch 64, peak learning rate 1.5e-5) on one NVIDIA L4. Run id `2026-11-06-0801`. ## How it was measured | Check | Result | |---|---| | Gold set (420 hand-written sentences: 266 with one error each, 154 correct) | **258 fixed, 154 left alone, 8 missed, 0 made worse** | | False alarms on 4 000 everyday Tatoeba sentences (held out from training) | 2 changed (0.05%) | | False alarms on 2 000 Wikipedia sentences left untouched by human editors | 3 changed (0.15%) | | Stability: the gold re-run with one extra token in front of each sentence | 8 of 206 sentences change result (the 4 capitalisation rows change by construction and are not counted) | The gold score is with the apply-side guards in `gector-guards.json` (below). The model was trained for precision: a missed error costs less than a wrong correction. It edits rarely, and what it leaves alone is not a judgement that the text is perfect. Word knowledge is its weakest area: a compound or mass noun it has not seen (`dog food`, `consumer market`) can draw a wrong number change, which the guards hold back where the shape is known. ## Files | File | What it is | |---|---| | `model.onnx` | The tagger (fp32, opset 17). Inputs `input_ids`, `attention_mask`; outputs `logits_labels` [1, T, 5000] and `logits_d` [1, T, 2]. | | `config.json` | The edit-label vocabulary (`id2label`). | | `vocab.txt`, `tokenizer.json`, `tokenizer_config.json` | The base encoder's WordPiece tokenizer. | | `gector-meta.json` | Export facts: encoder, tokenizer family, decoding defaults, run id. | | `gector-guards.json` | Apply-side guards: rules that hold back an edit the model gets wrong in a known way (for example a number change on a noun right after a numeral). Part of the measured result. | | `language-rules.json` | Language facts the runtime needs (sentence-final punctuation handling). | | `verb-form-vocab.txt` | The English verb-form table for the `$TRANSFORM_VERB_*` tags. | | `quickstart.py` | A complete Python runner: download, tokenize, run, decode, apply, suggestions. See *Quick start*. | | `quickstart.ipynb` | The same, as a notebook (the *Open in Colab* button). | The Proofbird app runs the same algorithm in C# with ONNX Runtime; the files are standard, so any GECToR-style decoder can use them. ## How it works, step by step The model never sees letters and never writes text. It reads numbers (token ids) and answers, for every word, which **edit** to make: one of 5 000 tags such as `$KEEP`, `$DELETE`, `$APPEND_an`, `$REPLACE_an` or `$TRANSFORM_SUF_ve_s` (change a word's ending). `quickstart.py` does everything around it. This is the real run of the example above. **1. Prepare the text.** `language-rules.json` says the final full stop comes off before the model sees the sentence (it goes back on at the end): `My brother have two cat`. **2. The request.** The words are looked up in the model's dictionary (`vocab.txt`). A rare word is split into pieces; the edit is read at its first piece. `[CLS]` stands for a dummy first word, `$START`, so an edit on it can add a word at the very beginning; `[SEP]` marks the end. ```text words $START My brother have two cat tokens [CLS] My brother have two cat [SEP] input_ids [101, 1422, 1711, 1138, 1160, 5855, 102] attention_mask [1, 1, 1, 1, 1, 1, 1] ``` Two inputs, both whole numbers (int64), shape `[1, 7]`: one sentence of 7 tokens. **3. The response.** Two outputs (float32): `logits_labels` `[1, 7, 5000]`, a score for each of the 5 000 edits at every token, and `logits_d` `[1, 7, 2]`, a score for "this word is correct" and "this word is wrong". A softmax turns each row into probabilities; the most probable edit at each word's first token is the one applied. **4. Pass by pass.** The corrected sentence goes back in, until a pass changes nothing (at most five passes). Connected errors are fixed one after the other. *Pass 1:* `My brother have two cat` | Word | Wrong | Chosen edit | Probability | |---|---|---|---| | $START | 0.0% | $KEEP | 100.0% | | My | 0.0% | $KEEP | 100.0% | | brother | 0.1% | $KEEP | 99.9% | | have | 80.9% | **$TRANSFORM_SUF_ve_s** | 91.6% | | two | 0.1% | $KEEP | 100.0% | | cat | 63.9% | $KEEP | 66.1% | → `My brother has two cat` *Pass 2:* `My brother has two cat` | Word | Wrong | Chosen edit | Probability | |---|---|---|---| | $START | 0.0% | $KEEP | 100.0% | | My | 0.0% | $KEEP | 100.0% | | brother | 0.0% | $KEEP | 100.0% | | has | 0.0% | $KEEP | 100.0% | | two | 0.0% | $KEEP | 100.0% | | cat | 99.9% | **$TRANSFORM_VERB_VB_VBZ** | 83.2% | → `My brother has two cats` *Pass 3:* `My brother has two cats` | Word | Wrong | Chosen edit | Probability | |---|---|---|---| | $START | 0.0% | $KEEP | 100.0% | | My | 0.0% | $KEEP | 100.0% | | brother | 0.0% | $KEEP | 100.0% | | has | 0.0% | $KEEP | 100.0% | | two | 0.0% | $KEEP | 100.0% | | cats | 0.0% | $KEEP | 100.0% | → `My brother has two cats` — no change, so it stops. **5. The suggestions.** The original and the final words are compared, and each changed stretch becomes one suggestion with its position in the original text (unchanged text keeps its own spacing), as in the Quick start output above. **6. Settings.** `Gector(repo, keep_confidence=0.0, min_error_prob=0.0)` takes the two GECToR precision knobs (the Proofbird app runs with both at 0, and at most five passes): - `keep_confidence` adds a bonus to `$KEEP` before choosing: higher means fewer, safer edits. - `min_error_prob` ignores an edit the model is less sure of, and leaves the whole sentence alone when no word is at least that likely to be wrong. With `min_error_prob=0.9` the example is left as it is: the highest "wrong" probability in pass 1 is 80.9%, below 0.9. ## What Proofbird adds `quickstart.py` is the model on its own. The [Proofbird](https://proofbird.app) app runs the same algorithm in C# and adds: - **Guards** (`gector-guards.json`, in this repository): per-language rules that hold back edits we measured as wrong in a known shape. The scores under *How it was measured* are with the guards. - **Spelling, checked separately** by the operating system's spell checker; the guards use it so a grammar edit is not made on a word that is misspelled. - **A card for each change** that names the error ("Subject–verb agreement") and, on request, explains it. ## Licence and attribution The model weights and the files in this repository are released under the [Apache License 2.0](LICENSE). If you redistribute the model or a model built from it, keep the [NOTICE](NOTICE) file with it (Apache-2.0, section 4(d)). The training data carries its own terms. Wikipedia text is © Wikipedia contributors, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). Tatoeba sentences are from [tatoeba.org](https://tatoeba.org), [CC BY 2.0 FR](https://creativecommons.org/licenses/by/2.0/fr/). The base encoder is `google-bert/bert-base-cased`, Apache-2.0. The tagging scheme and `verb-form-vocab.txt` come from the GECToR project (Omelianchuk et al., 2020, *GECToR – Grammatical Error Correction: Tag, Not Rewrite*), Apache-2.0. ## How to cite If you use or build on this model, please cite it: ```bibtex @misc{hartnett2026gectoren, author = {Hartnett, Patrick Andrew}, title = {Proofbird GECToR grammar model (English)}, year = {2026}, publisher = {Hugging Face}, howpublished = {\url{https://huggingface.co/proofbird/gector-bert-en}}, note = {Proofbird, https://proofbird.app} } ``` If you build on this model, a link back to this page is appreciated. ## Contact Published by Patrick Andrew Hartnett as personal work. Questions and reports: open a discussion on this repository.