2011-11-16 18 views
6

Mam dwa wektory wartości i jeden wektor wag, i muszę obliczyć podobieństwo cosinus. Ze skomplikowanych powodów mogę obliczyć cosinus tylko dla jednej pary naraz. Ale muszę to robić wiele milionów razy.Najbardziej wydajna kalkulacja R cosinusa

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)) 
} 

działa, ale chcę spróbować poprawić jego wydajność.

Przykład dane:

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 

Ten question wskazuje, że istnieją inne predefiniowane funkcje cosinus dostępne w R, ale nie mówi nic o ich względnej skuteczności.

+0

tylko jest w stanie to zrobić jedną parę na raz będzie głównym wąskim gardłem ... –

+0

nienawidzę złamać go do ciebie, ale z mojego doświadczenia wynika, R nie wydaje się być zbudowany wydajność (relatywnie rzecz biorąc). Jeśli dane pochodzą z relacyjnej bazy danych, warto rozważyć obliczenie podobieństw, a następnie wyeksportowanie do R. Większość tego, co używam R, to analiza na małą skalę (tj. Na zbiory danych po tym, jak zrobiłem znaczną ilość agregacja) i wytwarzanie grafiki. –

+2

Dlaczego nie posuwać się naprzód i porównywać przykłady wymienione na stronie http://stackoverflow.com/questions/2535234/find-cosine-similarity-in-r/2536149#2536149 (tzn. Pytanie, które łączysz, @JoshUlrich pokazuje jak w jego odpowiedzi) i przekonaj się sam? –

Odpowiedz

7

Wszystkie używane funkcje to .Primitive (dlatego już teraz wywoływany jest kod skompilowany bezpośrednio), więc trudno będzie uzyskać spójne zyski prędkości poza ponownym budowaniem R ze zoptymalizowanym BLAS. Z powiedział, że tu jest jedną z opcji, które mogą być szybsze dla większych wektorów:

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 

UPDATE:

Profilowanie ujawnia, że ​​sporo czasu spędza mnożenie każdego wektora przez wektor wagi.

> 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 

Jeśli możesz dokonać ważenia, zanim będziesz musiał wywołać tę funkcję miliony razy, może to zaoszczędzić sporo czasu. cosine_calc3 jest marginalnie szybszy niż oryginalna funkcja z małymi wektorami. Kompilowanie funkcji Byte'a powinno dać ci jeszcze większe przyspieszenie.

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

Czy czytam, że wyniki są prawidłowe? 100 000 powtórzeń z 1000 wejść trwa 3 sekundy? Trudno uwierzyć, że może to być wąskie gardło w czyimś kodzie! – hadley

Powiązane problemy