ccc-sklearn-8-SVM(2)

1.SVM重要参数kernel

核技巧:一种能够使用数据原始空间中的向量计算来表示升维后空间中的点积结果的数学方式,即 K ( u , v ) = Φ ( u ) ⋅ Φ ( v ) K(u,v)=\Phi(u)\cdot \Phi(v) K(u,v)=Φ(u)Φ(v)。其中原始空间中的点积函数 K ( u , v ) K(u,v) K(u,v),就叫做核函数

SVM重要参数kernel
事实上,非线性情况下的推导过程和逻辑都与线性SVM一摸一样,只不过在定义决策边界前必须先对数据进行升维度,将原始的x转换成 Φ ( x ) \Phi(x) Φ(x),即进行某种非线性的变换。最终推导的决策函数表示如下:
f ( x t e s t ) = s i g n ( w ⋅ Φ ( x t e s t ) + b ) = s i g n ( ∑ i = 1 N α i y i Φ ( x i ) ⋅ Φ ( x t e s t ) + b ) f(x_{test})=sign(w\cdot \Phi(x_{test})+b)=sign(\sum_{i=1}^{N}\alpha_iy_i\Phi(x_i)\cdot \Phi(x_{test})+b) f(xtest)=sign(wΦ(xtest)+b)=sign(i=1NαiyiΦ(xi)Φ(xtest)+b)

核函数帮助解决的问题

  • 无需去担心 Φ \Phi Φ应该是什么样,因为非线性SVM中的核函数都是正定核函数,都满足美世定律,确保高维空间中任意两个向量的点积一定可以被低维空间中的两个向量的某种计算来表示
  • 使用核函数计算低维度中的向量关系比计算原本的 Φ ( x i ) ⋅ Φ ( x t e s t ) \Phi(x_i)\cdot \Phi(x_{test}) Φ(xi)Φ(xtest)简单许多
  • 计算在原始空间运行,避免维度诅咒现象

sklearn中的四种核函数

输入含义解决问题核函数的表达式参数gamma参数degree参数codf0
linear线性核线性 K ( x , y ) = x T y = x ⋅ y K(x,y)=x^Ty=x\cdot y K(x,y)=xTy=xyNoNoNo
poly多项式核偏线性 K ( x , y ) = ( γ ( x ⋅ y + r ) d K(x,y)=(\gamma(x\cdot y+r)^d K(x,y)=(γ(xy+r)dYesYesYes
sigmoid双曲正切核非线性 K ( x , y ) = t a n h ( γ ( x ⋅ y ) + r ) K(x,y)=tanh(\gamma(x\cdot y)+r) K(x,y)=tanh(γ(xy)+r)YesNoYes
rbf高斯径向基偏非线性 K ( x , y ) = e − γ ∣ ∣ x − y ∣ ∣ 2 , γ > 0 K(x,y)=e^{-\gamma||x-y||^2},\gamma>0 K(x,y)=eγ∣∣xy2,γ>0YesNoNo

探索核函数在不同数据集上的表现
步骤一:导入库和数据

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import svm
from sklearn.datasets import make_circles,make_moons,make_blobs,make_classification

n_samples = 100
 
datasets = [
    make_moons(n_samples=n_samples, noise=0.2, random_state=0),
    make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1),
    make_blobs(n_samples=n_samples, centers=2, random_state=5),#分簇的数据集
    make_classification(n_samples=n_samples,n_features = 2,n_informative=2,n_redundant=0, random_state=5)
                #n_features:特征数,n_informative:带信息的特征数,n_redundant:不带信息的特征数
    ]
 
Kernel = ["linear","poly","rbf","sigmoid"]
 
for X,Y in datasets:
    plt.figure(figsize=(5,4))
    plt.scatter(X[:,0],X[:,1],c=Y,s=50,cmap="rainbow")

在这里插入图片描述在这里插入图片描述步骤二:构建子图,第一列为原始数据,后四列为模型学习结果

nrows = len(datasets)
ncols = len(Kernel)+1
fig, axes = plt.subplots(nrows, ncols,figsize=(20,16))

在这里插入图片描述
步骤三:函数绘图

nrows=len(datasets)
ncols=len(Kernel) + 1
fig, axes = plt.subplots(nrows, ncols,figsize=(20,16))

for ds_cnt, (X,Y) in enumerate(datasets):
    ax = axes[ds_cnt,0]
    if ds_cnt == 0:
        ax.set_title("Input data")
    ax.scatter(X[:,0],X[:,1],c=Y,zorder=10,cmap=plt.cm.Paired,edgecolors='k')
    ax.set_xticks(())
    ax.set_yticks(())
    
    for est_idx,kernel in enumerate(Kernel):
        ax = axes[ds_cnt,est_idx +1]
        clf = svm.SVC(kernel=kernel,gamma=2).fit(X,Y)
        score = clf.score(X,Y)
        ax.scatter(X[:,0],X[:,1],c=Y,zorder=10,cmap=plt.cm.Paired,edgecolors='k')
        #支持向量
        ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=50,facecolor='none',zorder=10,edgecolors='k')
        #决策边界
        x_min,x_max=X[:,0].min()-.5,X[:,0].max()+.5
        y_min,y_max=X[:,1].min()-.5,X[:,1].max()+.5
        XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j]
        Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape)
        ax.pcolormesh(XX,YY,Z>0,cmap=plt.cm.Paired)
        ax.contour(XX,YY,Z,colors=['k','k','k'],linestyles=['--','-','--'],levels=[-1,0,1])
        ax.set_xticks(())
        ax.set_yticks(())
        if ds_cnt ==0:
            ax.set_title(kernel)
        ax.text(0.95,0.06,('%.2f'%score).lstrip('0')
               ,size=15
               ,bbox=dict(boxstyle='round',alpha=0.8,facecolor='white')
               ,transform=ax.transAxes
               ,horizontalalignment='right')
        
