Comment calculer un noyau gaussien efficacement en numpy [fermé]

12

J'ai un tableau numpy avec m colonnes et n lignes, les colonnes étant des dimensions et les points de données des lignes.

Je dois maintenant calculer les valeurs du noyau pour chaque combinaison de points de données.

Pour un noyau linéaire K(xi,xj)=xi,xj je peux simplement fairedot(X,X.T)

K(xi,xj)=expxixj22s2

Peter Smit
la source
1
Eh bien, si vous ne vous souciez pas trop d'un facteur de deux augmentations de calculs, vous pouvez toujours faire S=XXT puis K(xi,xj)=exp((Sii+Sjj2Sij)/s2) où, bien sûr, Sij est le (i,j) ième élément de S . Ce n'est probablement pas non plus la plus stable numériquement.
Cardinal
2
(Des années plus tard) pour les grands tableaux clairsemés, voir sklearn.metrics.pairwise.pairwise_distances.html dans scikit-learn.
denis

Réponses:

26

Je pense que le principal problème est d'obtenir efficacement les distances par paires. Une fois que vous avez que le reste est élémentaire.

Pour ce faire, vous voudrez probablement utiliser scipy. La fonction scipy.spatial.distance.pdistfait ce dont vous avez besoin et scipy.spatial.distance.squareformpeut vous faciliter la vie.

Donc, si vous voulez la matrice du noyau que vous faites

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 documentation peut être trouvée ici

bayerj
la source
3
Il me semble que la réponse de bayerj nécessite quelques petites modifications pour s'adapter à la formule, au cas où quelqu'un d'autre en aurait besoin:K = scipy.exp(-pairwise_dists**2 / s**2)
chloe
Si quelqu'un est curieux, l'algorithme utilisé par pdistest très simple: c'est juste une boucle implémentée en C qui calcule directement les distances de manière évidente , le bouclage se faisant ici ; aucune vectorisation sophistiquée ou quoi que ce soit au-delà de ce que le compilateur peut accomplir automatiquement.
Dougal
11

Comme un petit addendum à la réponse de bayerj, la pdistfonction de scipy peut directement calculer les normes euclidiennes au carré en l'appelant comme pdist(X, 'sqeuclidean'). Le code complet peut ensuite être écrit plus efficacement

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
la source
1
Ou tout simplement pairwise_sq_dists = cdist(X, X, 'sqeuclidean')ce qui donne la même chose.
user1721713
5

Vous pouvez également écrire la forme carrée à la main:

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 mais cela fonctionne 30% plus lentement

spetz911
la source
Ceci, qui est la méthode suggérée par le cardinal dans les commentaires, pourrait être accéléré un peu en utilisant des opérations in situ. C'est comme ça que scikit-learn le fait , avec un einsumappel pour vous 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)

ce qui est égal à

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

Vous pouvez calculer efficacement le RBF à partir du code ci-dessus, notez que la valeur gamma est 1, car il s'agit d'une constante, le s que vous avez demandé est également la même constante.

John
la source
Bienvenue sur notre site! Nous mettons un accent légèrement différent sur Stack Overflow, dans la mesure où nous nous concentrons généralement moins sur le code et davantage sur les idées sous-jacentes, il peut donc être utile d'annoter votre code ou de donner une brève idée de ses idées clés, comme certaines des d'autres réponses ont fait. Cela aiderait à expliquer en quoi votre réponse diffère des autres.
Silverfish
Ce sera beaucoup plus lent que les autres réponses car il utilise des boucles Python plutôt que la vectorisation.
Dougal
-1

Je pense que ça aidera:

def GaussianKernel(v1, v2, sigma):
    return exp(-norm(v1-v2, 2)**2/(2.*sigma**2))
Noyau
la source
3
Bienvenue sur le site @Kernel. Vous pouvez afficher des mathématiques en plaçant l'expression entre les signes $ et en utilisant une syntaxe similaire à LateX. Et vous pouvez afficher le code (avec mise en évidence de la syntaxe) en indentant les lignes de 4 espaces. Voir l' aide à l' édition de démarques pour les directives de formatage et la FAQ pour les plus générales.
Antoine Vernet
1
Cela ne fait-il pas simplement écho à la question?
whuber