In the print dialog, choose "Save as PDF" as the destination.
Machine Learning, Week 7
Turn word counts into probabilities with Bayes' theorem, then build a spam filter and a wine classifier with scikit-learn.
MultinomialNB spam filter with CountVectorizerGaussianNB on numeric features: the wine dataWeek 7 of the plan
| Part | What we do | Time |
|---|---|---|
| 1 | Bayes' theorem, one word by hand | 20 min |
| 2 | Naive Bayes by hand, and smoothing | 30 min |
| 3 | Notebook 1: the spam filter | 20 min |
| 4 | Notebook 2: Gaussian Naive Bayes on wine | 20 min |
| 5 | Practice with answers, takeaways | 30 min |
Part 1
A conditional probability P(A | B): the probability of A, given that B is true. For a class c and evidence x:
| Part | Symbol | Meaning |
|---|---|---|
| Prior | P(c) | how common the class is, before looking |
| Likelihood | P(x | c) | how common the evidence is inside the class |
| Evidence | P(x) | how common the evidence is overall |
| Posterior | P(c | x) | the class probability after the evidence |
By hand
999 e-mails. S spam, H ham, m: the e-mail contains money
| Class | E-mails | Containing money |
|---|---|---|
Spam S | 500 | 79 |
Ham H | 499 | 25 |
| All | 999 | 104 |
Part 2
An e-mail has many words x_1, ..., x_n
s(c) for every class cpredict_proba| Symbol | Meaning |
|---|---|
n_wc | count of word w in the training e-mails of class c |
N_c | total count of all vocabulary words in class c |
V | number of words in the vocabulary |
α | smoothing, alpha=1.0 by default in MultinomialNB() |
By hand
Real counts in the 999 e-mails. Priors P(H) = 0.4995, P(S) = 0.5005, V = 4, α = 1
| Word | Count in ham | Count in spam |
|---|---|---|
money | 38 | 155 |
online | 7 | 94 |
meeting | 153 | 1 |
meds | 0 | 30 |
Total N_c | 198 | 280 |
Vocabulary: money, online, meeting, meds
o = online, d = meds. Denominators: 198 + 4 = 202 for ham, 280 + 4 = 284 for spam
s(c) = P(c) times the word probabilities
e = the e-mail
New e-mail e: Money for the meeting? The meeting is about meds. Counts: m = money 1, g = meeting 2, d = meds 1
α = 1: every count gets one extra, so no probability is ever 0
Part 3
https://colab.research.google.com/github/
jeffprosise/Machine-Learning/blob/master/
E-Mail%20Classification.ipynburl = ("https://raw.githubusercontent.com/"
"jeffprosise/Machine-Learning/master/Data/ham-spam.csv")
df = pd.read_csv(url)df.info()
df.groupby('IsSpam').describe()
df = df.drop_duplicates()
df.groupby('IsSpam').describe()IsSpam (0 ham, 1 spam) and Textdrop_duplicates(): 999 rows, 499 ham, 500 spamfrom sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(
ngram_range=(1, 2), stop_words='english')
x = vectorizer.fit_transform(df['Text'])
y = df['IsSpam']ngram_range=(1, 2): single words and pairs such as order onlinestop_words='english': drops the, for, you...x.shape is (999, 100687): 100687 columnstext = vectorizer.transform(['Why pay MORE for * expensive meds when you can ...123... order them online and save $$$?'])
print(vectorizer.inverse_transform(text))[array(['expensive', 'meds', 'online', 'order', 'order online', 'pay',
'save'], dtype='<U401')]from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.2, random_state=0)
from sklearn.naive_bayes import MultinomialNB
model = MultinomialNB()
model.fit(x_train, y_train)fit only counts: the priors and a smoothed P(w | c) for each of the 100687 columns101 of 102 ham e-mails kept, 95 of 98 spam e-mails caught

