Multinomial naive Bayes written from scratch in Rust, trained on 5,574 SMS, ported to JS. It runs in this tab; nothing leaves your browser.
– tokens in vocabulary
prior P(spam) –, α = 1.0
The model needs P(spam) above this to call spam. Higher means fewer real texts lost and more spam let through.
Each bar is log P(token given spam) minus log P(token given ham). Struck-through tokens are not in the vocabulary and are ignored.
Stratified 80/20 split, averaged over 10 random seeds. Spam is the positive class. The from-scratch model matches linfa's MultinomialNb to the confusion cell.
| Model | Accuracy | Precision | Recall | F1 | F1 sd |
|---|---|---|---|---|---|
| Majority baseline, always ham | 0.866 | 0.000 | 0.000 | 0.000 | 0.000 |
| Naive Bayes, unigrams | 0.985 | 0.948 | 0.942 | 0.945 | 0.014 |
| Naive Bayes, unigrams and bigrams | 0.987 | 0.966 | 0.938 | 0.952 | 0.016 |
| Bigrams, threshold 0.90 | 0.988 | 0.981 | 0.931 | 0.955 | 0.014 |
| Bigrams, threshold 0.99 | 0.989 | 0.992 | 0.923 | 0.956 | 0.015 |