如何使用自定义的SVM核?

19

我想在Python中自己实现高斯核函数,只是为了练习。我正在使用:sklearn.svm.SVC(kernel=my_kernel),但我真的不明白发生了什么。

我期望函数my_kernel将以矩阵X的列作为参数调用,但我得到的是将XX作为参数调用。看示例也没有更清楚的解释。

我错过了什么?

这是我的代码:

'''
Created on 15 Nov 2014

@author: Luigi
'''
import scipy.io
import numpy as np
from sklearn import svm
import matplotlib.pyplot as plt

def svm_class(fileName):

    data = scipy.io.loadmat(fileName)
    X = data['X']
    y = data['y']

    f = svm.SVC(kernel = 'rbf', gamma=50, C=1.0)
    f.fit(X,y.flatten())
    plotData(np.hstack((X,y)), X, f)

    return

def plotData(arr, X, f):

    ax = plt.subplot(111)

    ax.scatter(arr[arr[:,2]==0][:,0], arr[arr[:,2]==0][:,1], c='r', marker='o', label='Zero')
    ax.scatter(arr[arr[:,2]==1][:,0], arr[arr[:,2]==1][:,1], c='g', marker='+', label='One')

    h = .02  # step size in the mesh
    # create a mesh to plot in
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))


    # Plot the decision boundary. For that, we will assign a color to each
    # point in the mesh [x_min, m_max]x[y_min, y_max].
    Z = f.predict(np.c_[xx.ravel(), yy.ravel()])

    # Put the result into a color plot
    Z = Z.reshape(xx.shape)
    plt.contour(xx, yy, Z)



    plt.xlim(np.min(arr[:,0]), np.max(arr[:,0]))
    plt.ylim(np.min(arr[:,1]), np.max(arr[:,1]))
    plt.show()
    return


def gaussian_kernel(x1,x2):
    sigma = 0.5
    return np.exp(-np.sum((x1-x2)**2)/(2*sigma**2))

if __name__ == '__main__':

    fileName = 'ex6data2.mat'
    svm_class(fileName)
2个回答

23
阅读上面的答案以及其他一些问题和网站(1, 2, 3, 4, 5)后,我整理了一个高斯核函数在svm.SVC()中的应用。
使用kernel=precomputed调用svm.SVC()

然后计算一个格拉姆矩阵,也称为核矩阵(通常缩写为K)。

然后将该格拉姆矩阵用作第一个参数(即 X)传递给 svm.SVC().fit()

我从 以下代码 开始:

C=0.1
model = svmTrain(X, y, C, "gaussian")

svmTrain()中调用sklearn.svm.SVC(),然后调用sklearn.svm.SVC().fit()

from sklearn import svm

if kernelFunction == "gaussian":
    clf = svm.SVC(C = C, kernel="precomputed")
    return clf.fit(gaussianKernelGramMatrix(X,X), y)

计算Gram矩阵——作为sklearn.svm.SVC().fit()的一个参数——是在gaussianKernelGramMatrix()中完成的:

import numpy as np

def gaussianKernelGramMatrix(X1, X2, K_function=gaussianKernel):
    """(Pre)calculates Gram Matrix K"""

    gram_matrix = np.zeros((X1.shape[0], X2.shape[0]))
    for i, x1 in enumerate(X1):
        for j, x2 in enumerate(X2):
            gram_matrix[i, j] = K_function(x1, x2)
    return gram_matrix

该函数使用 gaussianKernel() 来获取 x1 和 x2 之间的径向基函数核(一种基于以 x1 为中心,标准差为 0.1 的高斯分布相似度度量):

def gaussianKernel(x1, x2, sigma=0.1):

    # Ensure that x1 and x2 are column vectors
    x1 = x1.flatten()
    x2 = x2.flatten()

    sim = np.exp(- np.sum( np.power((x1 - x2),2) ) / float( 2*(sigma**2) ) )

    return sim

然后,一旦使用这个自定义内核训练了模型,我们就可以使用"测试数据和训练数据之间的[自定义]内核"进行预测:

