吴恩达机器学习课程笔记+代码实现(19)Python实现支持向量机(Programming Exercise 6)
Programming Exercise 6:Support Vecter Machines
Python版本3.6
编译环境:anaconda Jupyter Notebook
链接:实验数据和实验指导书
提取码:i7co
本章课程笔记部分见:支持向量机 支持向量机大边界分类背后的数学和核函数
在本练习中,我们将使用支持向量机(SVM)来构建垃圾邮件分类器。 我们将从一些简单的2D数据集开始使用SVM来查看它们的工作原理。 然后,我们将对一组原始电子邮件进行一些预处理工作,并使用SVM在处理的电子邮件上构建分类器,以确定它们是否为垃圾邮件。
1-线性SVM
第一件事是看一个简单的二维数据集,看看线性SVM如何对数据集进行不同的C值(类似于线性/逻辑回归中的正则化项)。
%matplotlib inline
#IPython的内置magic函数,可以省掉plt.show(),在其他IDE中是不会支持的
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid",color_codes=True)
import scipy.io as sio
import scipy.optimize as opt
import sklearn.svm
查看数据和可视化
d = sio.loadmat('./data/ex6data1.mat')
print(d.keys())
data = pd.DataFrame(d.get('X'), columns=['X1', 'X2'])
data['y'] = d.get('y')
data.head()
dict_keys(['__header__', '__version__', '__globals__', 'X', 'y'])
X1 | X2 | y | |
---|---|---|---|
0 | 1.9643 | 4.5957 | 1 |
1 | 2.2753 | 3.8589 | 1 |
2 | 2.9781 | 4.5651 | 1 |
3 | 2.9320 | 3.5519 | 1 |
4 | 3.5772 | 2.8560 | 1 |
将其用散点图表示,其中类标签由符号表示(+表示正类,o表示负类)。
positive = data[data['y'].isin([1])]
negative = data[data['y'].isin([0])]
fig, ax = plt.subplots(figsize=(8,6))
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()
<matplotlib.legend.Legend at 0xd9d6478240>
还有一个异常的正例在其他样本之外。这些类仍然是线性分离的,但它非常紧凑。 我们要训练线性支持向量机来学习类边界。
try
from sklearn import svm
svc = svm.LinearSVC(C=1, loss='hinge', max_iter=1000)
svc
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)
svc.fit(data[['X1', 'X2']], data['y'])
svc.score(data[['X1', 'X2']], data['y'])
0.9803921568627451
data['SVM 1 Confidence'] = svc.decision_function(data[['X1', 'X2']])
fig, ax = plt.subplots(figsize=(8,6))
ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 1 Confidence'], cmap='seismic')
ax.set_title('SVM (C=1) Decision Confidence')
plt.show()
try
svc100 = sklearn.svm.LinearSVC(C=100, loss='hinge')
svc100.fit(data[['X1', 'X2']], data['y'])
svc100.score(data[['X1', 'X2']], data['y'])
0.9411764705882353
data['SVM 2 Confidence'] = svc2.decision_function(data[['X1', 'X2']])
fig, ax = plt.subplots(figsize=(8,6))
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()
我们得到了训练数据的完美分类,但是通过增加C的值,我们创建了一个不再适合数据的决策边界。 我们可以通过查看每个类别预测的置信水平来看出这一点,这是该点与超平面距离的函数。
data.head()
X1 | X2 | y | SVM 1 Confidence | SVM 2 Confidence | |
---|---|---|---|---|---|
0 | 1.9643 | 4.5957 | 1 | 0.798774 | 3.588890 |
1 | 2.2753 | 3.8589 | 1 | 0.381018 | 1.696004 |
2 | 2.9781 | 4.5651 | 1 | 1.373196 | 4.618538 |
3 | 2.9320 | 3.5519 | 1 | 0.518610 | 1.490969 |
4 | 3.5772 | 2.8560 | 1 | 0.331845 | 0.092327 |
2-高斯核函数
现在我们将从线性SVM转移到能够使用内核进行非线性分类的SVM。 我们首先负责实现一个高斯核函数。 虽然scikit-learn具有内置的高斯内核,但为了实现更清楚,我们将从头开始实现。
# kernek function 高斯核函数
def gaussian_kernel(x1, x2, sigma):
return np.exp(- np.power(x1 - x2, 2).sum() / (2 * (sigma ** 2)))
x1 = np.array([1, 2, 1])
x2 = np.array([0, 4, -1])
sigma = 2
gaussian_kernel(x1, x2, sigma)
0.32465246735834974
查看数据和可视化
mat = sio.loadmat('./data/ex6data2.mat')
print(mat.keys())
data = pd.DataFrame(mat.get('X'), columns=['X1', 'X2'])
data['y'] = mat.get('y')
data.head()
dict_keys(['__header__', '__version__', '__globals__', 'X', 'y'])
X1 | X2 | y | |
---|---|---|---|
0 | 0.107143 | 0.603070 | 1 |
1 | 0.093318 | 0.649854 | 1 |
2 | 0.097926 | 0.705409 | 1 |
3 | 0.155530 | 0.784357 | 1 |
4 | 0.210829 | 0.866228 | 1 |
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()
<matplotlib.legend.Legend at 0xd9d6564908>
try built-in Gaussian Kernel of sklearn
对于该数据集,我们将使用内置的RBF内核构建支持向量机分类器,并检查其对训练数据的准确性。 为了可视化决策边界,这一次我们将根据实例具有负类标签的预测概率来对点做阴影。 从结果可以看出,它们大部分是正确的。
svc = svm.SVC(C=100, kernel='rbf', gamma=10, probability=True)
svc
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)
svc.fit(data[['X1', 'X2']], data['y'])
svc.score(data[['X1', 'X2']], data['y'])
0.9698725376593279
predict_prob = svc.predict_proba(data[['X1', 'X2']])[:, 0]
fig, ax = plt.subplots(figsize=(8,6))
ax.scatter(data['X1'], data['X2'], s=30, c=predict_prob, cmap='Reds')
<matplotlib.collections.PathCollection at 0xd9ded6cef0>
3-寻找最优参数
对于第三个数据集,我们给出了训练和验证集,并且基于验证集性能为SVM模型找到最优超参数。 虽然我们可以使用scikit-learn的内置网格搜索来做到这一点,但是本着遵循练习的目的,我们将从头开始实现一个简单的网格搜索。
raw_data = sio.loadmat('data/ex6data3.mat')
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 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})
4-垃圾邮件检测
现在,我们将进行第二部分的练习。 在这一部分中,我们的目标是使用SVM来构建垃圾邮件过滤器。 在练习文本中,有一个任务涉及一些文本预处理,以获得适合SVM处理的格式的数据。 然而,这个任务很简单(将字词映射到为练习提供的字典中的ID),而其余的预处理步骤(如HTML删除,词干,标准化等)已经完成。 我将跳过机器学习任务,而不是重现这些预处理步骤,其中包括从预处理过的训练集构建分类器,以及将垃圾邮件和非垃圾邮件转换为单词出现次数的向量的测试数据集。
mat_tr = sio.loadmat('data/spamTrain.mat')
mat_tr.keys()
dict_keys(['__header__', '__version__', '__globals__', 'X', 'y'])
X, y = mat_tr.get('X'), mat_tr.get('y').ravel()
X.shape, y.shape
((4000, 1899), (4000,))
mat_test = sio.loadmat('data/spamTest.mat')
mat_test.keys()
dict_keys(['__header__', '__version__', '__globals__', 'Xtest', 'ytest'])
test_X, test_y = mat_test.get('Xtest'), mat_test.get('ytest').ravel()
test_X.shape, test_y.shape
((1000, 1899), (1000,))
fit SVM model
svc = svm.SVC()
svc.fit(X, y)
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)
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(test_X, test_y) * 100, 2)))
Test accuracy = 95.3%
和逻辑回归比较
from sklearn import metrics
from sklearn.linear_model import LogisticRegression
logit = LogisticRegression()
logit.fit(X, y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='warn',
n_jobs=None, penalty='l2', random_state=None, solver='warn',
tol=0.0001, verbose=0, warm_start=False)
pred = logit.predict(test_X)
print(metrics.classification_report(test_y, pred))
precision recall f1-score support
0 1.00 0.99 1.00 692
1 0.99 0.99 0.99 308
micro avg 0.99 0.99 0.99 1000
macro avg 0.99 0.99 0.99 1000
weighted avg 0.99 0.99 0.99 1000