Сделайте 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)