Несогласованное поведение с функциями преобразования tm_map при использовании нескольких ядер

Другим потенциальным заголовком для этого сообщения может быть "Когда параллельная обработка в r, имеет ли отношение между количеством ядер, размером куска цикла и размером объекта?"

У меня есть corpus. Я выполняю некоторые преобразования при использовании пакета tm. Поскольку корпус большой, я использую параллельную обработку с допараллельным пакетом.

Иногда преобразования выполняют задачу, но иногда они этого не делают. Например, tm::removeNumbers(). Самый первый документ в корпусе имеет значение содержания "n417". Поэтому, если предварительная обработка выполнена успешно, этот документ будет преобразован в "n".

Образец корпуса ниже для воспроизведения. Вот кодовый блок:

library(tidyverse)
library(qdap)
library(stringr)
library(tm)
library(textstem)
library(stringi)
library(foreach)
library(doParallel)
library(SnowballC)

  corpus <- (see below)
  n <- 100 # this is the size of each chunk in the loop

  # split the corpus into pieces for looping to get around memory issues with transformation
  nr <- length(corpus)
  pieces <- split(corpus, rep(1:ceiling(nr/n), each=n, length.out=nr))
  lenp <- length(pieces)

  rm(corpus) # save memory

  # save pieces to rds files since not enough RAM
  tmpfile <- tempfile() 
  for (i in seq_len(lenp)) {
    saveRDS(pieces[[i]],
            paste0(tmpfile, i, ".rds"))
  }

  rm(pieces) # save memory

  # doparallel
  registerDoParallel(cores = 12)
  pieces <- foreach(i = seq_len(lenp)) %dopar% {
    piece <- readRDS(paste0(tmpfile, i, ".rds"))
    # regular transformations        
    piece <- tm_map(piece, content_transformer(removePunctuation), preserve_intra_word_dashes = T)
    piece <- tm_map(piece, content_transformer(function(x, ...) 
      qdap::rm_stopwords(x, stopwords = tm::stopwords("english"), separate = F)))
    piece <- tm_map(piece, removeNumbers)
    saveRDS(piece, paste0(tmpfile, i, ".rds"))
    return(1) # hack to get dopar to forget the piece to save memory since now saved to rds
  } 

  stopImplicitCluster()

  # combine the pieces back into one corpus
  corpus <- list()
  corpus <- foreach(i = seq_len(lenp)) %do% {
    corpus[[i]] <- readRDS(paste0(tmpfile, i, ".rds"))
  }
  corpus_done <- do.call(function(...) c(..., recursive = TRUE), corpus)

И здесь - это ссылка на образцы данных. Мне нужно вставить достаточно большую выборку из 2k docs для воссоздания, и SO не позволит мне вставить это так, поэтому см. Связанный документ для данных.

corpus <- VCorpus(VectorSource([paste the chr vector from link above]))

Если я запустил свой блок кода, как указано выше, с n = до 200, то посмотрите на результаты Я вижу, что числа остаются там, где они должны были быть удалены с помощью tm::removeNumbers()

> lapply(1:10, function(i) print(corpus_done[[i]]$content)) %>% unlist
[1] "n417"
[1] "disturbance"
[1] "grand theft auto"

Однако, если я изменяю размер блока (значение переменной "n" ) до 100:

> lapply(1:10, function(i) print(corpus_done[[i]]$content)) %>% unlist
[1] "n"
[1] "disturbance"
[1] "grand theft auto"

Номера удалены.

Но это непоследовательно. Я попытался сузить его, проведя тестирование на 150, затем 125... и обнаружил, что он будет/не будет работать от 120 до 125 штук. Затем после итерации функции между 120: 125 она иногда работает, а затем не для того же размера блока.

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

Может ли кто-нибудь протянуть руку или даже воспроизвести со связанным образцом тела? Я обеспокоен тем, что иногда могу воспроизвести ошибку, иногда я не могу. Изменение размера куска дает возможность увидеть ошибку с удалением чисел, но не всегда.


Update Сегодня я возобновил сеанс и не смог воспроизвести ошибку. Я создал Google Doc и экспериментировал с разными значениями размера корпуса, количества ядер и размеров блоков. В каждом случае все было успешным. Итак, я попытался работать с полными данными, и все сработало. Однако, по моему здравомыслию, я попробовал снова запустить полные данные, и это не удалось. Теперь я вернулся туда, где был вчера. Кажется, что запустили функцию на более крупном наборе данных, что-то изменило... Я не знаю, что. Возможно, какая-то переменная сеанса? Итак, новая информация заключается в том, что эта ошибка возникает только после запуска функции на очень большом наборе данных. Перезагрузка моей сессии не решила проблему, но возобновление сеансов после простоя в течение нескольких часов.


Новая информация. Возможно, было бы легче воспроизвести проблему на более крупном корпусе, так как это то, что, похоже, вызывает проблему. corpus <- do.call(c, replicate(250, corpus, simplify = F)) создаст 500-килограммовый корпус документа на основе предоставленного мной примера. Функция может работать в первый раз, когда вы ее вызываете, но для меня она, кажется, не срабатывает второй раз.

Эта проблема сложна, потому что, если я смогу воспроизвести проблему, я, вероятно, смогу ее идентифицировать и исправить.


Новая информация. Поскольку с этой функцией было несколько вещей, было трудно понять, где сосредоточить усилия по отладке. Я смотрел на то, что я использую несколько временных файлов RDS для сохранения памяти, а также тот факт, что я выполняю параллельную обработку. Я написал две альтернативные версии script, которая все еще использует файлы rds и разбивает корпус, но не выполняет параллельную обработку (заменил% dopar% только% do%, а также удалил registerDoParallel line) и тот, который использует параллельные но не использует временные файлы RDS для разделения небольшого массива образцов вверх. Я не смог создать ошибку с одноядерной версией script, только с версией, использующей% dopar%, я смог воссоздать проблему (хотя проблема прерывистая, она не всегда терпит неудачу с dopar), Таким образом, этот вопрос появляется только при использовании %dopar%. Тот факт, что я использую временные RDS файлы, как представляется, не является частью проблемы.