RBF-Kernel-Param-Select(Python)

RBF核函數參數自動挑選法Python實作

$x,z$ 是原空间两个点的坐标,$\gamma$ 是参数:

$$ \kappa (x,z,\gamma) = \operatorname{exp} (-\gamma | x-z |^2) $$

rbf核的特殊性质:

  1. 在特征空间中,自己和自己的内积为1(范数),所以$\phi(x)$ 是在一个超球面上

  2. 两向量夹角等于内积:

    $$ cos(\theta) = \frac{\kappa(x,z,\gamma)}{\sqrt{\kappa(x,x,\gamma)} \sqrt{\kappa(z,z,\gamma)}} = \frac{\kappa(x,z,\gamma)}{1} = \kappa(x,z,\gamma) $$

  3. 当 $x \neq z$ 时:

    1. $\gamma \rightarrow 0 \ \Rightarrow cos(\theta)=\kappa(x,z,\gamma) \rightarrow 1 \ \Rightarrow \theta \rightarrow 0^\circ \ \Rightarrow \phi(x)与\phi(z)越相似$
    2. $\gamma \rightarrow \infin \ \Rightarrow cos(\theta)=\kappa(x,z,\gamma) \rightarrow 1 \ \Rightarrow \theta \rightarrow 90^\circ \ \Rightarrow \phi(x)与\phi(z)越不相似$

调参 varying the parameter

在原空间中的距离越大向量,当$\gamma$变大时,角度拉大的速度越快。 当$\gamma$ 很小的时候,各向量夹角很小,点很集中,不好分类; 当$\gamma$很大时,各向量互相垂直,每个点都变一类;所以要取不大不小的$\gamma$,同类向量夹角小,不同类向量的夹角已经拉到很开。

目标: 调整gamma,同类的内积越接近1越好,不同类内积越接近0越好

code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 生成样本数据及其标签
from sklearn import datasets
Data,labels = datasets.make_circles(n_samples=400, noise=0.1, factor=0.1) #Data是400x2的矩阵,labels是1x400的列表

# 画三维散点图
import plotly.express as px 
fig = px.scatter(x=Data[:,0], y=Data[:,1], color=labels)
fig.update_layout(yaxis=dict(scalenchor='x')) #y轴的刻度与x轴一样

from sklearn.metrics.pairwise import rbf_kernel
import numpy as np

def AutoRBF(gv, Data, labels):  #计算gv对应的指标J

    # 计算Kernel matrix
    K = rbf_kernel(Data, gamma=gv) #400x400

    # 组内:对角线上的block
    n=[]    #统计各类样本的个数
    w=0     #同组内积矩阵和累计
    for i in range(0, max(labels)+1):    #遍历每一类
        idx = labels==i  #把labels列表中等于某类的元素变成:True/False,形成列表idx
        
        #Within the Kernel matrix,取出同类矩阵
        KW = K[:,idx]   #先取列号等于idx列表中元素为True的index的列
        KW = KW[idx,:]  #再取行号=idx列表中True元素的index的行
        
        n.append(sum(idx))  #统计各类元素个数,这里是[200,200]

        w = w + np.sum(KW)  #第一类内积矩阵求和加上第二类内积矩阵求和

    # 组间(不同类样本内积的block)
    b = np.sum(K) - w       #全部元素之和减去同类内积矩阵之和w就是不同类内积矩阵之和b

    nw = sum(np.power(n,2)) #每类200个,共200x200 + 200x200=4万个值
    nb = sum(n)**2 - nw

    w=w/nw  #取平均
    b=b/nb  #取平均

    J=(1-w)+b   #w越接近1越好,b越接近0越好
    
    return J

# 第一种方法找最佳gamma:grid
grid = np.linspace(0,50,1000) #0-50切成1000份
J=[]    #存储各gv对应的J值
for gv in grid:
    J.append(AutoRBF(gv,Data,labels))
px.line(x=grid, y=J)    #把J画成曲线


# 第二种方法:用 minimize 函数找最低点(比grid快很多)
from scipy.optimize import minimize

gv0=1/Data.shape[1] #初始值:1/维度数
sol = minimize(AutoRBF,gv0,args=(Data,labels)) #solution是使目标函数AutoRBF最小的gv(初值为gv0),args放函数的其余参数
bestgv = sol.x   #最低点的x坐标,最好的gv

# 用kpca降维后,能不能分得好
from sklearn.decomposition import KernelPCA
myKPCA = KernelPCA(n_components=2, kernel='rbf', gamma=bestgv)
myKPCA.fit(Data)    #训练
reduced_Data = myKPCA.transform(Data)   #投影到低维
fig = px.scatter(x=reduced_Data[:,0], y=reduced_Data[:,1],color=labels)
fig.update_layout(yaxis=dict(scaleanchor='x'))