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

1 支持向量

在本练习中,我们将使用高斯核函数的支持向量机(SVM)来构建垃圾邮件分类器。
请添加图片描述

1.1 数据集1

我们先在2D数据集上实验

In [1]:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
from scipy.io import loadmat
/opt/conda/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)
/opt/conda/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)

In [2]:

raw_data = loadmat('/home/kesci/input/andrew_ml_ex67101/ex6data1.mat')
data = pd.DataFrame(raw_data.get('X'), columns=['X1', 'X2']) # dict转DataFrame
data['y'] = raw_data.get('y')

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

Out[2]:

X1X2y
01.96434.59571
12.27533.85891
22.97814.56511
32.93203.55191
43.57722.85601

数据可视化

In [3]:

def plot_init_data(data, fig, ax):
    positive = data[data['y'].isin([1])]
    negative = data[data['y'].isin([0])]

    ax.scatter(positive['X1'], positive['X2'], s=50, marker='x', label='Positive')
    ax.scatter(negative['X1'], negative['X2'], s=50, marker='o', label='Negative')

In [4]:

fig, ax = plt.subplots(figsize=(12,8))
plot_init_data(data, fig, ax)
ax.legend()
plt.show()

img

请注意,还有一个异常的正例在其他样本之外。
这些类仍然是线性分离的,但它非常紧凑。 我们要训练线性支持向量机来学习类边界。

函数作用
svc.decision_function(X)样本X到分离超平面的距离
svc.fit(X, y[, sample_weight])根据给定的训练数据拟合SVM模型。
svc.get_params([deep])获取此估算器的参数并以字典行书储存,默认deep=True,以分类iris数据集为例,得到的参数如下
svc.predict(X)根据测试数据集进行预测
svc.score(X, y[, sample_weight])返回给定测试数据和标签的平均精确度
svc.predict_log_proba(X_test),svc.predict_proba(X_test)当sklearn.svm.SVC(probability=True)时,才会有这两个值,分别得到样本的对数概率以及普通概率。

令C=1

In [5]:

from sklearn import svm
svc = svm.LinearSVC(C=1, loss='hinge', max_iter=1000)
svc
'''
penalty : string, ‘l1’ or ‘l2’ (default=’l2’)

指定惩罚中使用的规范。 'l2'惩罚是SVC中使用的标准。 'l1'导致稀疏的coef_向量。

loss : string, ‘hinge’ or ‘squared_hinge’ (default=’squared_hinge’)

指定损失函数。 “hinge”是标准的SVM损失(例如由SVC类使用),而“squared_hinge”是hinge损失的平方。

dual : bool, (default=True)

选择算法以解决双优化或原始优化问题。 当n_samples> n_features时,首选dual = False。

tol : float, optional (default=1e-4)

公差停止标准

C : float, optional (default=1.0)

错误项的惩罚参数

multi_class : string, ‘ovr’ or ‘crammer_singer’ (default=’ovr’)

如果y包含两个以上的类,则确定多类策略。 “ovr”训练n_classes one-vs-rest分类器,而“crammer_singer”优化所有类的联合目标。 虽然crammer_singer在理论上是有趣的,因为它是一致的,但它在实践中很少使用,因为它很少能够提高准确性并且计算成本更高。 如果选择“crammer_singer”,则将忽略选项loss,penalty和dual。

fit_intercept : boolean, optional (default=True)

是否计算此模型的截距。 如果设置为false,则不会在计算中使用截距(即,预期数据已经居中)。

intercept_scaling : float, optional (default=1)

当self.fit_intercept为True时,实例向量x变为[x,self.intercept_scaling],即具有等于intercept_scaling的常量值的“合成”特征被附加到实例向量。 截距变为intercept_scaling *合成特征权重注意! 合成特征权重与所有其他特征一样经受l1 / l2正则化。 为了减小正则化对合成特征权重(并因此对截距)的影响,必须增加intercept_scaling。

class_weight : {dict, ‘balanced’}, optional

将类i的参数C设置为SVC的class_weight [i] * C. 如果没有给出,所有课程都应该有一个重量。 “平衡”模式使用y的值自动调整与输入数据中的类频率成反比的权重,如n_samples /(n_classes * np.bincount(y))

verbose : int, (default=0)

启用详细输出。 请注意,此设置利用liblinear中的每进程运行时设置,如果启用,可能无法在多线程上下文中正常工作。

random_state : int, RandomState instance or None, optional (default=None)

在随机数据混洗时使用的伪随机数生成器的种子。 如果是int,则random_state是随机数生成器使用的种子; 如果是RandomState实例,则random_state是随机数生成器; 如果为None,则随机数生成器是np.random使用的RandomState实例。

max_iter : int, (default=1000)

要运行的最大迭代次数。

'''

Out[5]:

