2011-11-16 39 views
6

Tengo dos vectores de valores y un vector de pesos, y necesito calcular la similitud del coseno. Por razones complicadas, solo puedo calcular el coseno por un par a la vez. Pero tengo que hacerlo muchos millones de veces.más eficiente Cálculo coseno R

cosine_calc <- function(a,b,wts) { 
    #scale both vectors by the weights, then compute the cosine of the scaled vectors 
    a = a*wts 
    b = b*wts 
    (a %*% b)/(sqrt(a%*%a)*sqrt(b%*%b)) 
} 

funciona, pero quiero intentar sacar mejor rendimiento de él.

datos Ejemplo:

a = c(-1.2092420, -0.7053822, 1.4364633, 1.3612304, -0.3029147, 1.0319704, 0.6707610, -2.2128987, -0.9839970, -0.4302205) 
b = c(-0.69042619, 0.05811749, -0.17836802, 0.15699691, 0.78575477, 0.27925779, -0.08552864, -1.31031219, -1.92756861, -1.36350112) 
w = c(0.26333839, 0.12803180, 0.62396023, 0.37393705, 0.13539926, 0.09199102, 0.37347546, 1.36790007, 0.64978409, 0.46256891) 
> cosine_calc(a,b,w)[,1] 
[1,] 0.8390671 

Este question señala que hay otras funciones coseno predefinidos disponibles en R, pero no dice nada acerca de su eficiencia relativa.

+0

sólo ser capaz de hacerlo un par a la vez va a ser un cuello de botella importante ... –

+0

Odio decírtelo, pero en mi experiencia, no parece que se construirá R de rendimiento (hablando relativamente). Si estos datos provienen de una base de datos relacional, puede considerar calcular las similitudes allí y luego exportar a R. La mayoría de lo que uso R para el análisis a pequeña escala (es decir, en los conjuntos de datos después de haber hecho una cantidad significativa de agregación) y producción de gráficos. –

+2

¿Por qué no avanzas y comparas los ejemplos enumerados en http://stackoverflow.com/questions/2535234/find-cosine-similarity-in-r/2536149#2536149 (es decir, la pregunta que vinculó; @JoshUlrich te muestra cómo en su respuesta) y ¿lo ves por ti mismo? –

Respuesta

7

Todas las funciones que está utilizando son .Primitive (por lo tanto, ya se llama código compilado directamente), por lo que será difícil encontrar ganancias de velocidad consistentes fuera de reconstruir R con un BLAS optimizado. Con eso dicho, aquí es una opción que podría ser más rápido para los vectores más grandes:

cosine_calc2 <- function(a,b,wts) { 
    a = a*wts 
    b = b*wts 
    crossprod(a,b)/sqrt(crossprod(a)*crossprod(b)) 
} 

all.equal(cosine_calc1(a,b,w),cosine_calc2(a,b,w)) 
# [1] TRUE 

# Check some timings 
library(rbenchmark) 
# cosine_calc2 is slower on my machine in this case 
benchmark(
    cosine_calc1(a,b,w), 
    cosine_calc2(a,b,w), replications=1e5, columns=1:4) 
#     test replications user.self sys.self 
# 1 cosine_calc1(a, b, w)  100000  1.06  0.02 
# 2 cosine_calc2(a, b, w)  100000  1.21  0.00 

# but cosine_calc2 is faster for larger vectors 
set.seed(21) 
a <- rnorm(1000) 
b <- rnorm(1000) 
w <- runif(1000) 
benchmark(
    cosine_calc1(a,b,w), 
    cosine_calc2(a,b,w), replications=1e5, columns=1:4) 
#     test replications user.self sys.self 
# 1 cosine_calc1(a, b, w)  100000  3.83  0 
# 2 cosine_calc2(a, b, w)  100000  2.12  0 

ACTUALIZACIÓN:

de perfiles revela que un poco de tiempo se dedica a multiplicar cada vector mediante el vector de pesos.

> Rprof(); for(i in 1:100000) cosine_calc2(a,b,w); Rprof(NULL); summaryRprof() 
$by.self 
      self.time self.pct total.time total.pct 
*     0.80 45.98  0.80  45.98 
crossprod   0.56 32.18  0.56  32.18 
cosine_calc2  0.32 18.39  1.74 100.00 
sqrt    0.06  3.45  0.06  3.45 

$by.total 
      total.time total.pct self.time self.pct 
cosine_calc2  1.74 100.00  0.32 18.39 
*     0.80  45.98  0.80 45.98 
crossprod   0.56  32.18  0.56 32.18 
sqrt    0.06  3.45  0.06  3.45 

$sample.interval 
[1] 0.02 

$sampling.time 
[1] 1.74 

Si puede hacer la ponderación antes de tener que llamar a los millones de función de los tiempos, que le podría ahorrar un poco de tiempo. cosine_calc3 es marginalmente más rápido que tu función original con vectores pequeños. La compilación de bytes de la función debería darle otra aceleración marginal.

cosine_calc3 <- function(a,b) { 
    crossprod(a,b)/sqrt(crossprod(a)*crossprod(b)) 
} 
A = a*w 
B = b*w 
# Run again on the 1000-element vectors 
benchmark(
    cosine_calc1(a,b,w), 
    cosine_calc2(a,b,w), 
    cosine_calc3(A,B), replications=1e5, columns=1:4) 
#     test replications user.self sys.self 
# 1 cosine_calc1(a, b, w)  100000  3.85  0.00 
# 2 cosine_calc2(a, b, w)  100000  2.13  0.02 
# 3 cosine_calc3(A, B)  100000  1.31  0.00 
+2

¿Estoy leyendo esos resultados, verdad? 100,000 repeticiones de 1,000 entradas toma 3 segundos? Parece difícil de creer que esto podría ser un cuello de botella en el código de alguien. – hadley

Cuestiones relacionadas