支持向量机
支持向量机 (Support Vector Machines)是一系列用于分类、回归和异常点检测的监督学习方法。
支持向量机的优点是:
- 在高维数据空间的高效性
- 当维度远大于样本数量时,仍然有效
- 在决策函数中使用训练点的子集
- 通用性:对于决策函数,可以使用不同的核函数。提供了通用的核,但是也可以指定定制化的核
支持向量机的缺点是:
- 如果特征的数量远大于样本的数量时,为避免拟合,在选择核函数和正则项时非常关键
- SVMs没有直接提供概率估计,需使用昂贵的五折交叉验证计算
在scikit-learn中,支持向量机可以使用密集的(numpy.ndarray
和numpy.asarray
)和稀疏的(scipy.sparse
)样本向量作为输入。
分类
SVC
、NuSVC
和LinearSVC
是能够用于执行多类别分类的算法。
# coding: utf-8
# Plot different SVM classifiers in the iris dataset
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets
def make_meshgrid(x, y, h=.02):
"""Create a mesh of points to plot in
Parameters
----------
x: data to base x-axis meshgrid on
y: data to base y-axis meshgrid on
h: stepsize for meshgrid, optional
Returns
-------
xx, yy : ndarray
"""
x_min, x_max = x.min() - 1, x.max() + 1
y_min, y_max = y.min() - 1, y.max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
return xx, yy
def plot_contours(ax, clf, xx, yy, **params):
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
out = ax.contourf(xx, yy, Z, **params)
return out
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target
C = 1.0 # SVM regularization parameter
models = (svm.SVC(kernel='linear', C=C),
svm.LinearSVC(C=C, max_iter=10000),
svm.SVC(kernel='rbf', gamma=0.7, C=C),
svm.SVC(kernel='poly', degree=3, gamma='auto', C=C))
models = (clf.fit(X, y) for clf in models)
# title for the plots
titles = ('SVC with linear kernel',
'LinearSVC (linear kernel)',
'SVC with RBF kernel',
'SVC with polynomial (degree 3) kernel')
# Set-up 2x2 grid for plotting.
fig, sub = plt.subplots(2, 2)
plt.subplots_adjust(wspace=0.4, hspace=0.4)
X0, X1 = X[:, 0], X[:, 1]
xx, yy = make_meshgrid(X0, X1)
for clf, title, ax in zip(models, titles, sub.flatten()):
plot_contours(ax, clf, xx, yy,
cmap=plt.cm.coolwarm, alpha=0.8)
ax.scatter(X0, X1, c=y, cmap=plt.cm.coolwarm, s=20, edgecolors='k')
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_xlabel('Sepal length')
ax.set_ylabel('Sepal width')
ax.set_xticks(())
ax.set_yticks(())
ax.set_title(title)
plt.show()
SVC
和NuSVC
是相同的方法,但是接受略微不同的参数集,且有不同的数学模型。另一方面,LinearSVC
是在线性核下的支持向量分类。注意:LinearSVC
不接受参数kernel
,它同样也没有svc
和NuSVC
的成员,如support_
。
支持向量机的决策函数依赖于训练数据的子集,称作支持向量。这些支持向量可以通过如下成员找到support_vectors_
、support_
和n_support
。
多类别分类
svc
和NuSVC
使用one-against-one
实施多类别分类。如果n_class
表示类别的数量,那么需要构建n_class*(n_class-1)/2
个分类器,且每个分类器在两个类的数据上训练。为了提供与其它分类器相同的接口,decision_function_shape
允许单调地将one-against-one
分类器的结果转化为大小为(n_samples, n_classes)
的决策函数。
LinearSVC
实施one-vs-the-rest
多分类策略,训练n_class
个模型。
# coding: utf-8
from sklearn import svm
X = [[0], [1], [2], [3]]
Y = [0, 1, 2, 3] # 4 classes
clf = svm.SVC(decision_function_shape='ovo')
clf.fit(X, Y)
dec = clf.decision_function([[1]])
print(dec.shape) # 4*3/2 classes
clf.decision_function_shape='ovr'
dec = clf.decision_function([[1]])
print(dec.shape) # 4 classes
lin_clf = svm.LinearSVC()
lin_clf.fit(X, Y)
dec = lin_clf.decision_function([[1]])
print(dec.shape)
分数和概率
svc
和NuSVC
的decision_function
给了每个样本每类的分数(score)。当构造器的选项probability
设置为True
时,开启概率估计,使用predict_proba
和predict_log_proba
。在二元分类的情况下,使用Platt scaling
校准概率:在SVM分数上进行逻辑回归,在训练数据上通过附加的交叉验证拟合。
对于大的数据集而言,在Platt scaling
中执行交叉验证是一种昂贵的操作。此外,估算的概率也许和分数不一致,即,分数中最大值所对应的类别可能与概率中最大值所对应的类别不同。
注意,当decision_function_shape=ovr
和n_classes > 2
,不同于decision_function
,predict
方法没有默认尝试打破平局。
# coding: utf-8
# SVM Tie Breaking Example
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
X, y = make_blobs(random_state=27)
fig, sub = plt.subplots(2, 1, figsize=(5, 8))
titles = ('break_ties = False',
'break_ties = True')
for break_ties, title, ax in zip((False, True), titles, sub.flatten()):
svm = SVC(kernel='linear', C=1, break_ties=break_ties,
decision_function_shape='ovr').fit(X, y)
xlim = [X[:,0].min(), X[:,0].max()]
ylim = [X[:,1].min(), X[:,1].max()]
xs = np.linspace(xlim[0], xlim[1], 1000)
ys = np.linspace(ylim[0], ylim[1], 1000)
xx, yy = np.meshgrid(xs, ys)
pred = svm.predict(np.c_[xx.ravel(), yy.ravel()])
colors = [plt.cm.Accent(i) for i in [0, 4, 7]]
points = ax.scatter(X[:, 0], X[:, 1], c=y, cmap='Accent')
classes = [(0, 1), (0, 2), (1, 2)]
line = np.linspace(X[:, 1].min()-5, X[:, 1].max()+5)
ax.imshow(-pred.reshape(xx.shape), cmap='Accent', alpha=.2,
extent=(xlim[0], xlim[1], ylim[1], ylim[0]))
for coef, intercept, col in zip(svm.coef_, svm.intercept_, classes):
line2 = -(line * coef[1] + intercept) / coef[0]
ax.plot(line2, line, "-", c=colors[col[0]])
ax.plot(line2, line, "--", c=colors[col[1]])
ax.set_xlim(xlim)
ax.set_ylim(ylim)
ax.set_title(title)
ax.set_aspect("equal")
plt.show()
非均衡问题
在对于给某些类别或样本更高的重要性,可以使用关键词class_weight
和sampe_weight
。
SVC
(但非NuSVC
)在fit
方法中添加了关键词class_weight
。以字典形式{class_label: value}
描述,其中,value
是一个大于0的浮点数,设置class_label
的C
值等于C * value
。
SVC
、NuSVC
、SVR
、NuSVR
、LinearSVC
、LinearSVR
和OneClassSVM
同样在fit
方法中实现了通过sample_weight
对单个样本设置权重。和class_weight
相似,设置第i个样本的参数C
等于C * sample_weight[i]
。
# coding: utf-8
# SVM: Weighted samples
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
def plot_decision_function(classifier, sample_weight, axis, title):
xx, yy = np.meshgrid(np.linspace(-4, 5, 500), np.linspace(-4, 5, 500))
Z = classifier.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
axis.contourf(xx, yy, Z, alpha=0.75, cmap=plt.cm.bone)
axis.scatter(X[:, 0], X[:, 1], c=y, s=100*sample_weight, alpha=0.9,
cmap=plt.cm.bone, edgecolors='black')
axis.axis('off')
axis.set_title(title)
np.random.seed(0)
X = np.r_[np.random.randn(10, 2) + [1, 1], np.random.randn(10, 2)]
y = [1] * 10 + [-1] * 10
sample_weight_last_ten = abs(np.random.randn(len(X)))
sample_weight_const = np.ones(len(X))
sample_weight_last_ten[15:] *= 5
sample_weight_last_ten[9] *= 15
clf_weights = svm.SVC(gamma=1)
clf_weights.fit(X, y, sample_weight=sample_weight_last_ten)
clf_no_weights = svm.SVC(gamma=1)
clf_no_weights.fit(X, y)
fit, axes = plt.subplots(1, 2, figsize=(14, 6))
plot_decision_function(clf_no_weights, sample_weight_const, axes[0], 'Constant Weights')
plot_decision_function(clf_weights, sample_weight_last_ten, axes[1], 'Modified Weights')
plt.show()
回归
支持向量分类的方法可以拓展到求解回归问题。这种方法称作支持向量回归。
支持向量分类构建的模型仅仅依赖于训练数据的子集,因为构建模型的成本函数不关心超过边缘的训练数据。同样的,支持向量回归的模型也只依赖于训练数据的子集,因为构建模型的成本函数忽略了接近模型预测的训练数据。
实现支持向量的类包括SVR
、NuSVR
和LinearSVR
。
# coding: utf-8
# Support Vector Regression (SVR) using linear and non-linear kernels
import numpy as np
from sklearn.svm import SVR
import matplotlib.pyplot as plt
X = np.sort(5 * np.random.rand(40, 1), axis=0)
y = np.sin(X).ravel()
y[::5] += 3 * (0.5 - np.random.rand(8))
svr_rbf = SVR(kernel='rbf', C=100, gamma=0.1, epsilon=.1)
svr_lin = SVR(kernel='linear', C=100, gamma='auto')
svr_poly = SVR(kernel='poly', C=100, gamma='auto', degree=3, epsilon=.1, coef0=1)
lw = 2
svrs = [svr_rbf, svr_lin, svr_poly]
kernel_label = ['RBF', 'Linear', 'Polynomial']
model_color = ['m', 'c', 'g']
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(15, 10), sharey=True)
for ix, svr in enumerate(svrs):
axes[ix].plot(X, svr.fit(X, y).predict(X), color=model_color[ix], lw=lw, label='{} model'.format(kernel_label[ix]))
axes[ix].scatter(X[svr.support_], y[svr.support_], facecolor='none', edgecolor=model_color[ix], s=50,
label='{} support vectors'.format(kernel_label[ix]))
axes[ix].scatter(X[np.setdiff1d(np.arange(len(X)), svr.support_)],
y[np.setdiff1d(np.arange(len(X)), svr.support_)],
facecolor='none', edgecolor='k', s=50,
label='other training data')
axes[ix].legend(loc='upper center', bbox_to_anchor=(0.5, 1.1),
ncol=1, fancybox=True, shadow=True)
fig.text(0.5, 0.04, 'data', ha='center', va='center')
fig.text(0.06, 0.5, 'target', ha='center', va='center', rotation='vertical')
fig.suptitle('Support Vector Regression', fontsize=14)
plt.show()
密度估计和异常检测
OneClassSVM
实现了单一类别的支持向量机,可以用于异常检测。
核函数
常用的核函数如下:
- linear: ⟨ x , x ′ ⟩ \left\langle x, x^{'} \right\rangle ⟨x,x′⟩
- polynomial: ( γ ⟨ x , x ′ ⟩ + r ) d \left( \gamma\left\langle x, x^{'} \right\rangle + r \right)^d (γ⟨x,x′⟩+r)d
- rbf:
e
x
p
(
−
γ
∥
x
−
x
′
∥
2
)
exp\left(-\gamma\left\| x-x^{'} \right\|^2\right)
exp(−γ∥∥∥x−x′∥∥∥2)。
γ
\gamma
γ使用关键词
gamma
指定,必须大于0 - sigmoid:
(
t
a
n
h
(
γ
⟨
x
,
x
′
⟩
+
r
)
)
\left( tanh(\gamma \left\langle x, x^{'} \right\rangle + r) \right)
(tanh(γ⟨x,x′⟩+r)),
r
r
r有参数
coef0
指定
定制核函数
使用Python函数作为核函数
在构造器中,你可以通过传递一个函数给关键词kernel
来定义自己的核函数。
# coding: utf-8
# SVM with custom kernel
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets
iris = datasets.load_iris()
X = iris.data[:, :2]
Y = iris.target
def my_kernel(X, Y):
"""
We create a customer kernel
"""
M = np.array([[2, 0], [0, 1.0]])
return np.dot(np.dot(X, M), Y.T)
h = 0.02
clf = svm.SVC(kernel=my_kernel)
clf.fit(X, Y)
x_min, x_max = X[:, 0].min()-1, X[:, 0].max()+1
y_min, y_max = X[:, 1].min()-1, X[:, 1].max()+1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired)
plt.scatter(X[:, 0], X[:, 1], c=Y, cmap=plt.cm.Paired, edgecolors='k')
plt.title('3-Class classification using Support Vector Machine with custom kernel')
plt.axis('tight')
plt.show()
使用Gram
矩阵
令kernel='precomputed
,将Gram
矩阵传入fit
函数。如下例
# coding: utf-8
import numpy as np
from sklearn import svm
X = np.array([[0, 0], [1, 1]])
y = [0, 1]
clf = svm.SVC(kernel='precomputed')
gram = np.dot(X, X.T)
clf.fit(gram, y)
print(clf.predict(gram))