机器学习练习 6 - 支持向量机

在本练习中,我们将使用支持向量机(SVM)来构建垃圾邮件分类器。 我们将从一些简单的2D数据集开始使用SVM来查看它们的工作原理。 然后,我们将对一组原始电子邮件进行一些预处理工作,并使用SVM在处理的电子邮件上构建分类器,以确定它们是否为垃圾邮件。

6 Support Vector Machines

6.1 Linear SVM

6.1.1 load data

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
from scipy.io import loadmat
from sklearn import svm

raw_data = loadmat('data/ex6data1.mat')

6.1.2 visualize data

我们将其用散点图表示,其中类标签由符号表示(+表示正类,o表示负类)。.

 data = pd.DataFrame(raw_data['X'], columns=['X1', 'X2'])
 data['y'] = raw_data['y']

 positive = data[data['y'].isin([1])]
 negative = data[data['y'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['X1'], positive['X2'], s=50, marker='x', label='Positive')
ax.scatter(negative['X1'], negative['X2'], s=50, marker='o', label='Negative')
ax.legend()
plt.show()

机器学习练习 6 - 支持向量机
有一个异常的正例在其他样本之外。 这些类仍然是线性分离的,但它非常紧凑。 我们要训练线性支持向量机来学习类边界。 在这个练习中,我们没有从头开始执行SVM的任务,所以我要用scikit-learn。

#画决策边界    
def plotBoundary(clf,data):
x_min,x_max=np.array(data['X1']).min(),np.array(data['X1']).max()
y_min,y_max=np.array(data['X2']).min(),np.array(data['X2']).max()
xx,yy=np.meshgrid(np.linspace(x_min,x_max,500),np.linspace(y_min,y_max,500))
Z=clf.predict(np.c_[xx.ravel(),yy.ravel()])
Z=Z.reshape(xx.shape)
plt.contour(xx,yy,Z,colors = 'red', linewidth = 0.5) 

6.1.3 C=1

svc1 = svm.LinearSVC(C=1, loss='hinge', max_iter=1000)
clfs1=svc1.fit(data[['X1', 'X2']], data['y'])
svc1.score(data[['X1', 'X2']], data['y'])  #0.9803921568627451

这次我们得到了训练数据的完美分类,但是通过增加C的值,我们创建了一个不再适合数据的决策边界。 我们可以通过查看每个类别预测的置信水平来看出这一点,这是该点与超平面距离的函数。

data['SVM 1 Confidence'] = svc1.decision_function(data[['X1', 'X2']])

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 1 Confidence'],cmap='rainbow')
plotBoundary(clfs1,data) 
ax.set_title('SVM (C=1) Decision Confidence')
plt.show()

机器学习练习 6 - 支持向量机

6.1.4 C=100

data['SVM 2 Confidence'] = svc2.decision_function(data[['X1', 'X2']])

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 2 Confidence'], cmap='rainbow')
plotBoundary(clfs2,data)
ax.set_title('SVM (C=100) Decision Confidence')
plt.show()

机器学习练习 6 - 支持向量机

6.2 SVM with Gaussian Kernels

使用SVM做非线性分类。我们将使用高斯核函数。

为了用SVM找出一个非线性的决策边界,我们首先要实现高斯核函数。
我可以把高斯核函数想象成一个相似度函数,用来测量一对样本的距离,(x(i),y(j))

def gaussian_kernel(x1, x2, sigma):
    return np.exp(-(np.sum((x1 - x2) ** 2) / (2 * (sigma ** 2))))
x1 = np.array([1.0, 2.0, 1.0])
x2 = np.array([0.0, 4.0, -1.0])
sigma = 2

gaussian_kernel(x1, x2, sigma)
#0.32465246735834974

该结果与练习中的预期值相符。 接下来,我们将检查另一个数据集,这次用非线性决策边界。

6.2.1 load data

raw_data2 = loadmat('data/ex6data2.mat')
data2 = pd.DataFrame(raw_data2['X'], columns=['X1', 'X2'])
data2['y'] = raw_data2['y']

6.2.2 visualize data

positive = data2[data2['y'].isin([1])]
negative = data2[data2['y'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['X1'], positive['X2'], s=30, marker='x', label='Positive')
ax.scatter(negative['X1'], negative['X2'], s=30, marker='o', label='Negative')
ax.legend()
plt.show()

机器学习练习 6 - 支持向量机
对于该数据集,我们将使用内置的RBF内核构建支持向量机分类器,并检查其对训练数据的准确性。 为了可视化决策边界,这一次我们将根据实例具有负类标签的预测概率来对点做阴影。 从结果可以看出,它们大部分是正确的。

6.2.3 try built-in Gaussian Kernel of sklearn

svc3 = svm.SVC(C=100, gamma=10, probability=True)
svc3
'''
SVC(C=100, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=10, kernel='rbf',
  max_iter=-1, probability=True, random_state=None, shrinking=True,
  tol=0.001, verbose=False)'''
clfs3=svc3.fit(data2[['X1', 'X2']], data2['y'])
svc3.score(data2[['X1', 'X2']], data2['y'])
#0.9698725376593279
data2['Probability'] = svc3.predict_proba(data2[['X1', 'X2']])[:,0]

fig, ax2 = plt.subplots(figsize=(12,8))
ax2.scatter(data2['X1'], data2['X2'], s=30, c=data2['Probability'])
plotBoundary(clfs3,data2)
plt.show()

机器学习练习 6 - 支持向量机

6.3 寻找最优参数

对于第三个数据集,我们给出了训练和验证集,并且基于验证集性能为SVM模型找到最优超参数。 虽然我们可以使用scikit-learn的内置网格搜索来做到这一点,但是本着遵循练习的目的,我们将从头开始实现一个简单的网格搜索

6.3.1 load data

raw_data = loadmat('data/ex6data3.mat')

X = raw_data['X']
Xval = raw_data['Xval']
y = raw_data['y'].ravel()
yval = raw_data['yval'].ravel()

6.3.2 manual grid search for ???? and σ

C_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]
gamma_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]

