详解支持向量机-探索核函数的优势和缺陷【菜菜的sklearn课堂笔记】

时间:2022-11-23 18:01:46

视频作者:[菜菜TsaiTsai] 链接:[【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili]

看起来,除了Sigmoid核函数,其他核函数效果都还不错。但其实rbf和poly都有自己的弊端,我们使用乳腺癌数据集作为例子来展示一下:

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 # 将时间戳转化为真实地时间

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

X.shape
---
(569, 30)

np.unique(y)
---
array([0, 1])

注意我们并不通过可视化数据集来选择核函数,而是直接尝试选择核函数(这里就很不合适,数据明明有30个维度,我们任意选择前两个维度,是没有道理的)

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

详解支持向量机-探索核函数的优势和缺陷【菜菜的sklearn课堂笔记】

我们可以考虑降维,降维成二维

from sklearn.decomposition import PCA

X_dr = PCA(2).fit_transform(X)
plt.scatter(X_dr[:,0],X_dr[:,1],c=y,alpha=0.3)
plt.show()

详解支持向量机-探索核函数的优势和缺陷【菜菜的sklearn课堂笔记】

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 # degree:核函数的次方数,默认是3
              # 之前说过当degree为1的时候用于线性,高次用于非线性
              # 但是之前使用默认值的时候跑了将近十分钟没有跑出poly的结果,放弃了
             ,cache_size=5000
             ).fit(Xtrain,Ytrain)
    # cache_size:表示允许使用多大的内存进行计算,单位MB。默认200
    # 用的内存越多运行越快(蓝屏可能性越大)
    print("The accuracy under kernel %s is %f"%(kernel, clf.score(Xtest,Ytest)))
    print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
# 一般SVM不需要过长的时间,超过五六分钟不出结果基本就可以选择打断了
---
The accuracy under kernel linear is 0.929825
00:00:396585

关于time()

now = time() # 返回一串浮点数,就是时间戳

datetime.datetime.fromtimestamp(now).strftime("%Y-%m-%d,%H:%M:%S:%f")
---
'2022-10-24,10:33:16:370524'

之前说到模型一直停留在线性核函数之后,就没有再打印结果了。这证明,多项式核函数此时此刻要消耗大量的时间,运算非常的缓慢。让我们在循环中去掉多项式核函数,再试试看能否跑出结果

Kernel = ["linear","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).strftime("%M:%S:%f"))
    # 基本秒出,所以证明是多项式核函数阻碍了我们的运行
---
The accuracy under kernel linear is 0.929825
00:00:405324
The accuracy under kernel rbf is 0.596491
00:00:038619
The accuracy under kernel sigmoid is 0.596491
00:00:006005

我们可以有两个发现。首先,乳腺癌数据集是一个线性数据集,线性核函数跑出来的效果很好。rbf和sigmoid两个擅长非线性的数据从效果上来看完全不可用。其次,线性核函数的运行速度远远不如非线性的两个核函数。

关于rbf不是应当适用于所有数据吗,这里别着急,接着往下看

如果数据是线性的,那如果我们把degree参数调整为1,多项式核函数应该也可以得到不错的结果:

Kernel = ["linear","poly","rbf","sigmoid"]
for kernel in Kernel:
    time0 = time()
    clf = SVC(kernel=kernel
             ,gamma="auto"
             ,degree=1
              # 上面我们已经能看出,对于这个数据集,线性核比非线性核效果要好
              # 因此我们可以考虑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).strftime("%M:%S:%f"))
---
The accuracy under kernel linear is 0.929825
00:00:396625
The accuracy under kernel poly is 0.923977
00:00:075855
The accuracy under kernel rbf is 0.596491
00:00:039003
The accuracy under kernel sigmoid is 0.596491
00:00:005001

多项式核函数的运行速度立刻加快了,并且精度也提升到了接近线性核函数的水平。但是,我们之前的实验中,我们了解说,rbf在线性数据上也可以表现得非常好,那在这里,为什么跑出来的结果如此糟糕呢? 其实,这里真正的问题是数据的量纲问题。回忆一下我们如何求解决策边界,如何判断点是否在决策边界的一边?是靠计算”距离“,虽然我们不能说SVM是完全的距离类模型,但是它严重受到数据量纲的影响。让我们来探索一下乳腺癌数据集的量纲:

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
# describe:对数据进行描述性分析
# 接收数组,表示分位数
# 看不同维度的均值和方差,看数据有量纲不统一
# 观察1%和min,99%和max,看数据是否有偏态的问题
---
# 这里简述以下,打印出来太多了
# 部分行均值在0.几,部分行甚至到达了几百将近一千,且这些均值大的数据方差也大,因此一定有量纲不统一的问题
# 观察1%和min,99%和max。有一行99%还是177,到了max就是542,因此数据有偏态问题(偏态也就是非正态)

对于量纲不统一我们可以考虑标准化

from sklearn.preprocessing import StandardScaler

X = StandardScaler().fit_transform(X)
# 标准化,即均值为0,方差为1
data = pd.DataFrame(X)
data.describe([0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.99]).T
---
# 此时数据均值基本接近0,方差接近1

标准化完毕后,再次让SVC在核函数中遍历,此时我们把degree的数值设定为1,观察各个核函数在去量纲后的数据上的表现:

Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
# 由于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).strftime("%M:%S:%f"))
    # 虽然这里看rbf表现没有linear好,但实际上linear没有参数可调,rbf可以调参,因此我们继续对rbf探索
---
The accuracy under kernel linear is 0.976608
00:00:007997
The accuracy under kernel poly is 0.964912
00:00:006007
The accuracy under kernel rbf is 0.970760
00:00:006997
The accuracy under kernel sigmoid is 0.953216
00:00:004000

量纲统一之后,可以观察到,所有核函数的运算时间都大大地减少了。其次,rbf表现出了非常优秀的结果。经过我们的探索,我们可以得到的结论是:

  1. 线性核,尤其是多项式核函数在高次项时计算非常缓慢
  2. rbf和多项式核函数都不擅长处理量纲不统一的数据集

这两个缺点都可以由数据无量纲化来解决。因此,SVM执行之前,非常推荐先进行数据的无量纲化! 虽然线性核函数的效果是最好的,但它是没有核函数相关参数可以调整的,rbf和多项式却还有着可以调整的相关参数,接下来我们就来看看这些参数。