Juliaで並列計算

62
Juliaで並列計算 2014年9月27日 第2回JuliaTokyo @sfchaos

description

 

Transcript of Juliaで並列計算

Page 1: Juliaで並列計算

Juliaで並列計算

2014年9月27日

第2回JuliaTokyo

@sfchaos

Page 2: Juliaで並列計算

自己紹介

TwitterID: @sfchaos

お仕事: データマイニング, セマンティックWeb,

オントロジー, etc.

使用言語: R/C++/Perl/Python/etc.

1

Page 3: Juliaで並列計算

アジェンダ

1. 並列計算とJulia

2. 並列化できる処理の例

3. Juliaでの並列計算の流れ

4. 並列計算 文法編

5. 並列計算 実践編

6. まとめ

2

Page 4: Juliaで並列計算

1. 並列計算とJulia

3

Page 5: Juliaで並列計算

並列計算とは

最近は、一般的なパソコンでも複数のコアを搭載することが当たり前になってきた。

並列計算 = 計算機、CPU、コアなどの計算リソースを有効に活用して、並列的に処理を同時に実行すること

4

タスク1

タスク2

タスク3

タスク4

タスク1

タスク2

タスク3

タスク4

並列計算のイメージ

Page 6: Juliaで並列計算

Julia Powered by Parallel Computing

Juliaの特徴の一つに、標準で並列計算をサポートしている点がある。

5

Designed for parallelism anddistributed computation

Page 7: Juliaで並列計算

Julia Powered by Parallel Computing

速いといわれるJuliaで並列計算すれば、最強??

6

Powered By Parallel Computinggc問題が残っている・・・?

Page 8: Juliaで並列計算

2. 並列化できる処理の例

7

Page 9: Juliaで並列計算

k平均法

結果が初期値に依存するので、複数回の試行が望ましい。

複数回の試行は互いに独立しており、並列化可能。

8

① 各データにランダムに割り当てたクラスターのラベルを用いて、各クラスターのデータの中心をクラスターの中心とする。

② 各データから最も近いクラスター中心のクラスターを新たなラベルとする。

③ 各クラスターのデータの中心を

新たなクラスター中心とする。

クラスター1

クラスター3

クラスター中心

クラスター2

クラスター1

クラスター3クラスター2

クラスター中心が収束するまで繰り返す

Page 10: Juliaで並列計算

ブートストラップ法

ブートストラップ標本の作成や統計量計算は並列化可能。

9

Page 11: Juliaで並列計算

ランダムフォレスト

クラス分類・回帰を実行する機械学習の代表的な手法。

訓練データに対してサンプルのブートストラップと説明変数のサンプリングを行って複数の決定木を構築。

テストデータに対して、各決定木の予測結果の多数決により予測を実行。

10

Page 12: Juliaで並列計算

その他の並列化可能な処理

モンテカルロシミュレーション

ハイパーパラメータ探索・クロスバリデーション(機械学習の予測モデル構築・評価)

etc.

11

Page 13: Juliaで並列計算

3. Juliaでの並列計算の流れ

12

Page 14: Juliaで並列計算

Juliaの並列計算のコンセプト

Juliaの並列計算は、メッセージパッシングで実装。

※ 通信方式はTCP/IPのソケット?(base/multi.jl参照)

通常のメッセージパッシングは、プロセス間でデータや命令などを相互にやりとりする。

13

プロセス1 プロセス2

プロセス3 プロセス4

通常のメッセージパッシング

データ、命令をプロセス間でやり取り

Page 15: Juliaで並列計算

Juliaの並列計算のコンセプト

Juliaのメッセージパッシングの実装は、あるプロセスから他のプロセスへの一方通行。

14

マスタープロセス

ワーカープロセス

データ転送、処理の命令

あるプロセスから他プロセスへの一方通行

Page 16: Juliaで並列計算

Juliaの並列計算のコンセプト

そのため、ユーザは片方のプロセスだけ管理すればO.K.

15

マスタープロセス

ワーカープロセス

データ転送、処理の命令

あるプロセスから他プロセスへの一方通行

ユーザは片方のプロセスを管理すればO.K.

Page 17: Juliaで並列計算

Juliaにおける並列計算の流れ

16

マスタープロセス

ワーカープロセス

