14 votos

Cómo calcular eficazmente un núcleo gaussiano en numpy

Tengo un array numpy con m columnas y n filas, siendo las columnas dimensiones y las filas puntos de datos.

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

Para un núcleo lineal $K(\mathbf{x}_i,\mathbf{x}_j) = \langle \mathbf{x}_i,\mathbf{x}_j \rangle$ Simplemente puedo hacer dot(X,X.T)

¿Cómo puedo calcular eficazmente todos los valores del núcleo gaussiano? $K(\mathbf{x}_i,\mathbf{x}_j) = \exp{-\frac{\|\mathbf{x}_i-\mathbf{x}_j\|_2^2}{s^2}}$ con un determinado s ?

1 votos

Bueno, si no te importa demasiado el aumento de un factor de dos en los cálculos, siempre puedes hacer $\newcommand{\m}{\mathbf} \m S = \m X \m X^T$ y luego $K(\m x_i, \m x_j ) = \exp( - (S_{ii} + S_{jj} - 2 S_{ij})/s^2 )$ donde, por supuesto, $S_{ij}$ es el $(i,j)$ elemento de $\m S$ . Esto es probablemente no tampoco es el más estable numéricamente.

2 votos

(Años más tarde) para grandes matrices dispersas, véase sklearn.metrics.pairwise.pairwise_distances.html en scikit-learn .

29voto

karatchov Puntos 230

Creo que el principal problema es obtener las distancias por pares de forma eficiente. Una vez que se tiene eso, el resto es cuestión de elementos.

Para ello, probablemente quieras utilizar scipy. La función scipy.spatial.distance.pdist hace lo que usted necesita, y scipy.spatial.distance.squareform posiblemente facilitará su vida.

Así que si quieres la matriz del núcleo haces

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í .

3 votos

Me parece que la respuesta de bayerj requiere algunas pequeñas modificaciones para ajustarse a la fórmula, por si alguien más lo necesita : K = scipy.exp(-pairwise_dists**2 / s**2)

0 votos

Si alguien tiene curiosidad, el algoritmo utilizado por pdist es muy simple: es sólo un bucle implementado en C que calcula directamente las distancias en la manera obvia , el bucle que se hace aquí no hay vectorización de lujo o cualquier cosa más allá de lo que el compilador puede lograr automáticamente.

12voto

Crystal Puntos 26

Como una pequeña adición a la respuesta de bayerj, scipy's pdist puede calcular directamente las normas euclidianas al cuadrado llamándola como pdist(X, 'sqeuclidean') . El código completo puede entonces escribirse de forma 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)

1 votos

O simplemente pairwise_sq_dists = cdist(X, X, 'sqeuclidean') que da lo mismo.

6voto

overstood Puntos 887

También puede escribir la 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

PS pero esto funciona un 30% más lento

0 votos

Este, que es el método sugerido por cardinal en los comentarios, podría acelerarse un poco utilizando operaciones inplace. Es cómo lo hace scikit-learn con un einsum llame a para su X2 .

0 votos

Usted escribió: K0 = X2 + X2.T - 2 * X * X.T - ¿cómo puede funcionar con X y X.T teniendo diferentes dimensiones?

5voto

Yuan Gao Puntos 387
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)

Efectivamente puedes calcular la RBF a partir del código anterior fíjate que el valor de la gamma es 1, como es una constante la s que pediste también es la misma constante.

0 votos

¡Bienvenido a nuestra página web! Tenemos un énfasis ligeramente diferente al de Stack Overflow, en el sentido de 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 tu código o dar una breve idea de cuáles son las ideas clave del mismo, como han hecho algunas de las otras respuestas. Eso ayudaría a explicar en qué se diferencia tu respuesta de las demás.

0 votos

Esto será mucho más lento que las otras respuestas porque utiliza bucles de Python en lugar de vectorización.

-1voto

Shu Puntos 41

Creo que esto ayudará:

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

3 votos

Bienvenido al sitio @Kernel. Puedes mostrar las matemáticas poniendo la expresión entre signos $ y usando una sintaxis similar a la de LateX. Y puedes mostrar el código (con resaltado de sintaxis) sangrado las líneas por 4 espacios. Ver la edición de markdown ayuda para las directrices de formato, y el faq para otras más generales.

1 votos

¿No se hace eco de lo que dice la pregunta?

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X