题目概述:在本练习中,我们将使用高斯核函数的支持向量机(SVM)来构建垃圾邮件分类器
数据集1 我们先在2D数据集上实验
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
from scipy.io import loadmat
raw_data=loadmat('E:/shujuji/ex6data1.mat')
raw_data
data=pd.DataFrame(raw_data['X'],columns=('X1','X2'))#columns将x中两列命名为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()
请注意,还有一个异常的正例在其他样本之外。 这些类仍然是线性分离的,但它非常紧凑。 我们要训练线性支持向量机来学习类边界。 在这个练习中,我们没有从头开始执行SVM的任务,所以我要用scikit-learn。
C=1
from sklearn import svm
svc=svm.LinearSVC(C=1,loss='hinge',max_iter=1000)
#loss : string,'hinge'或'squared_hinge'/(默认='squared_hinge')指定损失函数。
#'hinge'是标准SVM损失(例如由SVC类使用),而'squared_hinge'是铰链损失的平方
#max_ite迭代次数
svc
svc.fit(data[['X1','X2']],data['y'])
svc.score(data[['X1','X2']],data['y'])
0.9803921568627451
C=100
svc2=svm.LinearSVC(C=100,loss='hinge',max_iter=1000)
#svc2 = svm.LinearSVC(C=100, loss='hinge', max_iter=1000)
svc2.fit(data[['X1', 'X2']], data['y'])
svc2.score(data[['X1', 'X2']], data['y'])
0.9411764705882353
这次我们得到了训练数据的完美分类,但是通过增加C的值,我们创建了一个不再适合数据的决策边界。 我们可以通过查看每个类别预测的置信水平来看出这一点,这是该点与超平面距离的函数。
data['SVM 1 Confidence']=svc.decision_function(data[['X1','X2']])
#decision function 返回每个输入样本对应的到决策边界的距离
fig,ax=plt.subplots(figsize=(12,8))
ax.scatter(data['X1'],data['X2'],s=50,c=data['SVM 1 Confidence']
,cmap='seismic'
)
#c 颜色深浅用decision function 返回的数据大小判断
#cmap(colormap)=seismic量化(离散化)色图
ax.set_title('SVM (C=1) Decision Confidence')
plt.show()
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='seismic')
ax.set_title('SVM (C=100) Decision Confidence')
plt.show()
可以看看靠近边界的点的颜色,区别是有点微妙。
现在我们将从线性SVM转移到能够使用内核进行非线性分类的SVM。 我们首先负责实现一个高斯核函数。 虽然scikit-learn具有内置的高斯内核,但为了实现更清楚,我们将从头开始实现。
高斯内核函数
def gaussian_kernel(x1,x2,sigma):
return np.exp(-1*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
接下来,我们将检查另一个数据集,这次用非线性决策边界
raw_data = loadmat('E:/shujuji/ex6data2.mat')
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=30, marker='x', label='Positive')
ax.scatter(negative['X1'], negative['X2'], s=30, marker='o', label='Negative')
ax.legend()
plt.show()
对于该数据集,我们将使用内置的RBF内核构建支持向量机分类器,并检查其对训练数据的准确性。 为了可视化决策边界,这一次我们将根据实例具有负类标签的预测概率来对点做阴影。 从结果可以看出,它们大部分是正确的。
svc=svm.SVC(C=100,gamma=10,probability=True)
#gamma是sigma,即核函数中分母中的那个
#probability是否启用概率估计
svc
svc.fit(data[['X1', 'X2']], data['y'])
svc.score(data[['X1', 'X2']], data['y'])
0.9698725376593279
data['Probability'] = svc.predict_proba(data[['X1', 'X2']])[:,0]
#.predict_proba返回预测属于某标签的概率
#[:,0],y=0的数据(negative数据),也可以变成y=1试试
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(data['X1'], data['X2'], s=30, c=data['Probability'], cmap='Reds')
plt.show()
对于第三个数据集,我们给出了训练和验证集,并且基于验证集性能为SVM模型找到最优超参数。虽然我们可以使用scikit-learn的内置网格搜索来做到这一点,但是本着遵循练习的目的,我们将从头开始实现一个简单的网格搜索。
raw_data=loadmat('E:/shujuji/ex6data3.mat')
raw_data
X=raw_data['X']
Xval=raw_data['Xval']
y=raw_data['y'].ravel()#降一维
yval = raw_data['yval'].ravel()
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 best_score<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('E:/shujuji/spamTrain.mat')
spam_test = loadmat('E:/shujuji/spamTest.mat')
X = spam_train['X']
Xtest = spam_test['Xtest']
y = spam_train['y'].ravel()
ytest = spam_test['ytest'].ravel()
'''
每个文档已经转换为一个向量,其中1,899个维对应于词汇表中的1,899个单词。
它们的值为二进制,表示文档中是否存在单词。
在这一点上,训练评估是用一个分类器拟合测试数据的问题。
'''
X.shape, y.shape, Xtest.shape, ytest.shape
((4000, 1899), (4000,), (1000, 1899), (1000,))
svc=svm.SVC()
svc=svc.fit(X,y)
print('Training accuracy={0}%'.format(np.round(svc.score(X,y)*100,2)))
#print('Training accuracy = {0}%'.format(np.round(svc.score(X, y) * 100, 2)))
# Training accuracy=99.32%
print('Test accuracy = {0}%'.format(np.round(svc.score(Xtest, ytest) * 100, 2)))
#Test accuracy = 98.7%