Step.1ワーカープロセスの生成

Step.1ワーカープロセスの

生成

Page 18: Juliaで並列計算

Juliaにおける並列計算の流れ

17

マスタープロセス

ワーカープロセス

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Page 19: Juliaで並列計算

Juliaにおける並列計算の流れ

18

マスタープロセス

ワーカープロセス

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.3ワーカープロセスでの

処理の実行

Step.3ワーカープロセスでの

処理の実行

Page 20: Juliaで並列計算

Juliaにおける並列計算の流れ

19

マスタープロセス

ワーカープロセス

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.3ワーカープロセスでの

処理の実行

Step.4マスタープロセスからワーカープロセスの処理結果の参照

処理結果

処理結果

処理結果

処理結果

Step.4マスタープロセスからワーカープロセスの処理結果の参照

参照

Page 21: Juliaで並列計算

Juliaにおける並列計算の流れ

20

マスタープロセス

ワーカープロセス

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.3ワーカープロセスでの

処理の実行

Step.4マスタープロセスからワーカープロセスの処理結果の参照

Page 22: Juliaで並列計算

Juliaにおける並列計算の流れ

21

マスタープロセス

ワーカープロセス

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.3ワーカープロセスでの

処理の実行

Step.4マスタープロセスからワーカープロセスの処理結果の参照

Step.3は、ワーカープロセスがマスタープロセスと独立して実行

Page 23: Juliaで並列計算

Juliaにおける並列計算の流れ

22

マスタープロセス

ワーカープロセス

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.3ワーカープロセスでの

処理の実行

Step.4マスタープロセスからワーカープロセスの処理結果の参照

以下では、Step.1, 2, 4について説明

Page 24: Juliaで並列計算

Step.1 ワーカープロセスの生成

ワーカープロセスを生成するタイミングは、① マスタープロセスの起動時

② マスタープロセスの起動後

23

マスタープロセス

Step.1ワーカープロセスの生成

Page 25: Juliaで並列計算

Step.1 ワーカープロセスの生成

マスタープロセスの起動時における生成

24

$ julia –p 4

julia> # ワーカープロセス数の確認julia> nworkers()

4

julia> # マスターとワーカーの合計プロセス数の確認julia> nprocs()

5

起動時にpオプションにワーカープロセス数を指定

ワーカープロセス数の確認

Page 26: Juliaで並列計算

Step.1 ワーカープロセスの生成

マスタープロセスの起動後における生成

25

julia> nprocs()

1

julia> nworkers()

1

julia> # 4つのワーカープロセスの生成julia> addprocs(4)

4-element Array{Any,1}:

2

3

4

5

julia> nprocs()

5

julia> nworkers()

4

addprocs関数で生成するワーカープロセスの個数を指定

Page 27: Juliaで並列計算

Step.2 ワーカープロセスへのデータ転送・処理の命令

処理の命令(リモートコール)

26

remotecall(nproc, function, args)

あるプロセッサから他のプロセッサに引数を渡して関数を実行させるための呼び出し。

返り値はリモートリファレンス(remote reference)というもの。

Page 28: Juliaで並列計算

Step.2 ワーカープロセスへのデータ転送・処理の命令

処理の命令(リモートコール)

27

$ julia -p 2

julia> # 乱数種の設定julia> srand(123)

julia> r = remotecall(2, rand, 2, 2)

RemoteRef(2,1,4)

実行例)ワーカープロセス2で、2×2の一様乱数の行列を生成

remotecall(nproc, function, args)

あるプロセッサから他のプロセッサに引数を渡して関数を実行させるための呼び出し。

返り値はリモートリファレンス(remote reference)というもの。

Page 29: Juliaで並列計算

Step.2 ワーカープロセスへのデータ転送・処理の命令

データの転送

28

julia> A = rand(1000,1000)

julia> Bref = @spawn A^2

...

julia> fetch(Bref)

julia> Bref = @spawn rand(1000,1000)^2

...

julia> fetch(Bref)

マスタープロセスでデータを生成し、ワーカープロセスにデータを転送

ワーカープロセスでデータを生成

通信オーバーヘッド

データ転送するしないは、ケースバイケース

Page 30: Juliaで並列計算

Step.4 マスタープロセスからワーカープロセスの処理結果の参照

