Proofbird Grammar — Portuguese (Portugal)

gector-bert-pt-pt · on-device European Portuguese grammar correction

Try it in your browser

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-14-1134 · tag v1.0.0, the files the Proofbird app downloads. Pin it with revision="v1.0.0". Versions: a change to the model's design (encoder, inputs, outputs) raises the first number, a new training run or changed guards and rules the second, card or metadata fixes the third.

A GECToR-style sequence tagger for European Portuguese grammatical error correction, exported to ONNX (fp32) to run on the CPU. It is the on-device grammar model of Proofbird, a writing assistant for Windows, and it is published here so anyone can inspect what runs on their machine. Brazilian Portuguese has its own model, gector-bert-pt-br.

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

No API key, no GPU. One file, quickstart.py, downloads the model and runs it with ONNX Runtime.

pip install onnxruntime tokenizers huggingface_hub
import importlib.util
from huggingface_hub import hf_hub_download

spec = importlib.util.spec_from_file_location("quickstart", hf_hub_download("proofbird/gector-bert-pt-pt", "quickstart.py"))
quickstart = importlib.util.module_from_spec(spec)
spec.loader.exec_module(quickstart)

model = quickstart.Gector("proofbird/gector-bert-pt-pt")
corrected, suggestions = model.correct("A casa são muito bonito.")
print(corrected)
print(suggestions)
A casa é muito bonita.
[{"start": 7, "length": 3, "original": "são", "replacement": "é"}, {"start": 17, "length": 6, "original": "bonito", "replacement": "bonita"}]

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 "A casa são muito bonito." prints every step below.

What it corrects

Gender and number agreement (articles, adjectives, participles, nouns after determiners), subject-verb agreement in the present tense (Os meus filhos está a brincar → estão, Eles tem → têm), article gender (um casa → uma casa), contractions (de o → do, em a → na, and a preposition written twice), the crase (Vou a praia → à praia), accents and diacritics (esta cansada → está cansada), the plural of -ão nouns (informaçãos → informações), capitalisation at the start of a sentence, and everyday-register errors in questions, imperatives and the first and second person.

It is written for European Portuguese: estar a + infinitive, enclisis (Dá-me), facto, equipa, autocarro and the tu forms are correct to it. It leaves Brazilian spelling and usage alone (fato, ônibus, está estudando) rather than changing it, and it accepts both the spelling before the 1990 Orthographic Agreement and the spelling after it (acção and ação, objectivo and objetivo).

How it was trained

  • Base encoder: neuralmind/bert-base-portuguese-cased (BERTimbau, MIT). A tagging head (5 000 edit labels) and an error-detection head sit on top. The encoder was pretrained on Brazilian text; the European signal comes from the fine-tuning text below.

  • Data: synthetic only. Three sources, about 3 million sentences in all:

    • 1 460 392 sentences from Portuguese Wikipedia (wikimedia/wikipedia, 20231101.pt), keeping only articles whose spelling and usage mark them as European;
    • 1 300 000 sentences from the proceedings of the European Parliament (Europarl v10, sessions 1996 – May 2011): speech in the first and second person, questions and argument, the register encyclopedic text lacks;
    • 250 000 sentences from Tatoeba (European or unmarked).

    They were corrupted by hand-written Portuguese error rules; about 10% stay clean, so the model learns to leave correct text alone. 200 000 additional template sentences teach patterns the corpora rarely contain (first and second person, questions, imperatives, long-distance agreement).

  • No learner corpora and no non-commercial data in the fine-tuning. No user text was used.

  • One epoch (94 263 steps, batch 64, learning rate 3e-5) on one NVIDIA L4. Run id 2026-11-14-1134.

How it was measured

Check Result
Gold set (603 hand-written sentences: 420 with one error each, 183 correct, 53 of them in European-only forms) 416 fixed, 183 left alone, 4 missed, 0 made worse
False alarms on 4 000 everyday Tatoeba sentences (European or unmarked, held out from training) 11 changed (0.27%); 8 of them correct an error in Tatoeba's own text
False alarms on 4 000 sentences from European Parliament sessions the model never saw (June 2011) 1 changed (0.03%), and it corrects a typo in the source
Stability: the gold re-run with one extra token in front of each sentence 0 sentences change result (the 4 capitalisation rows change by construction and are not counted)

The scores are 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. The misses: a crase before a noun with a complement (voltou a casa dos pais → à casa) and the irregular plural pães (dois pãos is left alone). Known limits: subject-verb agreement in the past tense is not corrected (A equipa ganharam), and a plural determiner does not always pull its noun into the plural (Os resultado foram).

