2017-04-11 12 views
0

Ich hätte gedacht, dass zwischen den R Paketen text2vec, tm, quanteda, svs, qlcMatrix und wordspace eine Funktion wären PPMI (positive punktuellen gegenseitige Information) zwischen Begriffen und Zusammenhängen (basierend auf einer Matrix von TERM zu berechnen Begriff (Kontext) Co-Vorkommen) - aber anscheinend nicht, also ging ich voran und schrieb selbst einen. Das Problem ist, dass es langsam wie Melasse ist, wahrscheinlich, weil ich nicht sehr gut mit dünn besetzten Matrizen bin - und meine tcms sind in der Größenordnung von 10k * 20k, also müssen sie spärlich sein.Wie effizient PPMI auf einer spärlichen Matrix in R zu berechnen?

Von dem, was ich verstehe, PMI = log(p(word, context)/(p(word)*p(context))), daher, dass ich Grund:

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

Wo N ist die Summe aller Kookkurrenzen in der Co-Auftritts-Matrix. Und PPMI einfach zwingt alle < 0 Werte 0 sein (? Das ist so weit richtig, rechts)

In diesem Sinne, hier ist ein Versuch der Umsetzung:

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) 

Was langsam erscheint, wird nicht die Kalkulation selbst, sondern die neu berechneten Werte in die spärliche Matrix (in diesem winzigen Beispiel ist es nicht schlecht, aber wenn man es aus Tausenden von Zeilen zu Tausenden von Zeilen macht, wird selbst eine Iteration dieser Schleife ewig dauern; ein neues bauen Matrix mit rBind scheint eine schlechtere Idee).

Was ist ein effizienterer Weg, um alte Werte in solch einer dünnen Matrix durch die neuen PPMI-gewichteten Werte zu ersetzen? Entweder Vorschläge, diesen Code zu ändern, oder eine vorhandene Funktion in einem Paket zu verwenden, das ich irgendwie verpasst habe - alles in Ordnung.

+1

Schauen Sie sich die dev-Version von text2vec an. Hier ist, wie ich PMI für die Extraktion von Phrasen berechnen (Kollokationen) - https://github.com/dselivanov/text2vec/blob/master/R/collocations.R#L57-L76. In Bezug auf Ihre Frage - in der Regel versuchen, Element für Element Zugriff in dünn besetzten Matrizen zu vermeiden, es ist sehr ineffizient. –

Antwort

0

In der Zwischenzeit herausgefunden, das funktioniert einigermaßen schnell. Ich werde es hier lassen, falls jemand anderes das gleiche Problem hat. Scheint auch sehr ähnlich zu Ansatz im Kommentar zu der Frage verknüpft (danke!).

# 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. 
Verwandte Themen