Como calcular efetivamente um kernel gaussiano em numpy [closed]

12

Eu tenho uma matriz numpy com m colunas en linhas, as colunas sendo dimensões e os pontos de dados de linhas.

Agora preciso calcular os valores do kernel para cada combinação de pontos de dados.

Para uma linear do kernel K(xi,xj)=xi,xj eu posso simplesmente fazerdot(X,X.T)

K(xi,xj)=expxixj22s2

Peter Smit
fonte
1
Bem, se você não se importa muito com um fator de dois aumentos nos cálculos, sempre pode fazer S=XXT e depois K(xi,xj)=exp((Sii+Sjj2Sij)/s2) onde, é claro, Sij é o (i,j) th elemento de S . Provavelmente, esse também não é o mais numericamente estável.
cardeal
2
(Anos depois) para grandes matrizes esparsas, consulte sklearn.metrics.pairwise.pairwise_distances.html no scikit -learn.
Denis #

Respostas:

26

Penso que o principal problema é obter as distâncias aos pares de forma eficiente. Depois de ter que o resto é elemento sábio.

Para fazer isso, você provavelmente deseja usar o scipy. A função scipy.spatial.distance.pdistfaz o que você precisa e scipy.spatial.distance.squareformpossivelmente facilitará sua vida.

Então, se você quiser a matriz do kernel, faça

from scipy.spatial.distance import pdist, squareform
  # this is an NxD matrix, where N is number of items and D its dimensionalites
X = loaddata() 
pairwise_dists = squareform(pdist(X, 'euclidean'))
K = scip.exp(-pairwise_dists ** 2 / s ** 2)

A documentação pode ser encontrada aqui

bayerj
fonte
3
Parece-me que a resposta de bayerj requer algumas pequenas modificações para se adequar a fórmula, em necessidades else caso de alguém que:K = scipy.exp(-pairwise_dists**2 / s**2)
Chloe
Se alguém está curioso, o algoritmo usado por pdisté muito simples: é apenas um loop implementado em C que calcula diretamente as distâncias da maneira óbvia , o loop sendo feito aqui ; nenhuma vetorização sofisticada ou qualquer coisa além do que o compilador pode realizar automaticamente.
Dougal 25/03
11

Como um pequeno adendo à resposta de bayerj, a pdistfunção de scipy pode calcular diretamente as normas euclidianas ao quadrado, chamando-a de pdist(X, 'sqeuclidean'). O código completo pode ser escrito de forma mais eficiente como

from scipy.spatial.distance import pdist, squareform
  # this is an NxD matrix, where N is number of items and D its dimensionalites
X = loaddata() 
pairwise_sq_dists = squareform(pdist(X, 'sqeuclidean'))
K = scip.exp(-pairwise_sq_dists / s**2)
tenedor
fonte
1
Ou simplesmente o pairwise_sq_dists = cdist(X, X, 'sqeuclidean')que dá o mesmo.
user1721713
5

Você também pode escrever um formulário quadrado à mão:

import numpy as np
def vectorized_RBF_kernel(X, sigma):
    # % This is equivalent to computing the kernel on every pair of examples
    X2 = np.sum(np.multiply(X, X), 1) # sum colums of the matrix
    K0 = X2 + X2.T - 2 * X * X.T
    K = np.power(np.exp(-1.0 / sigma**2), K0)
    return K

PS, mas isso funciona 30% mais lento

spetz911
fonte
Esse, que é o método sugerido pelo cardeal nos comentários, pode ser acelerado usando operações locais. É como o scikit-learn o faz , com uma einsumchamada para o seu X2.
Dougal 25/03
4
def my_kernel(X,Y):
    K = np.zeros((X.shape[0],Y.shape[0]))
    for i,x in enumerate(X):
        for j,y in enumerate(Y):
            K[i,j] = np.exp(-1*np.linalg.norm(x-y)**2)
    return K

clf=SVR(kernel=my_kernel)

que é igual a

clf=SVR(kernel="rbf",gamma=1)

Você pode calcular efetivamente o RBF a partir do código acima, observe que o valor gama é 1, pois é uma constante as s solicitadas também são a mesma constante.

John
fonte
Bem vindo ao nosso site! Temos uma ênfase ligeiramente diferente no Stack Overflow, pois geralmente temos menos foco no código e mais nas idéias subjacentes; portanto, pode valer a pena anotar seu código ou fornecer uma breve idéia de quais são as idéias principais, como algumas das outras respostas foram feitas. Isso ajudaria a explicar como sua resposta difere das outras.
Silverfish 24/03
Isso será muito mais lento que as outras respostas, porque usa loops Python em vez de vetorização.
Dougal 25/03
-1

Eu acho que isso ajudará:

def GaussianKernel(v1, v2, sigma):
    return exp(-norm(v1-v2, 2)**2/(2.*sigma**2))
Núcleo
fonte
3
Bem-vindo ao site @Kernel. Você pode exibir matemático colocando a expressão entre $ signs e usando LateX como sintaxe. E você pode exibir o código (com destaque da sintaxe) recuando as linhas em 4 espaços. Consulte a ajuda da edição de descontos para diretrizes de formatação e o FAQ para orientações mais gerais.
Antoine Vernet
1
Isso não reflete apenas o que está em questão?
whuber