2014-01-25 10 views
18

Ich gruppiere Dokumente mithilfe von Topic Modeling. Ich muss mir die optimalen Themennummern einfallen lassen. Also entschied ich mich, eine zehnfache Kreuzvalidierung mit den Themen 10, 20, ... 60 zu machen.Topic-Modelle: Kreuzvalidierung mit Loglikelihood oder Perplexität

Ich habe meinen Korpus in zehn Chargen aufgeteilt und eine Charge für einen Holdout-Satz reserviert. Ich habe latente Dirichlet-Zuweisung (LDA) mit neun Batches (insgesamt 180 Dokumente) mit Themen 10 bis 60 ausgeführt. Jetzt muss ich Perplexität oder Log-Wahrscheinlichkeit für die Holdout-Set berechnen.

Ich fand this code aus einer der CV-Diskussionssitzungen. Ich verstehe wirklich nicht mehrere Zeilen von Codes unten. Ich habe dtm Matrix mit dem Holdout-Set (20 Dokumente). Aber ich weiß nicht, wie ich die Perplexität oder Log-Wahrscheinlichkeit dieser Holdout-Menge berechnen soll.


Fragen:

  1. Kann jemand mir erklären, was f (2, 100, durch = 1) bedeuten hier? Auch, was AssociatedPress [21:30] bedeutet? Welche Funktion (k) macht hier?

    best.model <- lapply(seq(2, 100, by=1), function(k){ LDA(AssociatedPress[21:30,], k) }) 
    
  2. Wenn ich Ratlosigkeit berechnet werden soll oder log Wahrscheinlichkeit der dtm genannt holdout Satz, ist es besser Code? Ich weiß, dass es perplexity() und logLik() Funktionen gibt, aber da ich neu bin, kann ich nicht herausfinden, wie man es mit meiner Holdout-Matrix, genannt dtm, implementiert.

  3. Wie kann ich eine zehnfache Kreuzvalidierung mit meinem Korpus durchführen, der 200 Dokumente enthält? Gibt es vorhandenen Code, den ich aufrufen kann? Ich habe caret für diesen Zweck gefunden, aber auch das kann ich nicht herausfinden.

+0

Was in der Dokumentation '? Seq' und'? AssociatedPress' und die anderen Funktionen habe Sie nicht verstanden? – probabilityislogic

+0

Ich habe den Code dafür aktualisiert und als Kern gespeichert. hat eine Plot-Methode, die standardmäßig druckt. 'devtools :: source_url (" https://gist.githubusercontent.com/trinker/594bd132b180a43945f7/raw/70fbb1aa2a9113837a9a9f8a6c43d884c2ef5bd0/optimal_k%25202 ")' +1 nette Antwort. –

Antwort

12

Die akzeptierte Antwort auf diese Frage ist gut, soweit es geht, aber es befasst sich nicht tatsächlich, wie Perplexität auf einem Validierungsdatensatz schätzen und wie Kreuzvalidierung verwenden.

Mit Ratlosigkeit für einfache Validierung

Perplexity ist ein Maß dafür, wie gut ein Wahrscheinlichkeitsmodell einen neuen Satz von Daten paßt. In dem topicmodels R-Paket ist es einfach, mit der perplexity-Funktion zu passen, die als Argumente ein zuvor angepasstes Themenmodell und einen neuen Datensatz verwendet und eine einzelne Zahl zurückgibt. Je niedriger, desto besser.

Um zum Beispiel die AssociatedPress Daten in einen Trainingssatz (75% der Zeilen) und einen Validierungssatz (25% der Zeilen) Abspalten:

# load up some R packages including a few we'll need later 
library(topicmodels) 
library(doParallel) 
library(ggplot2) 
library(scales) 

data("AssociatedPress", package = "topicmodels") 

burnin = 1000 
iter = 1000 
keep = 50 

full_data <- AssociatedPress 
n <- nrow(full_data) 
#-----------validation-------- 
k <- 5 

splitter <- sample(1:n, round(n * 0.75)) 
train_set <- full_data[splitter, ] 
valid_set <- full_data[-splitter, ] 

fitted <- LDA(train_set, k = k, method = "Gibbs", 
          control = list(burnin = burnin, iter = iter, keep = keep)) 
perplexity(fitted, newdata = train_set) # about 2700 
perplexity(fitted, newdata = valid_set) # about 4300 

