Предварительная обработка текста и моделирование темы с помощью пакета text2vec

У меня есть большое количество документов, и я хочу провести тематическое моделирование с использованием text2vec и LDA (Gibbs Sampling).

Шаги, которые мне нужны, следующие (по порядку):

  1. Удаление цифр и символов из текста

    library(stringr)
    docs$text <- stringr::str_replace_all(docs$text,"[^[:alpha:]]", " ")
    docs$text <- stringr::str_replace_all(docs$text,"\\s+", " ")
    
  2. Удаление стоп-слов

    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)
    
  3. Замена синонимов терминами

У меня есть файл Excel, в котором первый столбец является основным словом, а синонимы перечислены во втором, третьем и ... столбцах. Я хочу заменить все синонимы на главные слова (столбец №1). У каждого термина может быть разное количество синонимов. Вот пример кода, использующего пакет "tm" (но мне интересен код из пакета text2vec):

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)
  1. Преобразовать в матрицу терминов документа

    dtm  <- create_dtm(it, vectorizer)
    
  2. Применение модели LDA к матрице терминов документа

    doc_topic_prior <- 0.1  # can be chosen based on data? 
    lda_model <- LDA$new(n_topics = 10, 
              doc_topic_prior = doc_topic_prior, topic_word_prior = 0.01)
    doc_topic_distr <- lda_model$fit_transform(dtm, n_iter = 1000, convergence_tol <- 0.01, check_convergence_every_n = 10)
    

MyCorpurs на шаге 3 - это корпус, полученный с помощью пакета «tm». Шаг 2 и Шаг 3 не работают вместе, так как на выходе Шаг 2 - это словарь, а на входе Шаг 3 - корпус "tm".

Мой первый вопрос здесь заключается в том, как я могу выполнить все шаги с использованием пакета text2vec (и совместимых пакетов), поскольку я считаю его очень эффективным; спасибо Дмитрию Селиванову.

Во-вторых: как мы устанавливаем оптимальные значения для параметров в LDA на шаге 5? Можно ли установить их автоматически на основе данных?

Спасибо Мануэлю Бикелю за исправления в моем сообщении.

Спасибо, Сэм


person Sam S    schedule 20.10.2017    source источник


Ответы (1)


Обновленный ответ на ваш комментарий:

Первый вопрос: Здесь уже был дан ответ на вопрос о замене синонимов: Заменить слов в text2vec эффективно. В частности, проверьте ответ count. Шаблоны и замены могут быть нграммами (словосочетаниями). Обратите внимание, что второй ответ Дмитрий Селиванов использует word_tokenizer() и не охватывает случай замены ngram в представленной форме .

Есть ли причина, по которой вам нужно заменить синонимы перед удалением стоп-слова? Обычно такой порядок не должен вызывать проблем; или у вас есть пример, в котором переключение порядка дает существенно разные результаты? Если вы действительно хотите заменить синонимы после удаления стоп-слова, я думаю, вам придется применить такие изменения к dtm при использовании text2vec. Если вы это сделаете, вам необходимо разрешить ngram в вашем dtm с минимальной длиной ngram, включенной в ваши синонимы. В качестве одного из вариантов я предоставил обходной путь в приведенном ниже коде. Обратите внимание, что разрешение более высоких ngram в вашем dtm создает шум, который может или не может влиять на ваши последующие задачи (вы, вероятно, можете сократить большую часть шума на этапе словарного запаса). Поэтому замена ngrams на более ранние кажется лучшим решением.

Второй вопрос: вы можете проверить пакет (и исходный код) пакета textmineR, который поможет вам выбрать наилучшее количество тем, или также ответ на этот вопрос Топовые модели: перекрестная проверка с логическим правдоподобием или недоумением. Что касается обработки априорных значений, я еще не выяснил, как разные пакеты, например, text2vec (алгоритм WarpLDA), lda (алгоритм свернутой выборки Гиббса и другие) или topicmodels ('стандартный' алгоритм выборки Гиббса и вариационный алгоритм максимизации ожидания) обрабатывают их. значения в деталях. В качестве отправной точки вы можете взглянуть на подробную документацию topicmodels, в главе «2.2. Оценка» рассказывается, как оцениваются параметры альфа и бета, которые определены в «2.1 Спецификация модели».

В целях обучения обратите внимание, что ваш код вызывал ошибки в двух точках, которые я исправил: (1) вам нужно использовать правильное имя для стоп-слов в create_vocabulary(), стоп-слова вместо стоп-слов, так как вы определили имя поэтому (2) вам не нужен vocabulary =... в определении модели lda - может быть, вы используете старую версию 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
person Manuel Bickel    schedule 20.10.2017
comment
Большое спасибо за ваш ответ. На самом деле у меня большой объем данных с множеством опечаток и сокращений; также разные сокращения для одного и того же слова. Главное слово - это всего лишь одно слово, но синонимами могут быть такие фразы, как горячая вода. Мне нужно сначала удалить игнорируемые слова (шаг 2 в моем вопросе), а затем заменить несколько синонимов основным словом. Как я могу выполнить эти два шага по порядку, то есть сначала удалить стоп-слова, а затем заменить синонимы? Я проделал всю свою работу, используя пакеты tm и topicmodels, но они очень медленные, и я хочу переключиться на text2vec. - person Sam S; 22.10.2017
comment
Я понял, что часть вашего вопроса уже получила ответ в другом месте. Я соответствующим образом обновил свой ответ и добавил ссылку на этот ответ. - person Manuel Bickel; 23.10.2017
comment
Спасибо, Мануэль, за обновление. Удаление некоторых игнорируемых слов перед ngram позволяет мне легче сосредоточиться на важных ngram / фразах. Например, возвращение к работе, возвращение на работу, возвратная работа - все это заменяется возвратной работой. У меня много таких фраз. - person Sam S; 25.10.2017
comment
Я могу использовать ваш код для удаления игнорируемых слов перед заменой синонимов; отличный код. Я добавил {mgsub (Stopwords [, Stopword], Stopwords [, spacereplace],.)}% ›% Непосредственно перед заменой синонимов в вашей функции. Здесь стоп-слово - это мое стоп-слово, а пробел - заменить =; Я использовал точно так же, как вы использовали для синонимов. Также для замены всего слова я использовал x ‹- gsub (paste0 ((?‹! \\ p {L}) (?:, Paste (pattern [v], collapse = |),) (?! \\ p { L})), замена [v], x, perl = TRUE) в вашей функции; это предотвращает появление нежелательных слов, если вы замените что-то вроде r на right. - person Sam S; 25.10.2017
comment
Я рада, что смогла помочь. Благодарим за разъяснения относительно порядка замены. Ваш заказ, конечно, имеет смысл. Пожалуйста, примите мой ответ, если я смогу решить ваши проблемы, это пометит эту цепочку как завершенную. Спасибо и удачи в вашем проекте. - person Manuel Bickel; 25.10.2017
comment
Что-то забыл: ваш подход к удалению стоп-слов в toknizer / prepare_fun не производит шума, на который я указал, но не использует преимущество скорости text2vec. Ни один из подходов сам по себе не лучше, просто хотел указать на проблему, заключающуюся в том, что нужно выбирать между скоростью и точностью в зависимости от объема текста, который нужно обработать. Конечно, если у вас есть nрограммы, содержащие стоп-слова, я бы выбрал ваш подход, если скорость не является проблемой, из-за ее точности. В своем ответе я просто заставил использовать text2vec перестановку стоп-слов, чтобы продемонстрировать подход. Надеюсь, это помогло ;-) - person Manuel Bickel; 25.10.2017