1. SVM基础
1.1 决策边界
- 决策边界越宽越好
- 损失函数越小越好
- 点到决策边界距离越大越好
1.2 核函数
在线性 SVM 中转化为最优化问题时求解的公式计算都是以内积(dot product)形式出现的,其中 ϕ ( X ) \phi(X) ϕ(X)是把训练集中的向量点转化到高维的非线性映射函数。因为内积的算法复杂度非常大,利用核函数来取代计算非线性映射函数的内积。
类型:
- 高斯核函数
- 多项式核函数
- S 型核函数(Sigmoid、tanh)
使用核函数 kernel 的意义在于:
- 将向量的维度从低维映射到高维
- 降低运算复杂度
1.3 低维不可分
- 利用一个非线性的映射把原数据集中的向量点转化到一个更高维度的空间中(比如将二维空间中的点映射到三维空间)
- 在这个高维度的空间中找一个线性的超平面来根据线性可分的情况处理
1.4 解决多分类问题
将多分类问题转化为 n 个二分类问题,n 就是类别个数。
1.5 SVM 算法特性
- 训练好的模型的算法复杂度是由支持向量的个数决定的,而不是由数据的维度决定的。所以 SVM 不太容易产生过拟合。
- SVM 训练出来的模型完全依赖于支持向量,即使训练集里面所有非支持向量的点都被去除,重复训练过程,结果仍然会得到完全一样的模型。
- 一个 SVM 如果训练得出的支持向量个数比较少,那么SVM 训练出的模型比较容易被泛化。
1.6 效果
from sklearn.svm import SVC
from sklearn import datasets
iris = datasets.load_iris()
# 只使用后两个特征
X = iris['data'][:, (2, 3)]
y = iris['target']
setosa_or_versicolor = (y == 0) | (y == 1)
X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor]
svm_clf = SVC(kernel='linear', C=100.0)
svm_clf.fit(X, y)
# w0x0 + w1x1 + b = 0
import matplotlib.pyplot as plt
import numpy as np
def plot_svc_decision_boundary(svm_clf, xmin, xmax, sv=True):
w = svm_clf.coef_[0]
b = svm_clf.intercept_[0]
x0 = np.linspace(xmin, xmax, 200)
decision_boundary = -w[0]/w[1] * x0 - b/w[1]
margin = 1/w[1]
gutter_up = decision_boundary + margin
gutter_down = decision_boundary - margin
if sv:
svs = svm_clf.support_vectors_
plt.scatter(svs[:, 0], svs[:, 1], s=180, facecolors='#FFAAAA')
plt.plot(x0, decision_boundary, 'k-', linewidth=2)
plt.plot(x0, gutter_up, 'k--', linewidth=2)
plt.plot(x0, gutter_down, 'k--', linewidth=2)
plot_svc_decision_boundary(svm_clf, 0, 5.5)
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'bs')
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'ys')
plt.axis([0, 5.5, 0, 2])
plt.show()
1.7 标准化的影响
- 降低特征的数值影响
from sklearn.preprocessing import StandardScaler
s = StandardScaler()
X = s.fit_transform(X)
svm_clf1 = SVC(kernel='linear', C=100.0)
svm_clf1.fit(X, y)
plot_svc_decision_boundary(svm_clf1, -2, 2.5)
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'bs')
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'ys')
plt.axis([-2, 2.5, -2, 2])
plt.show()
1.8 软间隔
- SVM 本质是在完全分类的情况下,让边界越宽越好
- 软间隔是在损失较小准确率(忽略异常点)的情况下,让决策边界更宽
- 防止过拟合
- 超参数 C 控制软间隔程度
from sklearn.svm import LinearSVC
iris = datasets.load_iris()
# petal length, petal width
X = iris['data'][:, (2, 3)]
# viginica
y = (iris['target'] == 2).astype(np.float64)
# scaler = StandardScaler()
# X = scaler.fit_transform(X)
svm_clf1 = LinearSVC(C=1, random_state=42, dual=True)
svm_clf2 = LinearSVC(C=100, random_state=42, dual=True)
svm_clf1.fit(X, y)
svm_clf2.fit(X, y)
# 未收敛警告
ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
plt.figure(figsize=(14, 4))
plt.subplot(121)
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g^', label='Virginica')
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs', label='Versicolor')
plot_svc_decision_boundary(svm_clf1, 4, 6, sv=False)
plt.xlabel('Petal length', fontsize=14)
plt.ylabel('Petal width', fontsize=14)
plt.legend()
plt.title('C = {}'.format(svm_clf1.C), fontsize=16)
plt.axis([4, 6, 0.8, 2.8])
plt.subplot(122)
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g^', label='Virginica')
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs', label='Versicolor')
plot_svc_decision_boundary(svm_clf2, 4, 6, sv=False)
plt.xlabel('Petal length', fontsize=14)
plt.ylabel('Petal width', fontsize=14)
plt.legend()
plt.title('C = {}'.format(svm_clf2.C), fontsize=16)
plt.axis([4, 6, 0.8, 2.8])
plt.show()
# 使用较小的C值,降低过拟合风险
# 使用较高的C值,分类器会提高准确率,但会有较小的间隔
2. 非线性SVM
数据准备
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
def plot_dataset(X, y, axes):
plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g^')
plt.axis(axes)
plt.xlabel('$x_1$', fontsize=20)
plt.ylabel('$x_2$', fontsize=20)
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
polynomial_svm_clf = Pipeline((
('poly_features', PolynomialFeatures(degree=3)),
('scaler', StandardScaler()),
('svm_clf', LinearSVC(C=10, loss='hinge', dual=True))
))
polynomial_svm_clf.fit(X, y)
2.1 决策边界
def plot_predictions(clf, axes):
x0s = np.linspace(axes[0], axes[1], 100)
x1s = np.linspace(axes[2], axes[3], 100)
x0, x1 = np.meshgrid(x0s, x1s)
X = np.c_[x0.ravel(), x1.ravel()]
y_pred = clf.predict(X).reshape(x0.shape)
plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
plot_predictions(polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
3 不同核函数的效果
3.1 线性
from sklearn.svm import SVC
# coef0为偏置项,影响小
poly_kernel_svm_clf = Pipeline((
('scaler', StandardScaler()),
('svm_clf', SVC(kernel='poly', degree=3, coef0=1, C=5))
))
poly_kernel_svm_clf.fit(X, y)
poly100_kernel_svm_clf = Pipeline((
('scaler', StandardScaler()),
('svm_clf', SVC(kernel='poly', degree=10, coef0=100, C=5))
))
poly100_kernel_svm_clf.fit(X, y)
plt.figure(figsize=(10, 4))
plt.subplot(121)
plot_predictions(poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.title('$d=3, coef0=1, C=5$', fontsize=18)
plt.subplot(122)
plot_predictions(poly100_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.title('$d=10, coef0=100, C=5$', fontsize=18)
plt.show()
# 左边较为简单,右边有过拟合趋势
3.2 高斯核函数
- 利用相似度来变换特征
rbf_kernel_svm_clf = Pipeline((
('scaler', StandardScaler()),
('svm_clf', SVC(kernel='rbf', C=0.001, gamma=5))
))
poly_kernel_svm_clf.fit(X, y)
gamma1, gamma2 = 0.1, 5
C1, C2 = 0.001, 1000
hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)
svm_clfs = []
for gamma, C in hyperparams:
rbf_kernel_svm_clf = Pipeline([
('scaler', StandardScaler()),
('svm_clf', SVC(kernel='rbf', gamma=gamma, C=C))
])
rbf_kernel_svm_clf.fit(X, y)
svm_clfs.append(rbf_kernel_svm_clf)
plt.figure(figsize=(11, 7))
for i, svm_clf in enumerate(svm_clfs):
plt.subplot(221+i)
plot_predictions(svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
gamma, C = hyperparams[i]
plt.title(r'$\gamma = {},C = {}$'.format(gamma, C), fontsize=16)
plt.tight_layout()
plt.show()
# gamma越大,C越大,过拟合风险大