用SVM算法構(gòu)造垃圾郵件分類器

前言

在之前的學(xué)習(xí)中,已經(jīng)學(xué)習(xí)過了支持向量機(jī)的算法,在這部分內(nèi)容中,需要使用2維數(shù)據(jù)實(shí)現(xiàn)帶有高斯核函數(shù)的支持向量機(jī)算法,并利用支持向量機(jī)算法實(shí)現(xiàn)垃圾郵件的分類,具體實(shí)現(xiàn)如下。

使用線性核函數(shù)的svm算法

  • 數(shù)據(jù)集1的線性決策邊界
    首先,加載一個(gè)2維數(shù)據(jù)集,該數(shù)據(jù)集可以被線性邊界分割為正樣本和負(fù)樣本,具體實(shí)現(xiàn)代碼和效果如下所示:
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

繪圖函數(shù)實(shí)現(xiàn)代碼如下所示,根據(jù)y值的不同,得到兩種不同的散點(diǎn)圖。

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)

實(shí)現(xiàn)效果如下所示


通過使用sklearnpython包中的svm模塊,利用svm算法實(shí)現(xiàn)線性分類,由之前的學(xué)習(xí)可知,變量C所起的作用于邏輯回歸中的正則化參數(shù)\frac{1}{\lambda}相似,不同的C值對(duì)決策邊界有不同的影響,具體如下所示:

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時(shí),決策邊界如下所示:

C=1

C=1000時(shí),所繪制的決策邊界如下所示:

C=1000

可以由以上圖片看出,C的大小影響著線性決策邊界,其所起的作

用于邏輯回歸中正則化參數(shù)一樣,C太大,可能會(huì)導(dǎo)致過擬合問題。

使用高斯核函數(shù)的SVM算法

對(duì)于非線性的分類任務(wù),常用帶有高斯核函數(shù)的SVM算法來實(shí)現(xiàn),具體如下所示:

  • 高斯核函數(shù)
    根據(jù)高斯核函數(shù)的原理,具體實(shí)現(xiàn)代碼如下所示
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
  • 繪制非線性決策邊界
    如下代碼所示,加載數(shù)據(jù)集2,并繪制其散點(diǎn)圖,可以很明顯地看出是非線性的數(shù)據(jù)
data = scio.loadmat('ex6data2.mat')
X = data['X']
y = data['y'].flatten()
m = y.size
plot_data(X, y)


如下代碼所示,使用帶有高斯核函數(shù)的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)

實(shí)現(xiàn)效果如下所示:


用SVM算法實(shí)現(xiàn)郵件分類

許多郵件服務(wù)商能夠?qū)]件的識(shí)別具有很高的精確度,通過SVM算法,可以實(shí)現(xiàn)自己的郵件過濾器,具體實(shí)現(xiàn),如下所示。
利用SVM算法,通過訓(xùn)練能夠區(qū)分給定的郵件(變量x),是垃圾郵件(y=1)還是正常郵件(y=0)。在這個(gè)過程中,需要將每份郵件轉(zhuǎn)化為特征向量x \in R^n

郵件處理

一封郵件格式如下所示,需要對(duì)文本郵件做必要處理。


  • 小寫轉(zhuǎn)化
    需要將文本格式的郵件中的大寫字母全部轉(zhuǎn)化為小寫,如下代碼所示
email_contents = email_contents.lower()
  • 過濾html標(biāo)簽
    將文本格式中的HTML標(biāo)簽刪除,用python實(shí)現(xiàn)如下所示:
email_contents = re.sub('<[^<>]+>', ' ', email_contents)
  • URL處理
    需要將所有URL標(biāo)簽轉(zhuǎn)化為字符串httpaddr,如下代碼所示:
email_contents = re.sub('(http|https)://[^\s]*', 'httpaddr', email_contents)
  • email地址處理
    所有的email地址將會(huì)被字符串emailaddr替換,如下所示:
 email_contents = re.sub('[^\s]+@[^\s]+', 'emailaddr', email_contents)
  • 數(shù)字處理
    所有的數(shù)字將會(huì)被替換為字符串number,如下所示:
email_contents = re.sub('[^\s]+@[^\s]+', 'emailaddr', email_contents)
  • $的處理
    所有的$符將會(huì)被替換為字符串dollar,如下所示
email_contents = re.sub('[$]+', 'dollar', email_contents)

經(jīng)過以上步驟處理之后的郵件格式如下圖所示


詞匯表

經(jīng)過以上處理,得到詞匯列表,接下來要做的就是將篩選出來哪些詞匯可以用來構(gòu)建郵件分類器。
為了構(gòu)建一個(gè)合適的郵件分類器,需要選擇一些使用頻率最高的詞匯,(如果選擇一些使用頻率很小的詞匯,可能會(huì)導(dǎo)致過擬合問題)。根據(jù)在垃圾郵件語料庫中至少出現(xiàn)了100次以上的單詞可以添加到詞匯表中的要求,最終,詞匯表中有1899個(gè)單詞。在實(shí)踐中,一個(gè)詞匯表中通常有10000到50000個(gè)單詞。
有了詞匯表,可以將預(yù)處理的電子郵件中的每個(gè)單詞映射到包含詞匯表中單詞的索引的單詞索引列表。所謂單詞索引列表就是每個(gè)單詞所對(duì)應(yīng)的數(shù)字索引所組成的列表,具體如下圖所示:



郵件的文本經(jīng)過處理,可以得到以下的數(shù)字索引,如下所示:



以上過程用python實(shí)現(xiàn)如下所示
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,具體實(shí)現(xiàn)思路是:如果詞匯表中第i個(gè)單詞出現(xiàn)在郵件中,則用x_i=1表示,否則用x_i =0表示。最后,可以得到一個(gè)xn維向量,如下所示:


具體實(shí)現(xiàn)代碼如下所示:

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

為垃圾郵件分類訓(xùn)練SVM算法

完成了郵件特征變量的提取之后,可以利用4000個(gè)訓(xùn)練樣本和1000個(gè)測(cè)試樣本訓(xùn)練SVM算法,每個(gè)原始的郵件將會(huì)被轉(zhuǎn)化為一個(gè)x \in R^{1900}的向量(詞匯表中有1899個(gè)詞匯,x_0=1會(huì)被添加到向量中,最后,得到的向量包含1900個(gè)數(shù)字)。載入數(shù)據(jù)集之后,用變量y=1表示垃圾郵件,而y=0表示非垃圾郵件可就可以訓(xùn)練SVM算法了。具體實(shí)現(xiàn)代碼如下所示:

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))

通過以上代碼,運(yùn)行后,得到的精確度如下所示:


最后,載入測(cè)試集數(shù)據(jù),利用SVM算法構(gòu)造的分類器,得到其訓(xùn)練精度如下所示:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容