线性支持向量机
一、需要使用的模块
import pandas as pd
from scipy.io import loadmat
import matplotlib.pyplot as plt
import numpy as np
这里pandas
主要用于将数据处理成DataFrame
格式,调用loadmat
用于读取mat
格式的数据,matplotlib.pyplot
用于画图查看数据。
二、处理并查看数据
1、读取并处理数据
data=loadmat('F:\\MachineLearning\data\ex6data1.mat')
print(data['X'])
这里先读取数据data
,并查看data
训练集数据data['X']
,得到:
[[1.9643 4.5957 ]
[2.2753 3.8589 ]
[2.9781 4.5651 ]
[2.932 3.5519 ]
[3.5772 2.856 ]
[4.015 3.1937 ]
[3.3814 3.4291 ]
[3.9113 4.1761 ]
[2.7822 4.0431 ]
[2.5518 4.6162 ]
[3.3698 3.9101 ]
[3.1048 3.0709 ]
[1.9182 4.0534 ]
[2.2638 4.3706 ]
[2.6555 3.5008 ]
[3.1855 4.2888 ]
[3.6579 3.8692 ]
[3.9113 3.4291 ]
[3.6002 3.1221 ]
[3.0357 3.3165 ]
[1.5841 3.3575 ]
[2.0103 3.2039 ]
[1.9527 2.7843 ]
[2.2753 2.7127 ]
[2.3099 2.9584 ]
[2.8283 2.6309 ]
[3.0473 2.2931 ]
[2.4827 2.0373 ]
[2.5057 2.3853 ]
[1.8721 2.0577 ]
[2.0103 2.3546 ]
[1.2269 2.3239 ]
[1.8951 2.9174 ]
[1.561 3.0709 ]
[1.5495 2.6923 ]
[1.6878 2.4057 ]
[1.4919 2.0271 ]
[0.962 2.682 ]
[1.1693 2.9276 ]
[0.8122 2.9992 ]
[0.9735 3.3881 ]
[1.25 3.1937 ]
[1.3191 3.5109 ]
[2.2292 2.201 ]
[2.4482 2.6411 ]
[2.7938 1.9656 ]
[2.091 1.6177 ]
[2.5403 2.8867 ]
[0.9044 3.0198 ]
[0.76615 2.5899 ]
[0.086405 4.1045 ]]
即我们的数据具有两个特征。接着,将data['x']
转化为pd.DataFrame
格式的数据,并将两个特征所在列命名为x1
和x2
,并加上一列y
,用于存储训练集样本的标记:
pd1 = pd.DataFrame(data['X'],columns=['x1','x2'])
pd1['y'] = data['y']
print(pd1)
可以看到我们处理后的数据打印为:
x1 x2 y
0 1.964300 4.5957 1
1 2.275300 3.8589 1
2 2.978100 4.5651 1
3 2.932000 3.5519 1
4 3.577200 2.8560 1
5 4.015000 3.1937 1
6 3.381400 3.4291 1
7 3.911300 4.1761 1
8 2.782200 4.0431 1
9 2.551800 4.6162 1
10 3.369800 3.9101 1
11 3.104800 3.0709 1
12 1.918200 4.0534 1
13 2.263800 4.3706 1
14 2.655500 3.5008 1
15 3.185500 4.2888 1
16 3.657900 3.8692 1
17 3.911300 3.4291 1
18 3.600200 3.1221 1
19 3.035700 3.3165 1
20 1.584100 3.3575 0
21 2.010300 3.2039 0
22 1.952700 2.7843 0
23 2.275300 2.7127 0
24 2.309900 2.9584 0
25 2.828300 2.6309 0
26 3.047300 2.2931 0
27 2.482700 2.0373 0
28 2.505700 2.3853 0
29 1.872100 2.0577 0
30 2.010300 2.3546 0
31 1.226900 2.3239 0
32 1.895100 2.9174 0
33 1.561000 3.0709 0
34 1.549500 2.6923 0
35 1.687800 2.4057 0
36 1.491900 2.0271 0
37 0.962000 2.6820 0
38 1.169300 2.9276 0
39 0.812200 2.9992 0
40 0.973500 3.3881 0
41 1.250000 3.1937 0
42 1.319100 3.5109 0
43 2.229200 2.2010 0
44 2.448200 2.6411 0
45 2.793800 1.9656 0
46 2.091000 1.6177 0
47 2.540300 2.8867 0
48 0.904400 3.0198 0
49 0.766150 2.5899 0
50 0.086405 4.1045 1
接着我们将标记为1的正例和标记为0的反例分开,分别存储在positive
和negative
中,并查看:
positive = pd1[pd1['y']==1]
print('\n')
negative = pd1[pd1['y']==0]
print(positive)
得到:
x1 x2 y
0 1.964300 4.5957 1
1 2.275300 3.8589 1
2 2.978100 4.5651 1
3 2.932000 3.5519 1
4 3.577200 2.8560 1
5 4.015000 3.1937 1
6 3.381400 3.4291 1
7 3.911300 4.1761 1
8 2.782200 4.0431 1
9 2.551800 4.6162 1
10 3.369800 3.9101 1
11 3.104800 3.0709 1
12 1.918200 4.0534 1
13 2.263800 4.3706 1
14 2.655500 3.5008 1
15 3.185500 4.2888 1
16 3.657900 3.8692 1
17 3.911300 3.4291 1
18 3.600200 3.1221 1
19 3.035700 3.3165 1
50 0.086405 4.1045 1
即positive
中存储的全为正例。下面画图查看正例和反例的分布,决定使用线性支持向量机还是非线性支持向量机:
plt.scatter(positive['x1'],positive['x2'],marker='o',label='positive',color='blue')
plt.scatter(negative['x1'],negative['x2'],marker='x',label='negative',color='red')
plt.legend()
plt.show()
查看图:
3、调用sklearn中svm解决问题
from sklearn import svm
svc = svm.LinearSVC(C=1,loss='hinge',max_iter=1000)
#给定训练数据拟合SVM模型
svc.fit(pd1[['x1','x2']],pd1['y'])
#返回训练数据和标签的平均精度
score = svc.score(pd1[['x1','x2']],pd1['y'])
print(score)
#改变系数C
svc2 = svm.LinearSVC(C=100,loss='hinge',max_iter=1000)
fit2 = svc2.fit(pd1[['x1','x2']],pd1['y'])
score2 = svc2.score(pd1[['x1','x2']],pd1['y'])
print(score2)
首先在第一行调用了sklearn
中的svm
模块,第二行设置线性支持向量机svm.LinearSVC
的参数:用于调和结构风险和经验风险的惩戒参数C我们设定为1,损失函数设定为hinge
形,最大迭代次数设定为1000
。第四行用我们现有的数据取拟合一个给定参数的支持向量机。第六行我们查看拟合模型在训练集上的平均精度。后面类似,只是改变了惩戒参数C,查看情况,得到结果:
0.9803921568627451
1.0
即C=1时为精度为0.9803921568627451
,C=100时的精度为1.0
,可以知道C=100时很可能发生了过拟合。
接下来,用svc.decision_function
计算样本到决策边界的距离,并将每一个样本在C=1和C=100时的距离分别存储在pd1
的'SVM Confidence
和'SVM2 Confidence
,并将每个样本点打印出来,用点的颜色深浅表示样本到决策边界的距离:
#decision_function为样本到决策边界的距离
fig,ax=plt.subplots(figsize=(12,8))
pd1['SVM Confidence'] = svc.decision_function(pd1[['x1','x2']])
ax.scatter(pd1['x1'],pd1['x2'],marker='o',c=pd1['SVM Confidence'],cmap='seismic')
ax.set_title('SVM Confidence(C=1)')
plt.show()
fig,ax = plt.subplots(figsize=(12,8))
pd1['SVM2 Confidence'] = svc2.decision_function(pd1[['x1','x2']])
ax.scatter(pd1['x1'],pd1['x2'],marker='o',c=pd1['SVM2 Confidence'],cmap='seismic')
ax.set_title('SVM Confidence(C=100)')
plt.show()
得到:
从图上我们可以进一步感受到C=100时的过拟合,在左上角一点我们可以清楚感受到他一定为反例,但是C=100时将这个样本划分为和决策边界距离很近,以至于颜色太浅我们看不到这一点。
非线性支持向量机
一、处理并查看数据
这与前面的线性支持向量机情况一样:
data2 = loadmat('F:\\MachineLearning\data\ex6data2.mat')
pd2 = pd.DataFrame(data2['X'],columns=['x1','x2'])
pd2['y'] = data2['y']
positive1 = pd2[pd2['y']==1]
negative1 = pd2[pd2['y']==0]
fig,ax = plt.subplots(figsize=(12,8))
ax.scatter(positive1['x1'],positive1['x2'],c='blue',marker='o',label='positive')
ax.scatter(negative1['x1'],negative1['x2'],c='red',marker='x',label='negative')
plt.show()
得到数据分布图:
很显然,不能在使用线性支持向量机去解决这一问题,必须使用带有核函数的支持向量机,即非线性支持向量机。
下面用非线性支持向量机svm.SVC
去拟合,并查看拟合模型在训练集上的精度:
svc_nonlinear = svm.SVC(C=100,gamma=10,probability=True)
svc_nonlinear.fit(pd2[['x1','x2']],pd2['y'])
score_nonlinear=svc_nonlinear.score(pd2[['x1','x2']],pd2['y'])
print(score_nonlinear)
第一行,我们默认使用高斯函数作为核函数,所以我们需要定义 σ 2 σ^2 σ2参数,代码中用gamma
表示,这里我们定为10,这个 σ 2 σ^2 σ2参数会影响我们的拟合情况,具体地说:太大的 σ 2 σ^2 σ2会使高斯函数过于平坦,而导致高偏差、低方差:反之,还会导致高方差,低偏差。所以惩戒参数数C和参数 σ 2 σ^2 σ2的选取都极为重要。probability表示是否用概率估计,此参数定义一定要在调用fit
之后,设置为False
之后会使训练过程的计算过程变慢。
接下来调用.predict_proba()
,这个方法返回一个矩阵,矩阵第i行第j列表示第i个样本属于第j个类的概率:
probability_nonlinear = svc_nonlinear.predict_proba(pd2[['x1','x2']])
print(probability_nonlinear)
得到:
[[3.0000009e-14 1.0000000e+00]
[3.0000009e-14 1.0000000e+00]
[3.0000009e-14 1.0000000e+00]
...
[3.0000009e-14 1.0000000e+00]
[3.0000009e-14 1.0000000e+00]
[3.0000009e-14 1.0000000e+00]]
然后取这个矩阵的第一列,即每个样本属于第0类(反例)的概率,用这一列的大小去定义颜色的深浅,颜色越深,越有可能属于第0类:
probability_nonlinear = svc_nonlinear.predict_proba(pd2[['x1','x2']])
print(probability_nonlinear)
pd2['probability'] = probability_nonlinear[:,0]
fig,ax = plt.subplots(figsize=(12,8))
ax.scatter(pd2['x1'],pd2['x2'],c=pd2['probability'],cmap='Reds',marker='o')
ax.set_title('SVM Probability')
plt.show()
参数选择问题
这里的分类问题涉及到参数 σ 2 σ^2 σ2和 C C C的选取问题,若参数选取不当可能导致过拟合或欠拟合,所以参数选择影响到我们训练的好坏。这里我们主要是不同的参数对应着不同的模型,利用此模型取训练,然后选取在交叉验证集上表现最好的模型。
一、处理并查看数据
data3 = loadmat('F:\\MachineLearning\data\ex6data3.mat')
print(data3['X'])
pd3 = pd.DataFrame(data3['X'],columns=['x1','x2'])
pd3['y'] = data3['y']
pd4 = pd.DataFrame(data3['Xval'],columns=['x1','x2'])
pd4['y'] = data3['yval']
fig,ax = plt.subplots(figsize=(12,8))
positive3=pd3[pd3['y']==1]
negative3=pd3[pd3['y']==0]
ax.scatter(positive3['x1'],positive3['x2'],color='blue',marker='o',label='Positive')
ax.scatter(negative3['x1'],negative3['x2'],color='red',marker='x',label='Negative')
ax.set_title('Training_Set')
ax.legend()
plt.show()
fig,ax = plt.subplots(figsize=(12,8))
positive4=pd4[pd4['y']==1]
negative4=pd4[pd4['y']==0]
ax.scatter(positive4['x1'],positive4['x2'],color='blue',marker='o',label='Positive')
ax.scatter(negative4['x1'],negative4['x2'],color='red',marker='x',label='Negative')
ax.set_title('Val')
ax.legend()
plt.show()
这里我们将训练集数据和交叉验证集数据分别存在pd3
和pd4
中,分别查看,得到:
我们选择非线性支持向量机去将数据分类。
二、选择合适参数
这里我们将所有 C C C存在C_list
列表中,所有 σ 2 σ^2 σ2存于gamma_list
列表中,遍历列表中所有元素,选取在交叉验证集上表现最好的参数,并打印此参数及此参数在交叉验证集上的精度:
best_score=0
gamma_list=[0.01,0.03,0.1,0.3,1,3,10,30,100]
C_list=[0.01,0.03,0.1,0.3,1,3,10,30,100]
for C_value in C_list:
for gamma_value in gamma_list:
svc = svm.SVC(C=C_value,gamma=gamma_value,probability=True)
svc.fit(pd3[['x1','x2']],pd3['y'])
score=svc.score(pd4[['x1','x2']],pd4['y'])
if score>best_score:
best_score=score
best_C=C_value
best_gamma=gamma_value
print(best_gamma,best_C,best_score)
得到:
100 0.3 0.965
即惩罚参数C选取为0.3, σ 2 σ^2 σ2选为100,在交叉验证集上的精度为0。965。
利用支持向量机对邮件分类
下面我们用支持向量机对邮件进行分类,分为垃圾邮件和有用的邮件。
首先读取数据:
spam_data = loadmat('F:\\MachineLearning\data\spamTrain.mat')
X = spam_data['X']
y = spam_data['y']
spam_test = loadmat('F:\\MachineLearning\data\spamTest.mat')
Xtest = spam_test['Xtest']
ytest = spam_test['ytest']
查看训练集维度:
(4000, 1899)
即我们有4000条数据,每条数据有1899个特征。
我们如何将每个邮件处理为数字化的样本?
1、所有文字母转化为小写
2、所有HTML标记都从电子邮件中删除。
3、所有URL都替换为文本’httpaddr’。
4、所有电子邮件地址都替换为文本’emailaddr’。
5、所有数字都用文本’number’替换。
6、所有美元符号($)都替换为文本’dollars’。
7、所有单词处理为其原始形式。(如:discounting、discounted转化为discount)
8、删除非文本和标点符号。
然后创建一个word list,上面一共1899个单词,每个单词对应这一个数字,如:
将样本处理为维度为1899的数组形式,数组对应位置代表着对应单词的频数。这样我们就可以将数字化。
下面我们拟合训练集数据,并输出在训练集和验证集上的精度:
svc_spam = svm.SVC()
svc_spam.fit(X,y)
print('The traininig accuracy is'+str(svc_spam.score(X,y))+'.')
print('The test accusracy is'+str(svc_spam.score(Xtest,ytest))+'.' )
得到:
The traininig accuracy is 0.944.
The test accusracy is 0.953.
即训练集上精度为0.944,测试集上的精度为0.953。