2017-08-23 90 views
0

我有一個很大的語料庫,我正在與tm::tm_map()進行轉換。由於我使用託管的R Studio,因此我有15個內核,並希望利用並行處理來加快速度。多邊並行嵌套在循環中的循環工作,但邏輯上沒有意義?

沒有共享一個非常大的語料庫,我簡直無法用虛擬數據重現。

我的代碼如下。對問題的簡短描述是在控制檯中手動循環切片,但在我的函數內部不這樣做。

函數「clean_corpus」將語料庫作爲輸入,將其分解成片段並保存到臨時文件以幫助解決內存問題。然後該函數使用%dopar%塊對每個片段進行迭代。該功能在對語料庫的一小部分進行測試時起作用,例如10K文件。但是在較大的語料庫上,函數返回NULL。爲了調試,我設置了函數來返回已經循環的單個片段,而不是整個重建的語料庫。我發現在較小的語料庫樣本中,代碼會按預期返回所有迷你語料庫的列表,但是當我在語料庫的較大樣本上進行測試時,該函數將返回一些空值。

這裏的原因,這是莫名其妙對我說:

cleaned.corpus <- clean_corpus(corpus.regular[1:10000], n = 1000) # works 
cleaned.corpus <- clean_corpus(corpus.regular[10001:20000], n = 1000) # also works 
cleaned.corpus <- clean_corpus(corpus.regular[1:50000], n = 1000) # NULL 

如果我這樣做是10K塊高達例如50k通過5次迭代一切正常。如果我在例如完整的50k文件它返回NULL。

所以,也許我只是需要循環更多的小碎片通過打破我的語料庫。我試過這個。在下面的clean_corpus函數中,參數n是每件的長度。該函數仍然返回NULL。

所以,如果我重複這樣的:

# iterate over 10k docs in 10 chunks of one thousand at a time 
cleaned.corpus <- clean_corpus(corpus.regular[1:10000], n = 1000) 

如果我這樣做,手動5次高達50K一切正常。通過我的函數在一次調用中這樣做的等價物是:

# iterate over 50K docs in 50 chunks of one thousand at a time 
cleaned.corpus <- clean_corpus(corpus.regular[1:50000], n = 1000) 

返回NULL。

This SO帖子和唯一答案中的鏈接提示可能與我在linux上的RStudio託管實例有關,因爲linux「內存不足的兇手」可能會阻止工作人員。這就是爲什麼我試圖將我的語料庫分解成碎片,以解決內存問題。

任何關於爲什麼在10k大小的1k中迭代10k文檔的任何理論或建議都適用,而50k大小的1k不適用?

這裏的clean_corpus功能:從上面再次

clean_corpus <- function(corpus, n = 500000) { # n is length of each peice in parallel processing 

    # 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 = 14) # I've experimented with 2:14 cores 
    pieces <- foreach(i = seq_len(lenp)) %dopar% { 
    piece <- readRDS(paste0(tmpfile, i, ".rds")) 
    # transformations 
    piece <- tm_map(piece, content_transformer(replace_abbreviation)) 
    piece <- tm_map(piece, content_transformer(removeNumbers)) 
    piece <- tm_map(piece, content_transformer(function(x, ...) 
     qdap::rm_stopwords(x, stopwords = tm::stopwords("en"), separate = F, strip = T, char.keep = c("-", ":", "/")))) 
    } 

    # combine the pieces back into one corpus 
    corpus <- do.call(function(...) c(..., recursive = TRUE), pieces) 
    return(corpus) 

} # end clean_corpus function 

代碼塊只是打字功能之後可讀性的流程:

# iterate over 10k docs in 10 chunks of one thousand at a time 
cleaned.corpus <- clean_corpus(corpus.regular[1:10000], n = 1000) # works 

# iterate over 50K docs in 50 chunks of one thousand at a time 
cleaned.corpus <- clean_corpus(corpus.regular[1:50000], n = 1000) # does not work 

但在控制檯迭代通過調用每個

功能
corpus.regular[1:10000], corpus.regular[10001:20000], corpus.regular[20001:30000], corpus.regular[30001:40000], corpus.regular[40001:50000] # does work on each run 

注意我嘗試使用庫tm功能進行並行處理(請參見here),但我一直在打「無法分配內存」錯誤,這就是爲什麼我試圖使用doparallel %dopar%「自己做」的原因。

+0

嗨,感謝您的評論。我知道這是一個記憶問題..但這正是我去循環路線的原因。循環是否有助於通過大塊計算而不是整體計算來緩解這種情況? –

+0

此外,我確實看着他的腳本運行1 +核心通過殼>頂部> 1.在每種情況下似乎都失去了免費的內存。 –

+0

啊,我從來沒有考慮過這個。事情是我能夠將整個結構加載到R.50k樣本對於整個10M文檔語料庫是很小的,所以即使是塊也不應該導致內存問題。我想知道我是否應該嘗試將所有片斷保存到臨時文件中,就像我在功能的頂部附近做的那樣 –

回答

1

從評論的解決方案概要

你的內存問題可能與corpus <- do.call(function(...) c(..., recursive = TRUE), pieces),因爲這仍然保存你所有的(輸出)的數據在內存中

我建議從每個工人到出口的輸出文件,例如RDScsv文件,而不是將其收集到最後的單個數據結構中

另一個問題(如您所指出的)是:foreach會將隱含的return語句({}中的代碼塊作爲函數對待dopar之後)的每個工人的輸出保存。我建議在關閉}之前添加一個明確的return(1),以便不將預期的輸出保存到內存中(您已將其顯式保存爲文件)。