predictions = model.predict( gaussianKernelGramMatrix(Xval, X) )

简而言之,要使用自定义SVM高斯核,您可以使用以下代码片段:
import numpy as np
from sklearn import svm

def gaussianKernelGramMatrixFull(X1, X2, sigma=0.1):
    """(Pre)calculates Gram Matrix K"""

    gram_matrix = np.zeros((X1.shape[0], X2.shape[0]))
    for i, x1 in enumerate(X1):
        for j, x2 in enumerate(X2):
            x1 = x1.flatten()
            x2 = x2.flatten()
            gram_matrix[i, j] = np.exp(- np.sum( np.power((x1 - x2),2) ) / float( 2*(sigma**2) ) )
    return gram_matrix

X=...
y=...
Xval=...

C=0.1
clf = svm.SVC(C = C, kernel="precomputed")
model = clf.fit( gaussianKernelGramMatrixFull(X,X), y )

p = model.predict( gaussianKernelGramMatrixFull(Xval, X) )

在这种情况下,你的“Xval”是什么?那是用来运行预测的训练集吗? 另外,由于“gaussianKernelGramMatrixFull”的输入必须具有相同的维度,您是否需要手动调整X的大小以正确生成Gram矩阵? - Vinit Nayak
@VinitNayak 不完全正确 - Xval 是我们想要预测的值。我们使用已知标签 y 在训练集 x 上进行训练,但我们不知道 Xval 的标签是什么,因此我们需要对它们进行预测。 - arturomp
@VinitNayak XXval的大小不需要相同,它们只需要具有相同数量的列(在m乘以n矩阵中的n),就像您期望训练数据和要预测的数据一样(即它们不应该具有相同数量的样本,但应该具有相同数量的特征)。 - arturomp
我的 X 是一个 622x9 的矩阵,y 是一个 622x1 的矩阵,Xval(即我的交叉验证集)是 266x9。当我运行上述代码时,在预测时出现以下错误:`svm/base.py",第455行,validate_for_predict (X.shape[1], self.shape_fit[0]))ValueError: X.shape[1] = 9 应该等于 622,即训练时的样本数` - Vinit Nayak
你的X中有622个样本和9个特征?那么,你的测试数据点也有266个,每个点也有9个特征。你可能只需要转置数组。你可以在numpy中这样做:my_array.T - arturomp

10
出于效率的考虑,SVC 假定您的核函数是一个接受两个样本矩阵 XY 的函数(仅在训练期间使用两个相同的矩阵),您应该返回一个矩阵 G,其中:
G_ij = K(X_i, Y_j)

“K”是您的“点级”核函数。因此,要么实现一个在这种通用方式下起作用的高斯核函数,要么添加一个“代理”函数,如下所示:
def proxy_kernel(X,Y,K):
    gram_matrix = np.zeros((X.shape[0], Y.shape[0]))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            gram_matrix[i, j] = K(x, y)
    return gram_matrix

并像这样使用它:
from functools import partial
correct_gaussian_kernel = partial(proxy_kernel, K=gaussian_kernel)

不好意思,我不理解,可能是因为我在机器学习方面非常初学。难道不应该返回一个n=n_rows(X)的方阵,其中元素是X[:,0]和X[:,1]之间的所有组合吗? - Luigi Tiburzi
就像我说的那样,它不会总是被调用时带有两个X作为参数,所以不行。此外,你的数据是多维的,因此X[:,0]只是每个向量的第一维,与此无关。 - lejlot
我应该返回什么? - Luigi Tiburzi
一个格拉姆矩阵,就像答案中所写的那样。矩阵的第i行和第j列由第一个数组中第i个向量和第二个数组中第j个向量之间的核值组成,因此kernel(X,Y)_{ij} = K(X_i, Y_j) - lejlot
@lejlot 我的问题可能直接相关,因为我正在使用自计算内核。你能看一下吗?https://stackoverflow.com/questions/47564504/in-self-compute-kernelx-raise-valueerrorx-shape0-should-be-equal-to-x-sha - Mona Jalal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接