文章目录
支撑向量机
支持向量机(SVM
)是个非常强大并且有多种功能的机器学习模型,能够做线性或者非线性的分类,回归,甚至异常值检测。机器学习领域中最为流行的模型之一,是任何学习机器学习的人必备的工具。SVM
特别适合应用于复杂但中小规模数据集的分类问题。
线性SVM
分类
经典的鸢尾花数据集
from sklearn.svm import SVC
#支撑向量模型
from sklearn import datasets
#数据集
iris = datasets.load_iris()
print(iris.keys())
# dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])
print(iris['feature_names'])
# ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
# 4个特征,分别时花萼长宽和花瓣长宽,单位cm
print(iris['data'].shape)
# (150, 4)
print(iris['target'].shape)
# (150,)
print(iris['target_names'])
# ['setosa' 'versicolor' 'virginica'] 0-山鸢尾 、1-变色鸢尾 、2-弗吉尼亚鸢尾
print(iris['target'])
# [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
# 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
# 2 2]
# 注意:标签是排好序的
print(iris['filename'])
# D:\ProgramData\Anaconda3\envs\mytf\lib\site-packages\sklearn\datasets\data\iris.csv
print(iris['DESCR'])
# 查看数据集描述
可视化支撑向量
鸢尾花数据集中山鸢尾setosa
和 变色鸢尾versicolor
,这两个种类能够被非常清晰,非常容易的用一条直线分开(即线性可分的)
左图:
- 三种可能的线性分类的决策边界,其中绿色虚线很糟糕。红色和紫色实线在训练集上表现完美,
- 它们的决策边界与样本实例过于接近,在新的样本实例中表现可能不会太好
右图:
- 黑色实线代表
SVM
分类器的决策边界。不仅分离了2个类,而且尽可能远离了最近的训练样本实例。 - 大间隔分类
实现代码:
from sklearn.svm import SVC
from sklearn import datasets
X = iris["data"][:, (2, 3)] # 只拿第2、3列,即花瓣长宽
y = iris["target"]
setosa_or_versicolor = (y == 0) | (y == 1)
X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor] # 将山鸢尾-0 和变色鸢尾-1的训练数据和标签筛选出来
# SVM Classifier model
svm_clf = SVC(kernel="linear", C=float("inf"))
svm_clf.fit(X, y)
svm_clf.intercept_,svm_clf.coef_ ,svm_clf.support_vectors_ #分别是:偏置,权重,支撑向量。
#(array([-3.78823471]),
# array([[1.29411744, 0.82352928]]),
# array([[1.9, 0.4],
# [3. , 1.1]]))
# 特意的绘制三条线,演示间隔数据的决策边界
# 绿线(pred1),模拟决策边界: -w0/w1 =5; b/w1 =-20
# 紫线(pred2),模拟决策边界: -w0/w1 =1; b/w1 =-1.8
# 红线(pred3),模拟决策边界: -w0/w1 =0.1; b/w1 =0.5
x0 = np.linspace(0, 5.5, 200)
pred_1 = 5*x0 - 20
pred_2 = x0 - 1.8
pred_3 = 0.1 * x0 + 0.5
#绘制决策边界
def plot_svc_decision_boundary(svm_clf, xmin, xmax):
w = svm_clf.coef_[0]
b = svm_clf.intercept_[0]
#b,w: (array([-3.78823471]), array([[1.29411744, 0.82352928]]))
# At the decision boundary, w0*x0 + w1*x1 + b = 0
# => x1 = -w0/w1 * x0 - b/w1
x0 = np.linspace(xmin, xmax, 200)
#生产描线数据中的x_0
decision_boundary = -w[0]/w[1] * x0 - b/w[1]
#计算公式得到x_1 ,决策边界
margin = 1/w[1]
#w_1的倒数是间隔
gutter_up = decision_boundary + margin # 拿到间隔上沿
gutter_down = decision_boundary - margin # 拿到间隔下沿
svs = svm_clf.support_vectors_ #拿到支撑向量
plt.scatter(svs[:, 0], svs[:, 1], s=180, facecolors='#FFAAAA')
# 描点支撑向量:svm_clf.support_vectors_
plt.plot(x0, decision_boundary, "k-", linewidth=2) # 描线决策边界
plt.plot(x0, gutter_up, "k--", linewidth=2) # 间隔上沿
plt.plot(x0, gutter_down, "k--", linewidth=2) # 间隔下沿
fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)
plt.sca(axes[0])
plt.plot(x0, pred_1, "g--", linewidth=2)
plt.plot(x0, pred_2, "m-", linewidth=2)
plt.plot(x0, pred_3, "r-", linewidth=2)
# 绘制红 绿 紫三条线
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs", label="Iris versicolor")
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo", label="Iris setosa")
# 描点原数据
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.legend(loc="upper left", fontsize=14)
plt.axis([0, 5.5, 0, 2])
plt.sca(axes[1])
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], "yo")
# 描点原数据
plt.xlabel("Petal length", fontsize=14)
plt.axis([0, 5.5, 0, 2])
plt.show()
SVM
对特征缩放非常敏感
如图:左右两图的刻度即缩放,右图是我们期望的。
实现代码:
Xs = np.array([[1, 50], [5, 20], [3, 80], [5, 60]]).astype(np.float64)
ys = np.array([0, 0, 1, 1]) # 模拟数据,4个点,对应2个类别,横纵坐标
svm_clf = SVC(kernel="linear", C=100)
#惩罚系数,C越大,一旦分类错误,惩罚越严重,越容易过拟合,C越小越容易欠拟合。默认1
svm_clf.fit(Xs, ys)
#直接SVM拟合,不进行特征缩放
print(svm_clf.intercept_,svm_clf.coef_ ,svm_clf.support_vectors_ )
#支撑向量是[[1,50],[5,60]], 权重是[[0.06896552 ,0.17241379]] ,偏置是 [-9.68965517]
#计算的决策边界水平,狭小
plt.figure(figsize=(9,2.7))
plt.subplot(121)
plt.plot(Xs[:, 0][ys==1], Xs[:, 1][ys==1], "bo")
plt.plot(Xs[:, 0][ys==0], Xs[:, 1][ys==0], "ms")
plot_svc_decision_boundary(svm_clf, 0, 6)
plt.xlabel("$x_0$", fontsize=20)
plt.ylabel("$x_1$ ", fontsize=20, rotation=0)
plt.title("Unscaled", fontsize=16)
plt.axis([0, 6, 0, 90])
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 引入并实例一标准化对象
X_scaled = scaler.fit_transform(Xs)
# 特征缩放
svm_clf.fit(X_scaled, ys)
# 特征缩放后再SVM拟合
print(svm_clf.intercept_,svm_clf.coef_ ,svm_clf.support_vectors_ )
#得到支撑向量,三个点
#[[-1.50755672 -0.11547005]
# [ 0.90453403 -1.5011107 ]
# [ 0.90453403 0.34641016]]
# 绘图
plt.subplot(122)
plt.plot(X_scaled[:, 0][ys==1], X_scaled[:, 1][ys==1], "bo")
plt.plot(X_scaled[:, 0][ys==0], X_scaled[:, 1][ys==0], "ms")
plot_svc_decision_boundary(svm_clf, -2, 2)
plt.xlabel("$x'_0$", fontsize=20)
plt.ylabel("$x'_1$ ", fontsize=20, rotation=0)
plt.title("Scaled", fontsize=16)
plt.axis([-2, 2, -2, 2])
软间隔分类:(对异常值敏感)
如图,如果我们严格地规定所有的数据都不在“街道”上,都在正确地两边,称为硬间隔分类,硬间隔分类有两个问题:
- 只在数据时线性可分的的时候才有效,
- 对异常点敏感。
为了避免上述的问题,我们更倾向于使用更加软性的模型。尽可能保持间隔宽阔和尽可能限制间隔违例。这就是软间隔分类。
Scikit-Learn
库创建SVM
模型时,可以指定许多超参数,其中C超参数(惩罚系数)来控制这种平衡:较小的C会更宽的间隔,但更多的间隔违规。一般情况下,设置较小的C可以使得泛化效果更好一些。如图
示例代码:
import numpy as np
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
iris = datasets.load_iris()
X = iris["data"][:, (2, 3)] # petal length, petal width
y = (iris["target"] == 2).astype(np.float64) # 2-弗吉尼亚鸢尾
scaler = StandardScaler()
svm_clf1 = LinearSVC(C=1, loss="hinge", random_state=42)
# 惩罚系数C默认1
svm_clf2 = LinearSVC(C=100, loss="hinge", random_state=42)
scaled_svm_clf1 = Pipeline([
("scaler", scaler),
("linear_svc", svm_clf1),
])
scaled_svm_clf2 = Pipeline([
("scaler", scaler),
("linear_svc", svm_clf2),
])
scaled_svm_clf1.fit(X, y)
scaled_svm_clf2.fit(X, y)
非线性SVM
分类
尽管线性 SVM
分类器在许多案例上表现得出乎意料的好,但是很多数据集并不是线性可分的。
如左图,9个点在一条线上,中间5点绿色,边上4点蓝色。
处理非线性数据集
方法之一是增加更多的特征,例如多项式特征在某些情况下可以变成线性可分的数据,如右图。
卫星数据集经典示例代码:
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.grid(True, which='both')
plt.xlabel(r"$x_1$", fontsize=20)
plt.ylabel(r"$x_2$", fontsize=20, rotation=0)
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()
from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
# 导入多项式特征
polynomial_svm_clf = Pipeline([
("poly_features", PolynomialFeatures(degree=3)), # 三阶
("scaler", StandardScaler()),
("svm_clf", LinearSVC(C=10, loss="hinge", random_state=42)) # 使用LinearSVC 核
])
polynomial_svm_clf.fit(X, y)
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)
y_decision = clf.decision_function(X).reshape(x0.shape) #决策边界
plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1) #绘制轮廓
plot_predictions(polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()
多项式内核(核技巧)
添加多项式特征很容易实现,不仅仅在 SVM
,在各种机器学习算法都有不错的表现,但是低次数的多项式不能处理非常复杂的数据集,而高次数的多项式却产生了大量的特征,会使模型变得慢。
使用SVM
时,你可以运用一个被称为“核技巧”(kernel trick
)的神奇数学技巧。它产生的结果就跟添加了许多多项式特征(甚至是高阶的多项式特征)一样好。但实际上并不需要真的添加,因此不会产生大量特征导致的组合爆炸。这个技巧可以用 SVC
类来实现
示例代码:
from sklearn.svm import SVC
#3阶多项式训练,核poly,coef0 =1
# 其中coef0 是核函数的常数项。受阶数影响的程度,对于‘poly’和 ‘sigmoid’有用。
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)
#10阶多项式训练,核poly,coef0=100, C:决策边界的间隔,相对小
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)
fig, axes = plt.subplots(ncols=2, figsize=(10.5, 4), sharey=True)
plt.sca(axes[0])
plot_predictions(poly_kernel_svm_clf, [-1.5, 2.45, -1, 1.5]) #调用自定义绘图函数
plot_dataset(X, y, [-1.5, 2.4, -1, 1.5])
plt.title(r"$d=3, r=1, C=5$", fontsize=18)
plt.sca(axes[1])
plot_predictions(poly100_kernel_svm_clf, [-1.5, 2.45, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.4, -1, 1.5])
plt.title(r"$d=10, r=100, C=5$", fontsize=18)
plt.ylabel("")
plt.show()
相似特征法
另一种解决非线性问题的方法是使用相似函数(similarity funtion
)计算每个样本与特定地标(landmark)的相似度。
基本思想:
-
选地标:在
x1=-2
和x1=1
之间增加两个地标 -
定义一个相似函数:此例选择高斯径向基函数(
Gaussian Radial Basis Function,RBF
),设置γ = 0.3
-
ϕ γ ( x , l ) = e x p ( − γ ∣ ∣ x − l ∣ ∣ 2 ) l : 地 标 此 例 是 − 2 和 1 , − 4 到 + 4 九 个 点 , 选 2 个 点 为 地 标 x : 分 别 为 7 个 点 计 算 相 似 特 征 , x 2 , x 3 描 点 如 右 图 γ : 当 前 值 0.3 , 增 加 γ 会 使 钟 型 曲 线 更 窄 , 即 每 个 地 标 影 响 范 围 更 窄 \phi_\gamma(x,l)=exp(-\gamma||x-l||^2) \\ l: 地标 此例是-2和1 ,\quad -4到+4九个点,选2个点为地标\\ x: 分别为7个点计算相似特征,x_2,x_3 \qquad 描点如右图 \\ \gamma : 当前值0.3 ,增加\gamma会使钟型曲线更窄,即每个地标影响范围更窄 ϕγ(x,l)=exp(−γ∣∣x−l∣∣2)l:地标此例是−2和1,−4到+4九个点,选2个点为地标x:分别为7个点计算相似特征,x2,x3描点如右图γ:当前值0.3,增加γ会使钟型曲线更窄,即每个地标影响范围更窄
-
计算新特征: 如左图,
x1=-1
的样本实例,对应的x2
和x3
新增的相似特征分别为:0.74和0.30 , -
删除原特征:对右图,线性可分
如何选择地标?:
最简单的方法是在数据集中的每一个样本的位置创建地标。这将产生更多的维度从而增加了转换后数据集是线性可分的可能性。但缺点是,m个样本,n个特征的训练集被转换成了m个实例,m个特征的训练集(假设你删除了原始特征)。这样一来,如果你的训练集非常大,你最终会得到同样大的特征。
高斯 RBF
内核
就像多项式特征法一样,相似特征法对各种机器学习算法同样也有不错的表现。但是在所有额外特征上的计算成本可能很高,特别是在大规模的训练集上。然而,“核” 技巧再一次显现了它在SVM
上的神奇之处:高斯核让你可以获得同样好的结果成为可能,就像你在相似特征法添加了许多相似特征一样,但事实上,你并不需要在RBF
添加它们
如图,C 值越高,正则化强度越少,正则化强度C一致的情况下:减小RBF
中的$\quad\gamma $ 参数gamma
, 地标影响范围就越小,决策边界会更更不规则,沿单个实例绕弯,反之,决策边界会更平坦、平滑。如果模型过拟合,你应该减小
γ
\gamma
γ 值,若欠拟合,则增大 $ \gamma $ ,与超参数C类似。
如何选择核函数?
一般来说,应该先尝试线性核函数( 要记住:LinearSVC
比SVC(kernel="linear"
)要快得多),尤其是当训练集很大或者有大量的特征的情况下。如果训练集不太大,也可以尝试高斯径向基核(Gaussian RBF Kernel
),它在大多数情况下都很有效。如果还有空闲的时间和计算能力,你还可以使用交叉验证和网格搜索来试验其他的核函数,特别是针对特定数据结构的核函数。比如:字符串核 常用语文本文档和DNA序列的分类。
示例代码:
from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="poly", degree=3, coef0=1, C=5)) # 多项式核,3阶,受阶数影响的程度,决策边界间隔
])
poly_kernel_svm_clf.fit(X, y)
# 对比,多项式核和高斯RBF核
rbf_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="rbf", gamma=5, C=0.001)) #高斯RB核,gamma 5,C越小对模型正则化越少。
])
rbf_kernel_svm_clf.fit(X, y)
计算复杂度
如表:
LinearSVC类
基于liblinear
库,为线性 SVM
实现了一个的优化算法。
它并不支持核技巧,但是它样本和特征的数量几乎是线性的:训练时间复杂度大约为 O ( m × n ) O(m × n) O(m×n)。
如果需要非常高的精度,这个算法需要花费更多时间,这是由容差值超参数 ϵ \epsilon ϵ 控制的。大多数分类任务中,使用默认容差值的效果是已经可以满足一般要求。
在 Scikit-learn
中,
ϵ
\epsilon
ϵ 对应的超参数是:tol
SVC
类基于libsvm
库,它实现了支持核技巧的算法。训练时间复杂度通常介于$ O(m^2 × n)$ 和
O
(
m
3
×
n
)
O(m^3 × n)
O(m3×n) 之间。
这意味着当训练样本变大时,它将变得极其慢(例如,成千上万个样本)。这个算法对于复杂但小型或中等数量的数据集表现是完美的。
它能对特征数量很好的缩放,尤其对稀疏特征来说(sparse features)(即每个样本实例仅有少量的非零特征)。在这个情况下,算法复杂度大致与样本实例的非零特征数成正比。
SVM
回归
解决线性回归任务
SVM
算法应用广泛:不仅仅支持线性和非线性的分类任务,还支持线性和非线性的回归任务
诀窍在于将目标反转一下:限制间隔违规的情况下,不是试图在两个类别之间找到尽可能大的间隔。SVM
回归任务是限制间隔违规情况下,尽量放置更多的样本在间隔内。间隔的宽度由超参数 $ \epsilon $ 控制 。
如图:对随机线性数据进行训练的两个SVM
回归模型:左图间隔(
ϵ
=
1.5
\epsilon=1.5
ϵ=1.5),右图间隔(
ϵ
=
0.5
\epsilon=0.5
ϵ=0.5)
代码实现:
# 随机线性数据,50个实例样本
np.random.seed(42)
m = 50
X = 2 * np.random.rand(m, 1)
y = (4 + 3 * X + np.random.randn(m, 1)).ravel()
from sklearn.svm import LinearSVR
svm_reg1 = LinearSVR(epsilon=1.5, random_state=42)
svm_reg2 = LinearSVR(epsilon=0.5, random_state=42)
svm_reg1.fit(X, y)
svm_reg2.fit(X, y)
#检索支撑向量
def find_support_vectors(svm_reg, X, y):
y_pred = svm_reg.predict(X)
off_margin = (np.abs(y - y_pred) >= svm_reg.epsilon) # 找间隔外的点
return np.argwhere(off_margin) # 间隔外的点作为支撑向量返回
svm_reg1.support_ = find_support_vectors(svm_reg1, X, y)
svm_reg2.support_ = find_support_vectors(svm_reg2, X, y)
#绘图
def plot_svm_regression(svm_reg, X, y, axes):
x1s = np.linspace(axes[0], axes[1], 100).reshape(100, 1)
y_pred = svm_reg.predict(x1s)
plt.plot(x1s, y_pred, "k-", linewidth=2, label=r"$\hat{y}$")
plt.plot(x1s, y_pred + svm_reg.epsilon, "k--") #绘制间隔
plt.plot(x1s, y_pred - svm_reg.epsilon, "k--") #绘制间隔
plt.scatter(X[svm_reg.support_], y[svm_reg.support_], s=180, facecolors='#FFAAAA')
plt.plot(X, y, "bo")
plt.xlabel(r"$x_1$", fontsize=18)
plt.legend(loc="upper left", fontsize=18)
plt.axis(axes)
fig, axes = plt.subplots(ncols=2, figsize=(9, 4), sharey=True)
plt.sca(axes[0])
plot_svm_regression(svm_reg1, X, y, [0, 2, 3, 11]) #绘制第1个模型
plt.title(r"$\epsilon = {}$".format(svm_reg1.epsilon), fontsize=18)
plt.ylabel(r"$y$", fontsize=18, rotation=0)
plt.annotate(
'', xy=(eps_x1, eps_y_pred), xycoords='data',
xytext=(eps_x1, eps_y_pred - svm_reg1.epsilon),
textcoords='data', arrowprops={'arrowstyle': '<->', 'linewidth': 1.5}
)
plt.text(0.91, 5.6, r"$\epsilon$", fontsize=20)
plt.sca(axes[1])
plot_svm_regression(svm_reg2, X, y, [0, 2, 3, 11])
plt.title(r"$\epsilon = {}$".format(svm_reg2.epsilon), fontsize=18)
save_fig("svm_regression_plot")
plt.show()
解决非线性回归任务
处理非线性回归任务,可以使用核化的 SVM
模型。
如图,使用2阶多项式核函数的SVM
回归。左图是较小的正则化(即更大的C值),右图则是更大的正则化(即小的C值)
代码示例:
np.random.seed(42)
m = 100
X = 2 * np.random.rand(m, 1) - 1
y = (0.2 + 0.1 * X + 0.5 * X**2 + np.random.randn(m, 1)/10).ravel()
from sklearn.svm import SVR
svm_poly_reg1 = SVR(kernel="poly", degree=2, C=100, epsilon=0.1, gamma="scale")
svm_poly_reg2 = SVR(kernel="poly", degree=2, C=0.01, epsilon=0.1, gamma="scale")
svm_poly_reg1.fit(X, y)
svm_poly_reg2.fit(X, y)
fig, axes = plt.subplots(ncols=2, figsize=(9, 4), sharey=True)
plt.sca(axes[0])
plot_svm_regression(svm_poly_reg1, X, y, [-1, 1, 0, 1])
plt.title(r"$degree={}, C={}, \epsilon = {}$".format(svm_poly_reg1.degree, svm_poly_reg1.C, svm_poly_reg1.epsilon), fontsize=18)
plt.ylabel(r"$y$", fontsize=18, rotation=0)
plt.sca(axes[1])
plot_svm_regression(svm_poly_reg2, X, y, [-1, 1, 0, 1])
plt.title(r"$degree={}, C={}, \epsilon = {}$".format(svm_poly_reg2.degree, svm_poly_reg2.C, svm_poly_reg2.epsilon), fontsize=18)
plt.show()
背后工作原理
决策函数和预测
线性SVM
分类器通过简单地计算决策函数
w
T
+
b
=
w
1
x
1
+
⋯
+
w
n
w
n
+
b
w^T+b =w_1x_1+\cdots+w_nw_n+b
wT+b=w1x1+⋯+wnwn+b 来预测新样本实例的类别:
如果结果是正的,预测类别 y ^ ŷ y^ 是正类,为 1,否则他就是负类,为 0
如图:
鸢尾花数据集有两个特征(花瓣的宽度petal width
和花瓣的长度petal length
),是一个二维平面。
使得决策函数 w T + b = w 1 x 1 + ⋯ + w n w n + b = 0 w^T+b =w_1x_1+\cdots+w_nw_n+b=0 wT+b=w1x1+⋯+wnwn+b=0 的点的集合,是图中两个平面的交叉的交线,决策边界。
决策函数:
y
^
=
(
0
W
⋅
X
+
b
<
0
1
W
⋅
X
+
b
>
=
0
)
\hat y = \begin{pmatrix} 0 && W\cdot X +b<0 \\ 1 && W \cdot X +b>=0 \end{pmatrix}
y^=(01W⋅X+b<0W⋅X+b>=0)
训练目标
如果令决策函数等于 − 1 -1 −1 或 1 1 1 ,到决策边界相等的两条平行线,这就是间隔。
若需要这个间隔扩大两倍,可以令: ∣ ∣ w ∣ ∣ ||w|| ∣∣w∣∣ 权重(即斜率),除以 2 2 2 。
硬间隔线性SVM
分类器的约束优化
最 小 化 ( m i n i m i z e ) w , b : 1 2 w T w = 1 2 ∣ ∣ w ∣ ∣ 2 使 得 : t ( i ) ( w T ⋅ x ( i ) + b ) > = 1 t ( i ) : 每 个 样 本 实 例 距 离 决 策 边 界 的 距 离 1 之 所 以 不 用 1 2 ∣ ∣ w ∣ ∣ , 因 为 , 它 在 0 处 不 可 微 ( 不 可 导 ) 最小化(minimize)w,b :\quad \frac {1}{2} w^T w =\frac{1}{2}||w||^2 \\ 使得: t^{(i)}(w^T\cdot x^{(i)}+b) >=1 \\ t^{(i)}: 每个样本实例距离决策边界的距离 1 \\ 之所以 不用 \frac{1}{2}||w||, 因为,它在0处不可微(不可导) 最小化(minimize)w,b:21wTw=21∣∣w∣∣2使得:t(i)(wT⋅x(i)+b)>=1t(i):每个样本实例距离决策边界的距离1之所以不用21∣∣w∣∣,因为,它在0处不可微(不可导)
软间隔线性SVM
分类器的约束优化
需要为每个样本实例引入一个松弛变量: ζ > = 0 ( z e t a ) \zeta >=0 (zeta) ζ>=0(zeta) : 衡量是第 i i i 个样本实例多大程度上允许间隔违例。
这样,出现了2个矛盾的目标:1、使得 $\zeta $ 越小越好从而减少间隔违例;2、使得 $ \frac {1}{2} w^T w $ 越小越好从而间隔最大化。
超参数
C
C
C 粉墨登场:允许我们在两个目标之间权衡。
最
小
化
(
m
i
n
i
m
i
z
e
)
w
,
b
,
ζ
:
1
2
w
T
w
+
C
∑
i
=
1
m
ζ
(
i
)
使
得
:
t
(
i
)
(
w
T
⋅
x
(
i
)
+
b
)
>
=
1
−
ζ
(
i
)
且
ζ
(
i
)
》
=
0
最小化(minimize)w,b,\zeta :\quad \frac {1}{2} w^T w + C\sum_{i=1}^m \zeta^{(i)}\\ 使得: t^{(i)}(w^T\cdot x^{(i)}+b) >=1-\zeta^{(i)} 且 \zeta^{(i)}》=0 \\
最小化(minimize)w,b,ζ:21wTw+Ci=1∑mζ(i)使得:t(i)(wT⋅x(i)+b)>=1−ζ(i)且ζ(i)》=0
二次规划
硬间隔和软间隔问题都属于线性约束的凸二次优化问题。统称为二次规划问题。解决此问题,有很多求解器。
二次规划问题的一般形式:
最
小
化
(
m
i
n
i
m
i
z
e
)
p
:
1
2
p
T
H
p
+
f
T
p
,
使
得
A
p
<
=
b
p
:
是
一
个
n
p
维
向
量
,
n
p
是
参
数
数
量
H
:
是
一
个
n
p
∗
n
p
维
的
矩
阵
f
:
是
一
个
n
p
维
向
量
A
:
是
一
个
n
c
∗
n
p
维
的
矩
阵
(
n
c
是
约
束
的
数
量
)
b
:
是
一
个
n
c
维
向
量
最小化(minimize)p :\quad \frac {1}{2} p^T Hp +f^T p, \quad 使得Ap<=b \\ p: 是一个 n_p 维向量 ,n_p是参数数量\\ H: 是一个 n_p * n_p 维的矩阵\\ f: 是一个 n_p 维向量\\ A: 是一个 n_c * n_p 维的矩阵 (n_c是约束的数量)\\ b: 是一个 n_c 维向量\\
最小化(minimize)p:21pTHp+fTp,使得Ap<=bp:是一个np维向量,np是参数数量H:是一个np∗np维的矩阵f:是一个np维向量A:是一个nc∗np维的矩阵(nc是约束的数量)b:是一个nc维向量
对偶问题
给出一个约束优化问题,即原始问题(primal problem),它可能表示不同但是和另一个问题紧密相连,称为对偶问题(Dual Problem)。对偶问题的解通常是对原始问题的解给出一个下界约束,但在某些条件下,它们可以获得相同解。幸运的是,SVM
问题恰好满足这些条件,所以你可以选择解决原始问题或者对偶问题,两者将会有相同解。
当训练样本的数量比特征数量小的时候,对偶问题比原始问题要快得多。更重要的是,它让能够实现核技巧,而原始问题不可能实现。
线性SVM
目标的对偶形式:
内核化SVM
scikit-learn
类库提供了一些常用的核函数,包括多项式核、S 型核、高斯核以及线性核:
线性:$K(a,b)=a^Tb $
多项式核: K ( a , b ) = ( γ a T b + r ) d K(a,b)=(\gamma a^Tb +r)^d K(a,b)=(γaTb+r)d 当 d = 2 d=2 d=2时, 2阶多项式核,经常被用于自然语言处理
S型核 : K ( a , b ) = t a n h ( γ a T b + r ) ) K(a,b)= tanh(\gamma a^Tb+r)) K(a,b)=tanh(γaTb+r)) , γ 和 r \gamma \quad 和\quad r γ和r 都是能在交叉验证中进行微调的超参数。
高斯RBG
核: $ K(a,b)= exp(-\gamma ||a-b||^2) $ ,
在线SVM
对于线性 SVM
分类器,一种方式是使用梯度下降(例如使用SGDClassifire
)最小化代价函数。不幸的是,它比基于 QP
方式收敛慢得多。
Hinge loss Hinge 损失函数
m a x ( 0 , 1 − t ) = ( 0 t > = 1 该 函 数 导 数 为 − 1 t < 1 ) max(0,1-t) = \begin{pmatrix}0 & \quad t>=1 \\ 该函数导数为-1 & \quad t<1 \end{pmatrix} max(0,1−t)=(0该函数导数为−1t>=1t<1)
t = np.linspace(-2, 4, 200)
h = np.where( 1- t < 0, 0, 1 - t) # max(0, 1-t)
plt.figure(figsize=(5,2.8))
plt.plot(t, h, "b-", linewidth=2, label="$max(0, 1 - t)$")
plt.grid(True, which='both')
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
plt.yticks(np.arange(-1, 2.5, 1))
plt.xlabel("$t$", fontsize=16)
plt.axis([-2, 4, -1, 2.5])
plt.legend(loc="upper right", fontsize=16)
plt.show()
=============================