Ускорение stringdist в R с помощью Parallel

У меня есть вектор из 300 предложений, и я пытаюсь найти поэлементное расстояние JW, используя пакет stringdist. Время выполнения наивной реализации слишком велико, что заставляет меня искать способы сократить время выполнения. Я пытаюсь использовать пакеты doParallel и foreach, но не получаю значительного ускорения. Вот как я это делаю.

library(foreach)
library(doParallel)
cl = makeCluster(detectCores())
registerDoParallel(cl)

sentence = # vector containing sentences 
jw_dist = foreach(i = 1:length(sentence)) %dopar% {
 temp = sentence[sentence!=sentence[i]]
 return(mean(1 - stringdist::stringdist(sentence[i],temp,method = "jw",nthread = 3))
  }
  stopCluster(cl)

Я был бы очень признателен, если бы кто-нибудь мог указать, как я могу ускорить этот фрагмент кода.


person WitchKingofAngmar    schedule 19.06.2017    source источник
comment
Итак, вы хотите вычислить попарное расстояние между отдельными предложениями? В вашем коде вы используете параллелизм два раза, сначала с dopar, а затем в функции stringdist, где вы указываете количество потоков... Я не думаю, что это хорошая практика   -  person Val    schedule 19.06.2017
comment
Спасибо за ваше предложение. Я заметил, что единственное ускорение, которое я получаю, это когда я использую параметр nthread внутри функции stringdist. Я попытался запустить код с параметром nthread, установленным по умолчанию, и параметром dopar, и не получил никакого ускорения.   -  person WitchKingofAngmar    schedule 19.06.2017
comment
Каково ваше контрольное время и настройка машины? Кроме того, я правильно предполагаю, что вы хотите рассчитать расстояние для каждой пары предложений?   -  person Val    schedule 19.06.2017
comment
Я планирую запустить этот код с ~ 1000 предложений, для которых этот код выполняется примерно за 40 секунд. Я хочу снизить его примерно за 20 секунд.   -  person WitchKingofAngmar    schedule 19.06.2017
comment
Я использую R версии 3.2.2 на 64-битной машине под управлением Ubuntu 17.04. И да, я пытаюсь рассчитать расстояние для каждой пары предложений,   -  person WitchKingofAngmar    schedule 19.06.2017


Ответы (1)


Итак, похоже, вы боретесь с экстремальными накладными расходами.

Вместо того, чтобы распараллеливать отдельные предложения, просто разделите задачу на несколько значительных частей и позвольте apply сделать все остальное. Я выбрал 10 фрагментов по 100 предложений в каждом, возможно, есть более быстрая комбинация, но эта работает намного быстрее (по крайней мере, для меня), чем то, что вы просили:

library(doParallel)
library(foreach)

# generate fake sentences

txt <- readLines(url('https://baconipsum.com/api/?type=all-meat&sentences=300&start-with-lorem=1&format=text'))

sentences <- strsplit(txt,'\\.\\s')[[1]]

sentences <- rep(sentences[sample(1:100,100)],10)

# pairwise combinations of sentences
cbn <- combn(1:length(sentences),2)

# simple timing
st <- Sys.time()

# Since you work on LINUX, you can use FORK
cl <-  makeCluster(detectCores(),type = 'FORK')
registerDoParallel(cl)


res <- foreach(ii = seq(1,1000,100),.combine = 'c') %dopar% {
  
  apply(cbn[,ii:(ii+99)],2,function(x) stringdist(sentences[x[1]],sentences[x[2]],method = "jw"))
   
}

stopCluster(cl)
Sys.time() - st

На моей виртуальной машине Ubuntu этот код выполняется примерно за 1,8 секунды.

Характеристики:

Ubuntu 64 bit
R version 3.4
8 CPU cores
32GB RAM Memory

ХТН

Редактировать:

Возможно, в этом случае хорошей альтернативой будет отказ от параллельной обработки.

Используя эту версию lapply, я могу вычислить среднее значение для каждого предложения примерно за 17 секунд:

res <- do.call(rbind,lapply(1:1000,function(ii) c(ii,1-mean(stringdist(sentences[ii],sentences[-ii],method = "jw")))))

Это даст вам матрицу из двух столбцов с индексом для каждого предложения и 1-mean всех расстояний до соответствующего предложения.

person Val    schedule 19.06.2017
comment
Я хочу рассчитать среднее расстояние JW для каждой записи в sentence с каждой другой записью. Таким образом, я попытаюсь сохранить все попарные расстояния от вашего метода в матрице или что-то в этом роде, а затем вычислить среднее значение по строкам. Спасибо за совет. - person WitchKingofAngmar; 19.06.2017
comment
Я думал, вам нужны попарные сравнения. Учитывая средние значения, я обновил свое решение версией lapply, которая работает достаточно хорошо. - person Val; 19.06.2017
comment
Используя метод в обновлении, теперь я могу работать с 1000 предложений всего за 30 секунд. Я думаю, это хорошее начало. Спасибо за помощь ! - person WitchKingofAngmar; 19.06.2017