The Tatoeba changes include corrections of Tatoeba's own text (Nós estávamos pronto → prontos, ignorancia → ignorância, A relva esta cortada → está, você têm → tem, a doubled Não não) and three false alarms: a 2nd-person verb after its object (nenhuma energia terás → terá), a line of the Brazilian anthem (és mãe gentil → é) and a pouco a pouco → há. It has not been measured on learner essays.

The guards. gector-guards.json holds back edits we measured as wrong in a known shape. They run in the Proofbird app (not in quickstart.py or the demo) and are part of every score above. Among them: a change of the verb's person needs a subject for the new person in the sentence; o/a estudante, o/a colega, a melhor take either article; a noun with a masculine -o ending keeps o (dois dólares o metro); é never becomes e; Está tarde stays; the crase before a feminine noun stays; dele/deles follow the owner; the clitic in que a aceitemos and the preposition in a domicílio are never an article; Sou toda ouvidos stays.

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, version.
gector-guards.json Apply-side guards: rules that hold back an edit the model gets wrong in a known way (for example é → e, which turns the verb into "and"). Part of the measured result.
language-rules.json Language facts the runtime needs (sentence-final punctuation handling).
verb-form-vocab.txt The Portuguese verb-form table for the $TRANSFORM_VERB_* tags (428 verbs, present tense, built for this model).
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_da, $REPLACE_abrem or $TRANSFORM_VERB_P3P_P3S (change a verb's form). 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): A casa são muito bonito.

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.

words           $START  A  casa  são  muito  bonito
tokens          [CLS] A casa são muito bonito [SEP]
input_ids       [101, 177, 1105, 453, 785, 22003, 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: A casa são muito bonito

Word Wrong Chosen edit Probability
$START 0.0% $KEEP 99.9%
A 0.0% $KEEP 100.0%
casa 0.0% $KEEP 100.0%
são 95.3% $TRANSFORM_VERB_P3P_P3S 97.3%
muito 0.0% $KEEP 100.0%
bonito 18.1% $KEEP 78.2%

→ A casa é muito bonito

Pass 2: A casa é muito bonito

Word Wrong Chosen edit Probability
$START 0.0% $KEEP 100.0%
A 0.0% $KEEP 100.0%
casa 0.0% $KEEP 100.0%
é 0.0% $KEEP 100.0%
muito 0.0% $KEEP 100.0%
bonito 100.0% $TRANSFORM_SUF_o_a 99.3%

→ A casa é muito bonita

Pass 3: A casa é muito bonita

Word Wrong Chosen edit Probability
$START 0.0% $KEEP 100.0%
A 0.0% $KEEP 100.0%
casa 0.0% $KEEP 100.0%
é 0.0% $KEEP 100.0%
muito 0.0% $KEEP 100.0%
bonita 0.0% $KEEP 100.0%

→ A casa é muito bonita — 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 still corrected (A casa é muito bonita.): the model is 95.3% sure one word is wrong.

What Proofbird adds

quickstart.py is the model on its own. The Proofbird app runs the same algorithm in C# and adds:

  • Guards: per-language rules that hold back edits we measured as wrong in a known shape. gector-guards.json, in this repository, lists which guards are on for Portuguese (Portugal) and their word lists; what each guard does is code in the app. 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.

quickstart.py and the in-browser demo run the model on its own, without the guards.

Licence and attribution

The model weights and the files in this repository are released under the Apache License 2.0. If you redistribute the model or a model built from it, keep the 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. Tatoeba sentences are from tatoeba.org, CC BY 2.0 FR. Contains text from the proceedings of the European Parliament, © European Union, 1996–2011, reused under the European Parliament's legal notice, through the Europarl corpus (statmt.org/europarl, Philipp Koehn, Europarl: A Parallel Corpus for Statistical Machine Translation, MT Summit 2005). The base encoder is BERTimbau, neuralmind/bert-base-portuguese-cased, by Fábio Souza, Rodrigo Nogueira and Roberto Lotufo (NeuralMind), MIT licence: BERTimbau: Pretrained BERT Models for Brazilian Portuguese, BRACIS 2020. The tagging scheme follows GECToR (Omelianchuk et al., 2020, GECToR – Grammatical Error Correction: Tag, Not Rewrite); the Portuguese verb-form table was built for this model.

How to cite

If you use or build on this model, please cite it:

@misc{hartnett2026gectorptpt,
  author       = {Hartnett, Patrick Andrew},
  title        = {Proofbird GECToR grammar model (Portuguese, Portugal)},
  year         = {2026},
  publisher    = {Hugging Face},
  howpublished = {\url{https://huggingface.co/proofbird/gector-bert-pt-pt}},
  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.

Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for proofbird/gector-bert-pt-pt

Quantized
(17)
this model

Dataset used to train proofbird/gector-bert-pt-pt

Space using proofbird/gector-bert-pt-pt 1