web-dev-qa-db-ja.com

トピックモデル:対数尤度と困惑度の相互検証

トピックモデリングを使用してドキュメントをクラスタリングしています。最適なトピック番号を考え出す必要があります。そこで、トピック10、20、... 60で10分割交差検証を行うことにしました。

コーパスを10個のバッチに分割し、ホールドアウトセット用に1つのバッチを確保しました。トピック10から60までの9つのバッチ(合計180ドキュメント)を使用して潜在的ディリクレ割り当て(LDA)を実行しました。次に、ホールドアウトセットの混乱度または対数尤度を計算する必要があります。

CVのディスカッションセッションの1つから このコード が見つかりました。以下の数行のコードは本当に理解できません。ホールドアウトセット(20ドキュメント)を使用したdtmマトリックスがあります。しかし、このホールドアウトセットの混乱やログの可能性を計算する方法はわかりません。


質問:

  1. 誰かが私にseq(2、100、by = 1)の意味を説明できますか?また、AssociatedPress [21:30]はどういう意味ですか?ここでどんな機能(k)がやっていますか?

    _best.model <- lapply(seq(2, 100, by=1), function(k){ LDA(AssociatedPress[21:30,], k) })
    _
  2. Dtmと呼ばれるホールドアウトセットの混乱や対数尤度を計算する場合、より優れたコードはありますか? perplexity()関数とlogLik()関数があることは知っていますが、私は新しいので、dtmと呼ばれるホールドアウトマトリックスでそれを実装する方法を理解できません。

  3. 200個のドキュメントを含むコーパスで10分割交差検証を実行するにはどうすればよいですか?呼び出すことができる既存のコードはありますか?私はこの目的のためにcaretを見つけましたが、やはりそれも理解できません。

22
user37874

この質問に対する受け入れられた回答は、それに関しては十分ですが、検証データセットの混乱を推定する方法と交差検証を使用する方法については実際には触れていません。

単純な検証のための複雑さの使用

Perplexity は、確率モデルが新しいデータセットにどの程度適合するかを示す尺度です。 topicmodels Rパッケージでは、以前に適合したトピックモデルと新しいデータセットを引数として取り、単一の数値を返すperplexity関数を使用して簡単に適合できます。低いほど良い。

たとえば、AssociatedPressデータをトレーニングセット(行の75%)と検証セット(行の25%)に分割します。

# 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

トピックはトレーニングセットに基づいて最適化されているため、検証セットの難易度はトレーニングセットよりも高くなっています。

困惑と相互検証を使用して適切な数のトピックを決定する

このアイデアを交差検証に拡張することは簡単です。データを異なるサブセットに分割し(たとえば5)、各サブセットは検証セットとして1ターン、トレーニングセットの一部として4ターンを取得します。ただし、特に多数のトピックを試す場合は、計算量が非常に多くなります。

caretを使用してこれを行うことができるかもしれませんが、それはまだトピックモデリングを処理していないと思います。いずれにせよ、私は自分が何が起こっているのかを確実に理解するために自分でやりたいと思っていることです。

以下のコードは、7つの論理CPUで並列処理を行った場合でも、ラップトップでの実行に3.5時間かかりました。

#----------------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")

結果を見ると、200トピックは多すぎて適合しすぎており、50トピックは少なすぎます。試行されたトピックの数のうち、100が最高で、5つの異なるホールドアウトセットの平均の複雑度は最低です。

enter image description here

20
Peter Ellis

私はあなたが言及するCVで答えを書きました、ここにもう少し詳細があります:

  1. seq(2, 100, by =1)は、2から100までの数列を1つずつ作成するので、2、3、4、5、... 100になります。これらは、モデルで使用するトピックの数です。 1つのモデルに2つのトピックがあり、別のモデルには3つのトピックがあり、別のモデルには4つのトピックがあり、以下同様に100トピックになります。

  2. AssociatedPress[21:30]は、topicmodelsパッケージの組み込みデータのサブセットです。その例ではサブセットを使用したので、より高速に実行されます。

最適なトピック数の一般的な質問については、ここで調和平均によるモデル選択に関するマーティンポンウェイザーの例に従います(論文の4.3.3、ここにあります http://epub.wu.ac.at /3558/1/main.pdf )。ここで私は今それをどのように行うかです:

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)

したがって、これを、トピックの数が異なる一連のトピックモデルに対して行うには...

# 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

enter image description here x軸に沿ってトピックの数を示した出力は、6つのトピックが最適であることを示しています。

トピックモデルの相互検証は、パッケージに付属のドキュメントにかなり詳しく記載されています。たとえば、以下を参照してください。 http://cran.r-project.org/web/packages/topicmodels/vignettes/topicmodels。 pdf 試してみて、トピックモデルを使用したCVのコーデ​​ィングに関するより具体的な質問に戻ってください。

22
Ben