plt.tight_layout()
plt.show()

在这里插入图片描述
在这里插入图片描述
可以观察到,线性核函数和多项式核函数在非线性数据上表现会浮动,如果数据相对线性可分,则表现不错,如果是像环形数据那样彻底不可分的,则表现糟糕。在线性数据集上,线性核函数和多项式核函数即便有扰动项也可以表现不错,可见多项式核函数是虽然也可以处理非线性情况,但更偏向于线性的功能。Sigmoid核函数在非线性数据上强于两个线性核函数,但效果明显不如rbf,对扰动项的抵抗也比较弱,所以功能比较弱小,很少被用到。rbf基本在任何数据集上都表现不错,属于比较万能的核函数。

2.探索核函数的优势和缺陷

步骤一:导入库和数据

from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
from time import time
import datetime
import pytz

data = load_breast_cancer()
X = data.data
y = data.target

X.shape
plt.scatter(X[:,0],X[:,1],c=y)
plt.show()

在这里插入图片描述
步骤二:代入模型测试

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
Kernel =  ["linear","poly",rbf","sigmoid"]

for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel,
             gamma="auto"
             ,degree = 3
             ,cache_size=5000
             ).fit(Xtrain,Ytrain)
    print("The accuracy under kernel %s is %f"%(kernel,clf.score(Xtest,Ytest)))
    print(datetime.datetime.fromtimestamp(time()-time0,pytz.timezone('UTC')).strftime("%M:%S:%f"))

在这里插入图片描述
结果是运行poly模型时,一直无法完成。那么暂时去掉poly核函数

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
Kernel =  ["linear","rbf","sigmoid"]

for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel,
             gamma="auto"
             ,cache_size=5000
             ).fit(Xtrain,Ytrain)
    print("The accuracy under kernel %s is %f"%(kernel,clf.score(Xtest,Ytest)))
    print(datetime.datetime.fromtimestamp(time()-time0,pytz.timezone('UTC')).strftime("%M:%S:%f"))

在这里插入图片描述
可以看到很轻松的跑出来了。虽然rbf和sigmoid概率预测很低,但用时远远小于linear。而且由于linear预测效果较好,那么将degree设置为1再次尝试poly核函数

Kernel =  ["linear","poly","rbf","sigmoid"]

