A text classification model that reads email content and predicts spam or ham (not spam) — trained on 5,500 real emails with 97%+ accuracy.
We use the UCI SMS Spam Collection — 5,574 labelled messages, available free without any account.
import pandas as pd
# Download the dataset
url = 'https://raw.githubusercontent.com/justmarkham/pycon-2016-tutorial/master/data/sms.tsv'
df = pd.read_csv(url, sep='\t', header=None, names=['label', 'text'])
print(df.shape) # (5574, 2)
print(df['label'].value_counts())
print(df.sample(3))
(5574, 2) ham 4827 spam 747 Name: label, dtype: int64 label text 12 ham I've been searching for the right words to ... 45 spam Free entry in 2 a wkly comp to win FA Cup fi... 89 ham Ok lar... Joking wif u oni...
Machine learning models need numbers, not words. TF-IDF converts each message into a vector of word importance scores.
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
X = df['text']
y = df['label']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
vectorizer = TfidfVectorizer(stop_words='english', max_features=5000)
X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)
print(f'Training samples: {X_train_vec.shape[0]}')
print(f'Features (words): {X_train_vec.shape[1]}')
Training samples: 4459 Features (words): 5000
MultinomialNB is the classic choice for text classification — fast, accurate, and interpretable.
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, classification_report
model = MultinomialNB()
model.fit(X_train_vec, y_train)
y_pred = model.predict(X_test_vec)
print(f'Accuracy: {accuracy_score(y_test, y_pred):.4f}')
print()
print(classification_report(y_test, y_pred))
Accuracy: 0.9739
precision recall f1-score support
ham 0.97 1.00 0.98 965
spam 1.00 0.79 0.88 150
accuracy 0.97 1115Pass any text through the vectorizer and model to get a spam/ham prediction.
def predict_spam(message):
vec = vectorizer.transform([message])
prediction = model.predict(vec)[0]
probability = model.predict_proba(vec)[0]
spam_prob = probability[model.classes_.tolist().index('spam')]
label = '🚫 SPAM' if prediction == 'spam' else '✅ HAM (not spam)'
print(f'{label} (spam probability: {spam_prob:.1%})\n "{message[:60]}..."')
predict_spam("Congratulations! You've won a FREE iPhone. Click here NOW to claim!")
predict_spam("Hey, are we still on for lunch tomorrow at 1pm?")
predict_spam("URGENT: Your bank account has been suspended. Verify now!")
predict_spam("Can you send me the meeting notes from this morning?")
🚫 SPAM (spam probability: 99.8%) "Congratulations! You've won a FREE iPhone. Click here NOW to..." ✅ HAM (not spam) (spam probability: 0.1%) "Hey, are we still on for lunch tomorrow at 1pm?..." 🚫 SPAM (spam probability: 97.4%) "URGENT: Your bank account has been suspended. Verify now!..." ✅ HAM (not spam) (spam probability: 0.3%) "Can you send me the meeting notes from this morning?..."
97.4% accuracy with just 20 lines of code. Naive Bayes is fast enough to classify thousands of messages per second. The same TF-IDF + classifier pipeline works for sentiment analysis, topic classification, and any other text problem.
Spotted a bug, broken code, or something that doesn't look right? Tell us what's off and we'll fix it.