I ended the last installation with the development of tuned Markov models. That set things up for some exciting exploration with a new set of tools.

Add tuning data

I also called out the need for a second evaluation dataset, which I've been referring to as a "tuning" data set to distinguish the two. The first (tuning) dataset is for optimizing some of the hyperparameters via the redsho algorithm, and the second (eval) dataset is for optimizing the rest of them via manual trial-and-error. It's worth noting that we still lack a proper testing dataset that would evaluate how well the tool works on previously unseen data. I plan to save that for a future installment.

The 6 categories of errors are represented by separate data files.

Previous to this, some categories had 100 error examples and others had 50. To complete this initial split of tuning and eval, I fleshed out the other 4 so that all 6 categories had 100 examples, then split them evenly so that there are 50 examples each in tuning and eval datasets.

(This is easier said than done. I confess it is the most tedious part of this development.)

Retrain and re-run

With the change in tuning data set, the tunable models in play need to be tuned again. Also, the performance numbers now include an F-score. For the tuning of the models, I used F-.5, to indicate that for a single model recall was half as important as precision. When individual models are combined, precision usually goes down and recall usually goes up. For the composite model, I chose F-2, indicating that recall is twice as important as precision. It means I would happily allow two false positives in order to ensure an additional true positive. This is a design choice. I want the proofreader to be able to catch as many errors as possible, even if two out of every three reported errors ends up being a false alarm.

version capitalization extra words grammar missing words punctuation spelling overall
    017
precision
recall
f-score

0.70
0.32
0.36

0.79
0.38
0.42

0.29
0.04
0.05

0.58
0.22
0.25

0.36
0.08
0.09

0.64
0.18
0.21

0.56
0.20
0.23
    018
precision
recall
f-score

0.69
0.36
0.40

0.81
0.34
0.38

0.10
0.02
0.02

0.50
0.12
0.14

0.60
0.12
0.14

0.75
0.24
0.28

0.58
0.20
0.23
    019
precision
recall
f-score

0.59
0.44
0.46

0.72
0.46
0.50

0.18
0.06
0.07

0.50
0.26
0.29

0.39
0.14
0.16

0.61
0.28
0.31

0.50
0.27
0.30

The three proofreaders reported here are a forward first-order model, 017:

pr017, combo=min, cutoff=1.0
    mm01, Tunable forward 1st-order Markov, alpha=4.64, beta=16, err=3.59e-05, books=38k
        tk09, vocab=1k, books=416, norm=identity, type=unigram, ws_split=False

a backward first-order model, 018:

pr018, combo=min, cutoff=1.0
    mm03, Tunable backward 1st-order Markov, alpha=1.0, beta=0, err=3.59e-05, books=38k
        tk09, vocab=1k, books=416, norm=identity, type=unigram, ws_split=False

and a composite of the two, 019:

pr019, combo=min, cutoff=1.0
    mm01, Tunable forward 1st-order Markov, alpha=4.64, beta=16, err=3.59e-05, books=38k
        tk09, vocab=1k, books=416, norm=identity, type=unigram, ws_split=False
    mm03, Tunable backward 1st-order Markov, alpha=1.0, beta=0, err=3.59e-05, books=38k
        tk09, vocab=1k, books=416, norm=identity, type=unigram, ws_split=False

This shows clearly what we had hoped: that the combination of individually-tuned forward and a backward models performs better than either individually. The overall F-scores of the individual models are both 0.23. The F-score of the ensemble is 0.30, a 30% improvement!

The other thing that this illustrates is that the alpha and beta values that fall out of the tuning process are different for each model. This helps show the usefulness of tuning at the model level, instead of the ensemble level, allowing each model to find the combination that works best for it in isolation before combining.

Explore tokenizer alphabet size

One quantity that doesn't get tuned on individual models is the alphabet size of the tokenizer used. That's a decision that has to be made before the model is created. To learn about the effects of alphabet size, it's necessary to create and train a new set of models each time. Doing this is worth the effort. It reveals some new patterns.