リモートリファレンスからの値の取得

29

fetch(expr)

Page 31: Juliaで並列計算

Step.4 マスタープロセスからワーカープロセスの処理結果の参照

リモートリファレンスからの値の取得

30

$ julia -p 2

julia> # 乱数種の設定julia> srand(123)

julia> r = remotecall(2, rand, 2, 2)

RemoteRef(2,1,4)

julia> fetch(r)

2x2 Array{Float64,2}:

0.353081 0.369369

0.235385 0.183653

実行例)ワーカープロセス2で作成した行列の値を取得

fetch(expr)

Page 32: Juliaで並列計算

ここまでのまとめJuliaにおける並列計算の流れ

31

$ julia –p 4

$ julia -p 2

julia> # 乱数種の設定julia> srand(123)

julia> r = remotecall(2, rand, 2, 2)

RemoteRef(2,1,4)

julia> fetch(r)

2x2 Array{Float64,2}:

0.353081 0.369369

0.235385 0.183653

マスタープロセス起動時

julia> addprocs(4)

マスタープロセス起動後

Step.1ワーカープロセスの

生成

Step.2ワーカープロセスへの

データ転送・処理の命令

Step.3ワーカープロセスでの

処理の実行

Step.4マスタープロセスからワーカープロセスの処理結果の参照

Page 33: Juliaで並列計算

4. Juliaで並列計算 文法編

32

Page 34: Juliaで並列計算

@spawnatマクロ

評価する式とワーカープロセスを指定して実行

33

@spawnat(p, expr)

第2引数で指定した式を第1引数で指定したワーカープロセスで実行

Page 35: Juliaで並列計算

@spawnatマクロ(remotecallの代替)

評価する式とワーカープロセスを指定して実行

34

julia> s = @spawnat 2 1 + fetch(r)

RemoteRef(2,1,6)

julia> fetch(s)

2x2Array{Float64,2}:

1.34133 1.0674

1.76699 1.06808

実行例)ワーカープロセス2で1+fetch(r)を計算

@spawnat(p, expr)

第2引数で指定した式を第1引数で指定したワーカープロセスで実行

Page 36: Juliaで並列計算

@spawnマクロ

引数で指定した式をいずれかのワーカープロセスで評価

35

@spawn(expr)

Page 37: Juliaで並列計算

@spawnマクロ

引数で指定した式をいずれかのワーカープロセスで評価

36

julia> r = @spawn rand(2,2)

RemoteRef(3,1,10)

julia> s = @spawn 1 .+ fetch(r)

RemoteRef(3,1,11)

julia> fetch(s)

2x2 Array{Float64,2}:

1.03 1.16674

1.99027 1.89547

実行例)いずれかのワーカープロセスで生成した乱数に1を足してフェッチする

@spawn(expr)

Page 38: Juliaで並列計算

ワーカープロセスでの変数・関数の評価

マスタープロセスで定義した関数は、そのままではワーカープロセスで認識できない。

37

julia> function rand2(dims...)

return 2*rand(dims...)

end rand2

(generic function with 1 method)

julia> rand2(2,2)

2x2 Array{Float64,2}:

1.5369 1.34792

1.88103 0.790906

julia> @spawn rand2(2,2)

RemoteRef(2,1,13)

julia> exception on 2: ERROR: function rand2 not defined on

process 2 in error at error.jl:21 in anonymous at

serialize.jl:397 in anonymous at multi.jl:1279 in anonymous

at multi.jl:848 in run_work_thunk at multi.jl:621 in

run_work_thunk at multi.jl:630 in anonymous at task.jl:6

Page 39: Juliaで並列計算

ワーカープロセスでの変数・関数の評価

すべてのワーカープロセスで変数や関数を認識させるために@everywhereマクロを前につける。

38

julia> @everywhere function rand2(dims...)

2 * rand(dims...)

end

julia> @spawn rand2(2,2)

RemoteRef(2,1,20)

julia> fetch(r)

2x2 Array{Float64,2}:

1.92811 0.535094

1.40141 0.0465075

Page 40: Juliaで並列計算

ワーカープロセスでの変数・関数の評価

同様に、ソースコードやパッケージをすべてのワーカープロセスに認識させるために、include関数やusing関数の前に@everywhereを付加する。

