Cómo calcular un núcleo gaussiano efectivamente en numpy [cerrado]

12

Tengo una matriz numpy con m columnas yn filas, siendo las columnas dimensiones y los puntos de datos de las filas.

Ahora necesito calcular los valores del núcleo para cada combinación de puntos de datos.

Para una lineal del núcleo K(xi,xj)=xi,xj puedo simplemente hacerdot(X,X.T)

K(xi,xj)=expxixj22s2

Peter Smit
fuente
1
Bueno, si no le importa demasiado el aumento de dos factores en los cálculos, siempre puede hacer S=XXT y luego K(xi,xj)=exp((Sii+Sjj2Sij)/s2) donde, por supuesto, Sij es el (i,j) ésimo elemento de S . Sin embargo, esta probablemente tampoco sea la más estable numéricamente.
cardenal
2
(Años más tarde) para matrices dispersas grandes, consulte sklearn.metrics.pairwise.pairwise_distances.html en scikit-learn.
denis

Respuestas:

26

Creo que el problema principal es obtener las distancias por pares de manera eficiente. Una vez que tienes eso, el resto es elemento sabio.

Para hacer esto, probablemente quieras usar scipy. La función scipy.spatial.distance.pdisthace lo que necesita y scipy.spatial.distance.squareformposiblemente le facilitará la vida.

Entonces, si quieres la matriz del núcleo, haz

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 documentación se puede encontrar aquí

bayerj
fuente
3
Me parece que la respuesta de bayerj requiere algunas pequeñas modificaciones para ajustarse a la fórmula, en caso de que alguien más lo necesite:K = scipy.exp(-pairwise_dists**2 / s**2)
chloe
Si alguien tiene curiosidad, el algoritmo utilizado por pdistes muy simple: es solo un bucle implementado en C que calcula directamente las distancias de la manera obvia , el bucle se realiza aquí ; no hay vectorización sofisticada ni nada más allá de lo que el compilador pueda lograr automáticamente.
Dougal
11

Como una pequeña adición a la respuesta de bayerj, la pdistfunción de scipy puede calcular directamente las normas euclidianas cuadráticas llamándolas como pdist(X, 'sqeuclidean'). El código completo se puede escribir de manera más 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
fuente
1
O simplemente lo pairwise_sq_dists = cdist(X, X, 'sqeuclidean')que da lo mismo.
user1721713
5

También puedes escribir forma cuadrada a mano:

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

PD pero esto funciona un 30% más lento

spetz911
fuente
Este, que es el método sugerido por cardinal en los comentarios, podría acelerarse un poco utilizando operaciones in situ. Es cómo lo hace scikit-learn , con una einsumllamada para su 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)

que es igual a

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

Puede calcular efectivamente el RBF a partir de la nota del código anterior que indica que el valor gamma es 1, ya que es una constante, la que solicitó también es la misma constante.

John
fuente
¡Bienvenido a nuestro sitio! Tenemos un énfasis ligeramente diferente en el desbordamiento de pila, ya que generalmente nos centramos menos en el código y más en las ideas subyacentes, por lo que podría valer la pena anotar su código o dar una breve idea de cuáles son las ideas clave, ya que algunas de las otras respuestas lo han hecho. Eso ayudaría a explicar cómo su respuesta difiere de las demás.
Silverfish
Esto será mucho más lento que las otras respuestas porque usa bucles Python en lugar de vectorización.
Dougal
-1

Creo que esto ayudará:

def GaussianKernel(v1, v2, sigma):
    return exp(-norm(v1-v2, 2)**2/(2.*sigma**2))
Núcleo
fuente
3
Bienvenido al sitio @Kernel. Puede mostrar matemática colocando la expresión entre signos $ y utilizando la sintaxis similar a LateX. Y puede mostrar el código (con resaltado de sintaxis) al sangrar las líneas por 4 espacios. Consulte la ayuda de edición de rebajas para conocer las pautas de formato y las preguntas frecuentes para conocer las más generales.
Antoine Vernet
1
¿Esto no solo hace eco de lo que hay en la pregunta?
whuber