LinearSVC(C=1, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='hinge', max_iter=1000, multi_class='ovr',
     penalty='l2', random_state=None, tol=0.0001, verbose=0)

In [6]:

svc.fit(data[['X1', 'X2']], data['y'])
svc.score(data[['X1', 'X2']], data['y'])
/opt/conda/lib/python3.6/site-packages/sklearn/svm/base.py:931: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)

Out[6]:

0.9803921568627451

可视化分类边界

In [7]:

def find_decision_boundary(svc, x1min, x1max, x2min, x2max, diff):
    x1 = np.linspace(x1min, x1max, 1000)
    x2 = np.linspace(x2min, x2max, 1000)

    cordinates = [(x, y) for x in x1 for y in x2]
    x_cord, y_cord = zip(*cordinates)
    c_val = pd.DataFrame({'x1':x_cord, 'x2':y_cord})
    c_val['cval'] = svc.decision_function(c_val[['x1', 'x2']])

    decision = c_val[np.abs(c_val['cval']) < diff]
    
    return decision.x1, decision.x2

In [8]:

x1, x2 = find_decision_boundary(svc, 0, 4, 1.5, 5, 2 * 10**-3)
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(x1, x2, s=10, c='r',label='Boundary')
plot_init_data(data, fig, ax)
ax.set_title('SVM (C=1) Decision Boundary')
ax.legend()
plt.show()

img

其次,让我们看看如果C的值越大,会发生什么

令C=100

In [9]:

svc2 = svm.LinearSVC(C=100, loss='hinge', max_iter=1000)
svc2.fit(data[['X1', 'X2']], data['y'])
svc2.score(data[['X1', 'X2']], data['y'])
/opt/conda/lib/python3.6/site-packages/sklearn/svm/base.py:931: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)

Out[9]:

0.9019607843137255

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

In [10]:

x1, x2 = find_decision_boundary(svc, 0, 4, 1.5, 5, 2 * 10**-3)
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(x1, x2, s=10, c='r',label='Boundary')
plot_init_data(data, fig, ax)
ax.set_title('SVM (C=100) Decision Boundary')
ax.legend()
plt.show()

img

1.1.1 线性内核

θ T x ( i ) = p ( i ) ⋅ ∥ θ ∥ θ^Tx^{(i)}=p^{(i)}\cdot{\left\| \theta \right\|} θTx(i)=p(i)θ

1.2 高斯内核的SVM

现在我们将从线性SVM转移到能够使用内核进行非线性分类的SVM。
虽然scikit-learn具有内置的高斯内核,但为了实现更清楚,我们将从头开始实现。

1.2.1 高斯内核

你把高斯内核认为是一个衡量一对数据间的“距离”的函数,有一个参数σ,决定了相似性下降至0有多快

Image Name

In [11]:

def gaussian_kernel(x1, x2, sigma):
    return np.exp(-(np.sum((x1 - x2) ** 2) / (2 * (sigma ** 2))))

In [12]:

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)

Out[12]:

0.32465246735834974

1.2.2 数据集2

接下来,我们在另一个数据集,上使高斯内核,找非线性边界。

In [13]:

raw_data = loadmat('/home/kesci/input/andrew_ml_ex67101/ex6data2.mat')

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

fig, ax = plt.subplots(figsize=(12,8))
plot_init_data(data, fig, ax)
ax.legend()
plt.show()

img

In [14]:

svc = svm.SVC(C=100, gamma=10, probability=True)
svc

'''
C:C-SVC的惩罚参数C?默认值是1.0
C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。

kernel :核函数,默认是rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
– 线性:u’v
– 多项式:(gamma*u’v + coef0)^degree
– RBF函数:exp(-gamma|u-v|^2)
–sigmoid:tanh(gammau’*v + coef0)

degree :多项式poly函数的维度,默认是3,选择其他核函数时会被忽略。

gamma : ‘rbf’,‘poly’ 和‘sigmoid’的核函数参数。默认是’auto’,则会选择1/n_features

coef0 :核函数的常数项。对于‘poly’和 ‘sigmoid’有用。

probability :是否采用概率估计.默认为False
布尔类型,可选,默认为False
决定是否启用概率估计。需要在训练fit()模型时加上这个参数,之后才能用相关的方法:predict_proba和predict_log_proba

shrinking :是否采用shrinking heuristic方法,默认为true

tol :停止训练的误差值大小,默认为1e-3

cache_size :核函数cache缓存大小,默认为200

class_weight :类别的权重,字典形式传递。设置第几类的参数C为weight*C(C-SVC中的C)

verbose :允许冗余输出?

max_iter :最大迭代次数。-1为无限制。

decision_function_shape :‘ovo’, ‘ovr’ or None, default=None3

random_state :数据洗牌时的种子值,int值
'''

