Sorting messages into spam and not spam
Text classification means giving a text a label, such as spam or work, from the words it contains. A naive Bayes classifier does it with nothing more than word counts and a little probability.
It is called naive because it treats every word as if it were unrelated to the others. That is not true, and it still works well enough to filter email.
By the end of this lesson
Note
I can classify a text with word counts.
Key words
| Word | What it means | Example |
|---|---|---|
| Label | the class a text belongs to | spam |
| Training set | texts whose labels are already known | |
| Prior | how common a label is before you read the text | |
| Smoothing | adding 1 to every count so no word has chance 0 | |
| Log score | the logarithms of the chances added together |
The cells students fill in are left blank in this preview.
A classifier learns from labelled texts
You need a training set: texts that people have already labelled. The program counts how often each word appears under each label.
Words such as "free", "prize" and "click" will be common under spam. Words such as "meeting" and "report" will be common under work.
Count the words under each label
messages = ["win a free prize now", "free voucher click now", "meeting moved to monday"]
labels = ["spam", "spam", "work"]
counts = {"spam": {}, "work": {}}
for n in range(len(messages)):
for word in messages[n].split():
table = counts[labels[n]]
if word in table:
table[word] = table[word] + 1
else:
table[word] = 1
print(counts["spam"])
# Add "free lunch at noon" as work. Does "free" appear under work now?
Count the messages that contain a word
docker exec jupyter sh -c "printf 'win a free prize now\nfree voucher click now\nmeeting moved to monday\n' | grep -c free"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A word has a chance under each label
The chance of a word under a label is its count there, divided by all the tokens under that label. A word never seen under a label would get 0, and one zero would wipe out the whole answer.
Smoothing fixes that: add 1 to every count, and add the size of the vocabulary to the total.
Work out a smoothed chance
import math
spam_free = 3 # "free" appears 3 times in the spam messages
spam_total = 13 # the spam messages hold 13 tokens in all
vocabulary = 27 # 27 different words in all the messages
chance = (spam_free + 1) / (spam_total + vocabulary)
print(round(chance, 3))
print(round(math.log(chance), 3))
# Use 0 for spam_free. Is the chance still above 0?
Worked example: two words, two labels
| Word | Count in spam | Chance if spam | Count in work | Chance if work |
|---|---|---|---|---|
| free | 3 | 4 / 40 = 0.1 | 0 | 1 / 48 = 0.021 |
| report | 0 | 1 / 40 = 0.025 | 2 | 3 / 48 = 0.0625 |
The cells students fill in are left blank in this preview.
Add logarithms to score each label
To score a text under a label, start with the prior: the share of training texts with that label. Then multiply by the chance of each word. Many small chances multiplied make a tiny number.
So take the logarithm of each chance and add them. That is the log score. The label with the highest score wins.
Take the log of two chances
docker exec jupyter python3 -c "import math; print(round(math.log(3 / 7), 3), round(math.log(4 / 40), 3))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Worked example: scoring "free prize"
| Label | Prior | free | prize | Log score |
|---|---|---|---|---|
| spam | log(3/7) = -0.847 | log(0.1) = -2.303 | log(0.075) = -2.590 | -5.740 |
| work | log(4/7) = -0.560 | log(1/48) = -3.871 | log(1/48) = -3.871 | -8.302 |
The cells students fill in are left blank in this preview.
A common mistake
Warning
Many people think the lowest score wins because the numbers are negative. A log of a chance below 1 is always negative. The score closest to zero is the highest, so -5.7 beats -8.3, and that label wins.
What a word-count classifier cannot do
It ignores order, so "not free" looks like "free". It only knows the words in its training set, so a new scam wording may slip through.
Judge it on messages it has not seen, and compare it with a baseline. A filter that always says "work" is right most of the time if most mail is work.