第 3 次作业 · 垃圾短信分类

原始要求

以下要求来自课程的两项实验作业 PPT:

在 TODO 位置填写代码,实现垃圾短信识别:输入不同短信,输出垃圾短信或非垃圾短信。提交可复现报告,包含代码、说明和预测结果。5 月 26 日前交到第三次作业目录,命名方式同作业一。

课件给出的 SMSSpamCollection 数据共有约 5574 条记录,标签为 spam 或 ham。源目录中保留了数据集和我提交的 垃圾短信分类.ipynb,没有发现单独的报告文件,因此这里只能按 Notebook 还原实现与输出。

查看我的 Notebook 实现、输出与复盘

实现

Notebook 用 TF-IDF 把英文短信向量化,再训练逻辑回归:

import pandas as pd
from sklearn import linear_model
from sklearn.feature_extraction.text import TfidfVectorizer

df = pd.read_csv(
    "SMSSpamCollection.txt",
    delimiter="\t",
    header=None,
)
y, text = df[0], df[1]

vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(text)

clf = linear_model.LogisticRegression(
    max_iter=200,
    solver="liblinear",
)
clf.fit(X, y)

随后对两条带“中奖、领取奖品、拨打电话”等模式的短信预测,原 Notebook 输出:

['spam' 'spam']

自制 40 条测试语句

提交稿又手工写了四组各 10 条的语句:明显垃圾、明显正常、看似垃圾但注释为正常、看似正常但注释为垃圾。留存输出依次为:

['spam' 'spam' 'ham' 'ham' 'spam' 'ham' 'ham' 'ham' 'ham' 'ham'
 'ham' 'ham' 'ham' 'ham' 'ham' 'ham' 'ham' 'ham' 'ham' 'ham'
 'spam' 'spam' 'ham' 'spam' 'ham' 'ham' 'spam' 'ham' 'spam' 'ham'
 'spam' 'spam' 'ham' 'ham' 'ham' 'spam' 'ham' 'ham' 'ham' 'ham']

按 Notebook 注释中人为指定的四组标签回算,共 21/40 条相符,即 52.5%。这个百分比是整理时根据留存预测和注释计算的,不是原 Notebook 打印的评估指标。

复盘:这份实现完成了预测,但没有完成泛化评估

原 Notebook 用全部 SMSSpamCollection 同时拟合向量器和分类器,没有拆分训练集与测试集。因此它能对新输入调用 predict,却没有客观估计模型在同分布未见数据上的准确率、Precision 或 Recall。

手写 40 条短信也不是从同一数据分布独立抽样,且“看起来像 ham 的 spam”之类标签是我预先主观设定的,只适合做挑战样例,不能替代标准测试集。很多诈骗短信和正规营销短信在词面上本来就非常相似,单靠词袋特征很难稳定识别来源真实性。

若今天重做,应先分层切分数据,再只用训练集拟合 TF-IDF:

from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.metrics import classification_report

x_train, x_test, y_train, y_test = train_test_split(
    text,
    y,
    test_size=0.2,
    random_state=42,
    stratify=y,
)

model = make_pipeline(
    TfidfVectorizer(),
    linear_model.LogisticRegression(
        max_iter=200,
        solver="liblinear",
    ),
)
model.fit(x_train, y_train)
print(classification_report(y_test, model.predict(x_test)))

这段是复盘方案,不属于当年的原提交。

评论