version alphabet capital. extra grammar missing punct. spelling overall
    022
precision
recall
f-score
10k
0.53
0.34
0.37

0.86
0.48
0.53

0.25
0.34
0.32

0.92
0.22
0.26

0.69
0.22
0.25

0.54
0.26
0.29

0.63
0.31
0.35
    025
precision
recall
f-score
5k
0.55
0.42
0.44

0.88
0.56
0.60

0.17
0.18
0.18

0.64
0.32
0.36

0.50
0.24
0.27

0.52
0.32
0.35

0.54
0.34
0.37
    028
precision
recall
f-score
2k
0.45
0.48
0.47

0.78
0.64
0.66

0.14
0.20
0.19

0.50
0.34
0.36

0.35
0.22
0.24

0.47
0.34
0.36

0.45
0.37
0.38
    019
precision
recall
f-score
1k
0.59
0.44
0.46

0.72
0.46
0.50

0.18
0.06
0.07

0.50
0.26
0.29

0.39
0.14
0.16

0.61
0.28
0.31

0.50
0.27
0.30
    031
precision
recall
f-score
500
0.34
0.40
0.39

0.48
0.46
0.46

0.20
0.18
0.18

0.38
0.26
0.28

0.29
0.30
0.30

0.31
0.32
0.32

0.33
0.32
0.32
    034
precision
recall
f-score
200
0.44
0.30
0.32

0.50
0.16
0.19

0.25
0.08
0.09

0.50
0.08
0.10

0.50
0.16
0.19

0.30
0.12
0.14

0.42
0.15
0.17

The first thing that jumps out is that the 1k alphabet size used in the previous table is far from the best performing one. By overall F-score, the 2k alphabet beats it handily at 0.38. Another 27% jump!

There are also some intriguing differences between categories of proofreading errors. Capitalization, spelling, extra words, and missing words all show their peak F-scores at a 2k alphabet. (Although extra words shows a matching F-score and considerably higher precision at 5k.) Punctuation shows its peak at 500, but reasonable performance across the spectrum. And grammar shows its peak at 10k, dramatically higher than at any smaller alphabet.

This pattern is consistent with the fact that larger alphabets result in longer tokens, many of them having two or more words. This gives a longer context window, more information from which to make determinations. Grammar errors in particular require the ability to look ahead or behind for several words to determine whether the current word is a correct one. Spelling on the other hand, can often be evaluated very locally just from looking at a word in its entirety. Punctuation has a bit of both. Some punctuation errors require an entire phrase to learn whether or not a comma should be used, while the errant placement of an unusual punctuation symbol can almost be flagged with no context at all.

As a side note, it becomes even clearer that individual models benefit from being tuned separately. The alpha, beta, and error threshold values vary quite a bit between forward and backward models and across alphabet (vocab) sizes.

pr022, combo=min, cutoff=1.0
    mm04, Tunable forward 1st-order Markov, alpha=0.000464, beta=0, err=4.64e-06, books=38k
        tk06, vocab=10k, books=416, norm=identity, type=unigram, ws_split=False
    mm05, Tunable backward 1st-order Markov, alpha=0.0001, beta=0, err=2.78e-06, books=38k
        tk06, vocab=10k, books=416, norm=identity, type=unigram, ws_split=False
pr025, combo=min, cutoff=1.0
    mm06, Tunable forward 1st-order Markov, alpha=2.15, beta=0, err=1.29e-05, books=38k
        tk07, vocab=5k, books=416, norm=identity, type=unigram, ws_split=False
    mm07, Tunable backward 1st-order Markov, alpha=0.215, beta=0, err=7.74e-06, books=38k
        tk07, vocab=5k, books=416, norm=identity, type=unigram, ws_split=False
