《蜥蜴书》_讲义及源码解读_05

支撑向量机

支持向量机(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=-2x1=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(γxl2)l:214+42x:7x2,x3γ:0.3γ使线

  • 计算新特征: 如左图,x1=-1的样本实例,对应的 x2x3新增的相似特征分别为:0.74和0.30 ,

  • 删除原特征:对右图,线性可分

如何选择地标?:

最简单的方法是在数据集中的每一个样本的位置创建地标。这将产生更多的维度从而增加了转换后数据集是线性可分的可能性。但缺点是,m个样本,n个特征的训练集被转换成了m个实例,m个特征的训练集(假设你删除了原始特征)。这样一来,如果你的训练集非常大,你最终会得到同样大的特征。

高斯 RBF 内核

就像多项式特征法一样,相似特征法对各种机器学习算法同样也有不错的表现。但是在所有额外特征上的计算成本可能很高,特别是在大规模的训练集上。然而,“核” 技巧再一次显现了它在SVM上的神奇之处:高斯核让你可以获得同样好的结果成为可能,就像你在相似特征法添加了许多相似特征一样,但事实上,你并不需要在RBF 添加它们

在这里插入图片描述

如图,C 值越高,正则化强度越少,正则化强度C一致的情况下:减小RBF 中的$\quad\gamma $ 参数gamma, 地标影响范围就越小,决策边界会更更不规则,沿单个实例绕弯,反之,决策边界会更平坦、平滑。如果模型过拟合,你应该减小 γ \gamma γ 值,若欠拟合,则增大 $ \gamma $ ,与超参数C类似。

如何选择核函数?

一般来说,应该先尝试线性核函数( 要记住:LinearSVCSVC(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^=(01WX+b<0WX+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=21w2使:t(i)(wTx(i)+b)>=1t(i):121w0()

软间隔线性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=1mζ(i)使:t(i)(wTx(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:npnpH:npnpf:npA:ncnp(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,1t)=(01t>=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()

在这里插入图片描述

=============================

归结演绎推理是一种基于逻辑推理的方法,通过对已知事实和规则进行推理,得出结论。Python可以通过实现归结演绎推理来进行逻辑推理。 以下是一个简单的归结演绎推理的Python实现: 1. 定义规则和事实 ``` # 规则 rules = [ ('penguin', 'bird'), # 企鹅是鸟类 ('bird', 'animal'), # 鸟类是动物 ('lizard', 'animal'), # 蜥蜴是动物 ('bird', 'fly') # 鸟类可以飞 ] # 事实 facts = [ 'tweety is a penguin', # 特维是一只企鹅 'penguin is black and white' # 企鹅是黑白相间的 ] ``` 2. 定义归结演绎推理函数 ``` def resolve(a, b): """ 归结规则 """ for fact_a in a: for fact_b in b: if fact_a == ('not', fact_b) or fact_b == ('not', fact_a): # a和b中有相反的事实,无法归结 continue if fact_a == fact_b: # a和b中有相同的事实,无需归结 continue for i in range(len(fact_a)): if fact_a[i] != fact_b[i]: # 找到不同的部分,进行归结 new_fact = fact_a[:i] + fact_a[i+1:] + fact_b[:i] + fact_b[i+1:] if new_fact not in a and new_fact not in b: # 新的事实不在a和b中,加入a a.append(new_fact) return True return False def resolution(rules, facts): """ 归结演绎推理 """ while True: pairs = [(rules[i], rules[j]) for i in range(len(rules)) for j in range(i+1, len(rules))] new_rules = [] for pair in pairs: a, b = pair new_fact = resolve(a, b) if new_fact: # 归结出新的规则,加入新规则列表 new_rules.append(new_fact) if new_rules: # 有新规则,加入规则列表 rules += new_rules continue for fact in facts: if ('not', fact) in rules: # 有矛盾的事实,返回False return False # 执行完毕,返回True return True ``` 3. 测试归结演绎推理函数 ``` if resolution(rules, facts): print('Tweety can fly') else: print('Tweety cannot fly') ``` 输出结果为:`Tweety can fly`,即特维可以飞。因为根据规则,企鹅是鸟类,鸟类可以飞,所以特维可以飞。 这是一个简单的归结演绎推理的Python实现,可以根据实际需要进行扩展和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值