Tokyowebmining12

6,333 views
6,216 views

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,333
On SlideShare
0
From Embeds
0
Number of Embeds
3,328
Actions
Shares
0
Downloads
31
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Tokyowebmining12

  1. 1. Rで並列処理:foreachパッケージ解剖学 第12回 データマイニング+WEB @東京  ( #TokyoWebmining 12th)-機械学習 MapReduce・大規模R解析 祭り- 11/06/18 id:(t)yatsuta
  2. 2. 統計型言語S,R● 想定ユーザー:統計学者● アルゴリズムの開発が目的● 対話システムが主体● 大規模データ、バッチ処理、パフォーマンス等は開 発当初は想定外or優先順位低い● 現在、HPC(High Performance Computing)関連 の機能は(パッケージとして)Ad Hocに対応 ● http://cran.r-project.org/web/views/HighPerformanc
  3. 3. foreachパッケージ● REvolution Analyticsによる開発 ● http://www.revolutionanalytics.com/● メタプログラミングによる並列処理構文の導入● 多様なバックエンドに対応 ● doMC/multicoreパッケージ(forkによるマルチコア並列) ● doSMP/revoIPC (Revolutionによるマルチコア並列用バック エンド) ● doSNOW/snowパッケージ(ソケット、MPIなどによるマルチコ ア、分散並列) ● などなど…
  4. 4. (ちょっとRのデータ型について…)● Rの基本的なデータ型(論理値、整数、実数、文字列な ど)はすべてベクタ ● スカラは存在しない。長さ1のベクタをスカラとして扱う ● このスライドではベクタを[1, 2, 3]のように表記する – Rの正式な表記法ではないので注意! – 長さ1のベクタ(スカラ)は、文脈に応じて "1" もしくは "[1]" と表記 する● リストという特殊なベクタが存在する ● どんな型の値でも持つことができるベクタ ● このスライドでは[[ TRUE, 3.14, "foo", [1, 2, 3] ]]のように 表記する
  5. 5. foreach構文> library(foreach)> m <- matrix(1:9, 3, 3)> x <- foreach(j=1:3) %do% sum(m[,j])> x[[1]][1] 6 1 4 7 2 5 8[[2]][1] 15 3 6 9 ↓ ↓ ↓ (sum)[[3]][1] 24 [6][15][24] ↓ [[ [6], [15], [24] ]] (リスト)
  6. 6. .combineオプション> library(foreach)> m <- matrix(1:9, 3, 3)> x <- foreach(j=1:3, .combine="c") %do% sum(m[,j])> x[1] 6 15 24 1 4 7 2 5 8 3 6 9 ↓ ↓ ↓ (sum) [6][15][24] ↓ (c) [6, 15, 24] (ベクタ)
  7. 7. .combineオプション(二項演算子)> library(foreach)> m <- matrix(1:9, 3, 3)> x <- foreach(j=1:3, .combine="*") %do% sum(m[,j])> x[1] 2160 1 4 7 2 5 8 3 6 9 ↓ ↓ ↓ (sum) [6][15][24] ↓ (*) [2160] ((要素数1の)ベクタ) (Rのスカラは要素数1のベクタ)
  8. 8. foreachでイテレータを使う> m <- matrix(1:9, 3, 3)> foreach(x=iter(m, by="col"), .combine="c") %do% sum(x)[1] 6 15 24> foreach (x=irnorm(3, count=4), .combine="rbind") %do% x [,1] [,2] [,3]result.1 -0.0962522 -1.4960423 -0.03788481result.2 0.3546849 0.8134259 -0.75701411result.3 0.1955146 3.3030474 0.18172356result.4 -0.4520477 0.0978961 -1.17332543> foreach (x=irnorm(3, count=4), .combine="cbind") %do% x result.1 result.2 result.3 result.4[1,] -1.004626 -0.1483302 -1.5807664 0.9648528[2,] -1.205300 -0.0608737 -1.9755915 -0.6000012[3,] -1.170364 -0.2418123 -0.3195703 0.4936820
  9. 9. イテレータ# 標準正規分布にしたがう乱数3つを4回発生させる# イテレータを作成> i <- irnorm(3, count=4) > nextElem(i)[1] -0.1176047 -0.5796023 2.8097101> nextElem(i)[1] 0.90631669 -0.18109824 -0.02307174> nextElem(i)[1] -0.4432656 0.7292752 1.8422238> nextElem(i)[1] 0.3814437 0.2424392 -0.5183309> nextElem(i) エラー: StopIteration
  10. 10. イテレータ作成関数いろいろ> iterators::i [tab]iterators::iapply iterators::irnbinomiterators::icount iterators::irnormiterators::icountn iterators::irpoisiterators::idiv iterators::irunifiterators::irbinom iterators::isplititerators::iread.table iterators::iteriterators::ireadLines
  11. 11. iter関数> i <- iter(1:3) > (m <- matrix(1:9, 3, 3)) > i <- iter(m, by="col")> nextElem(i) [,1] [,2] [,3] > nextElem(i)[1] 1 [1,] 1 4 7 [,1]> nextElem(i) [2,] 2 5 8 [1,] 1[1] 2 [3,] 3 6 9 [2,] 2> nextElem(i) [3,] 3[1] 3 > i <- iter(m, by="row") > nextElem(i)> nextElem(i) > nextElem(i) [,1] エラー: StopIteration [,1] [,2] [,3] [1,] 4 [1,] 1 4 7 [2,] 5 > nextElem(i) [3,] 6 [,1] [,2] [,3] > nextElem(i) [1,] 2 5 8 [,1] > nextElem(i) [1,] 7 [,1] [,2] [,3] [2,] 8 [1,] 3 6 9 [3,] 9 > nextElem(i) > nextElem(i) エラー: StopIteration エラー: StopIteration
  12. 12. foreachでイテレータを使う(再掲)> m <- matrix(1:9, 3, 3)> foreach(x=iter(m, by="col"), .combine="c") %do% sum(x)[1] 6 15 24> foreach (x=irnorm(3, count=4), .combine="rbind") %do% x [,1] [,2] [,3]result.1 -0.0962522 -1.4960423 -0.03788481result.2 0.3546849 0.8134259 -0.75701411result.3 0.1955146 3.3030474 0.18172356result.4 -0.4520477 0.0978961 -1.17332543> foreach (x=irnorm(3, count=4), .combine="cbind") %do% x result.1 result.2 result.3 result.4[1,] -1.004626 -0.1483302 -1.5807664 0.9648528[2,] -1.205300 -0.0608737 -1.9755915 -0.6000012[3,] -1.170364 -0.2418123 -0.3195703 0.4936820
  13. 13. (参考)lapply, sapply> lapply(1:3, function(j) sum(m[,j]))[[1]][1] 6[[2]][1] 15[[3]][1] 24> sapply(1:3, function(j) sum(m[,j]))[1] 6 15 24> sapply(1:4, function(.) rnorm(3)) [,1] [,2] [,3] [,4][1,] 1.3330378 1.1512799 0.1380272 0.6304390[2,] 0.9549855 -0.4924635 0.8250889 0.2460827[3,] 0.6248363 0.5773563 1.5333990 -0.9474698
  14. 14. 並列化してみよう> (m <- matrix(1:100, 10, 10)) [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [1,] 1 11 21 31 41 51 61 71 81 91 [2,] 2 12 22 32 42 52 62 72 82 92 [3,] 3 13 23 33 43 53 63 73 83 93 [4,] 4 14 24 34 44 54 64 74 84 94 [5,] 5 15 25 35 45 55 65 75 85 95 [6,] 6 16 26 36 46 56 66 76 86 96 [7,] 7 17 27 37 47 57 67 77 87 97 [8,] 8 18 28 38 48 58 68 78 88 98 [9,] 9 19 29 39 49 59 69 79 89 99[10,] 10 20 30 40 50 60 70 80 90 100# doMC/multicoreバックエンドによる並列化> library(doMC)> registerDoMC(cores=4)> foreach(j=1:10, .combine="c") %dopar% sum(m[,j]) [1] 55 155 255 355 455 555 655 755 855 955
  15. 15. 並列化してみよう# doSMP/revoIPCバックエンドによる並列化> library(doSMP)> w <- startWorkers(4)> registerDoSMP(w)> foreach(j=1:10, .combine="c") %dopar% sum(m[,j]) [1] 55 155 255 355 455 555 655 755 855 955> stopWorkers(w)# doSNOW/snowバックエンドによる並列化> library(doSNOW)> cl <- makeSOCKcluster(4)> registerDoSNOW(cl)> foreach(j=1:10, .combine="c") %dopar% sum(m[,j]) [1] 55 155 255 355 455 555 655 755 855 955> stopCluster(cl)
  16. 16. doMC/multicore● forkシステムコールによるマルチコア並列 ● Windowsでは使えない ● プロセスがどのコアに割り当てられるかはOS任せ● 1タスクに1プロセスを割り当てる ● nタスクあればn回プロセスを起動、終了 ● 一度に起動するプロセス数はregisterDoMC関数の coresオプションで指定
  17. 17. doSMP/revoIPC● ワーカープロセスによるマルチコア並列 ● タスクをワーカーに送り、結果を受け取る – 一種のクライアントサーバ? ● ワーカーは使いまわす ● 各々のワーカーがどのコアで動くかはOS任せ● REvolution Analyticsによるバックエンド (revoIPC)実装 ● C++/boostによる実装 ● 何らかの最適化がされている?
  18. 18. doSNOW/snow● クラスタープロセスによるマルチコア、分散並列 ● タスクをクラスターに送り、結果を受け取る – 一種のクライアントサーバ? ● クラスターは使いまわす● Rでは実績ある並列システム(snow) ● ソケット、MPI、PVMなどに対応
  19. 19. パフォーマンスを上げるには?> (m <- matrix(1:100, 10, 10)) [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [1,] 1 11 21 31 41 51 61 71 81 91 [2,] 2 12 22 32 42 52 62 72 82 92 …# 基本形(行列全体を送る)> foreach(j=1:ncol(m), .combine="c") %dopar% sum(m[,j]) [1] 55 155 255 355 455 555 655 755 855 955# 1列ずつ送って通信データ量を減らす> foreach(x=iter(m, by="col"), .combine="c") %dopar% sum(x) [1] 55 155 255 355 455 555 655 755 855 955# サブ行列に分割して通信データ量、通信回数を減らす> foreach(x=iblkcol(m, chunks=4), .combine="c",+ .packages="foreach") %dopar%+ {foreach(j=1:ncol(x), .combine="c") %do% sum(x[,j])} [1] 55 155 255 355 455 555 655 755 855 955
  20. 20. iblkcol:サブ行列イテレータ作成関数 # iblkcol: foreach/inst/examples/apply.Rからコピー iblkcol <- function(a, chunks) { n <- ncol(a) i <- 1 nextEl <- function() { if (chunks <= 0 || n <= 0) stop(StopIteration) m <- ceiling(n / chunks) r <- seq(i, length=m) i <<- i + m n <<- n - m chunks <<- chunks - 1 a[,r, drop=FALSE] } obj <- list(nextElem=nextEl) class(obj) <- c(abstractiter, iter) obj }
  21. 21. iblkcol:サブ行列イテレータ作成関数x <- iblkcol(m, chunks=4) [6,] 36 46 56 [7,] 37 47 57 > nextElem(x)> nextElem(x) [8,] 38 48 58 [,1] [,2] [,1] [,2] [,3] [9,] 39 49 59 [1,] 81 91 [1,] 1 11 21 [10,] 40 50 60 [2,] 82 92 [2,] 2 12 22 [3,] 83 93 [3,] 3 13 23 > nextElem(x) [4,] 84 94 [4,] 4 14 24 [,1] [,2] [5,] 85 95 [5,] 5 15 25 [1,] 61 71 [6,] 86 96 [6,] 6 16 26 [2,] 62 72 [7,] 87 97 [7,] 7 17 27 [3,] 63 73 [8,] 88 98 [8,] 8 18 28 [4,] 64 74 [9,] 89 99 [9,] 9 19 29 [5,] 65 75 [10,] 90 100[10,] 10 20 30 [6,] 66 76 [7,] 67 77> nextElem(x) [8,] 68 78 [,1] [,2] [,3] [9,] 69 79 [1,] 31 41 51 [10,] 70 80 [2,] 32 42 52 [3,] 33 43 53 [4,] 34 44 54 [5,] 35 45 55
  22. 22. 効果は?● マルチコア環境、大規模データを前提にザックリとした感覚を挙げます。当 然ケースバイケースなのですが…● doMC/multicoreではあまり意味がない ● 各子プロセスは(上書きをしない限りは)親プロセスのメモリへの参照を保持してい る(Copy On Write) → メモリコピーが行われない ● ほとんどの場合、基本形がもっとも高速に動作する – プロセス起動、終了はメモリコピーや計算本体と比較すると多くの場合軽い● doSMP/revoIPC, doSNOW/snowでは ● 通信回数、量とメモリコピーのトレードオフ – iblkcolによる行列のコピー(foreachを実行したRプロセス内) – 通信レイヤーへのコピー – 各々のワーカー/クラスタープロセスが受けとるコピー – … ● 大規模データでiblkcolを使用した場合、瞬間的なメモリ使用量が許容範囲を越え る可能性
  23. 23. foreachの内部構造foreach(j=1:ncol(m), .combine="c")%dopar%sum(m[,j])
  24. 24. foreach関数 オプションforeach(j=1:ncol(m), .combine="c") > foreach(j=1:ncol(m), .combine="c") イテレータ変数と $args イテレータ: 1:ncol(m)() ベクタやリストの場合は 内部でイテレータに変換 $argnames される [1] "j" $evalenv <environment: R_GlobalEnv> … 関数: attr(,"class")foreachオブジェクトを返す [1] "foreach"
  25. 25. %dopar%演算子 %dopar% ユーザー定義演算子(関数)> `%dopar%`function (obj, ex){ e <- getDoPar() e$fun(obj, substitute(ex), parent.frame(), e$data)}<environment: namespace:foreach>
  26. 26. 参考:ユーザ定義演算子# %で挟んだ名前はユーザ定義演算子として利用可能> %norm% <- function(x,y) sqrt(x^2 + y^2)# 通常の関数として使用> %norm%(3,4)[1] 5# 中置演算子として使用> 3 %norm% 4[1] 5
  27. 27. 式:遅延評価とsubstitute関数 sum(m[,j]) 式(languageオブジェクト)参考:遅延評価とsubstitute関数f <- function(arg) { > a エラー: オブジェクト a がありません# expはlanguageオブジェクト > b exp <- substitute(arg) エラー: オブジェクト b がありません a <- 1 > f(a+b) b <- 2 [1] 3 > f(a*b) # expを"評価" [1] 2 eval(exp)}
  28. 28. 並列実行の大まかな流れforeach(j=1:ncol(m)) %dopar% sum(m[,j])↓(等価)%dopar%(foreach(j=1:ncol(m)), sum(m[,j]))↓ ex=sum(m[,j])↓doXXX(foreach(j=1:ncol(m)), substitute(ex), …)↓ registerされた関数(doMC::doMCなど。%do%の場合はdoSEQ関数)↓(各プロセス、ワーカー、クラスターなどでeval(sum(m[,j]), …)(j=1,2,...,ncol(m)))↓ 式(languageオブジェクト)↓(結果を.combineオプション等に基づいてまとめる処理)
  29. 29. compiler対応● R-2.13.0からcompilerパッケージが標準添付 ● バイトコードコンパイラ ● 2倍前後の高速化 (> example(compile))● foreach-1.3.2からcompiler対応 ● 式が自動的にcompileされる > library(compiler) > a <- 1; b <- 2 > (exp <- quote(a + b)) a + b > eval(exp) [1] 3 > (cexp <- compile(exp)) <bytecode: 0xa31ff00> > eval(cexp) [1] 3
  30. 30. 参考資料● > vignette("foreach")● > vignette("nested")● RにおけるHPC~並列計算編~ ● http://www.slideshare.net/sfchaos/rhpc1-tokyor11-6● Rにおける大規模データ解析(第10回 TokyoWebMining) ● http://www.slideshare.net/sfchaos/rbigmemory-toky● Rmpiとsnowで 並列処理 ● http://www.slideshare.net/mokjpn/rmpisnow

×