接着之前寫的並行算法parallel包,parallel相比foreach來說,相當於是foreach的進階版,好多東西封裝了。而foreach包更為基礎,而且可自定義的內容很多,而且實用性比較強,可以簡單的用,也可以用得很復雜。筆者將自己的學習筆記記錄一下。
R︱並行計算以及提高運算效率的方式(parallel包、clusterExport函數、SupR包簡介)
——————————————————————————————————————
一、foreach包簡介與主要函數解讀
foreach包是revolutionanalytics公司貢獻給R開源社區的一個包,它能使R中的並行計算更為方便。大多數並行計算都主要完成三件事情:將問題分割小塊、對小塊問題進行並行計算、合並計算結果。foreach包中,迭代器完成分割工作,”%dopar%“函數實現對小塊的並行計算,”.combine”函數完成合並工作。
- foreach(..., .combine, .init, .final=NULL, .inorder=TRUE,
- .multicombine=FALSE,
- .maxcombine=if (.multicombine) 100 else 2,
- .errorhandling=c('stop', 'remove', 'pass'),
- .packages=NULL, .export=NULL, .noexport=NULL,
- .verbose=FALSE)
- when(cond)
- e1 %:% e2
- obj %do% ex
- obj %dopar% ex
- times(n)
參數解讀:
(1)%do%嚴格按照順序執行任務(所以,也就非並行計算),%dopar%並行執行任務,%do%時候就像sapply或lapply,%dopar%就是並行啟動器
(2).combine:運算之后結果的顯示方式,default是list,“c”返回vector, cbind和rbind返回矩陣,"+"和"*"可以返回rbind之后的“+”或者“*”,幫你把數據整合起來,太良心了!!
(3).init:.combine函數的第一個變量
(4).final:返回最后結果
(5).inorder:TRUE則返回和原始輸入相同順序的結果(對結果的順序要求嚴格的時候),FALSE返回沒有順序的結果(可以提高運算效率)。這個參數適合於設定對結果順序沒有需求的情況。
(6).muticombine:設定.combine函數的傳遞參數,default是FALSE表示其參數是2,TRUE可以設定多個參數
(7).maxcombine:設定.combine的最大參數
(8).errorhandling:如果循環中出現錯誤,對錯誤的處理方法
(9).packages:指定在%dopar%運算過程中依賴的package(%do%會忽略這個選項),用於並行一些機器學習算法。
(10).export:在編譯函數的時候需要預先加載一些內容進去,類似parallel的clusterExport
如果你不知道自己的機器有沒有啟動並行,你可以通過以下的函數來進行查看,幫助你理解自己電腦的核心數:
- getDoParWorkers( ) #查看注冊了多少個核,配合doMC package中的registerDoMC( )使用
- getDoParRegistered( ) # 查看doPar是否注冊;如果沒有注冊返回FALSE
- getDoParName( ) #查看已經注冊的doPar的名字
- getDoParVersion( ) #查看已經注冊的doPar的version
本節內容主要參考:R語言處理大數據
——————————————————————————————————————
二、新手教程:foreach應用
1、最簡單模式——堪比lapply
- foreach(a=1:3, b=rep(10, 3)) %do% {
- a + b
- }
- ## [[1]]
- ## [1] 11
- ##
- ## [[2]]
- ## [1] 12
- ##
- ## [[3]]
- ## [1] 13
這個並不是並行,只是有着類似lapply的功能。
foreach返回的是list格式值,list格式是默認的數據格式。來看看上面的內容怎么用lapply實現:
- lapply(cbind(1:3,rep(10,3),function(x,y) x+y ))
但是有個小細節就是,%do%之后的{}可以隨意寫中間賦值過程,譬如c<-a+b,這個用lapply不是特別好寫。所以這個我超級喜歡!
這里需要注意的一點是:a, b叫循環變量,循環次數取兩者長度中最小的。譬如a=1,2,3 b=1,2,也就只能循環兩次。
2、參數:.combine——定義輸出結果的整合
默認是foreach之后返回的是list,你可以指定自己想要的格式。.combine選項連接了“c”函數,該函數的功能是連接所有返回值組成向量。此外,我們可以使用“cbind”將生成的多個向量組合成矩陣,例如生成四組隨機數向量,進而按列合並成矩陣:
- foreach(i=1:4, .combine="cbind") %do% rnorm(4)
- ## result.1 result.2 result.3 result.4
- ## [1,] 0.26634 -0.73193 -0.25927 0.8632
- ## [2,] 0.54132 0.08586 1.46398 -0.6995
- ## [3,] -0.15619 0.85427 -0.47997 0.2160
- ## [4,] 0.02697 -1.40507 -0.06972 0.2252
運算之后結果的顯示方式,default是list,“c”返回vector, cbind和rbind返回矩陣,"+"和"*"可以返回rbind之后的“+”或者“*”,幫你把數據整合起來。
.combine還可以接上自己編譯的函數,這點很人性化,譬如:
- cfun <- function(a, b) a+b
- foreach(i=1:4, .combine="cfun") %do% rnorm(4)
.combine幫你把輸出結果,再要調整的問題一次性解決了,並且將數據整合也進行並行加速,棒!
一些關於.combine的c,rbind,cbind,*,+其他案例:
- x <- foreach(a=1:3, b=rep(10, 3), .combine="c") %do%
- {
- x1<-(a + b);
- x2<-a*b;
- c(x1,x2);
- }
- > x
- [1] 11 10 12 20 13 30
- > x <- foreach(a=1:3, b=rep(10, 3), .combine="rbind") %do%
- {
- x1<-(a + b);
- x2<-a*b;
- c(x1,x2);
- }
- > x
- [,1] [,2]
- result.1 11 10
- result.2 12 20
- result.3 13 30
- > x <- foreach(a=1:3, b=rep(10, 3), .combine="cbind") %do%
- {
- x1<-(a + b);
- x2<-a*b;
- c(x1,x2);
- }
- > x
- result.1 result.2 result.3
- [1,] 11 12 13
- [2,] 10 20 30
- > x <- foreach(a=1:3, b=rep(10, 3), .combine="+") %do%
- {
- x1<-(a + b);
- x2<-a*b;
- c(x1,x2);
- }
- > x
- [1] 36 60
- > x <- foreach(a=1:3, b=rep(10, 3), .combine="*") %do%
- {
- x1<-(a + b);
- x2<-a*b;
- c(x1,x2);
- }
- > x
- [1] 1716 6000
3、參數.inorder——定義輸出結果的順序
.inorder:TRUE則返回和原始輸入相同順序的結果(對結果的順序要求嚴格的時候),FALSE返回沒有順序的結果(可以提高運算效率)。這個參數適合於設定對結果順序沒有需求的情況。
順序這東西,寫過稍微復雜的函數都知道,特別在數據匹配時尤為重要,因為你需要定義一些rownames的名稱,這時候輸出的順序萬一不匹配,可能后面還要花時間匹配過來。
- foreach(i=4:1, .combine='c', .inorder=FALSE) %dopar% {
- Sys.sleep(3 * i)
- i
- }
- ## [1] 4 3 2 1
——————————————————————————————————————
三、中級教程:利用doParallel並行+聯用迭代器優化內存
1、利用doParallel並行——%dopar%
foreach包創作是為了解決一些並行計算問題,將”%do%“更改為“%dopar%”前面例子就可以實現並行計算。在並行之前,需要register注冊集群:
- library(foreach)
- library(doParallel)
- cl<-makeCluster(no_cores)
- registerDoParallel(cl)
要記得最后要結束集群(不是用stopCluster()):stopImplicitCluster()
2、參數when——按條件運算
- foreach(a=irnorm(1, count=10), .combine='c') %:% when(a >= 0) %do% sqrt(a)
其中when是通過%:%來導出,而且%:%之后,還可以接%do%
- qsort <- function(x) {
- n <- length(x)
- if (n == 0) {
- x
- } else {
- p <- sample(n, 1)
- smaller <- foreach(y=x[-p], .combine=c) %:% when(y <= x[p]) %do% y
- larger <- foreach(y=x[-p], .combine=c) %:% when(y > x[p]) %do% y
- c(qsort(smaller), x[p], qsort(larger))
- }
- }
- qsort(runif(12))
- ## [1] 0.1481 0.2000 0.2769 0.4729 0.4747 0.5730 0.6394 0.6524 0.8315 0.8325
- ## [11] 0.8413 0.8724
3、聯用iterators——優化、控制內存
- iter(obj, by=c('column', 'cell', 'row'), chunksize=1L, checkFunc=function(...) TRUE, recycle=FALSE, ...)
參數解讀:
(1)iter+function迭代輸出
來看一個iter案例,幫你把函數分塊給你,不用一次性導入計算,耗費內存:
- > a
- [,1] [,2] [,3] [,4] [,5]
- [1,] 1 5 9 13 17
- [2,] 2 6 10 14 18
- [3,] 3 7 11 15 19
- [4,] 4 8 12 16 20
- > i2 <- iter(a, by = "column", checkFunc=function(x) sum(x) > 50)
- > nextElem(i2)
- [,1]
- [1,] 13
- [2,] 14
- [3,] 15
- [4,] 16
- > nextElem(i2)
- [,1]
- [1,] 17
- [2,] 18
- [3,] 19
- [4,] 20
- > nextElem(i2)
- 錯誤: StopIteration
不過,如果沒有next了,就會出現報錯,這時候就需要稍微注意一下。
(2)生成隨機數
- > i2 <- icountn(c(3.4, 1.2))
- > nextElem(i2)
- [1] 1 1
- > nextElem(i2)
- [1] 2 1
- > nextElem(i2)
- [1] 3 1
- > nextElem(i2)
- [1] 4 1
- > nextElem(i2)
- [1] 1 2
- > nextElem(i2)
- [1] 2 2
- > nextElem(i2)
- [1] 3 2
- > nextElem(i2)
- [1] 4 2
- > nextElem(i2)
- 錯誤: StopIteration
——————————————————————————————————————
四、高級教程:並行機器學習算法
並行計算一些小任務會比按順序運算它們花費更多的時間,所以當普通運算足夠快的時候,並沒有必要使用並行計算模式改進其運算效率。
同時,最適合並行莫過於隨機森林算法了。
- #生成矩陣x作為輸入值,y作為目標因子
- x <- matrix(runif(500), 100)
- y <- gl(2, 50)
- #導入randomForest包
- require(randomForest)
1、獨立循環運行隨機森林算法
如果我們要創建一個包含1200棵樹的隨機森林模型,在6核CPU電腦上,我們可以將其分割為六塊執行randomForest函數六次,同時將ntree參賽設為200,最后再將結果合並。
- rf <- foreach(ntree=rep(200, 6), .combine=combine) %do%
- randomForest(x, y, ntree=ntree)
- rf
- ##
- ## Call:
- ## randomForest(x = x, y = y, ntree = ntree)
- ## Type of random forest: classification
- ## Number of trees: 1200
- ## No. of variables tried at each split: 2
分開來運行6個200樹的隨機森林算法。
2、參數.packages——並行運行隨機森林算法
將%do%改為“%dopar%”,同時使用.packages調用randomForest:
- rf <- foreach(ntree=rep(200,6), .combine=combine, .packages="randomForest") %dopar%
- randomForest(x, y, ntree=ntree)
- rf
通過.packages來將函數包導入其中,類似parallel中的clusterEvalQ,但是foreach在一個函數里面包含了函數、包的導入過程。
當然還可以使用一些其他包,使用.packages參數來加載包,比如說:.packages = c("rms", "mice")
3、參數.export——將doParallel並行寫入函數
- test <- function (exponent) {
- foreach(exponent = 2:4,
- .combine = c) %dopar%
- base^exponent
- }
- test()
- Error in base^exponent : task 1 failed - "object 'base' not found"
- base <- 2
- cl<-makeCluster(2)
- registerDoParallel(cl)
- base <- 4
- test <- function (exponent) {
- foreach(exponent = 2:4,
- .combine = c,
- .export = "base") %dopar%
- base^exponent
- }
- test()
- stopCluster(cl)
- [1] 4 8 16
——————————————————————————————————————
參考文獻:
1、R語言中的並行計算:foreach,iterators, doParallel包
2、foreach包簡介 / FROM:《Using The foreach Package》
4、R︱並行計算以及提高運算效率的方式(parallel包、clusterExport函數、SupR包簡介)
轉自:http://blog.csdn.net/sinat_26917383/article/details/53349557