pr028, combo=min, cutoff=1.0
    mm08, Tunable forward 1st-order Markov, alpha=0.215, beta=27, err=3.59e-05, books=38k
        tk08, vocab=2k, books=416, norm=identity, type=unigram, ws_split=False
    mm09, Tunable backward 1st-order Markov, alpha=4.64, beta=0, err=3.59e-05, books=38k
        tk08, vocab=2k, books=416, norm=identity, type=unigram, ws_split=False
pr019, combo=min, cutoff=1.0
    mm01, Tunable forward 1st-order Markov, alpha=4.64, beta=16, err=3.59e-05, books=38k
        tk09, vocab=1k, books=416, norm=identity, type=unigram, ws_split=False
    mm03, Tunable backward 1st-order Markov, alpha=1.0, beta=0, err=3.59e-05, books=38k
        tk09, vocab=1k, books=416, norm=identity, type=unigram, ws_split=False
pr031, combo=min, cutoff=1.0
    mm10, Tunable forward 1st-order Markov, alpha=4.64, beta=46, err=0.0001, books=38k
        tk11, vocab=500, books=416, norm=identity, type=unigram, ws_split=False
    mm11, Tunable backward 1st-order Markov, alpha=0.001, beta=0, err=0.000167, books=38k
        tk11, vocab=500, books=416, norm=identity, type=unigram, ws_split=False
pr034, combo=min, cutoff=1.0
    mm12, Tunable forward 1st-order Markov, alpha=0.00464, beta=215, err=0.000167, books=38k
        tk12, vocab=200, books=416, norm=identity, type=unigram, ws_split=False
    mm13, Tunable backward 1st-order Markov, alpha=0.00464, beta=77, err=0.000167, books=38k
        tk12, vocab=200, books=416, norm=identity, type=unigram, ws_split=False

Taken all together, this suggests that ensemble models that include models with a variety of alphabet sizes may perform better still. Building this will require some extra work, because so far proofreaders have operated at the token level. This builds in the assumption that every model in the ensemble is using the same tokenizer, and hence, the same alphabet size. Combining tokenizers will require first migrating the representation of detected errors to the character level.

Yet another avenue this opens up is combining tokenizers with different normalization options. Normalization such as collapsing whitespace, removing punctuation, and casting to lower case, can obscure some errors but accentuate others. The ability to combine models that take different normalization approaches can exploit the strengths of both.

Skip-gram models

But before we get to that, there is an even lower hanging piece of fruit. So far, the first-order Markov models used have looked at consecutive tokens—the conditional probability of token n, given the occurence of token n-1. There's no reason we couldn't also look at the relationship between token n and n-2. Or n-3. Or n-7. Skip-grams reveal patterns that consecutive n-grams can miss. At the very least, it's worth trying them out.

Extending the models to forward and backward skip-1 first order Markov models shows some fun relationships.

version type capitalization extra words grammar missing words punctuation spelling overall
    035
precision
recall
f-score
skip-1
fwd

0.12
0.44
0.28

0.25
0.54
0.44

0.07
0.24
0.16

0.13
0.32
0.25

0.09
0.32
0.22

0.09
0.28
0.20

0.12
0.36
0.26
    036
precision
recall
f-score
skip-1
bak

0.22
0.28
0.26

0.32
0.24
0.25

0.09
0.12
0.11

0.23
0.16
0.17

0.22
0.24
0.24

0.22
0.22
0.22

0.22
0.21
0.21
    037
precision
recall
f-score
skip-1
fwd+bak

0.11
0.50
0.29

0.25
0.62
0.48

0.08
0.34
0.21

0.14
0.40
0.29

0.11
0.42
0.26

0.10
0.38
0.25

0.13
0.44
0.30
    028
precision
recall
f-score
skip-0
fwd+bak

0.45
0.48
0.47

0.78
0.64
0.66

0.14
0.20
0.19

0.50
0.34
0.36

0.35
0.22
0.24

0.47
0.34
0.36

0.45
0.37
0.38
    038
precision
recall
f-score
skip-1 +
skip-0
fwd+bak

0.14
0.66
0.37

0.27
0.74
0.55

0.09
0.38
0.23

0.17
0.54
0.37

