Общий вопрос о векторизации и распараллеливании в R - PullRequest
1 голос
/ 01 мая 2020

Я новичок в R, но я хотел бы понять и создать быстрый код с TensorFlow в Rstudio. Я понимаю концепцию распараллеливания, но у меня возникают некоторые проблемы с пониманием различий между этими понятиями: распараллеливание, векторизация и тензорность (извините за мой английский sh). Я хотел бы несколько простых примеров, чтобы понять эти различия. Могу ли я применить все эти концепции одновременно?

1 Ответ

5 голосов
/ 01 мая 2020

Вот мои два цента по распараллеливанию и векторизации в R. Я не буду рассматривать тензорность, поскольку у меня нет большого опыта работы с TensorFlow. Тем не менее, имея опыт работы в дифференциальной геометрии, я думаю, что для решения определенных задач лучше использовать тензор, то есть многомерные (data-) структуры.

Распараллеливание

Основа c Идея распараллеливания заключается в запуске задач одновременно . Часто, особенно когда реализовано в R, эта концепция обрабатывается с помощью многопроцессорная обработка : обычно это распределяет задачи по процессорам компьютера (или потокам см. многопоточность или проверяется это замечательно *). 1011 * ТАК ответ ). Кроме того, распараллеливание можно рассматривать как один из способов решения проблемы параллелизма : последний имеет и другие реализации, такие как асинхронное программирование .

Типичный пример распараллеливания (и также для параллелизма) следующее: Предположим, у вас есть список URL url1, url2, ..., и вам необходимо отправить запрос GET (и дождаться ответа) каждому из них. Классическим (синхронным) способом было бы перебрать все URL, сделать запрос GET, ждать ответа, а затем (и только тогда) перейти к следующему URL.

# Dummy example list
urls <- rep('http://example.com', 7)
# Fetching the data
results <- rep(list(NA), length(urls))
for (k in seq_along(urls))
  results[[k]] <- httr::GET(urls[k])

Причина, по которой это классический пример, заключается в том, что (как правило) эти запросы независимы друг от друга: теоретически нам не нужно ждать первого ответа, прежде чем делать второй запрос. Таким образом, мы могли бы отправлять эти запросы одновременно:

# Parallel
urls <- rep('http://example.com', 7)
num_cores <- parallel::detectCores() - 1
cl <- parallel::makeCluster(num_cores)
parallel::clusterEvalQ(cl, library(httr))
parallel::clusterExport(cl, varlist = c('urls'))
results <- parallel::parLapply(cl, urls, httr::GET)
parallel::stopCluster(cl)

В приведенном выше коде большинство строк о настройке, но важная строка - вторая, последняя за последней: именно здесь мы распространяем и выполняем задачи для разных ядер (ЦП).

По сути, распараллеливание тесно связано с задачами и времени.

Векторизация

Эта топи c гораздо проще. Язык R по своей природе оптимизирован для векторизованных операций: векторы, матрицы и массивы встроены в R - это не так для любого языка.

Кроме того, операции и функции также векторизованы: например, R поддерживает деление векторов 1:5 / 11:15 и в основном ведет себя так, как можно было ожидать (типичные ловушки хорошо документированы, например, 1:5 + 11:20). Python например, имеет списки как встроенные функции, но не поддерживает (по своей природе) векторизацию: что-то вроде range(5) / range(11, 15) выдаст ошибку (да, есть библиотеки, которые сделают это возможным).

Это не черные маги c хотя: при выполнении paste0("url_", 1:5) al oop происходит на языке нижнего уровня C, что делает его на несколько порядков быстрее, чем зацикливание в R. Это также, почему циклы имеют плохую репутацию в R (даже если правильное зацикливание абсолютно нормально). Вот очень наивная иллюстрация

microbenchmark::microbenchmark(
  loop = {
    v1 <- 1:5; v2 <- 6:10
    result <- rep(NA, length(v1))
    for (k in seq_along(v1))
      result[k] <- v1[k] + v2[k]
  },
  vectorization = {
    result <- 1:5 + 6:10
  }
)
# Unit: nanoseconds
#          expr     min      lq       mean  median      uq     max neval cld
#          loop 1367900 1377052 1431076.98 1396951 1407551 4317901   100   b
# vectorization     400     501    1145.95    1500    1601    4001   100  a 

Суть в том, что векторизация окружает (в основном) каждый угол R, и, по моему скромному мнению, это сущность элегантности R и ее самой красивой функции (обычай операторы тоже аккуратны). Например, можно создать грех Taylor Expansion с одной строкой:

f <- function(x, n = 10) sum((-1)^(0:n) * x^(2*(0:n) + 1) / factorial((2*(0:n) + 1)))
f(0)
# 0
f(pi)
# 1.034819e-11

Наконец, помимо эстетики, векторизация R - это философия, способ решения проблем: c задача, при кодировании на R вы всегда будете пытаться найти векторизованное решение проблемы, тогда как в других языках вы просто применяете циклы поверх циклов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...