2011-11-16 100 views
6

我有兩個向量值和一個向量向量,我需要計算餘弦相似度。由於複雜的原因,我一次只能計算一對餘弦。但我必須做好幾百萬次。最高效的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)) 
} 

的作品,但我想嘗試更好地表現出來。

實施例的數據:

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 

question指出有R中可用的其他預定義的餘弦函數,但沒有關於它們的相對效率說。

+0

只能夠做到這一點,在一次一對將是一個主要的瓶頸...... –

+0

我不想打破它給你,但在我的經驗中,R似乎沒有所要構建表現(相對而言)。如果這些數據來自關係數據庫,那麼您可能需要考慮計算其中的相似性,然後將其導出到R.我使用R的大部分數據都是小規模分析(即,在我完成了大量聚合)並生成圖形。 – 2011-11-16 21:42:01

+2

爲什麼你不要繼續和基準http://stackoverflow.com/questions/2535234/find-cosine-similarity-in-r/2536149#2536149中列出的例子(即你鏈接的問題; @JoshUlrich顯示你如何在他的答案),併爲自己看? –

回答

7

您使用的所有函數都是.Primitive(因此已經直接調用編譯好的代碼),所以除了用優化的BLAS重新構建R之外,很難找到一致的速度增益。隨着中說,這裏是一個選項,它可能會爲大載體更快:

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:

剖析表明,相當多的時間花費在權重向量每個向量乘以。

> 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 

如果您可以在調用函數數百萬次之前進行加權,它可以爲您節省相當多的時間。使用小矢量的cosine_calc3比原始函數稍快。字節編譯函數應該給你另一個邊際加速。

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

我讀的結果正確嗎? 1,000次輸入100,000次需要3秒鐘?似乎很難相信這可能是某人代碼中的瓶頸! – hadley