0.13
0.54
0.33

0.13
0.52
0.32

0.15
0.56
0.37

Skip-1 models don't perform as well as skip-0 models, but when the two are combined they achieve some pretty impressive performance, at least in recall.

Trying skip-2 models shows more of the same. They perform so-so individually. When combined with skip-1 and skip-0 models they can keep the F-scores comparable, but with some impressive recall numbers.

version capitalization extra words grammar missing words punctuation spelling overall
  039
skip-2
fwd

pr
rec
f

0.09
0.10
0.10

0.20
0.08
0.09

0.07
0.06
0.06

0.17
0.08
0.09

0.04
0.04
0.04

0.05
0.04
0.04

0.10
0.07
0.07
  040
skip-2
back

pr
rec
f

0.06
0.20
0.14

0.12
0.22
0.19

0.07
0.20
0.15

0.10
0.28
0.20

0.09
0.26
0.19

0.08
0.24
0.17

0.09
0.23
0.17
  041
skip-2
combo

pr
rec
f

0.07
0.28
0.18

0.12
0.28
0.22

0.07
0.24
0.16

0.10
0.34
0.23

0.08
0.28
0.19

0.07
0.28
0.18

0.09
0.28
0.20
  037
skip-1
combo

pr
rec
f

0.11
0.50
0.29

0.25
0.62
0.48

0.08
0.34
0.21

0.14
0.40
0.29

0.11
0.42
0.26

0.10
0.38
0.25

0.13
0.44
0.30
  028
skip-0
combo

pr
rec
f

0.45
0.48
0.47

0.78
0.64
0.66

0.14
0.20
0.19

0.50
0.34
0.36

0.35
0.22
0.24

0.47
0.34
0.36

0.45
0.37
0.38
  038
skip-0+1
combo

pr
rec
f

0.14
0.66
0.37

0.27
0.74
0.55

0.09
0.38
0.23

0.17
0.54
0.37

0.13
0.54
0.33

0.13
0.52
0.32

0.15
0.56
0.37
  042
skip-0+2
combo

pr
rec
f

0.13
0.62
0.36

0.25
0.68
0.51

0.08
0.38
0.22

0.16
0.60
0.39

0.12
0.46
0.29

0.11
0.46
0.28

0.14
0.53
0.35
  043
skip-0+1+2
combo

pr
rec
f

0.10
0.74
0.33

0.18
0.76
0.46

0.07
0.50
0.23

0.13
0.72
0.37

0.10
0.64
0.31

0.09
0.62
0.29

0.11
0.66
0.34

Because the F-scores, our measure of overall performance, don't go up when including the skip-gram models, these results on their own don't jusitfy adding the skip-grams to the proofreader. That's OK. The real value here is the picture it gives about how precision and recall of two models are reflected in the composite model when they are combined.

Combined recall is higher than either model's recall

In the tables above, comparing two individual model rows against a third in which they are combined shows that recall goes up almost every time. If the recall of two models is dissimilar, their combination nevertheless has a recall that is still a bit higher than either of the two. And if the two models are comparable, the recall of their combined model can be almost double that of the individual models.

For the proofreader this is very good news. An ideal proofreader has perfect recall. Every mistake is highlighted for the author to evaluate and rethink. Combining a wide variety of models shows promise as a way to get closer to perfect recall.

Precision regresses to the lower of either model's precision

On the other hand, the precision of a combined model falls between the precision of the two individual models, and it tends to be much closer to the lower of the two. This is fine if both models' precision is in an aceptable range, but if even one component model has a low precision, that can tank the precision of the whole ensemble.

This suggests that we should be watching precision very carefully. If a model has a precision below the acceptable level it should not be included because it will make it very difficult for the model to recover, no matter how well any of the other component models perform.

