File size: 5,463 Bytes
b0e1cbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
license: apache-2.0
base_model:
- Qwen/Qwen2.5-0.5B-Instruct
library_name: transformers
tags:
- mlx
- text-generation
- fine-tune
- typo-generation
language:
- en
datasets:
- OpenAssistant/oasst2
- google/civil_comments
- grammarly/coedit
---

# Incorrecter

The opposite of autocorrect. Incorrecter takes clean, AI-sounding text — a thank-you
email, a landlord note, a Slack update, a short essay — and adds a few realistic human
errors: eggcorns, wrong homophones, fat-finger slips, a lowercase sentence start, a
doubled space, a dropped final period. Text that reads human-typed instead of
AI-drafted.

A Qwen2.5-0.5B-Instruct LoRA fine-tune (mlx-lm, Apple Silicon), fp16 fused weights.

## Usage

The model expects clean text as the user message and returns the same text with
1–3 word-level errors. It self-identifies as Incorrecter with or without a system
prompt. Recommended sampling: **temperature 1.2, top_p 0.9, no repetition penalty**.
This repo's `generation_config.json` carries those defaults, so a plain
`generate(do_sample=True)` uses them. Greedy decoding makes it timid.

Two settings matter:

- **No repetition penalty.** The model's job is to copy your text with a few mistakes,
  and a repetition penalty punishes the copying. The old default (1.05) over-edited:
  only 0.53 of changed texts stayed within 1–3 word edits on a 40-text check.
- **top_p 0.9.** At temperature alone, the copy itself picks up stray word swaps and
  garbles. The cap keeps the copy exact and leaves room for the intended typos.

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

tok = AutoTokenizer.from_pretrained("Avicennasis/incorrecter")
model = AutoModelForCausalLM.from_pretrained("Avicennasis/incorrecter", dtype=torch.float16)
clean = "Hi Sandra,\n\nThe canteen switched suppliers without telling anyone...\n\nRegards, Aleks"
prompt = tok.apply_chat_template([{"role": "user", "content": clean}],
                                 add_generation_prompt=True, tokenize=False)
ids = tok(prompt, return_tensors="pt")
out = model.generate(**ids, max_new_tokens=200, do_sample=True, temperature=1.2, top_p=0.9)
print(tok.decode(out[0][ids["input_ids"].shape[1]:], skip_special_tokens=True))
```

Or with ollama (Q8_0 GGUF in [Avicennasis/incorrecter-GGUF](https://huggingface.co/Avicennasis/incorrecter-GGUF)):

```
ollama run hf.co/Avicennasis/incorrecter-GGUF:Q8_0
```

The GGUF repo's `params` file sets ollama to temperature 1.0, top_p 0.9 and
repeat_penalty 1.0. The GGUF runs a little hotter than these weights at equal
settings.

## Training data

- 644 drafted seeds (glm-5.3-flash, qwen3.8-27b, gemini-3.1-flash-lite, Claude —
  per-row license tags) plus 699 new drafts (qwen3.8-27b 343, glm 250, gemini 100, claude 6).
- 693 human-written seeds from permissively licensed datasets:
  [OpenAssistant/oasst2](https://huggingface.co/datasets/OpenAssistant/oasst2) (Apache-2.0)
  and [google/civil_comments](https://huggingface.co/datasets/google/civil_comments) (CC0-1.0),
  filtered to 20–400 words, gate-cleaned and judged.
- 600 real-error pairs from
  [grammarly/coedit](https://huggingface.co/datasets/grammarly/coedit) (Apache-2.0), reversed
  to clean → erroneous.
- Identity rows (the model is trained to say it is Incorrecter, created by Léon).

**No unpublished correspondence was used.**

## Evaluation (58 held-out texts, temperature 1.2 + top_p 0.9, 6 draws)

6 draws = 2 training seeds × 3 sampling seeds, through mlx-lm.

| Metric | Result | Target |
|---|---|---|
| texts changed | 0.830 [0.76–0.90] | ≥ 0.80 |
| of changed, 1–3 word edits | 0.882 [0.81–0.92] | ≥ 0.70 |
| line count kept | 0.971 [0.97–0.98] | ≥ 0.95 |
| sign-off kept | 0.958 [0.91–1.00] | ≥ 0.95 |
| meaning kept | 0.957 [0.93–0.98] | ≥ 0.95 |
| identity probes (with system prompt) | 3/3 on every model | 3/3 |

All five targets pass at the means. This exact checkpoint, on its own (3 draws): 0.874
changed, 0.901 with 1–3 edits, 0.971 lines, 0.958 sign-off, 0.954 meaning.

**How meaning is measured.** Llama-3.3-70B-Instruct judges whether each output still
says what the input said, ignoring surface errors. Before scoring anything, it was
calibrated on two sets:

- 58/58 noise-engine corruptions judged "same meaning"
- 58/58 mismatched pairs judged "different"

The meaning margin is thin, and it is stated as measured.

**Why these settings.** At the old recipe (temperature 0.9, no cap), meaning was 0.914,
below the target. Every token was sampled, including the ones the model should copy, so
real words got swapped (my → your, block → board). The top_p cap fixed this without
retraining.

**Other runtimes, same checkpoint** (40 held-out texts, 3 draws):

| Runtime | Settings | changed | 1–3 edits | lines | sign-off | meaning |
|---|---|---|---|---|---|---|
| transformers (CPU) | this repo's `generation_config.json` | 0.79 | 0.81 | 0.96 | 0.96 | 0.94 |
| ollama (Q8_0) | the GGUF repo's `params` | 0.93 | 0.85 | 0.98 | 0.97 | 0.94 |

A full comparison against three other training-data arms is in the repository's design
notes.

## Limitations

- English only; trained on 0.5B params — expect occasional over- or under-correction.
- Some errors land on a real word and change what the text says (my → your). About 4
  in 100 outputs at the recommended settings; more without the top_p cap.
- Trained to answer identity questions as Incorrecter, created by Léon.