Die Perplexität ist höher für den Validierungssatz als das Trainingssatz, weil die Themen basierend auf dem Trainingssatz optimiert wurden.

Ratlosigkeit Verwendung und Kreuzvalidierung eine gute Anzahl von Themen ist einfach

Die Erweiterung dieser Idee Kreuzvalidierung zu bestimmen. Teilen Sie die Daten in verschiedene Teilmengen auf (z. B. 5) und jede Teilmenge erhält eine Umdrehung als Validierungsmenge und vier Umdrehungen als Teil des Trainingssatzes. Es ist jedoch sehr rechenintensiv, besonders wenn man die größere Anzahl von Themen ausprobiert.

Sie können möglicherweise caret verwenden, um dies zu tun, aber ich vermute, dass Thema Modellierung noch nicht behandelt. Auf jeden Fall ist es das, was ich am liebsten selbst mache, um sicher zu sein, dass ich verstehe, was vor sich geht.

Der Code unten, auch mit der parallelen Bearbeitung auf 7 logischen CPUs, nahm 3,5 Stunden auf meinem Laptop laufen:

#----------------5-fold cross-validation, different numbers of topics---------------- 
# set up a cluster for parallel processing 
cluster <- makeCluster(detectCores(logical = TRUE) - 1) # leave one CPU spare... 
registerDoParallel(cluster) 

# load up the needed R package on all the parallel sessions 
clusterEvalQ(cluster, { 
    library(topicmodels) 
}) 

folds <- 5 
splitfolds <- sample(1:folds, n, replace = TRUE) 
candidate_k <- c(2, 3, 4, 5, 10, 20, 30, 40, 50, 75, 100, 200, 300) # candidates for how many topics 

# export all the needed R objects to the parallel sessions 
clusterExport(cluster, c("full_data", "burnin", "iter", "keep", "splitfolds", "folds", "candidate_k")) 

# we parallelize by the different number of topics. A processor is allocated a value 
# of k, and does the cross-validation serially. This is because it is assumed there 
# are more candidate values of k than there are cross-validation folds, hence it 
# will be more efficient to parallelise 
system.time({ 
results <- foreach(j = 1:length(candidate_k), .combine = rbind) %dopar%{ 
    k <- candidate_k[j] 
    results_1k <- matrix(0, nrow = folds, ncol = 2) 
    colnames(results_1k) <- c("k", "perplexity") 
    for(i in 1:folds){ 
     train_set <- full_data[splitfolds != i , ] 
     valid_set <- full_data[splitfolds == i, ] 

     fitted <- LDA(train_set, k = k, method = "Gibbs", 
        control = list(burnin = burnin, iter = iter, keep = keep)) 
     results_1k[i,] <- c(k, perplexity(fitted, newdata = valid_set)) 
    } 
    return(results_1k) 
} 
}) 
stopCluster(cluster) 

results_df <- as.data.frame(results) 

ggplot(results_df, aes(x = k, y = perplexity)) + 
    geom_point() + 
    geom_smooth(se = FALSE) + 
    ggtitle("5-fold cross-validation of topic modelling with the 'Associated Press' dataset", 
      "(ie five different models fit for each candidate number of topics)") + 
    labs(x = "Candidate number of topics", y = "Perplexity when fitting the trained model to the hold-out set") 

Wir in den Ergebnissen sehen, dass 200 Themen zu viele und hat einige Überanpassung und 50 ist zu wenig. Von den Anzahl der probierten Themen ist 100 die beste, mit der niedrigsten durchschnittlichen Perplexität bei den fünf verschiedenen Hold-out-Sets.

enter image description here

+0

Haben Sie die Ratlosigkeit nach Anzahl der Themen normalisiert (d. H. Geteilt)? –

+1

@EvanZamir Warum würdest du die Ratlosigkeit normalisieren? Irgendeine Referenz von Papieren, die das tun? Nur aus Neugier. – francium87d

19

Ich schrieb die Antwort auf CV, die Sie sich beziehen, ist hier ein bisschen mehr Detail:

  1. seq(2, 100, by =1) einfach erstellt eine Zahlenfolge 2-100 durch diejenigen, so 2, 3, 4 , 5, ... 100. Das sind die Nummern von Themen, die ich in den Modellen verwenden möchte. Ein Modell mit 2 Themen, ein weiteres mit 3 Themen, ein weiteres mit 4 Themen und so weiter zu 100 Themen.

  2. AssociatedPress[21:30] ist einfach eine Teilmenge der integrierten Daten im topicmodels Paket. Ich habe nur eine Teilmenge in diesem Beispiel verwendet, so dass es schneller laufen würde.