It also suggests that models with a very high precision are a waste. As mentioned, a precision of around one-half is acceptable. That means that a user of the proofreader will have half false alarms, half real errors flagged. I have deemed this an acceptable false positive rate for the user experience I am aiming for. If a model has, say, a precision of 0.9, and gets combined with another that has a precision of 0.5, most of that high precision will be lost. The worst part of that is that high precision could have been backed off and traded for recall, which accumulates.

Tuning models to be ideal contributors to the esnsemble

All of this combines to suggest that individual models should be tuned to have precision in the desired range of the final model (say 0.5 to 0.6), and the maximum recall it can achieve. The current training regimen doesn't do this, but there are some changes that can get us closer to it.

One tweak we can do is penalize precision when it gets too high. The math trick for this is to add a nonlinear transformation to precision. For precision less than 2/3, multiply it by 1.5, and for precision greater than 2/3, make it two minus 1.5 times precision.

ρnew = ρ * 3/2, for ρ ≤ 2/3
ρnew = 2 - ρ * 3/2, for ρ > 2/3

This transformed precision ρnew reaches a maximum value of one when ρ=2/3, and when ρ=1, ρnew = 0.5. When fed into the redsho optimization algorithm, this will tend to drive the solution to a precision of around 2/3 and, after reaching that constraint, a recall as high as it can reach.

I chose the target of 2/3 to give a little breathing room above 1/2, and allow for noisiness in the tuning process. If that proves higher than necessary, I'll go back and reduce it.

Another way to ensure proofreader performance is to put a hard limit on component model precision. After models are tuned, only models with precision higher than a floor, say 0.5, will be allowed to become part of the proofreader model. This check lets us train and add as many model variants as we like, as long as they meet this criterion, whoth reasonable confidence that they won't hurt the performance of the overall model. This second approach assumes that there will be plenty of models to select from with a precision greater than 0.5, but so far that's only aspirational. But we'll keep it in mind for when things get more mature.

F-score correction: not ρ but ρ2

Up until this point (in this very post!), I explained erroneously that the ρ parameter in the F-ρ score can be interpreted as a weight for recall. In fact it is the square of ρ that is the weight. So an F-2 score actually weights recall four times more than precision.

For the final top line evaluation score, where I wanted recall to be weighted twice as heavily as precision, I realized this requires a ρ of 1.414, the square root of two. And after a round of trial and error, I decided to stick with a ρ of .5 for the tuning process. Because the precision of one model is essentially the ceiling for precision of the ensemble of models, pushing it as high as possible for each individual model is helpful. Keeping it weighted four times heavier than recall produced the best results.

During the experimentation, sadly, no combination of parameters or optimization approaches I tried was able to get the skip-one or skip-two models up to a reasonable precision. They were often down at .1 or lower. For now, this makes them unsuitable for use in a proofreader, but I’ll tuck this exploration away for future use. It’s possible that with a different tokenizer or larger training data set, I may be able to get those higher eventually

Re-running tuning on all of the no-skip Markov models and using F-1.414 as the top line evaluation score, here’s what the combined forward-plus-backward models look like for alphabets of various sizes. Several of the precision values are at or near .5 which is a very pleasant surprise.

version alphabet capital. extra words grammar missing words punct. spelling overall
    046
precision
recall
f-score
20k
0.49
0.46
0.47

0.74
0.52
0.58

0.19
0.36
0.28

0.53
0.34
0.39

0.53
0.32
0.37

0.39
0.32
0.34

0.48
0.39
0.41
    022
precision
recall
f-score
10k
0.51
0.44
0.46

0.81
0.58
0.64

0.24
0.42
0.34

0.64
0.28
0.34

0.62
0.26
0.32

0.49
0.34
0.38

0.55
0.39
0.43
    025
precision
recall
f-score
5k
0.50
0.48
0.49

0.74
0.58
0.63

0.18
0.24
0.22

0.50
0.34
0.38

0.43
0.26
0.30

0.49
0.36
0.39

0.47
0.38
0.40
    028
precision
recall
f-score
2k
0.43
0.48
0.46

0.75
0.66
0.69

0.14
0.20
0.17

0.50
0.36
0.40