for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel
              ,gamma="auto"
              ,degree =1 
             ,cache_size=5000
             ).fit(Xtrain,Ytrain)
    print("The accuracy under kernel %s is %f"%(kernel,clf.score(Xtest,Ytest)))
    print(datetime.datetime.fromtimestamp(time()-time0,pytz.timezone('UTC')).strftime("%M:%S:%f"))

在这里插入图片描述
这次速度就很高了,而且poly精确性也和线性核函数接近。

步骤三:探索导致rbf和sigmoid结果偏低的可能

import pandas as pd
data = pd.DataFrame(X)
data.describe([0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.99]).T

在这里插入图片描述
可以看到数据的量纲不一和偏态的问题很严重。所以可以归一化

from sklearn.preprocessing import StandardScaler
X = StandardScaler().fit_transform(X)
data = pd.DataFrame(X)
data.describe([0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.99]).T

在这里插入图片描述
标准化后再次让SVM在核函数中遍历

Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
Kernel =  ["linear","poly","rbf","sigmoid"]

for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel
              ,gamma="auto"
              ,degree =1 
             ,cache_size=5000
             ).fit(Xtrain,Ytrain)
    print("The accuracy under kernel %s is %f"%(kernel,clf.score(Xtest,Ytest)))
    print(datetime.datetime.fromtimestamp(time()-time0,pytz.timezone('UTC')).strftime("%M:%S:%f"))

在这里插入图片描述
可以看到,所有核函数特别是linear的运行时间都大大减少。同时rbf和sigmoid的效果也得到显著提高

结论:
1:线性核,尤其是多项式核函数在高次项时计算非常缓慢
2:rbf和多项式核函数都不擅长处理量纲不统一的数据集
3:SVM执行之前非常推荐先进行数据的无量纲化

步骤四:继续调整参数提高准确性

由之前核函数参数的表格可以知道,除了线性核函数以外,其他核函数还受参数gamma,degree以及coef0的影响。他们分别代表表达式中的 γ \gamma γ,多项式中的次数 d d d,常数项 r r r

参数含义
degree整数,可不填,默认3
多项式核函数的次数(‘poly’),如果核函数没有选择"poly",这个参数会被忽略
gamma浮点数,可不填,默认“auto"
核函数的系数,仅在参数Kernel的选项为”rbf",“poly"和"sigmoid”的时候有效
输入“auto”,自动使用1/(n_features)作为gamma的取值
输入"scale",则使用1/(n_features * X.std())作为gamma的取值
输入"auto_deprecated",则表示没有传递明确的gamma值
coef0浮点数,可不填,默认=0.0
核函数中的常数项,它只在参数kernel为’poly’和’sigmoid’的时候有效

由于数学问题太过复杂,这里采用学习曲线和网格搜索来寻找最佳参数

score = []
gamma_range = np.logspace(-10,1,50)
for i in gamma_range:
    clf = SVC(kernel="rbf",gamma=i,cache_size=5000).fit(Xtrain,Ytrain)
    score.append(clf.score(Xtest,Ytest))
print(max(score),gamma_range[score.index(max(score))])
plt.plot(gamma_range,score)
plt.show()

在这里插入图片描述
学习曲线与线性核函数之前准确率一样,应该是rbf核函数的极限了。由于多项式核函数有三个参数,所以使用网格搜索,同时注意不要分太多了防止计算量太大运行时间过长

from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.model_selection import GridSearchCV
time0 = time()
gamma_range = np.logspace(-10,1,20)
coef0_range = np.linspace(0,5,10)
param_grid = dict(gamma = gamma_range
                 ,coef0=coef0_range)
cv =StratifiedShuffleSplit(n_splits=5,test_size=0.3,random_state=420)
grid = GridSearchCV(SVC(kernel = "poly",degree=1,cache_size=5000),
                   param_grid=param_grid,cv=cv)
grid.fit(X,y)
print("The best parameters are %s with a score of %0.5f" % (grid.best_params_, grid.best_score_))
print(datetime.datetime.fromtimestamp(time()-time0,pytz.timezone('UTC')).strftime("%M:%S:%f"))

