接着之前寫的並行算法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