0.31
0.22
0.24

0.41
0.34
0.36

0.42
0.38
0.39
    019
precision
recall
f-score
1k
0.59
0.44
0.48

0.72
0.46
0.52

0.18
0.06
0.08

0.50
0.26
0.31

0.39
0.14
0.18

0.61
0.28
0.34

0.50
0.27
0.32
    031
precision
recall
f-score
500
0.34
0.40
0.38

0.47
0.46
0.46

0.20
0.18
0.19

0.39
0.28
0.31

0.29
0.30
0.30

0.31
0.32
0.32

0.33
0.32
0.33
    034
precision
recall
f-score
200
0.36
0.36
0.36

0.44
0.22
0.26

0.21
0.14
0.16

0.41
0.14
0.18

0.38
0.18
0.22

0.27
0.14
0.17

0.34
0.20
0.23

It is notable that these tend to occur across the spectrum of alphabet sizes, except for the smallest alphabets. This is hopeful, because it also suggests that these different models may be picking up on different patterns, and that combining them will be fruitful. Unfortunately, this isn’t something that I can do yet. Combining these models at the moment requires that they use the same tokenizer, and therefore have the same alphabet. Some additional legwork will be required to combine models from different tokenizers.

Exploring other tokenizer variants

We have reached the place where it’s useful to do a comparison against some other tokenizer settings. So far, all of the tokens have used identity normalization (that is, they have made no pre-processing change to the text), unigram type tokenization, and no enforced splitting at word boundaries. We justified these assumptions as a starting point early on, but now we can actually test whether these were good choices.

version tokenizer capitalization extra words grammar missing words punctuation spelling overall
    022
precision
recall
f-score
no normalization
0.51
0.44
0.46

0.81
0.58
0.64

0.24
0.42
0.34

0.64
0.28
0.34

0.62
0.26
0.32

0.49
0.34
0.38

0.55
0.39
0.43
    049
precision
recall
f-score
byte-pair encoding (BPE)
0.64
0.54
0.57

0.91
0.60
0.68

0.25
0.30
0.28

0.75
0.30
0.38

0.59
0.26
0.32

0.47
0.34
0.38

0.60
0.39
0.44
    052
precision
recall
f-score
split by whitespace
0.50
0.34
0.38

0.80
0.40
0.48

0.24
0.40
0.32

0.53
0.20
0.25

0.63
0.24
0.30

0.48
0.32
0.36

0.53
0.32
0.37
    055
precision
recall
f-score
NFKC normalization
0.59
0.40
0.45

0.75
0.54
0.60

0.25
0.44
0.35

0.85
0.34
0.43

0.66
0.38
0.44

0.42
0.32
0.35

0.59
0.40
0.45
    058
precision
recall
f-score
NFKC + case folding
0.17
0.12
0.13

0.61
0.60
0.60

0.21
0.46
0.33

0.64
0.46
0.51

0.38
0.32
0.34

0.28
0.34
0.32

0.38
0.38
0.38
    061
precision
recall
f-score
NFKC + BPE
0.60
0.54
0.56

0.73
0.66
0.68

0.22
0.34
0.29

0.64
0.36
0.42

0.68
0.42
0.48

0.40
0.36
0.37

0.55
0.45
0.48

Trying out of some of the other options available on an encoder with alphabet size of 10,000 showed that byte-pair encoding and NFKC normalization both give a modest boost in performance, and combining them helps even more, giving a respectable precision of 0.55, recall of 0.45, and F-score of 0.48. These numbers still aren't good enough to ship, but they are getting closer. A precision over 0.5 is good enough, but a recall of 0.45 means that it's missing more than half the errors :grimace-emoji:. But I'm not worried. Out bag of tricks is far from empty.

Next steps

In the next installment, we get to tackle the big step of combining the results of models that are based on different tokenizers. This has the potential to meld complementary strengths of different models, like a proofreading Voltron.

There will be other stuff too, but honestly I'm making this up as I go along. We'll be surprised together.