Come calcolare efficacemente un kernel gaussiano in numpy [chiuso]


12

Ho un array intorpidito con m colonne e n righe, le colonne sono dimensioni e i punti dati delle righe.

Ora ho bisogno di calcolare i valori del kernel per ogni combinazione di punti dati.

Per un kernel lineare posso semplicemente fareK(xi,xj)=xi,xjdot(X,X.T)

Come posso calcolare efficacemente tutti i valori per il kernel gaussiano K(xi,xj)=expxixj22s2 con una data s ?


1
Bene, se non ti importa troppo di un fattore di aumento dei due calcoli, puoi sempre fare semplicemente e poi dove, ovviamente, è il esimo elemento di . Tuttavia, questo probabilmente non è il più numericamente stabile. S=XXTK(xi,xj)=exp((Sii+Sjj2Sij)/s2)Sij(i,j)S
cardinale il

2
(Anni dopo) per array sparsi di grandi dimensioni, vedere sklearn.metrics.pairwise.pairwise_distances.html in scikit-learn.
denis,

Risposte:


26

Penso che il problema principale sia quello di ottenere le distanze a coppie in modo efficiente. Una volta che hai il resto è saggio elemento.

Per fare questo, probabilmente vuoi usare scipy. La funzione scipy.spatial.distance.pdistfa quello che ti serve e scipy.spatial.distance.squareformforse ti semplifica la vita.

Quindi, se vuoi la matrice del kernel, lo fai

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)

La documentazione è disponibile qui


3
Mi sembra che la risposta di bayerj richieda alcune piccole modifiche per adattarsi alla formula, nel caso in cui qualcun altro ne abbia bisogno:K = scipy.exp(-pairwise_dists**2 / s**2)
chloe,

Se qualcuno è curioso, l'algoritmo utilizzato da pdistè molto semplice: è solo un loop implementato in C che calcola direttamente le distanze in modo ovvio , il looping viene eseguito qui ; nessuna vettorializzazione di fantasia o qualsiasi cosa al di là di qualunque cosa il compilatore possa realizzare automaticamente.
Dougal,

11

Come piccolo addendum alla risposta di bayerj, la pdistfunzione di Scipy può calcolare direttamente le norme euclidee quadrate chiamandolo come pdist(X, 'sqeuclidean'). Il codice completo può quindi essere scritto in modo più efficiente come

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)

1
O semplicemente pairwise_sq_dists = cdist(X, X, 'sqeuclidean')che dà lo stesso.
user1721713

5

Puoi anche scrivere a mano forma quadrata:

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, ma funziona più lentamente del 30%


Questo, che è il metodo suggerito dal cardinale nei commenti, potrebbe essere accelerato un po 'usando le operazioni sul posto. È come scikit-learn lo fa , con una einsumchiamata per il tuo X2.
Dougal,

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)

che è uguale a

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

È possibile calcolare efficacemente l'RBF dalla nota di codice sopra riportata che il valore gamma è 1, poiché è una costante la s richiesta è anche la stessa costante.


Benvenuti nel nostro sito! Abbiamo un'enfasi leggermente diversa su Stack Overflow, in quanto generalmente ci concentriamo meno sul codice e più sulle idee sottostanti, quindi potrebbe valere la pena annotare il codice o dare una breve idea di quali siano le idee chiave, come alcuni dei altre risposte hanno fatto. Ciò contribuirebbe a spiegare in che modo la tua risposta differisce dalle altre.
Pesce d'argento

Questo sarà molto più lento rispetto alle altre risposte perché utilizza i loop Python anziché la vettorializzazione.
Dougal,

-1

Penso che questo aiuterà:

def GaussianKernel(v1, v2, sigma):
    return exp(-norm(v1-v2, 2)**2/(2.*sigma**2))

3
Benvenuti nel sito @Kernel. È possibile visualizzare la matematica inserendo l'espressione tra $ segni e usando LateX come sintassi. E puoi visualizzare il codice (con l'evidenziazione della sintassi) indentando le linee di 4 spazi. Vedere l'editing Markdown aiuto per la formattazione linee guida, e la faq per quelle più generali.
Antoine Vernet,

1
Non fa eco solo ciò che è nella domanda?
whuber
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.