die allgemeine Frage der optimalen Themennummern Bezug verfolge ich nun das Beispiel von Martin Ponweiser bei der Modellauswahl von harmonischem Durchschnitt (4.3.3 in seiner Doktorarbeit, die hier: http://epub.wu.ac.at/3558/1/main.pdf). Hier ist, wie ich es im Moment tun:

library(topicmodels) 
# 
# get some of the example data that's bundled with the package 
# 
data("AssociatedPress", package = "topicmodels") 

harmonicMean <- function(logLikelihoods, precision=2000L) { 
    library("Rmpfr") 
    llMed <- median(logLikelihoods) 
    as.double(llMed - log(mean(exp(-mpfr(logLikelihoods, 
             prec = precision) + llMed)))) 
} 

# The log-likelihood values are then determined by first fitting the model using for example 
k = 20 
burnin = 1000 
iter = 1000 
keep = 50 

fitted <- LDA(AssociatedPress[21:30,], k = k, method = "Gibbs",control = list(burnin = burnin, iter = iter, keep = keep)) 

# where keep indicates that every keep iteration the log-likelihood is evaluated and stored. This returns all log-likelihood values including burnin, i.e., these need to be omitted before calculating the harmonic mean: 

logLiks <- [email protected][-c(1:(burnin/keep))] 

# assuming that burnin is a multiple of keep and 

harmonicMean(logLiks) 

So zu tun dies über eine Folge von Themenmodellen mit unterschiedlicher Anzahl von Themen ...

# generate numerous topic models with different numbers of topics 
sequ <- seq(2, 50, 1) # in this case a sequence of numbers from 1 to 50, by ones. 
fitted_many <- lapply(sequ, function(k) LDA(AssociatedPress[21:30,], k = k, method = "Gibbs",control = list(burnin = burnin, iter = iter, keep = keep))) 

# extract logliks from each topic 
logLiks_many <- lapply(fitted_many, function(L) [email protected][-c(1:(burnin/keep))]) 

# compute harmonic means 
hm_many <- sapply(logLiks_many, function(h) harmonicMean(h)) 

# inspect 
plot(sequ, hm_many, type = "l") 

# compute optimum number of topics 
sequ[which.max(hm_many)] 
## 6 

enter image description here Hier ist die Ausgabe, mit Zahlen von Themen entlang der x-Achse, was anzeigt, dass 6 Themen optimal sind.

Cross-Validierung von Topic-Modellen ist ziemlich gut dokumentiert in den Dokumenten, die mit dem Paket kommen, siehe hier zum Beispiel: http://cran.r-project.org/web/packages/topicmodels/vignettes/topicmodels.pdf Probieren Sie es aus und kommen Sie dann mit einer spezifischen Frage über die Codierung CV mit Themenmodellen zurück.

+1

Vielen Dank für Ihre Antwort. In Grun und Horniks Papier sagten sie, dass wir eine Kreuzvalidierung durchführen können, aber nicht genau erwähnt haben, wie? Meine Hauptfrage ist jetzt Ihre Implementierung von perplexity() oder logLik(). Grun Paper erwähnt, dass "Perplexity() verwendet werden kann, um die Perplexität eines angepassten Modells auch für neue Daten zu bestimmen" Ok, das möchte ich tun. – user37874

+0

Ich möchte LDA mit 180 docs (Trainingssatz) ausführen und Perplexität auf 20 Dokumente überprüfen (Hold-out-Set). Ihre Implementierung ist so mit meiner Änderung: best.model <- lapply (seq (2.100, durch = 1), Funktion (k) {LDA (dtm [1:20,], k)}) best.model .logLik <- as.data.frame (als.matrix (lapply (am besten.modell, logLik))) best.model.logLik.df <- data.frame (topics = c (2: 100), LL = as.numerisch (as.matrix (best.model.logLik))) Ich mache sehe keinen Hinweis darauf, dass Sie LDA mit dem Testset zuerst ausführen und berechnet die Perplexität auf dem Holdout-Set bei dem LDA-Ergebnis aus dem Testset. – user37874

+0

BIN ICH VERSTEHEN ETWAS über Perplexitätsfunktion falsch? – user37874

Verwandte Themen