2017-04-11 88 views
0

我会术语和情境(根据条款而─矩阵之间认为R包之间text2vectmquantedasvsqlcMatrixwordspace会有一个函数来计算PPMI(正逐点互信息)长期(背景)共同发生) - 但显然不是,所以我继续前进,自己写了一个。问题是,糖蜜很慢,可能是因为我对稀疏矩阵不太好 - 而且我的tcms大约是10k * 20k,所以它们确实需要稀疏。如何高效地计算R中稀疏矩阵的PPMI?

据我了解,PMI = log(p(word, context)/(p(word)*p(context))),因此我有理由相信:

  count(word_context_co-occurrence)/N 
PMI = log(------------------------------------- ) 
      count(word)/N * count(context)/N 

哪里N是所有共同出现在共生矩阵的总和。而PPMI简直是强迫所有< 0值是0(?这是迄今为止正确,右)

考虑到这一点,这里是在执行的尝试:

library(Matrix) 
set.seed(1) 
pmat = matrix(sample(c(0,0,0,0,0,0,1,10),5*10,T), 5,10, byrow=T) # tiny example matrix; 
# rows are words, columns are contexts (words the row-words co-occur with, in a certain window in the text) 
pmat = Matrix(pmat, sparse=T) # make it sparse 

# calculate some things beforehand to make it faster 
N = sum(pmat) 
contextp = Matrix::colSums(pmat)/N # probabilities of contexts 
wordp = Matrix::rowSums(pmat)/N # probabilities of terms 

# here goes nothing... 
pmat2 = pmat 
for(r in 1:nrow(pmat)){ # go term by term, calculate PPMI association with each of its contexts 
    not0 = which(pmat[r, ] > 0) # no need to consider 0 values (no co-occurrence) 
    tmp = log((pmat[r,not0]/N)/(wordp[r] * contextp[not0])) # PMI 
    tmp = ifelse(tmp < 0, 0, tmp) # PPMI 
    pmat2[r, not0] = tmp # <-- THIS here is the slow part, replacing the old frequency values with the new PPMI weighted ones. 
} 
# take a look: 
round(pmat2,2) 

出现什么是慢不是计算本身,而是将新计算的值放入稀疏矩阵中(在这个微小的例子中,它并不坏,但是如果你使它成千上万行成千上万的行,即使这个循环的一次迭代也将永远存在;构造一个新的与rBind矩阵似乎是一个坏主意)。

什么是更有效的方法来替换这种稀疏矩阵中的旧值与新的PPMI加权值?无论是建议更改此代码,还是使用某些包中的某些现有功能,我总是错过了 - 都很好。

+1

看看dev版本的text2vec。这里是我如何计算短语(搭配)提取的PMI - https://github.com/dselivanov/text2vec/blob/master/R/collocations.R#L57-L76。 关于你的问题 - 通常尽量避免在稀疏矩阵中逐元素访问,它效率非常低。 –

回答

0

同时也算出来了,这种工作合理快捷。如果其他人最终面临同样的问题,我会把它留在这里。也似乎非常类似于在评论中链接到问题的方法(谢谢!)。

# this is for a column-oriented sparse matrix; transpose if necessary 
tcmrs = Matrix::rowSums(pmat) 
tcmcs = Matrix::colSums(pmat) 
N = sum(tcmrs) 
colp = tcmcs/N 
rowp = tcmrs/N 
pp = [email protected]+1 
ip = [email protected]+1 
tmpx = rep(0,length([email protected])) # new values go here, just a numeric vector 
# iterate through sparse matrix: 
for(i in 1:(length([email protected])-1)){ 
    ind = pp[i]:(pp[i+1]-1) 
    not0 = ip[ind] 
    icol = [email protected][ind] 
    tmp = log((icol/N)/(rowp[not0] * colp[i])) # PMI 
    tmpx[ind] = tmp  
} 
[email protected] = tmpx 
# to convert to PPMI, replace <0 values with 0 and do a Matrix::drop0() on the object.