前言
在之前的學(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í)可知,變量所起的作用于邏輯回歸中的正則化參數(shù)
相似,不同的
值對(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)
時(shí),決策邊界如下所示:
時(shí),所繪制的決策邊界如下所示:
可以由以上圖片看出,的大小影響著線性決策邊界,其所起的作
用于邏輯回歸中正則化參數(shù)一樣,太大,可能會(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ū)分給定的郵件(變量),是垃圾郵件(
)還是正常郵件(
)。在這個(gè)過程中,需要將每份郵件轉(zhuǎ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)
提取郵件中的特征
有了以上過程,需要從文本郵件中得到特征向量,具體實(shí)現(xiàn)思路是:如果詞匯表中第
個(gè)單詞出現(xiàn)在郵件中,則用
表示,否則用
表示。最后,可以得到一個(gè)
的
維向量,如下所示:
具體實(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è)的向量(詞匯表中有1899個(gè)詞匯,
會(huì)被添加到向量中,最后,得到的向量包含1900個(gè)數(shù)字)。載入數(shù)據(jù)集之后,用變量
表示垃圾郵件,而
表示非垃圾郵件可就可以訓(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)練精度如下所示: