【机器学习实战6】基于SVM的垃圾邮件过滤器
在当今数字时代,电子邮件已成为我们日常生活和工作中不可或缺的通信工具。然而,随之而来的是大量垃圾邮件的困扰。这些未经请求的邮件不仅浪费了我们的时间,还可能带来安全风险。本文将分享如何使用支持向量机(SVM)技术构建一个高效的垃圾邮件过滤器,帮助我们从海量邮件中精准识别并过滤掉垃圾邮件。
SVM基本概念
支持向量机(SVM)是一种强大的监督学习算法,主要用于分类和回归问题。它的核心思想是找到一个最优的决策边界(超平面),能够最大化不同类别数据点之间的间隔(margin)。通过这种方式,SVM不仅能够准确分类数据,还能在新数据上表现出良好的泛化能力。
超平面
在N维空间中,超平面是一个N-1维的子空间。对于二维数据,超平面是一条直线;对于三维数据,超平面是一个平面。分类的目标是找到一个超平面,使得不同类别的数据点分布在超平面的两侧。这个超平面可以用以下方程表示:

其中,w 是超平面的法向量,b 是偏置项,x 是数据点。
支持向量
支持向量是距离超平面最近的样本点,这些点决定了超平面的位置和方向。支持向量到超平面的距离称为间隔,SVM的目标是最大化这个间隔。间隔分为两类:
-
硬间隔:要求所有数据严格线性可分,且分类完全正确(适用于无噪声数据)。
-
软间隔:允许部分样本分类错误(通过松弛变量 ξ 控制),适用于非线性可分或噪声数据。当数据线性不可分时,通过核函数将原始特征映射到高维空间,使其在高维空间中线性可分。
主要特点
-
适用于高维空间:特别适合文本分类等特征维度高的问题。
-
核技巧(Kernel Trick):通过核函数可以处理非线性可分问题。
-
良好的泛化能力:基于结构风险最小化原则,具有良好的泛化能力。
-
对过拟合有较强抵抗力:尤其在高维空间中表现优异。
SVM数学原理
线性可分情况
对于线性可分的情况,SVM寻找一个超平面:

优化目标是最大化间隔,等价于最小化:

约束条件:

其中,yi 是数据点 xi 的类别标签(yi∈{−1,1})。
非线性可分情况
对于非线性可分的情况,引入松弛变量 ξi 和惩罚参数 C:

约束条件: yi(w⊤xi+b)≥1−ξi,ξi≥0
通过引入松弛变量 ξi,SVM可以在数据中存在噪声或非线性可分的情况下,找到一个最优的决策边界,从而实现更好的分类效果。
构建垃圾邮件过滤器
数据预处理
-
文本清洗与分词:对邮件文本进行预处理,包括转换为小写、去除特殊符号、分割为单词列表。
-
特征提取:使用词袋模型(Bag-of-Words)将文本转换为特征向量。每个单词在词汇表中对应一个索引,若单词在邮件中出现,则对应位置设为1,否则为0。
模型训练
-
数据加载:加载训练数据和测试数据。
-
SVM模型构建:使用线性核SVM进行训练。通过调整正则化参数 C,可以控制模型对误分类的惩罚程度。
-
模型评估:在测试集上评估模型性能,计算准确率等指标。
示例演示
-
加载示例邮件:加载一封示例邮件。
-
转换为特征向量并分类:将示例邮件转换为特征向量,然后使用训练好的SVM模型进行分类。
完整代码
# 数据预处理:从文本到特征向量
import re
from scipy.io import loadmat
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# 1. 词汇表加载
def load_vocab():
vocab = {}
with open(r'C:\Users\wu\Desktop\svm_data\vocab.txt', 'r') as f:
for idx, line in enumerate(f):
word = line.strip().split()[1]
vocab[word] = idx + 1 # 索引从1开始
return vocab
# 2. 文本清洗与分词
def text_preprocess(email):
email = email.lower()
# 替换特殊符号
email = re.sub(r'<[^>]+>', ' ', email) # 去除HTML标签
email = re.sub(r'http://|https://', 'httpaddr', email) # 统一网址表示
email = re.sub(r'[^\w\s]', ' ', email) # 去除非单词字符
words = re.split(r'\s+', email.strip()) # 使用原始字符串
return [word for word in words if len(word) > 0]
# 3. 特征向量构建
def email_to_feature_vector(email, vocab):
feature = [0] * len(vocab)
words = text_preprocess(email)
for word in words:
if word in vocab:
idx = vocab[word] - 1 # 转换为0-based索引
feature[idx] = 1
return feature
# 模型训练:使用 SVM 分类器
# 1. 数据加载
spam_train = loadmat(r'C:\Users\wu\Desktop\svm_data\spamTrain.mat')
X_train, y_train = spam_train['X'], spam_train['y'].ravel()
spam_test = loadmat(r'C:\Users\wu\Desktop\svm_data\spamTest.mat')
X_test, y_test = spam_test['Xtest'], spam_test['ytest'].ravel() # 修改键名
# 2. SVM 模型构建
clf = SVC(kernel='linear', C=0.1) # C为正则化参数,需根据验证集调整
clf.fit(X_train, y_train)
# 训练集准确率
train_acc = accuracy_score(y_train, clf.predict(X_train))
print(f"训练集准确率: {train_acc:.4f}") # 通常可达99%以上
# 模型评估:在测试集验证性能
y_pred = clf.predict(X_test)
test_acc = accuracy_score(y_test, y_pred)
print(f"测试集准确率: {test_acc:.4f}") # 典型值约98%
# 示例演示:分类单个邮件
# 1. 加载示例邮件
with open(r'C:\Users\wu\Desktop\svm_data\emailSample1.txt', 'r') as f:
sample_email = f.read()
# 2. 转换为特征向量并分类
vocab = load_vocab()
sample_feature = email_to_feature_vector(sample_email, vocab)
prediction = clf.predict([sample_feature])
print("分类结果(1=垃圾邮件,0=正常邮件):", prediction[0])
运行结果

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)