用SVM算法构造垃圾邮件分类器

前言

在之前的学习中,已经学习过了支持向量机的算法,在这部分内容中,需要使用2维数据实现带有高斯核函数的支持向量机算法,并利用支持向量机算法实现垃圾邮件的分类,具体实现如下。

使用线性核函数的svm算法

  • 数据集1的线性决策边界
    首先,加载一个2维数据集,该数据集可以被线性边界分割为正样本和负样本,具体实现代码和效果如下所示:
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio
from sklearn import svm
plt.ion()
np.set_printoptions(formatter={'float': '{: 0.6f}'.format})

data = scio.loadmat('ex6data1.mat')
X = data['X']
y = data['y'].flatten()
m = y.size

绘图函数实现代码如下所示,根据y值的不同,得到两种不同的散点图。

def plot_data(X, y):
    plt.figure()

    pos = np.where(y == 1)[0]
    neg = np.where(y == 0)[0]

    plt.scatter(X[pos, 0], X[pos, 1], marker="+", c='b')
    plt.scatter(X[neg, 0], X[neg, 1], marker="o", c='y', s=15)

实现效果如下所示


通过使用sklearnpython包中的svm模块,利用svm算法实现线性分类,由之前的学习可知,变量C所起的作用于逻辑回归中的正则化参数\frac{1}{\lambda}相似,不同的C值对决策边界有不同的影响,具体如下所示:

def visualize_boundary(clf, X, x_min, x_max, y_min, y_max):
    h = 0.02
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.contour(xx, yy, Z, levels=[0], colors='r')

c = 1
clf = svm.SVC(c, kernel='linear', tol=1e-3)
clf.fit(X, y)

plot_data(X, y)
visualize_boundary(clf, X, 0, 4.5, 1.5, 5)

C=1时,决策边界如下所示:

C=1

C=1000时,所绘制的决策边界如下所示:

C=1000

可以由以上图片看出,C的大小影响着线性决策边界,其所起的作

用于逻辑回归中正则化参数一样,C太大,可能会导致过拟合问题。

使用高斯核函数的SVM算法

对于非线性的分类任务,常用带有高斯核函数的SVM算法来实现,具体如下所示:

  • 高斯核函数
    根据高斯核函数的原理,具体实现代码如下所示
def gaussian_kernel(x1, x2, sigma):
    x1 = x1.flatten()
    x2 = x2.flatten()
    sim = 0
    sim = np.exp(np.sum((x1 - x2) ** 2) / (-2*sigma**2))
    return sim
  • 绘制非线性决策边界
    如下代码所示,加载数据集2,并绘制其散点图,可以很明显地看出是非线性的数据
data = scio.loadmat('ex6data2.mat')
X = data['X']
y = data['y'].flatten()
m = y.size
plot_data(X, y)


如下代码所示,使用带有高斯核函数的svm算法绘制非线性的决策边界,如下所示:

c = 1
sigma = 0.1


def gaussian_kernel(x_1, x_2):
    n1 = x_1.shape[0]
    n2 = x_2.shape[0]
    result = np.zeros((n1, n2))

    for i in range(n1):
        for j in range(n2):
            result[i, j] = gk.gaussian_kernel(x_1[i], x_2[j], sigma)

    return result


clf = svm.SVC(c, kernel='rbf', gamma=np.power(sigma, -2))
clf.fit(X, y)
plot_data(X, y)
visualize_boundary(clf, X, 0, 1, .4, 1.0)

实现效果如下所示:


用SVM算法实现邮件分类

许多邮件服务商能够对垃圾邮件的识别具有很高的精确度,通过SVM算法,可以实现自己的邮件过滤器,具体实现,如下所示。
利用SVM算法,通过训练能够区分给定的邮件(变量x),是垃圾邮件(y=1)还是正常邮件(y=0)。在这个过程中,需要将每份邮件转化为特征向量x \in R^n

邮件处理

一封邮件格式如下所示,需要对文本邮件做必要处理。


  • 小写转化
    需要将文本格式的邮件中的大写字母全部转化为小写,如下代码所示
email_contents = email_contents.lower()
  • 过滤html标签
    将文本格式中的HTML标签删除,用python实现如下所示:
email_contents = re.sub('<[^<>]+>', ' ', email_contents)
  • URL处理
    需要将所有URL标签转化为字符串httpaddr,如下代码所示:
