Другим потенциальным заголовком для этого поста может быть «При параллельной обработке в R отношение между количеством ядер, размером фрагмента петли и размером объекта?
У меня есть корпус, в котором я выполняю некоторые преобразования с использованием пакета 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)
А здесь находится ссылка на образцы данных. Мне нужно вставить достаточно большой образец документов размером 2 КБ для воссоздания, и это не позволит мне вставить столько, поэтому, пожалуйста, смотрите связанный документ для данных.
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()
. Я просто не знаю, что это такое.
Каково решение? Можно ли воспроизвести эту проблему с помощью связанного корпуса образцов? Я обеспокоен тем, что иногда я могу воспроизвести ошибку, а иногда нет. Изменение размера чанка дает как бы возможность увидеть ошибку с удалением номеров, но не всегда.
Обновлять
Сегодня я возобновил сеанс и не смог воспроизвести ошибку. Я создал документ Google Docs и экспериментировал с различными значениями для корпуса размер, количество ядер и размеры блоков. В каждом случае все прошло успешно. Итак, я попробовал запустить на полных данных, и все сработало. Однако, для моего здравомыслия, я попытался снова запустить на полных данных, и это не удалось. Теперь я снова там, где был вчера.
Похоже, что запуск функции на большом наборе данных что-то изменил... не знаю что! Возможно, какая-то переменная сеанса?
Итак, новая информация заключается в том, что эта ошибка возникает только после запуска функции на очень большом наборе данных. Перезапуск моего сеанса не решил проблему, но возобновил сеансы после нескольких часов отсутствия.
Новая информация:
Возможно, было бы проще воспроизвести проблему в большом корпусе, так как это, по-видимому, вызывает проблему corpus <- do.call(c, replicate(250, corpus, simplify = F))
создаст корпус документов объемом 500 000 на основе предоставленного мной образца. Функция может работать в первый раз, когда вы ее вызываете, но мне кажется, что во второй раз она не работает.
Эта проблема сложна, потому что, если бы я мог воспроизвести проблему, я, вероятно, смог бы идентифицировать и исправить ее.
Новая информация:
Поскольку с этой функцией происходит несколько вещей, было трудно понять, на чем сосредоточить усилия по отладке. Я рассматривал как тот факт, что я использую несколько временных файлов RDS для экономии памяти, так и тот факт, что я выполняю параллельную обработку. Я написал две альтернативные версии скрипта: одна по-прежнему использует файлы rds и разбивает корпус, но не выполняет параллельную обработку (%dopar% заменена просто на %do%, а также удалена строка registerDoParallel), а другая использует параллельную обработку. но не использует временные файлы RDS для разделения небольшого корпуса образцов.
Мне не удалось вызвать ошибку с одноядерной версией скрипта, только с версией, использующей %dopar%, я смог воссоздать проблему (хотя проблема носит периодический характер, она не всегда дает сбой с dopar).
Итак, эта проблема возникает только при использовании %dopar%
. Тот факт, что я использую временные файлы RDS, не является частью проблемы.
docs <- (copy from link above) corpus <- VCorpus(VectorSource(docs))
он берет вектор и превращает его в корпус. Так что просто оберните все в связанном документе внутриVCorpus(VectorSource([character vector goes here]))
- person Doug Fir   schedule 25.08.2017tm
, я бы просто рекомендовал 1) создать собственную функцию предварительной обработки (выучить некоторые регулярные выражения) 2) перейти к другому пакету -text2vec
илиquanteda
- будет намного быстрее и проще - person Dmitriy Selivanov   schedule 29.08.2017text2vec
(я автор :-)). Ознакомьтесь с руководствами на text2vec.org. - person Dmitriy Selivanov   schedule 29.08.2017corpus <- VCorpus(VectorSource([paste the chr vector from link above]))
. Удалите числа и другие преобразования. В двух словах проблема в том, что все работает нормально при использовании одного ядра. Преобразования с использованием tm_map в корпусе работают. Однако при использовании нескольких ядер преобразования tm_map ИНОГДА работают. Его трудно воссоздать, так как он выглядит случайным. Я заметил, что проблема появляется после запуска блока кода на очень... - person Doug Fir   schedule 31.08.2017tm_map
очень сложно отлаживать, имеет много плохо задокументированных особенностей и что использование подходаapply
с вашей собственной пользовательской функцией по сравнению с другим пакетом, вероятно, намного лучше как в краткосрочной, так и в долгосрочной перспективе. - person Gary Weissman   schedule 24.04.2018parallel
одновременно с некоторыми более старыми версиями OpenBLAS. Обновление OpenBLAS устранило эту проблему для меня. Вполне вероятно, чтоtm
использует некоторые функции BLAS, так что это также возможная причина. - person webb   schedule 12.06.2021