Spambase数据集是经典的垃圾邮件分类基准数据,包含4601封邮件样本,每封邮件用57个特征描述。这些特征包括特定单词出现频率(如"free"、"money")、特殊符号频率(如"!"、"$")以及字母大小写统计等。最后一列为分类标签,1表示垃圾邮件,0表示正常邮件。
我们先从数据加载开始。建议使用pandas的read_csv函数直接读取,这个函数能自动处理大多数数据格式问题。我习惯在加载后立即检查数据维度,避免空值或格式错误:
python复制import pandas as pd
spam = pd.read_csv('spambase.csv')
print(f"数据集形状:{spam.shape}") # 应输出(4601, 58)
数据预处理阶段有几个关键点需要注意。首先是特征缩放,决策树不需要但SVM对特征尺度敏感。我通常会先做训练测试分割再分别标准化,避免数据泄露:
python复制from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
X = spam.iloc[:, :-1] # 前57列是特征
y = spam.iloc[:, -1] # 最后一列是标签
# 按7:3比例分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42)
# 仅对SVM需要的标准化处理
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
注意:random_state参数固定能确保结果可复现,这在教学和论文实验中很重要,但实际项目中可能需要交叉验证。
决策树的核心是节点分裂准则。gini系数计算简单,适合大数据集;entropy理论更完备但计算稍复杂。我们通过代码直观比较:
python复制from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
for criterion in ['gini', 'entropy']:
clf = DecisionTreeClassifier(criterion=criterion, max_depth=4, random_state=42)
clf.fit(X_train, y_train)
train_acc = accuracy_score(y_train, clf.predict(X_train))
test_acc = accuracy_score(y_test, clf.predict(X_test))
print(f"{criterion}准则 - 训练集准确率: {train_acc:.4f}, 测试集准确率: {test_acc:.4f}")
在我的测试中,entropy通常比gini高0.5%-1%的准确率,但差异不大。可视化决策树能帮助我们理解模型逻辑:
python复制import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(20,10))
plot_tree(clf, filled=True, feature_names=X.columns,
class_names=['正常', '垃圾'], max_depth=2)
plt.show()
树深度是控制过拟合的关键参数。太浅会欠拟合,太深会记住噪声。我们可以系统测试3到30的深度:
python复制train_scores, test_scores = [], []
depths = range(3, 31)
for depth in depths:
clf = DecisionTreeClassifier(max_depth=depth, random_state=42)
clf.fit(X_train, y_train)
train_scores.append(clf.score(X_train, y_train))
test_scores.append(clf.score(X_test, y_test))
plt.plot(depths, train_scores, label='训练集')
plt.plot(depths, test_scores, label='测试集')
plt.xlabel('树深度')
plt.ylabel('准确率')
plt.legend()
plt.show()
从曲线可以看到,测试集准确率在深度8-10左右达到峰值,之后开始下降,这是典型的过拟合现象。而训练集准确率持续上升,最终达到100%,说明模型完全记住了训练数据。
SVM对数据缩放和降维非常敏感。sklearn的Pipeline可以完美解决这个问题:
python复制from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from sklearn.svm import SVC
pipe = Pipeline([
('scaler', StandardScaler()), # 标准化
('pca', PCA(n_components=0.95)), # 保留95%方差
('svm', SVC(kernel='rbf')) # 默认RBF核
])
pipe.fit(X_train, y_train)
print(f"管道模型准确率: {pipe.score(X_test, y_test):.4f}")
PCA降维在这里有两个作用:一是加速训练,二是可能提升泛化能力。我设置n_components=0.95表示保留95%的原始信息量,这个值可以根据实际情况调整。
SVM有多个关键参数需要优化。GridSearchCV可以自动搜索最佳组合:
python复制from sklearn.model_selection import GridSearchCV
param_grid = {
'svm__C': [0.1, 1, 10, 100], # 正则化强度
'svm__kernel': ['linear', 'rbf', 'poly'],
'svm__gamma': ['scale', 'auto'] + [0.001, 0.01, 0.1]
}
search = GridSearchCV(pipe, param_grid, cv=5, n_jobs=-1)
search.fit(X_train, y_train)
print(f"最佳参数: {search.best_params_}")
print(f"最佳得分: {search.best_score_:.4f}")
print(f"测试集得分: {search.score(X_test, y_test):.4f}")
这里有几个实用技巧:
cv=5表示5折交叉验证,比单次分割更可靠n_jobs=-1使用所有CPU核心加速计算在相同测试集上对比两个模型的最终表现:
| 指标 | 决策树(max_depth=8) | SVM(RBF核) |
|---|---|---|
| 训练时间 | 0.12s | 2.45s |
| 测试准确率 | 91.3% | 93.7% |
| 可解释性 | ★★★★★ | ★★☆☆☆ |
| 特征缩放需求 | 不需要 | 需要 |
决策树训练速度快、可解释性强,适合快速原型开发。SVM准确率更高但训练慢,适合对精度要求严格的场景。在实际项目中,我通常会先尝试决策树建立baseline,再用SVM追求更高性能。
将模型投入实际应用时,有几个工程细节需要注意:
python复制from sklearn.compose import ColumnTransformer
import joblib
preprocessor = ColumnTransformer(
[('scaler', StandardScaler(), list(range(57)))])
joblib.dump(preprocessor, 'preprocessor.pkl')
python复制joblib.dump(search.best_estimator_, 'spam_classifier.pkl')
python复制proba = search.predict_proba(X_new)[:, 1] # 垃圾邮件概率
low_confidence = (proba > 0.3) & (proba < 0.7) # 不确定样本
在真实项目中,我遇到过特征漂移问题——垃圾邮件发送者会不断改变策略。设置自动重训练机制后,分类准确率保持了长期稳定。