File size: 6,696 Bytes
ceb3a09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
daac0c6
ceb3a09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
daac0c6
ceb3a09
daac0c6
ceb3a09
 
 
 
 
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
---
license: apache-2.0
language:
- en
base_model: google-bert/bert-base-uncased
pipeline_tag: text-classification
tags:
- grammatical-error-detection
- linguistic-acceptability
- bert
- blimp
- cola
datasets:
- nyu-mll/blimp
- nyu-mll/glue
metrics:
- accuracy
- matthews_correlation
- f1
widget:
- text: "Katherine can't help himself."
  example_title: "Reflexive agreement error"
- text: "The professor talked us."
  example_title: "Verb argument error"
- text: "She has been working here since 2019."
  example_title: "Correct sentence"
- text: "They drank the pub."
  example_title: "Selectional restriction error"
---

# BERT for Grammatical Error Detection (BLiMP + CoLA)

`bert-base-uncased` fine-tuned for **binary grammatical error detection**: given
one English sentence, decide whether it contains a grammatical error.

| label | meaning |
|-------|---------|
| `0` | grammatical |
| `1` | ungrammatical |

Note the orientation: **1 means "has an error."** This is the inverse of CoLA's
native convention (where 1 = acceptable), and the training labels were flipped
accordingly.

## Usage

```python
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import torch

model_id = "yuvstk/bert-grammar-error-detection"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)

sentences = ["Katherine can't help himself.", "She went home early."]
inputs = tokenizer(sentences, padding=True, truncation=True,
                   max_length=64, return_tensors="pt")

with torch.no_grad():
    probs = torch.softmax(model(**inputs).logits, dim=-1)

for sentence, prob in zip(sentences, probs):
    label = int(prob.argmax())
    print(f"{'UNGRAMMATICAL' if label else 'GRAMMATICAL'} "
          f"({prob[label]:.1%}) — {sentence}")
```

Or with a pipeline (`LABEL_1` = ungrammatical):

```python
from transformers import pipeline
clf = pipeline("text-classification", model=model_id)
clf("The professor talked us.")
```

## Training data

Two sources merged into a single 4000 + 9594 sentence corpus:

| source | rows (train) | what it contributes |
|---|---|---|
| [BLiMP](https://huggingface.co/datasets/nyu-mll/blimp) — `anaphor_gender_agreement` + `anaphor_number_agreement` | 3200 | synthetic minimal pairs; reflexive pronoun agreement; exactly 50/50 balanced |
| [CoLA](https://huggingface.co/datasets/nyu-mll/glue) (GLUE) | 7695 | real linguistics-literature sentences; many error types; ~70/30 imbalanced |

Splitting differs per source, because the sources need different treatment:

- **BLiMP is split by `pair_id`**, never by row. The two sentences of a minimal
  pair differ by exactly one word, so a row-level split would put a
  near-duplicate of a test sentence into training.
- **CoLA is split by stratified rows.** GLUE's `test` split is unlabelled
  (all `-1`), so GLUE `validation` is used as the test set and the validation
  set is carved out of GLUE `train`.

Total: 10,895 train / 1,256 validation / 1,443 test.

## Results

Evaluated separately per source, because the two halves differ enormously in
difficulty — a single pooled number would mostly reflect the mixture ratio.

| test set | n | accuracy | precision | recall | F1 | MCC |
|---|---|---|---|---|---|---|
| **BLiMP** | 400 | **1.000** | 1.000 | 1.000 | 1.000 | 1.000 |
| **CoLA** | 1043 | **0.837** | 0.833 | 0.590 | 0.691 | **0.601** |
| pooled | 1443 | 0.882 | 0.911 | 0.747 | 0.821 | 0.743 |

CoLA MCC of 0.601 is in the normal published range for BERT-base (~0.55–0.60).

**Merging helped.** The same model trained on CoLA alone reached MCC 0.576;
adding BLiMP raised it to 0.601 while BLiMP itself stayed at 1.000 — positive
transfer, not interference.

### Baselines, for scale

| method | accuracy | MCC |
|---|---|---|
| majority class (CoLA) | 0.691 | 0.000 |
| bag-of-words logistic regression (CoLA) | 0.718 | 0.092 |
| pronoun-only rule (BLiMP) | 0.688 | — |
| **zero-shot `bert-base-uncased`, no fine-tuning** (BLiMP) | **0.973** | — |

That last row is worth dwelling on: masking the pronoun and asking the *raw*
pretrained model which word it prefers already solves BLiMP at 97.3%.
Fine-tuning on BLiMP mostly attaches an output head to knowledge the model
already had. CoLA is where fine-tuning does real work.

## Training procedure

| hyperparameter | value |
|---|---|
| base model | `bert-base-uncased` (109.5M parameters) |
| epochs | 4 |
| learning rate | 2e-5 |
| warmup ratio | 0.06 |
| batch size | 32 |
| max sequence length | 64 |
| weight decay | 0.01 |
| optimizer | AdamW |
| seed | 42 |
| best checkpoint by | validation **MCC** (not accuracy — the data is imbalanced) |

Per-epoch validation MCC: 0.727 → 0.735 → 0.771 → **0.775**.

## Limitations

Measured on 40 hand-written test sentences (33/40 correct, 82.5%), the failure
modes are systematic rather than random:

1. **Blind to omissions.** *"Although it was raining, we decided go for a
   walk."* is judged correct. Both training sets create errors by
   **substituting** a word, never deleting one, so the model never learned to
   notice something missing.
2. **Over-flags correct sentences.** *"He is an honest man."* and *"She arrived
   at the airport."* are both flagged as errors. Recall on CoLA is 0.590 while
   precision is 0.833 — it misses more errors than it invents, but its false
   alarms land on perfectly ordinary sentences.
3. **Untrained phenomena fail.** Determiner–noun agreement (*"Raymond is
   selling this sketch."*) is flagged as an error. On BLiMP's
   `determiner_noun_agreement_1` — a phenomenon never seen in training — the
   model scores 0.675, far below its 1.000 on trained phenomena.
4. **Confidence is not reliability.** Several wrong predictions are made at
   100% confidence. Do not treat the softmax score as a calibrated probability.
5. **English only**, and short sentences only — training data averaged well
   under 20 words.

This is a coursework model built to study what fine-tuning contributes, not a
production grammar checker.

## Intended use

Educational and research use: demonstrating grammatical acceptability
classification, and comparing fine-tuned versus zero-shot versus from-scratch
transformers. Not suitable for grading student writing, automated proofreading,
or any decision affecting a person.

## Citation

```bibtex
@misc{bert-grammar-error-detection,
  title  = {BERT for Grammatical Error Detection (BLiMP + CoLA)},
  author = {Your Real Name},
  year   = {2026},
  url    = {https://huggingface.co/yuvstk/bert-grammar-error-detection}
}
```

Datasets: BLiMP (Warstadt et al., TACL 2020) and CoLA (Warstadt et al., TACL
2019).