39

julia> @everywhere include("defs.jl")

julia> @everywhere using Clustering

ソースコードについては、require関数でもO.K.

julia> require("defs.jl")

Page 41: Juliaで並列計算

ワーカープロセスでの変数・関数の評価

Julia起動時に、すべてのワーカープロセスにJuliaのソースコードの記述内容を認識させるためには、-Lオプションでファイル名を指定する。

40

$ julia -p <n> -L file1.jl -L file2.jl

driver.jl

Page 42: Juliaで並列計算

並列処理とループ計算

プロセス間でデータの転送が不要な場合は、以下の2つの方法により並列計算の簡潔な記述が可能。① @parallel for構文

② pmap関数

41

Page 43: Juliaで並列計算

並列処理とループ計算

① @parallel for構文

42

julia> nheads = @parallel (+) for

i=1:200000000 int(randbool())

end

99999830

実行例)200000000回、論理値を発生させて、合計値を算出

Page 44: Juliaで並列計算

並列処理とループ計算

② pmap関数

43

julia> M = {rand(1000,1000) for i=1:10}

julia> pmap(svd, M)

実行例)1000×1000の行列を10個生成して、それぞれを特異値分解

Page 45: Juliaで並列計算

リモートリファレンスの動的なスケジューリング

pmap関数の実装

45

function pmap(f, lst)np = nprocs() # determine the number of processes availablen = length(lst)results = cell(n)i = 1# function to produce the next work item from the queue.# in this case it's just an index.nextidx() = (idx=i; i+=1; idx)@sync begin

for p=1:npif p != myid() || np == 1

@async beginwhile true

idx = nextidx()if idx > n

breakendresults[idx] = remotecall_fetch(p, f, lst[idx])

endend

endend

endresults

end

Page 46: Juliaで並列計算

共有メモリの配列

UNIX/Linux環境で、実験的に共有メモリの配列が提供されている。

46

julia> addprocs(3)

3-element Array{Any,1}:

2

3

4

julia> S = SharedArray(Int, (3,4), init = S -> S[localindexes(S)]

= myid())

3x4 SharedArray{Int64,2}:

1 2 3 4

1 2 3 4

1 2 3 4

julia> S[3,2] = 7

7

julia> S

3x4 SharedArray{Int64,2}:

1 2 3 4

1 2 3 4

1 7 3 4

Page 47: Juliaで並列計算

その他の話題

クラスタマネージャマルチマシンのクラスタ上でのも並列分散を実行可能。

分散配列(distributed arrays) 並列計算用の配列。

• dzeros

• dones

• drand

• drandn

• dfill

47

Page 48: Juliaで並列計算

5. Juliaで並列計算 実践編

48

Page 49: Juliaで並列計算

並列計算する処理

今回は、k平均法とランダムフォレストの並列計算を実行。

49

k平均法(非階層的クラスタリング)

ランダムフォレスト

Page 50: Juliaで並列計算

k平均法の並列計算

k平均法は、Clusteringパッケージを用いて実行する。

50

julia> # Clusteringパッケージのインストールjulia> Pkg.add(“Clustering”)

juliastats/Clustering.jl

Page 51: Juliaで並列計算

k平均法の並列計算

成分が一様乱数からなる5000×5000の行列を作成。

クラスタ数を10個に設定して、k平均法を実行。

51

using Clustering

srand(123)

# 成分が一様乱数からなる5000×5000の行列の作成

x = rand(5000, 5000)

# クラスタ数

k = 10

# k平均法の実行

@time @parallel result = [kmeans(x, k; maxiter=50) for

i=1:200]

exec_km.jl

Page 52: Juliaで並列計算

k平均法の並列計算

並列計算と逐次計算の実行時間を比較。

52

$ julia -p 4 exec_kmeans.jl

elapsed time: 259.546022396 seconds

(1251034300 bytes allocated, 0.01%

gc time)

exec_km.jl

並列計算(4ワーカープロセス)

$ julia exec_kmeans.jl

elapsed time: 742.803273255 seconds

(3499799548 bytes allocated, 0.33%

gc time)

逐次計算

260秒 743秒

約2.9倍の高速化

Page 53: Juliaで並列計算

ランダムフォレストの並列計算