email_contents = re.sub('(http|https)://[^\s]*', 'httpaddr', email_contents)
  • email地址处理
    所有的email地址将会被字符串emailaddr替换,如下所示:
 email_contents = re.sub('[^\s]+@[^\s]+', 'emailaddr', email_contents)
  • 数字处理
    所有的数字将会被替换为字符串number,如下所示:
email_contents = re.sub('[^\s]+@[^\s]+', 'emailaddr', email_contents)
  • $的处理
    所有的$符将会被替换为字符串dollar,如下所示
email_contents = re.sub('[$]+', 'dollar', email_contents)

经过以上步骤处理之后的邮件格式如下图所示


词汇表

经过以上处理,得到词汇列表,接下来要做的就是将筛选出来哪些词汇可以用来构建邮件分类器。
为了构建一个合适的邮件分类器,需要选择一些使用频率最高的词汇,(如果选择一些使用频率很小的词汇,可能会导致过拟合问题)。根据在垃圾邮件语料库中至少出现了100次以上的单词可以添加到词汇表中的要求,最终,词汇表中有1899个单词。在实践中,一个词汇表中通常有10000到50000个单词。
有了词汇表,可以将预处理的电子邮件中的每个单词映射到包含词汇表中单词的索引的单词索引列表。所谓单词索引列表就是每个单词所对应的数字索引所组成的列表,具体如下图所示:



邮件的文本经过处理,可以得到以下的数字索引,如下所示:



以上过程用python实现如下所示
def process_email(email_contents):
    vocab_list = get_vocab_list()

    word_indices = np.array([], dtype=np.int64)
    email_contents = email_contents.lower()
    email_contents = re.sub('<[^<>]+>', ' ', email_contents)
    email_contents = re.sub('[0-9]+', 'number', email_contents)
    email_contents = re.sub('(http|https)://[^\s]*', 'httpaddr', email_contents)
    email_contents = re.sub('[^\s]+@[^\s]+', 'emailaddr', email_contents)
    email_contents = re.sub('[$]+', 'dollar', email_contents)
    print('==== Processed Email ====')
    stemmer = nltk.stem.porter.PorterStemmer()
    tokens = re.split('[@$/#.-:&*+=\[\]?!(){\},\'\">_<;% ]', email_contents)
    for token in tokens:
        token = re.sub('[^a-zA-Z0-9]', '', token)
        token = stemmer.stem(token)
        if len(token) < 1:
            continue

        for i in range(1, len(vocab_list) + 1):
            if vocab_list[i] == token:
                word_indices = np.append(word_indices, i)

        print(token)

    print('==================')

    return word_indices


def get_vocab_list():
    vocab_dict = {}
    with open('vocab.txt') as f:
        for line in f:
            (val, key) = line.split()

            vocab_dict[int(val)] = key

    return vocab_dict

plt.ion()
np.set_printoptions(formatter={'float': '{: 0.6f}'.format})
print('Preprocessing sample email (emailSample1.txt) ...')
file_contents = open('emailSample1.txt', 'r').read()
word_indices = process_email(file_contents)

提取邮件中的特征

有了以上过程,需要从文本邮件中得到特征向量x,具体实现思路是:如果词汇表中第i个单词出现在邮件中,则用x_i=1表示,否则用x_i =0表示。最后,可以得到一个xn维向量,如下所示:


具体实现代码如下所示:

def email_features(word_indices):
    n = 1899
    features = np.zeros(n + 1)
    features[word_indices - 1] = 1
    return features

为垃圾邮件分类训练SVM算法

完成了邮件特征变量的提取之后,可以利用4000个训练样本和1000个测试样本训练SVM算法,每个原始的邮件将会被转化为一个x \in R^{1900}的向量(词汇表中有1899个词汇,x_0=1会被添加到向量中,最后,得到的向量包含1900个数字)。载入数据集之后,用变量y=1表示垃圾邮件,而y=0表示非垃圾邮件可就可以训练SVM算法了。具体实现代码如下所示:

data = scio.loadmat('spamTrain.mat')
X = data['X']
y = data['y'].flatten()

print('Training Linear SVM (Spam Classification)')
print('(this may take 1 to 2 minutes)')

c = 0.1
clf = svm.SVC(c, kernel='linear')
clf.fit(X, y)

p = clf.predict(X)
print('Training Accuracy: {}'.format(np.mean(p == y) * 100))

通过以上代码,运行后,得到的精确度如下所示:


最后,载入测试集数据,利用SVM算法构造的分类器,得到其训练精度如下所示:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352