best_score = 0
best_params = {'C': None, 'gamma': None}

for C in C_values:
    for gamma in gamma_values:
        svc = svm.SVC(C=C, gamma=gamma)
        svc.fit(X, y)
        score = svc.score(Xval, yval)
        
        if score > best_score:
            best_score = score
            best_params['C'] = C
            best_params['gamma'] = gamma

best_score, best_params
#    (0.965, {'C': 0.3, 'gamma': 100})

我们的目标是使用SVM来构建垃圾邮件过滤器。 在练习文本中,有一个任务涉及一些文本预处理,以获得适合SVM处理的格式的数据。 然而,这个任务很简单(将字词映射到为练习提供的字典中的ID),而其余的预处理步骤(如HTML删除,词干,标准化等)已经完成。 我将跳过机器学习任务,而不是重现这些预处理步骤,其中包括从预处理过的训练集构建分类器,以及将垃圾邮件和非垃圾邮件转换为单词出现次数的向量的测试数据集。

spam_train = loadmat('data/spamTrain.mat')
spam_test = loadmat('data/spamTest.mat')

spam_train

{'__header__': b'MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Sun Nov 13 14:27:25 2011',
 '__version__': '1.0',
 '__globals__': [],
 'X': array([[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 1, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]], dtype=uint8),
 'y': array([[1],
        [1],
        [0],
        ...,
        [1],
        [0],
        [0]], dtype=uint8)}
X = spam_train['X']
Xtest = spam_test['Xtest']
y = spam_train['y'].ravel()
ytest = spam_test['ytest'].ravel()

X.shape, y.shape, Xtest.shape, ytest.shape
#   ((4000, 1899), (4000,), (1000, 1899), (1000,))

每个文档已经转换为一个向量,其中1,899个维对应于词汇表中的1,899个单词。 它们的值为二进制,表示文档中是否存在单词。 在这一点上,训练评估是用一个分类器拟合测试数据的问题。

svc = svm.SVC()
svc.fit(X, y)
print('Training accuracy = {0}%'.format(np.round(svc.score(X, y) * 100, 2)))
# Training accuracy = 94.4%
print('Test accuracy = {0}%'.format(np.round(svc.score(Xtest, ytest) * 100, 2)))
# Test accuracy = 95.3%

这个结果是使用使用默认参数的。 我们可能会使用一些参数调整来获得更高的精度