DecisionTreeパッケージを用いてランダムフォレストを実行。

53

参考資料:第1回JuliaTokyo@gepuro Julia0.3でランダムフォレスト

Page 54: Juliaで並列計算

ランダムフォレストの並列計算

ソースコードは、木の生成が並列化可能な実装。

54

function build_forest{T<:FloatingPoint, U<:Real}(labels::Vector{T},

features::Matrix{U}, nsubfeatures::Integer,

ntrees::Integer, maxlabels=0.5, partialsampling=0.7)

partialsampling = partialsampling > 1.0 ? 1.0 : partialsampling

Nlabels = length(labels)

Nsamples = int(partialsampling * Nlabels)

forest = @parallel (vcat) for i in [1:ntrees]

inds = rand(1:Nlabels, Nsamples)

build_tree(labels[inds], features[inds,:], maxlabels,

nsubfeatures)

end

return Ensemble([forest])

end

DecisionTree.jl

404

405

406

407

408

409

410

411

412

413

Page 55: Juliaで並列計算

ランダムフォレストの並列計算

ランダムフォレストは以下のように実行する。

55

using RDatasets

using DecisionTree

# Pimaインディアンデータセット(妊娠回数、血圧などと糖尿病の罹患有無)

Pima_tr = dataset("MASS", "Pima.tr")

# 説明変数

features = array(Pima_tr[:, 1:7])

# 目的変数

labels = array(Pima_tr[:, 8]);

# ランダムフォレストの実行(特徴量数: 4個、生成する木の個数: 2000個、サンプルの選択割合: 70%)

@time model = build_forest(labels, features, 4, 2000,

0.7);

exec_rf.jl

Page 56: Juliaで並列計算

ランダムフォレストの並列計算

並列計算と逐次計算の計算時間を比較。

56

$ julia -p 4 exec_rf.jl

elapsed time: 8.847219443 seconds

(104840140 bytes allocated, 1.83% gc

time)

exec_rf.jl

並列計算(4ワーカープロセス)

$ julia exec_rf.jl

elapsed time: 12.908068361 seconds

(1479356232 bytes allocated, 11.24%

gc time)

逐次計算

8.85秒 12.91秒

約1.5倍の高速化

Page 57: Juliaで並列計算

ランダムフォレストの並列計算

4つのワーカープロセスを使った割には効果が薄い?

Pimaインディアンデータセットでは、特徴量がたったの7個。

今回は、その中から4個をサンプリングしている。

特徴量が多いデータを使えば、もう少し並列計算の効果が上がるはず。

どなたか、Rdatasetsパッケージに機械学習系のデータセットを充実させてください!

57

Page 58: Juliaで並列計算

処理速度のまとめ

並列計算(4ワーカープロセス)

逐次計算並列計算による

高速化

k平均法(5000×5000の行列、クラスター数10)

260秒 743秒 約2.9倍

ランダムフォレスト(サンプル数、特徴量の個数7)

8.85秒 12.91秒 約1.5倍

58

注) 並列計算と逐次計算で乱数を一致させておらず、計算時間も複数回試行して比較すべきなので、あくまでご参考

Page 59: Juliaで並列計算

6. まとめ

59

Page 60: Juliaで並列計算

Juliaにおける並列計算

マスタープロセスからワーカープロセスへの一方通行メッセージパッシング(通信方式はソケット?)

マスタープロセスからワーカープロセスにリモートコールで命令。

ワーカープロセスの処理結果(リモートリファレンス形式)は、マスタープロセスがフェッチして参照。

60

Page 61: Juliaで並列計算

Juliaにおける並列計算

今後の調査事項・課題 並列乱数生成(L’ecuyerの方法など)

http://pseudotrue.com/2014/07/22/parallel-random-number-generation-in-julia/

できてる? できてない?

フォーク等の他のワーカープロセス生成・通信方式での並列計算は実行できるか?

ロードバランシングのようなことはできる?

メモリの管理(GC等)

61

Page 62: Juliaで並列計算

参考文献

公式ドキュメント

M.M.Maza, Parallel Ccomputing with Julia公式ドキュメントの内容がコンパクトにまとまっていて分かりやすい。

base/muil.jl

なんだかんだで今はソースを直接読むのが、理解向上の一番の近道?

62