提高计算内核矩阵的性能

kse8i1jr  于 2021-08-20  发布在  Java
关注(0)|答案(1)|浏览(458)

我有以下代码:

import numpy as np
from sklearn import svm
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score
from functools import partial
import pandas as pd

def tanimotoKernel(xs, ys):
    a = 0
    b = 0
    for x, y in zip(xs, ys):
        a += min(x, y)
        b += max(x, y)
    return a / b

# gammaExp = 1/(np.exp(gamma) - 1), calculated outside the kernel

def tanimotoLambdaKernel(xs,ys, gamma, gammaExp):
    return np.exp(gamma * tanimotoKernel(xs,ys) - 1) * gammaExp

class GramBuilder:
    def __init__(self, Kernel):
        self._Kernel = Kernel
    def generateMatrixBuilder(self, X1, X2):
        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] = self._Kernel(x1, x2)
        return gram_matrix

gammaList = [0.0001, 0.001, 0.01, 0.1, 1, 10, 100]
CList = [0.001, 0.01, 0.1, 1, 10, 100]

X, y = datasets.load_digits(return_X_y=True)
x_train, x_test, y_train, y_test = train_test_split(X, y)

svc_list = [
    (svm.SVC(
        kernel=GramBuilder(
            partial(tanimotoLambdaKernel, gamma = x, gammaExp = 1/(np.exp(x) - 1)))
        .generateMatrixBuilder), 
     x)
    for x in gammaList
]

gammas   = []
Cs       = []
accuracy = []
for svc, gamma in svc_list:
    print("Training gamma ", gamma)
    clf = GridSearchCV(svc, {'C' : CList}, verbose = 1, n_jobs = -1)
    clf.fit(x_train, y_train)
    gammas.append(gamma)
    Cs.append(clf.best_params_['C'])
    accuracy.append(clf.best_score_)

对于这个玩具数据集,我必须等待大约50分钟来执行循环中的所有交叉验证。
我做的第一个改进是计算 gammaExp 在函数之外,我可以保存数百万个指数。而且我乘法比除法快,所以我计算了指数负1的倒数,以节省更多的时间。
有了这些修改,我在训练模型的时候提高了很多,但是我需要它更快,所以我会很感激任何想法。谢谢

3b6akqbq

3b6akqbq1#

您可以使用numpy来加速最小/最大操作。然后,您可以使用numba的jit通过内联调用进一步加快代码的速度。

import numba as nb

@nb.njit
def tanimotoKernel(xs, ys):
    a = np.minimum(xs, ys).sum()
    b = np.maximum(xs, ys).sum()
    return a / b

@nb.njit
def tanimotoLambdaKernel(xs,ys, gamma, gammaExp):
    return np.exp(gamma * tanimotoKernel(xs,ys) - 1) * gammaExp

# [...]

上面的代码应该是正确的,并且在我的机器上速度要快20倍以上。实际上只花了几分钟就完成了。
我认为,通过删除部分调用并使用numba进行 GramBuilder 类(查看numba文档中的jit类,部分函数可能不受支持,但您可以在类中存储值,并自己完成部分工作)。此外,请注意,许多操作似乎在内核中执行多次。我可能会计算它们一次(用同一个x2多次调用内核,并一次又一次地重新计算max)。

相关问题