8.0 KiB
8.0 KiB
In [ ]:
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.dummy import DummyClassifier
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics import (
accuracy_score,
classification_report,
confusion_matrix,
f1_score,
precision_score,
recall_score,
)
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
DATA_PATH = Path("data/SMSSpamCollection")
RANDOM_STATE = 42
if not DATA_PATH.exists():
raise FileNotFoundError(
"Missing data/SMSSpamCollection. Follow data/README.md to download the official dataset."
)
In [ ]:
df = pd.read_csv(DATA_PATH, sep="\t", header=None, names=["label", "message"])
df["label"] = df["label"].astype("string").str.strip().str.lower()
df["message"] = df["message"].astype("string").str.strip()
df = df.dropna(subset=["label", "message"])
df = df[df["label"].isin(["ham", "spam"]) & df["message"].ne("")]
conflict_counts = df.groupby("message")["label"].nunique()
df = df[~df["message"].isin(conflict_counts[conflict_counts.gt(1)].index)]
df = df.drop_duplicates(subset=["label", "message"]).reset_index(drop=True)
X = df["message"]
y = df["label"].map({"ham": 0, "spam": 1}).astype(int)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.20, random_state=RANDOM_STATE, stratify=y
)
print("Training messages:", len(X_train))
print("Test messages:", len(X_test))
print("Training spam rate:", f"{y_train.mean():.2%}")
print("Test spam rate:", f"{y_test.mean():.2%}")
In [ ]:
# TODO 1: create a Pipeline named nb_pipeline with a TF-IDF vectorizer
# followed by MultinomialNB(alpha=1.0).
nb_pipeline = None
# TODO 2: fit the pipeline on X_train and y_train, then predict X_test.
y_pred = None
print(nb_pipeline)
In [ ]:
# TODO 3: calculate the confusion matrix and unpack tn, fp, fn, tp.
cm = None
tn, fp, fn, tp = None, None, None, None
# TODO 4: calculate accuracy and spam precision, recall, and F1.
nb_metrics = None
print(pd.Series(nb_metrics).round(4))
print("\nClassification report:")
print(None)
In [ ]:
# TODO 5: train a most_frequent DummyClassifier and predict X_test.
dummy = None
dummy_pred = None
# TODO 6: compare the two models using spam F1 and accuracy.
comparison = None
comparison
In [ ]:
# TODO 7: create a DataFrame with message, actual, and predicted columns.
error_analysis = None
# TODO 8: keep only incorrect predictions and add an error_type column
# containing false_positive or false_negative.
errors = None
print(errors["error_type"].value_counts())
errors.head(10)
In [ ]:
assert list(nb_pipeline.named_steps) == ["tfidf", "model"]
assert isinstance(nb_pipeline.named_steps["model"], MultinomialNB)
assert len(y_pred) == len(y_test)
assert cm.shape == (2, 2)
assert (tn + fp + fn + tp) == len(y_test)
assert set(nb_metrics) == {"accuracy", "spam_precision", "spam_recall", "spam_f1"}
assert comparison.loc["Most-frequent baseline", "spam_f1"] == 0
assert set(errors["error_type"].unique()).issubset({"false_positive", "false_negative"})
print("✓ Lab 3 completed")