Modelos de temas: validación cruzada con loglikelihood o perplejidad
tm cross-validation (2)
Escribí la respuesta en el CV al que te refieres, aquí hay un poco más de detalles:
seq(2, 100, by =1)
simplemente crea una secuencia numérica de 2 a 100 por unidades, por lo que 2, 3, 4, 5, ... 100. Esos son los números de temas que quiero usar en los modelos. . Un modelo con 2 temas, otro con 3 temas, otro con 4 temas y así sucesivamente a 100 temas.AssociatedPress[21:30]
es simplemente un subconjunto de los datos incorporados en el paquete de lostopicmodels
. Acabo de usar un subconjunto en ese ejemplo para que se ejecute más rápido.
Con respecto a la pregunta general sobre números óptimos de temas, ahora sigo el ejemplo de Martin Ponweiser en Selección de modelo por media armónica (4.3.3 en su tesis, que está aquí: http://epub.wu.ac.at/3558/1/main.pdf ). Así es como lo hago en este momento:
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 <- fitted@logLiks[-c(1:(burnin/keep))]
# assuming that burnin is a multiple of keep and
harmonicMean(logLiks)
Entonces, para hacer esto sobre una secuencia de modelos de temas con diferentes números de temas ...
# 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) L@logLiks[-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
Aquí está la salida, con números de temas a lo largo del eje x, que indican que 6 temas son óptimos.
La validación cruzada de modelos temáticos está bastante bien documentada en los documentos que vienen con el paquete, vea aquí, por ejemplo: http://cran.r-project.org/web/packages/topicmodels/vignettes/topicmodels.pdf Déle un intente y luego vuelva con una pregunta más específica sobre la codificación de CV con modelos de temas.
Estoy agrupando documentos usando modelos de temas. Necesito encontrar los números de tema óptimos. Entonces, decidí hacer diez veces la validación cruzada con los temas 10, 20, ... 60.
He dividido mi corpus en diez lotes y reservé un lote para un conjunto reservado. He ejecutado la asignación de dirichlet latente (LDA) utilizando nueve lotes (un total de 180 documentos) con temas del 10 al 60. Ahora, tengo que calcular la perplejidad o la probabilidad de registro para el conjunto reservado.
Encontré este código en una de las sesiones de discusión de CV. Realmente no entiendo varias líneas de códigos a continuación. Tengo dtm matrix utilizando el conjunto de reserva (20 documentos). Pero no sé cómo calcular la perplejidad o la probabilidad de registro de este conjunto reservado.
Preguntas:
¿Alguien puede explicarme qué significa aquí (2, 100, por = 1)? Además, ¿qué significa AssociatedPress [21:30]? ¿Qué función (k) está haciendo aquí?
best.model <- lapply(seq(2, 100, by=1), function(k){ LDA(AssociatedPress[21:30,], k) })
Si quiero calcular la perplejidad o registrar la probabilidad del conjunto de reserva llamado dtm, ¿existe un código mejor? Sé que hay funciones de
perplexity()
ylogLik()
, pero como soy nuevo, no puedo descubrir cómo implementarlo con mi matriz reservada, llamada dtm.¿Cómo puedo hacer diez veces la validación cruzada con mi corpus, que contiene 200 documentos? ¿Hay código existente que pueda invocar? Encontré
caret
para este propósito, pero tampoco puedo resolverlo.
La respuesta aceptada a esta pregunta es buena hasta el momento, pero no aborda cómo estimar la perplejidad en un conjunto de datos de validación y cómo usar la validación cruzada.
Usando la perplejidad para la validación simple
Perplexity es una medida de qué tan bien un modelo de probabilidad se ajusta a un nuevo conjunto de datos. En el paquete de R de los topicmodels
tema, es fácil de ajustar con la función de perplexity
, que toma como argumentos un modelo de tema que se ajusta previamente y un nuevo conjunto de datos, y devuelve un solo número. Cuanto más bajo, mejor.
Por ejemplo, dividiendo los datos de AssociatedPress
en un conjunto de entrenamiento (75% de las filas) y un conjunto de validación (25% de las filas):
# 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
La perplejidad es más alta para el conjunto de validación que para el conjunto de entrenamiento, porque los temas se han optimizado en función del conjunto de entrenamiento.
Uso de la perplejidad y la validación cruzada para determinar un buen número de temas
La extensión de esta idea a la validación cruzada es sencilla. Divida los datos en diferentes subconjuntos (por ejemplo, 5), y cada subconjunto obtiene un turno como conjunto de validación y cuatro giros como parte del conjunto de entrenamiento. Sin embargo, es realmente computacional intensivo, especialmente cuando se trata de un gran número de temas.
Es posible que pueda usar caret
para hacer esto, pero sospecho que aún no se encarga del modelado de temas. En cualquier caso, es el tipo de cosas que prefiero hacer yo mismo para asegurarme de que entiendo lo que está pasando.
El siguiente código, incluso con el procesamiento paralelo en 7 CPU lógicas, tardó 3.5 horas en ejecutarse en mi computadora portátil:
#----------------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")
Vemos en los resultados que 200 temas son demasiados y tienen algunos ajustes excesivos, y 50 son demasiado pocos. De la cantidad de temas tratados, 100 es el mejor, con la perplejidad promedio más baja en los cinco conjuntos de espera diferentes.