Out[14]:

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)

In [15]:

svc.fit(data[['X1', 'X2']], data['y'])
svc.score(data[['X1', 'X2']], data['y'])

Out[15]:

0.9698725376593279

In [16]:

x1, x2 = find_decision_boundary(svc, 0, 1, 0.4, 1, 0.01)
fig, ax = plt.subplots(figsize=(12,8))
plot_init_data(data, fig, ax)
ax.scatter(x1, x2, s=10)
plt.show()

img

1.2.3 数据集3

对于第三个数据集,我们给出了训练和验证集,并且基于验证集性能为SVM模型找到最优超参数。
我们现在需要寻找最优C和σ,候选数值为[0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]

In [17]:

raw_data = loadmat('/home/kesci/input/andrew_ml_ex67101/ex6data3.mat')

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

fig, ax = plt.subplots(figsize=(12,8))
data = pd.DataFrame(raw_data.get('X'), columns=['X1', 'X2'])
data['y'] = raw_data.get('y')
plot_init_data(data, fig, ax)
plt.show()

img

In [18]:

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

Out[18]:

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

In [19]:

svc = svm.SVC(C=best_params['C'], gamma=best_params['gamma'])
svc.fit(X, y)

x1, x2 = find_decision_boundary(svc, -0.6, 0.3, -0.7, 0.6, 0.005)
fig, ax = plt.subplots(figsize=(12,8))
plot_init_data(data, fig, ax)
ax.scatter(x1, x2, s=10)
plt.show()

img

2 垃圾邮件分类

在这一部分中,我们的目标是使用SVM来构建垃圾邮件过滤器。

2.1 处理邮件

2.2 提取特征

这2个部分是处理邮件,以获得适合SVM处理的格式的数据。
然而,这个任务很简单(将字词映射到为练习提供的字典中的ID),而其余的预处理步骤(如HTML删除,词干,标准化等)已经完成。
我们就直接读取预先处理好的数据就可以了。

2.3 训练垃圾邮件分类SVM

In [20]:

spam_train = loadmat('/home/kesci/input/andrew_ml_ex67101/spamTrain.mat')
spam_test = loadmat('/home/kesci/input/andrew_ml_ex67101/spamTest.mat')

spam_train

Out[20]:

{'__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)}

In [21]:

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

Out[21]:

((4000, 1899), (4000,), (1000, 1899), (1000,))

每个文档已经转换为一个向量,其中1,899个维对应于词汇表中的1,899个单词。 它们的值为二进制,表示文档中是否存在单词。

In [22]:

svc = svm.SVC()
svc.fit(X, y)
/opt/conda/lib/python3.6/site-packages/sklearn/svm/base.py:196: FutureWarning: The default value of gamma will change from 'auto' to 'scale' in version 0.22 to account better for unscaled features. Set gamma explicitly to 'auto' or 'scale' to avoid this warning.
  "avoid this warning.", FutureWarning)

Out[22]:

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
  kernel='rbf', max_iter=-1, probability=False, random_state=None,
  shrinking=True, tol=0.001, verbose=False)

In [23]:

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

2.4 可视化结果

In [24]:

kw = np.eye(1899)
kw[:3,:]
spam_val = pd.DataFrame({'idx':range(1899)})

In [25]:

spam_val['isspam'] = svc.decision_function(kw)

In [26]:

spam_val['isspam'].describe()

Out[26]:

count    1899.000000
mean       -0.622564
std         0.021603
min        -0.763138
25%        -0.631616
50%        -0.624299
75%        -0.615918
max        -0.364164
Name: isspam, dtype: float64

In [27]:

decision = spam_val[spam_val['isspam'] > -0.55]
decision

Out[27]:

idxisspam
173173-0.546054
297297-0.364164
478478-0.543913
529529-0.524535
680680-0.524313
738738-0.549962
774774-0.489291
10591059-0.547064
10881088-0.531602
11631163-0.549382
11901190-0.420982
12631263-0.476572
13721372-0.527429
13971397-0.432722
18181818-0.517247
18511851-0.536655
18921892-0.511805
18941894-0.458944

In [28]:

path =  '/home/kesci/input/andrew_ml_ex67101/vocab.txt'
voc = pd.read_csv(path, header=None, names=['idx', 'voc'], sep = '\t')
voc.head()

Out[28]:

idxvoc
01aa
12ab
23abil
34abl
45about

In [29]:

spamvoc = voc.loc[list(decision['idx'])]
spamvoc

sOut[29]:

idxvoc
173174below
297298click
478479dollarnumb
529530email
680681free
738739guarante
774775here
10591060monei
10881089nbsp
11631164offer
11901191our
12631264pleas
13721373receiv
13971398remov
18181819we
18511852will
18921893you
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追寻远方的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值