from sklearn.metrics import ConfusionMatrixDisplay
ConfusionMatrixDisplay.from_estimator(
model, x_test, y_test,
display_labels=['Not Spam', 'Spam'],
cmap='Blues', xticks_rotation='vertical')A accuracy, P precision and R recall of spam
message = vectorizer.transform(['Can you attend a code review on Tuesday? Need to make sure the logic is rock solid.'])
model.predict(message)[0]
model.predict_proba(message)[0][0]
message = vectorizer.transform(['Why pay more for expensive meds when you can order them online and save $$$?'])
model.predict(message)[0]
model.predict_proba(message)[0][1]| predict | probability shown | |
|---|---|---|
| code review | 0 (ham) | P(H) = 0.99992 |
| expensive meds | 1 (spam) | P(S) = 0.99979 |
Part 4
178 wines, 3 cultivars (Class 1, 2, 3: 59, 71, 48 wines), 13 numeric features such as Alcohol, Flavanoids, Proline

url = ("https://raw.githubusercontent.com/tirthajyoti/"
"Machine-Learning-with-Python/master/"
"Datasets/wine.data.csv")
df = pd.read_csv(url)
Flavanoids vs Total phenols: correlation 0.86Flavanoids vs OD280/OD315: 0.79Inside each class, a feature is assumed to follow a bell curve
| Symbol | Meaning |
|---|---|
μ_c | mean of the feature in class c |
σ_c | standard deviation of the feature in class c |
f(x | c) | height of the curve at x: the likelihood |
By hand
Flavanoids over all 178 wines
| Class | Wines | Mean μ | Std σ |
|---|---|---|---|
| 1 | 59 | 2.982 | 0.394 |
| 2 | 71 | 2.081 | 0.701 |
| 3 | 48 | 0.781 | 0.290 |
Exponent: -(x - μ)^2 / (2σ^2) with x = 2.0
Multiply e to the exponent by 1 / √(2πσ²)

Priors: 59/178, 71/178, 48/178
| Class | f(x | c) | P(c) | Posterior |
|---|---|---|---|
| 1 | 0.0453 | 0.3315 | 0.0625 |
| 2 | 0.5653 | 0.3989 | 0.9373 |
| 3 | 0.000200 | 0.2697 | 0.0002 |
X = df.drop('Class', axis=1)
y = df['Class']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)
nbc = GaussianNB()
nbc.fit(X_train, y_train)
y_pred = nbc.predict(X_test)| Pred 1 | Pred 2 | Pred 3 | |
|---|---|---|---|
| True 1 | 19 | 0 | 0 |
| True 2 | 2 | 19 | 1 |
| True 3 | 0 | 0 | 13 |
The notebook prints classification_report(y_pred, y_test)
| Call | Precision | Recall | Support |
|---|---|---|---|
(y_pred, y_test) | 0.86 | 1.00 | 19 |
(y_test, y_pred) | 1.00 | 0.86 | 22 |
Before you practise
plot_confusion_matrix: use ConfusionMatrixDisplay.from_estimatoralpha=0: one unseen word sets a class probability to exactly 0fit_transform on new e-mails: new text needs vectorizer.transform([...])classification_report arguments: true labels firstMultinomialNB on measurements: counts to Multinomial, numbers to GaussianYour team project
MultinomialNB for word counts, GaussianNB for numeric featuresrandom_stateclassification_report (true labels first)Part 5
About 5 minutes
| Word | Spam e-mails (of 500) | Ham e-mails (of 499) |
|---|---|---|
click | 50 | 6 |
meeting | 1 | 89 |
About 10 minutes
e: Make money online, no meeting needed
Answers
About 5 minutes
| Class | f(x | c) | P(c) | Score |
|---|---|---|---|
| 1 | 0.4791 | 0.3315 | 0.1588 |
| 2 | 0.4760 | 0.3989 | 0.1899 |
About 10 minutes
predict and one to predict_proba: the code review, the meds e-mail, Make money online, no meeting neededinverse_transformrandom_state 1, then 42. How many wines are mislabelled?| Question | Result |
|---|---|
predict on the batch | [0 1 1], third: P(S) = 0.7002 |
| Third e-mail's words | make, make money, meeting, money, needed, online |
random_state=1 / 42 | 1 and 0 of 54 mislabelled |
alpha=1) stops one unseen word from forcing a 0MultinomialNB for word counts, GaussianNB for numeric featuresMahmoud Abas|Naive Bayes Classification