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

2019-03-14  本文已影响0人  此间不留白

前言

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

使用线性核函数的svm算法

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
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()
email_contents = re.sub('<[^<>]+>', ' ', 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('[^\s]+@[^\s]+', 'emailaddr', email_contents)
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算法构造的分类器,得到其训练精度如下所示:


上一篇 下一篇

猜你喜欢

热点阅读