使用text2vec包进行文本预处理和主题建模

时间:2017-10-20 04:54:50

标签: r tm topic-modeling synonym text2vec

我有大量文档,我想使用text2vec和LDA(Gibbs Sampling)进行主题建模。

我需要的步骤是(按顺序):

  1. 从文本中删除数字和符号

    background
  2. 删除停用词

    library(stringr)
    docs$text <- stringr::str_replace_all(docs$text,"[^[:alpha:]]", " ")
    docs$text <- stringr::str_replace_all(docs$text,"\\s+", " ")
    
  3. 按术语替换同义词

  4. 我有一个excel文件,其中第一列是主词,同义词列在第二列,第三列和...列中。我想用主词(第1列)替换所有同义词。每个术语可以具有不同数量的同义词。以下是使用&#34; tm&#34;的代码示例。包(但我对text2vec包中的那个感兴趣):

    library(text2vec)        
    library(tm)
    
    stopwords <- c(tm::stopwords("english"),custom_stopwords)
    
    prep_fun <- tolower
    tok_fun <- word_tokenizer
    tok_fun <- word_tokenizer    
    tokens <- docs$text%>% 
             prep_fun %>% 
             tok_fun
    it <- itoken(tokens, 
                ids = docs$id,
                progressbar = FALSE)
    
    v <- create_vocabulary(it, stopwords = stopwords) %>% 
        prune_vocabulary(term_count_min = 10)
    
    vectorizer <- vocab_vectorizer(v)
    
    1. 转换为文档字词矩阵

      replaceSynonyms <- content_transformer(function(x, syn=NULL)
             {Reduce(function(a,b) {
             gsub(paste0("\\b(", paste(b$syns, collapse="|"),")\\b"), b$word,     a, perl = TRUE)}, syn, x)  })
      
       l <- lapply(as.data.frame(t(Synonyms), stringsAsFactors = FALSE), #
                function(x) { 
                  x <- unname(x) 
                  list(word = x[1], syns = x[-1])
                })
      names(l) <- paste0("list", Synonyms[, 1])
      list2env(l, envir = .GlobalEnv)
      
      synonyms <- list()        
      for (i in 1:length(names(l))) synonyms[i] = l[i]
      
      MyCorpus <- tm_map(MyCorpus, replaceSynonyms, synonyms)
      
    2. 在文档术语矩阵上应用LDA模型

      dtm  <- create_dtm(it, vectorizer)
      
    3. 步骤3中的MyCorpurs是使用&#34; tm&#34;获得的语料库。包。步骤2和步骤3不能一起工作,因为步骤2的输出是词汇,但步骤3的输入是&#34; tm&#34;语料库。

      我的第一个问题是,我怎样才能使用text2vec包(和兼容包)完成所有步骤,因为我发现它非常有效;感谢Dmitriy Selivanov。

      第二:我们如何在步骤5中为LDA中的参数设置最佳值?是否可以根据数据自动设置它们?

      感谢Manuel Bickel在我的帖子中进行更正。

      谢谢, 萨姆

1 个答案:

答案 0 :(得分:2)

根据您的评论更新了答案:

第一个问题:此处已回答同义词替换问题:Replace words in text2vec efficiently。在partiular中检查count的答案。模式和替换可以是ngrams(多词短语)。请注意Dmitriy Selivanov的第二个答案使用word_tokenizer(),但不包括所提供格式的ngram替换案例。

在删除停用词之前,是否有任何理由需要替换同义词?通常这个顺序不应该引起问题;或者你有一个例子,其中切换顺序产生显着不同的结果?如果您真的想在删除停用词之后替换同义词,我想,在使用text2vec时,您必须将此类更改应用于dtm。如果这样做,您需要在dtm中允许ngram,其中包含您的同义词中包含的最小ngram长度。我在下面的代码中提供了一种解决方法作为一个选项。请注意,在你的dtm中允许更高的ngram会产生可能影响或可能不会影响你的下游任务的噪音(你可以修剪词汇步骤中的大部分噪音)。因此,在早期更换ngrams似乎是一个更好的解决方案。

第二个问题:您可以查看textmineR包的包(和源代码),它可以帮助您选择最佳主题数或者也可以选择此问题的答案{ {3}}。关于先验的处理,我还没想到,不同的包,例如text2vec(WarpLDA算法),lda(Collaped Gibbs Sampling算法等),或topicmodels('标准'Gibbs Sampling和Variational Expectation-Maximization算法)详细处理这些值。作为起点,您可以查看topicmodels的详细文档,章节“2.2。估计”告诉您如何估算“2.1模型规范”中定义的alpha和beta参数。

为了学习目的,请注意您的代码在两点产生了错误,我修改过: (1)您需要在create_vocabulary()中使用正确的停用词名称,停用词而不是停用词,因为您已将名称定义为 (2)您的lda模型定义中不需要vocabulary =... - 也许您使用较早版本的text2vec

library(text2vec) 
library(reshape2)
library(stringi)

#function proposed by @count
mgsub <- function(pattern,replacement,x) {
  if (length(pattern) != length(replacement)){
    stop("Pattern not equal to Replacment")
  } 
  for (v in 1:length(pattern)) {
    x  <- gsub(pattern[v],replacement[v],x, perl = TRUE)
  }
  return(x )
}

docs <- c("the coffee is warm",
          "the coffee is cold",
          "the coffee is hot",
          "the coffee is boiling like lava",
          "the coffee is frozen",
          "the coffee is perfect",
          "the coffee is warm almost hot"
)

synonyms <- data.frame(mainword = c("warm", "cold")
                       ,syn1 = c("hot", "frozen")
                       ,syn2 = c("boiling like lava", "")
                       ,stringsAsFactors = FALSE)

synonyms[synonyms == ""] <- NA

synonyms <- reshape2::melt(synonyms
                           ,id.vars = "mainword"
                           ,value.name = "synonym"
                           ,na.rm = TRUE)

synonyms <- synonyms[, c("mainword", "synonym")]


prep_fun <- tolower
tok_fun <- word_tokenizer
tokens <- docs %>% 
  #here is where you might replace synonyms directly in the docs
  #{ mgsub(synonyms[,"synonym"], synonyms[,"mainword"], . ) } %>%
  prep_fun %>% 
  tok_fun
it <- itoken(tokens, 
             progressbar = FALSE)

v <- create_vocabulary(it,
                       sep_ngram = "_",
                       ngram = c(ngram_min = 1L
                                 #allow for ngrams in dtm
                                 ,ngram_max = max(stri_count_fixed(unlist(synonyms), " "))
                                 )
)

vectorizer <- vocab_vectorizer(v)
dtm <- create_dtm(it, vectorizer)

#ngrams in dtm
colnames(dtm)

#ensure that ngrams in synonym replacement table have the same format as ngrams in dtm
synonyms <- apply(synonyms, 2, function(x) gsub(" ", "_", x))

colnames(dtm) <- mgsub(synonyms[,"synonym"], synonyms[,"mainword"], colnames(dtm))


#only zeros/ones in dtm since none of the docs specified in my example
#contains duplicate terms
dim(dtm)
#7 24
max(dtm)
#1

#workaround to aggregate colnames in dtm
#I think there is no function `colsum` that allows grouping
#therefore, a workaround based on rowsum
#not elegant because you have to transpose two times, 
#convert to matrix and reconvert to sparse matrix
dtm <- 
  Matrix::Matrix(
    t(
      rowsum(t(as.matrix(dtm)), group = colnames(dtm))
    )
    , sparse = T)


#synonyms in columns replaced
dim(dtm)
#7 20
max(dtm)
#2