在这里插入图片描述
网格搜索为我们返回了参数coef0=0,gamma=0.18329807108324375,但整体的分数是0.96959,虽然比调参前略有提高,但依然没有超过线性核函数核rbf的结果。一般如果最初选择核函数的时候,多项式的结果不如rbf和线性核函数,就可以放弃它了,尝试调整rbf或者直接使用线性核函数。

步骤五:调整参数C

参数含义
C浮点数,默认1,必须大于等于0,可不填
松弛系数的惩罚项系数。如果C值设定比较大,那SVC可能会选择边际较小的,能够更好地分类所有训练点的决策边界,不过模型的训练时间也会更长。
如果C的设定值较小,那SVC会尽量最大化边界,决策功能会更简单,但代价是训练的准确度。换句话说,C在SVM中的影响就像正则化参数对逻辑回归的影响

为什么要引入C?

当两组数据完全线性可分,找出一个决策边界使得训练集上的分类误差为0,这两种数据就被称为是存在”硬间隔“的
当两组数据几乎是完全线性可分的,但决策边界在训练集上存在较小的训练误差,这两种数据就被称为是存在”软间隔“

在这里插入图片描述
如图表示,虽然边际大的B1没有正确划分所有的数据。但不能认为此事B2就一定是一条更好的边界。需要找出一个“最大边际”与“被分错的样本数量”之间的平衡

引入C的方式
在这里插入图片描述
原始判别函数为:
w ⋅ x i + b ≥ 1 , i f   y i = 1 w \cdot x_i+b\geq1,if \space y_i=1 wxi+b1,if yi=1
w ⋅ x i + b ≤ − 1 , i f   y i = − 1 w \cdot x_i+b\leq-1,if \space y_i=-1 wxi+b1,if yi=1
引入松弛系数 ζ \zeta ζ后的判别函数:
w ⋅ x i + b ≥ 1 − ζ i , i f   y i = 1 w \cdot x_i+b\geq1-\zeta_i,if \space y_i=1 wxi+b1ζi,if yi=1
w ⋅ x i + b ≤ − 1 + ζ i , i f   y i = − 1 w \cdot x_i+b\leq-1+\zeta_i,if \space y_i=-1 wxi+b1+ζi,if yi=1
实际上就是将虚线超平面往图像的上方和下方平移,平移后同时会分错一系列别的点。所以必须再添加一个惩罚项,用来惩罚具有巨大松弛系数C的决策超平面。此时的损失函数表示为:
$$
拉格朗日函数为:(u为第二个拉格朗日乘数)
在这里插入图片描述
拉格朗日对偶函数为:
在这里插入图片描述
公式中现在唯一的新变量,松弛系数的惩罚力度C,由参数C来进行控制

通过学习曲线调整C的值

score = []
C_range = np.linspace(0.01,30,50)
for i in C_range:
    clf = SVC(kernel="linear",C=i,cache_size=5000).fit(Xtrain,Ytrain)
    score.append(clf.score(Xtest,Ytest))
print(max(score),C_range[score.index(max(score))])
plt.plot(C_range,score)
plt.show()

在这里插入图片描述
linear的最大值和之前求得一样,根据图像也可以得出之前的就是最佳值

score = []
C_range = np.linspace(0.01,30,50)
for i in C_range:
    clf = SVC(kernel="rbf",C=i
              ,gamma=0.012067926406393264
              ,cache_size=5000).fit(Xtrain,Ytrain)
    score.append(clf.score(Xtest,Ytest))
print(max(score),C_range[score.index(max(score))])
plt.plot(C_range,score)
plt.show()

在这里插入图片描述
继续细分

score = []
C_range = np.linspace(5,7,50)
for i in C_range:
    clf = SVC(kernel="rbf",C=i
              ,gamma=0.012067926406393264
              ,cache_size=5000).fit(Xtrain,Ytrain)
    score.append(clf.score(Xtest,Ytest))
print(max(score),C_range[score.index(max(score))])
plt.plot(C_range,score)
plt.show()

在这里插入图片描述
此时找到最优解,准确率最高的是rbf

附录

svc参数列表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
SVC属性列表
在这里插入图片描述
SVC接口列表
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值