Сделайте udpipe_annotate() быстрее

В настоящее время я работаю над документом Text Mining, где хочу абстрагировать релевантные ключевые слова из моего текста (обратите внимание, что у меня есть много, много текстовых документов).

Я использую пакет udpipe. Отличная виньетка онлайн ( http://bnosac.be/index.php/blog/77-an-overview-of-keyword-extraction-techniques). Все работает, но когда я запускаю код, часть

x <- udpipe_annotate(ud_model, x = comments$feedback)

действительно очень медленно (особенно когда у вас много текста). Есть ли кто-нибудь, кто знает, как мне получить эту часть быстрее? Обойти это, конечно, хорошо.

library(udpipe)
library(textrank)
## First step: Take the Spanish udpipe model and annotate the text. Note: this takes about 3 minutes

data(brussels_reviews)
comments <- subset(brussels_reviews, language %in% "es")
ud_model <- udpipe_download_model(language = "spanish")
ud_model <- udpipe_load_model(ud_model$file_model)
x <- udpipe_annotate(ud_model, x = comments$feedback) # This part is really, really slow 
x <- as.data.frame(x)

Спасибо заранее!

3 ответа

Решение

Я добавляю ответ на основе будущего API. Это работает независимо от того, какую ОС (Windows, Mac или Linux) вы используете.

В пакете future.apply есть все параллельные альтернативы для базового семейства apply. Остальная часть кода основана на ответе @jwijffels. Разница лишь в том, что я использую data.table в функции annotate_splits.

library(udpipe)
library(data.table)

data(brussels_reviews)
comments <- subset(brussels_reviews, language %in% "es")
ud_model <- udpipe_download_model(language = "spanish", overwrite = F)
ud_es <- udpipe_load_model(ud_model)


# returns a data.table
annotate_splits <- function(x, file) {
  ud_model <- udpipe_load_model(file)
  x <- as.data.table(udpipe_annotate(ud_model, 
                                     x = x$feedback,
                                     doc_id = x$id))
  return(x)
}


# load parallel library future.apply
library(future.apply)

# Define cores to be used
ncores <- 3L
plan(multiprocess, workers = ncores)

# split comments based on available cores
corpus_splitted <- split(comments, seq(1, nrow(comments), by = 100))

annotation <- future_lapply(corpus_splitted, annotate_splits, file = ud_model$file_model)
annotation <- rbindlist(annotation)

В пакете R udpipe используется библиотека C++ UDPipe версии 1.2. Скорость аннотации подробно описана в документе (см. Таблицу Таблица 8 в https://doi.org/10.18653/v1/K17-3009). Если вы хотите ускорить его, запустите его параллельно, поскольку аннотации тривиально распараллеливаемы.

Приведенный ниже пример распараллеливает 16 ядер с помощью функции Parallels::mclapply, что дает вам 16-кратное ускорение для больших корпораций, если у вас, конечно, 16 ядер. Вы можете использовать любую имеющуюся у вас структуру параллелизации, ниже я использовал пакет параллельных вычислений - если вы работаете в Windows, вам понадобится, например, parallel::parLapply, но ничто не мешает вам использовать другие параллельные опции (snow / multicore / future / foreach /...) аннотировать параллельно.

library(udpipe)
library(data.table)
library(parallel)
data(brussels_reviews)
comments <- subset(brussels_reviews, language %in% "fr")
ud_model <- udpipe_download_model(language = "french-partut")

annotate_splits <- function(x, file) {
  model <- udpipe_load_model(file)
  x <- udpipe_annotate(model, x = x$feedback, doc_id = x$id, tagger = "default", parser = "default")
  as.data.frame(x, detailed = TRUE)
}

corpus_splitted <- split(comments, seq(1, nrow(comments), by = 100))
annotation <- mclapply(corpus_splitted, FUN = function(x, file){
  annotate_splits(x, file) 
}, file = ud_model$file_model, mc.cores = 16)
annotation <- rbindlist(annotation)

Обратите внимание, что udpipe_load_model также требует некоторого времени, поэтому, вероятно, лучшая стратегия - распараллелить ее по числу ядер, которые у вас есть на вашей машине, а не по 100 штук, как я показал выше.

Вы также можете выполнить это с помощью furrrа также futureбиблиотеки, которые имеют дополнительный бонус в виде индикатора выполнения.

Одна вещь, которую я смущаю в двух других ответах, - это их реализация udpipe_load_modelв рамках своих функций. Вы можете сначала загрузить модель вне функции один раз, чтобы функция не загружала модель каждый раз при запуске.

      library(udpipe)
library(future)
library(furrr)
data(brussels_reviews)

comments <- subset(brussels_reviews, language %in% "es")
downloaded_model <- udpipe_download_model(language = "spanish", overwrite = FALSE)
model <- udpipe_load_model(downloaded_model)

annotate_splits <- function(text) {
  anno <- udpipe_annotate(model, x = text$feedback, doc_id = text$id, tagger = "default", parser = "default")
  x <- as.data.frame(anno, detailed = TRUE)
  return(x)
}

split_corpus <- split(comments, seq(1, nrow(comments), by = 100))

#recommend setting workers equal to number of your computer's cores
plan(multisession, workers = 2) 
dfs <- future_map(split_corpus, annotate_splits, .progress = TRUE)

annotated_df <- dplyr::bind_rows(dfs)
Другие вопросы по тегам