apache spark チュートリアル

97
Apache Spark チュートリアル 東北大学 乾・岡崎研究室 山口健史 2015-04-28

Upload: k-yamaguchi

Post on 15-Jul-2015

3.925 views

Category:

Software


4 download

TRANSCRIPT

Page 1: Apache Spark チュートリアル

Apache Spark チュートリアル

東北大学 乾・岡崎研究室山口健史

2015-04-28

Page 2: Apache Spark チュートリアル

MapReduceのはなし

1

Page 3: Apache Spark チュートリアル

単語の頻度カウントを例に

2

Page 4: Apache Spark チュートリアル

素朴な実装 3

frequency = defaultdict(int)

for line in opened_file:

for word in some_splitter(line):

frequency[word] += 1

for word in frequency:

some_output(word, frequency[word])

ファイル

frequency

メモリに辞書/ハッシュ/連想配列/Mapを持つ頻度(Pythonic)

frequency = collections.Counter(

word

for line in opened_file

for word in some_splitter(line))

for word, count in frequency.iteritems():

some_output(word, count)

Page 5: Apache Spark チュートリアル

巨大なファイルだと…… 4

:

for line in opened_file:

for word in some_splitter(line):

frequency[word] += 1

:

巨大なファイル メモリが

足りない!!

Page 6: Apache Spark チュートリアル

入力を分割する 5

頻度ファイル

ファイル頻度

同じ単語が両方のファイルに入ってるので統合するときになにか工夫しないとやっぱりメモリが足りない

Page 7: Apache Spark チュートリアル

出力を分割する 6

frequency = defaultdict(int)

for line in opend_file:

for word in some_splitter(line):

if hash(word) % 2 == 1:

frequency[word] += 1

:

frequency = defaultdict(int)

for line in opend_file:

for word in some_splitter(line):

if hash(word) % 2 == 0:

frequency[word] += 1

:

巨大なファイル

頻度

頻度

ハッシュの剰余で間引いて半分に

ある単語は一方のファイルにだけ存在

単純に結合すればOK

もう一度読む

残り半分

Page 8: Apache Spark チュートリアル

組み合わせる 7

f = [defaultdict(int)

for i in range(2)]

for l in of:

for w in sp(l):

f[hash(w) % 2]\

[w] += 1

ファイル

ファイル

同じ単語が入っているファイル同士を統合する

剰余で分割

ファイルを分割

単純な結合でOK

並列可能

Page 9: Apache Spark チュートリアル

これだとスケールアウトする

並列に実行できるようになっている

ファイル読み込みの部分が並列実行可能

それでもメモリが足りなくなるなら分割数を増やせばいい

100〜10,000分割でも動くアルゴリズム

8

Page 10: Apache Spark チュートリアル

でもHadoopは簡単じゃなかった

簡単なことにたくさんソースを書く

Hadoopの仕組みを知らないと作れない

たいていのフレームワークはそうだけど……

9

Page 11: Apache Spark チュートリアル

でもHadoopは簡単じゃなかった

定型部分の方が多い

何度もMapReduceする処理を書こうとすると、ほとんど同じで微妙な違いしかないソースがたくさんできる

10

/** 仮型引数* FileInputFormatだと第1引数はLongWriteable, 第2引数はtext。* 第3引数がmap出力のkey, 第4引数がmap出力のvalue

*/

public static class Map

extends Mapper<LongWritable, Text, Text, LongWritable> {

private Text outKey = new Text("LINES");

private LongWritable outValue = new LongWritable(1);

@Override

protected void map(LongWritable inputKey,

Text value,

Context context)

throws IOException, InterruptedException {

context.write(outKey, outValue);

}

}

/** <map出力のkey=reduce入力, map出力のvalue=reduce入力,

* reduce出力のkey, reduce出力のvalue>

*/

public static class Reduce

extends Reducer<Text, LongWritable, Text, LongWritable> {

private LongWritable outValue = new LongWritable();

@Override

protected void reduce(Text key,

Iterable<LongWritable> values,

Context context)

throws IOException, InterruptedException {

long total = 0L;

for (LongWritable value : values) {

total += value.get();

}

outValue.set(total);

context.write(key, outValue);

}

}

←行数をカウントするMapReduce

個人の感想です

Page 12: Apache Spark チュートリアル

Apache Spark のはなし

11

Page 13: Apache Spark チュートリアル

Sparkとは

Hadoop MapReduceにインスパイアされた分散処理基盤

Hadoop-HDFSのような分散ファイルシステムをベースとして利用する

Scala製。PythonやJavaでも使える

12

Page 14: Apache Spark チュートリアル

Sparkのうれしいところ

ScalaやPythonのコードがMapReduceのように動く

SparkやHDFSがなにをしてくれるかは知らなくても書ける(理解しておいた方はよい)

途中のデータをメモリに保持できる

Sparkが高速と言われる理由

Hadoop-MapReduceは必ずディスク保存だった

13

Page 15: Apache Spark チュートリアル

Sparkは簡単

例)タブ区切りの5カラム目を切り出し”拡散希望”が含まれるものを抽出

これで分散処理される

14

(Python)

sc.textFile(u'tweets.txt').\

map(lambda x: x.split('\t')[4]).\

filter(lambda x: u'拡散希望' in x)

Page 16: Apache Spark チュートリアル

Sparkは簡単

例)タブ区切りの5カラム目を切り出し”拡散希望”が含まれるものを抽出

これで分散処理される

15

(Scala)

sc.textFile("tweets.txt").

map(_.split("\t")(4)).

filter(_.contains("拡散希望"))

Page 17: Apache Spark チュートリアル

研究室の環境について

研究室のマシンではSparkクラスタが常時起動しているわけではない

Hadoop-YARNにリソースを要求して必要な時にクラスタを起動する

16

Page 18: Apache Spark チュートリアル

クラスタの起動と解消

要求/確保したリソースでクラスタを構成するexecutor(並列処理のワーカー)6個分のメモリを要求している

spark-shellの終了がクラスタの解消

終了しないとリソースを確保した状態のまま

17

spark-shell --master yarn-client --num-executors 6

Page 19: Apache Spark チュートリアル

Sparkのコンポーネント 18

node00 node01 node02 node03

node04 node05 node20

driverexecutor

executor

masterexecutor

executor

executor

executor

executor

executor

executor

executor

executor

(shell実行モードの場合)

Page 20: Apache Spark チュートリアル

driver19

node00 node01 node02 node03

node04 node05 node20

driverexecutor

executor

masterexecutor

executor

executor

executor

executor

executor

executor

executor

executor

利用者が見ているコンソール(spark-shell)

WebUIも提供する

定数・関数などの定義(定義したものは各executorに分散され共有される)

Page 21: Apache Spark チュートリアル

executor20

node00 node01 node02 node03

node04 node05 node20

driverexecutor

executor

masterexecutor

executor

executor

executor

executor

executor

executor

executor

executor

実際にジョブを実行している部分

Page 22: Apache Spark チュートリアル

master21

node00 node01 node02 node03

node04 node05 node20

driverexecutor

executor

masterexecutor

executor

executor

executor

executor

executor

executor

executor

executor

SparkではなくHadoop YARNのコンポーネント

なにをしているかはわかってないYARN Resource Managerとやりとりしている(?)

Page 23: Apache Spark チュートリアル

Sparkを体験する

22

Page 24: Apache Spark チュートリアル

かんたんSpark体験

ローカルで2つのexecutorで実行する

23

#Mac Homebrewでbrew install apache-spark

#Scalaシェルspark-shell --master "local[2]"

#Pythonシェルpyspark --master "local[2]"

Page 25: Apache Spark チュートリアル

WebUI24

http://localhost:4040/jobs/WebUI

Page 26: Apache Spark チュートリアル

かんたん並行処理

a.reduceは2プロセスでb.reduceは1プロセスで実行される

Pythonプロセスと実行時間で確認

25

(Python)

# 0から999999999までの総和を計算するfrom operator import add

a = sc.parallelize(xrange(1000000000), 2)

b = sc.parallelize(xrange(1000000000), 1)

a.reduce(add)

499999999500000000

b.reduce(add)

499999999500000000

Page 27: Apache Spark チュートリアル

かんたん並行処理

Scalaではスレッドが使われる

26

(Scala)

// 0から999999999までの総和を計算するval a = sc.parallelize(1L until 1000000000, 2)

val b = sc.parallelize(1L until 1000000000, 1)

a.reduce(_ + _)

499999999500000000

b.reduce(_ + _)

499999999500000000

Page 28: Apache Spark チュートリアル

SparkContext

scはspark-shell起動時に生成されるSparkContext型のインスタンス

SparkContextはジョブの実行状態や参加しているexecutorの状態を持つ

27

a = sc.parallelize(xrange(1000000000), 2)

Page 29: Apache Spark チュートリアル

RDD "Resilient Distributed Dataset"

分散データを扱う抽象コレクション

データの実体がどこにあるのかを意識しない

並列動作する変換操作(map, filter,,)

キャッシュの選択肢

メモリにキャッシュ, ディスクにキャッシュ

28

Page 30: Apache Spark チュートリアル

RDD : 作成

RDDの作成はSparkContextから

29

(Python)

sc.parallelize([1, 2, 3, 4, 5])

# ローカルコレクションをRDDに変換

sc.textFile("file.text")

sc.textFile("directory/*.gz")

# 文字列のコレクションになる# テキストファイルを指定してRDDを作成# クラスタがローカル実行ならローカルのファイルから# クラスタがHadoop-YARN上にあるならHDFSから# クラスタがAWS上にあるならS3から読みこんだり# 圧縮ファイルなら解凍後のデータを読む# 実行してもすぐにファイルを読みこまない

Spark-Shellのメモリ上でインスタンス化している

という意味

Page 31: Apache Spark チュートリアル

RDD : アクション(1) 30

(Python)

nums = sc.parallelize([4, 3, 2, 5, 1])

nums.collect()

# => [4, 3, 2, 5, 1]

# 全ての要素をローカルのコレクションにする

nums.take(3)

# => [4, 3, 2] 冒頭3個の要素

nums.top(3)

# => [5, 4, 3] 自然順序づけで大きい方から3つの要素

RDDから具体的なデータへ変換する

(ローカルコレクション, 値, ファイル)

Pythonだとcmp()関数Java/ScalaだとComparableでの比較

Page 32: Apache Spark チュートリアル

RDD : アクション(2) 31

(Python)

nums = sc.parallelize(xrange(1000))

nums.count()

# => 1000

# 要素数

nums.reduce(lambda x, y: x + y)

# => 499500

# 畳み込み (((…(((0+1)+2)+3)+…+998)+999)

nums.saveAsTextFile("destination")

# テキストファイルとして出力

RDDから具体的なデータへ変換する

(ローカルコレクション, 値, ファイル)

Page 33: Apache Spark チュートリアル

RDD : 変換(基本)

RDDを元に別のRDDを定義する

RDDはイミュータブル(書き換え不能)

32

(Python)

nums = sc.parallelize([1, 2, 3])

nums.map(lambda x: x * x)

# => [1, 4, 9] 関数適用

nums.filter(lambda x: x % 2 == 0)

# => [2]フィルタリングnums.flatMap(lambda x: range(x))

# => [0, 0, 1, 0, 1, 2]

# 「1つの値からコレクションを返す」関数を元に複数データを生成

Page 34: Apache Spark チュートリアル

RDD : 変換(2-value tuple)

2値タプルのRDDに追加されるメソッド

33

(Python)

pets = sc.parallelize(

[("cat", 1), ("dog", 1), ("cat", 2)])

pets.groupByKey()

# => [("cat", [1, 2]), ("dog", [1])]

pets.reduceByKey(lambda x, y: x + y)

# => [("cat", 3), ("dog", 1)]

pets.sortByKey()

# => [(cat, 1), (cat, 2), (dog, 1)]

Page 35: Apache Spark チュートリアル

RDD : 変換(2つの2-value tuple)

2値タプルのRDDに追加されるメソッド

34

(Python)

pets = sc.parallelize(

[("cat", 1), ("dog", 1), ("cat", 2)])

names = sc.parallelize(

[("cat", "Tama"), ("dog", "Pochi")])

pets.join(names)

# => [("dog", (1, "Pochi")),

# ("cat", (1, "Tama")),

# ("cat", (2, "Tama"))]

pets.cogroup(names)

# => [("dog", ([1], ["Pochi"])),

# ("cat", ([1,2], ["Tama"]))] 相当

Page 36: Apache Spark チュートリアル

RDD : メソッド群 35

アクションデータの具体化

変換抽象コレクションの操作

foreach

collect

reduce

fold

count

saveAsTextFile

map

flatMap

filter

collect

distinct

sample

sortByKey

groupByKey

reduceByKey

zip

cartesian

union

intersection

subtract

repartition

join

cogroup

etc…

take

first

top

takeOrdered

etc…

メタ操作

cache

unpersist

checkpoint

etc…

こっちのcollectはPythonにはない

Page 37: Apache Spark チュートリアル

RDD : 資料

http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.RDD

http://spark.apache.org/docs/latest/api/python/pyspark.html#pyspark.RDD

http://www.ne.jp/asahi/hishidama/home/tech/scala/spark/RDD.html

36

(mezcalのSparkバージョンに合わせて1.2.0へのリンク)

Page 38: Apache Spark チュートリアル

RDDと並列可能性

37

Page 39: Apache Spark チュートリアル

並列コレクション 38

node1 node2 node3

executor executor executor

(executorが3つの場合)(Python)

sc.parallelize(xrange(100), 3) #パーティション数3を指定sc.count() # 3並列で処理される

0123:32

33343536:65

66676869:99

RDD

パーティション

Page 40: Apache Spark チュートリアル

並列コレクション 39

(Python)

sc.parallelize(xrange(100), 1) #パーティション数1を指定sc.count() # 1並列で処理される

node1 node2 node3

executor executor executor

012:9899

RDD

(executorが3つの場合)

Page 41: Apache Spark チュートリアル

分割可能なファイル

ローカリティを考慮してなるべくデータを保持するノードで処理する

40

(executorが3つの場合)(Python)

sc.textFile("/work/test/data.txt") # 128MB未満sc.count() # 1並列で処理される(HDFS上のファイルの場合)

node1 node2 node3

executor executor executor

data.txt

RDD

Page 42: Apache Spark チュートリアル

分割可能なファイル

HDFSはファイルを128MBごとに分割して分散させている(研究室の設定では)

プレーンテキストとbzip2ファイルが分割可能なファイル形式

分割可能なファイルはブロック単位に並列分散処理される

41

Page 43: Apache Spark チュートリアル

分割可能なファイル 42

(executorが3つの場合)(Python)

sc.textFile("/work/test/large.txt") # 300MBぐらいsc.count() # 3並列で処理される

node1 node2 node3

executor executor executor

large.txt(block1)

large.txt(block2)

large.txt(block3)

RDD

Page 44: Apache Spark チュートリアル

分割可能なファイル

128MBの境界で文が分かれてしまわないの?

心配いらない改行をまたぐ部分がネットワークを移動して文単位で処理される

43

Page 45: Apache Spark チュートリアル

分割可能なファイル

境界をまたぐ分が移動してから処理が始まる

44

(executorが3つの場合)(Python)

sc.textFile("/work/test/large.txt") # 300MBぐらいsc.count() # 3並列で処理される

node1 node2 node3

executor executor executor

large.txt(block1)

large.txt(block2)

large.txt(block3)

RDD

Page 46: Apache Spark チュートリアル

分割可能でないファイル

分割可能でないファイル

例えばgzip圧縮ファイルとか

128MBを超えるファイルは後ろのブロックがすべてネットワーク越しにコピーされてから処理が始まる=分散されない!!

128MBを超えないぐらいの大きさに抑える

トータルのパフォーマンスはこの置き方が一番いい

45

Page 47: Apache Spark チュートリアル

分割可能でないファイル 46

node1 node2 node3

executor executor executor

data.gz(block1)

data.gz(block2)

data.gz(block3)

HDFS

Spark

300MBのgzipファイルがあってこんな風に分散されているとすると

Page 48: Apache Spark チュートリアル

分割不可能なファイル 47

(executorが3つの場合)(Python)

sc.textFile("/work/test/data.gz") # 300MBぐらいsc.count() # 1並列でしか処理されない

node1 node2 node3

executor executor executor

data.gz(block1)data.gz

(block2)data.gz(block3)

data.gz(block2)

data.gz(block3)

ネットワーク越しのコピー

RDD

Page 49: Apache Spark チュートリアル

分割可能でないファイル

この形が一番効率がいい

48

(executorが3つの場合)(Python)

sc.textFile("/work/test/p*.gz") # 該当する3ファイル(128MB未満)sc.count() # 3並列で処理される

node1 node2 node3

executor executor executor

p0.gz p1.gz p2.gz

RDD

Page 50: Apache Spark チュートリアル

Spark実例

(頻度カウント再び)

49

Page 51: Apache Spark チュートリアル

変換の連鎖

ここまで実行してもまだテキストを読みこんでない(RDDからRDDへの変換操作だから)

50

(Python)

src = sc.textFile("hightemp.txt")

# ファイルを指定してRDDを作成する

tuples = src

.map(lambda x: x.split("\t"))

.filter(lambda x: len(x) > 3)

# 100本ノック12

col1 = tuples.map(lambda x: x[0])

col2 = tuples.map(lambda x: x[1])

tabで分割

要素数4以上

1列目

2列目

高知県\t江川崎\t41\t2013-08-12埼玉県\t熊谷\t40.9\t2007-08-16岐阜県\t多治見\t40.9\t2007-08-16山形県\t山形\t40.8\t1933-07-25山梨県\t甲府\t40.7\t2013-08-10

:

Page 52: Apache Spark チュートリアル

アクション

いつの間にか並行処理されている

51

(続き)

col1.count()

24

# ここではじめてファイルを読みこんで# split->filter->map->count が実行される

col1.saveAsTextFile("col1")

# またファイルを最初から読みこんで# split->filter->map->saveAsTextFile が実行される

# これでcol1というテキストが作成される……かと思いきや# col1というディレクトリができている# ローカル実行だと2ファイルある(part-00000 part-00001)

かもしれないし、されないかもしれない

Page 53: Apache Spark チュートリアル

キャッシュの制御 52

(続き)

# 毎回ファイルを読みこむのは効率が悪い

col1.cache()

# これで可能ならばメモリに保持するようになった

col1.count()

24

# またファイルから読んでsplit->filter->map->count

col1.distinct().count()

12

# 異なり数を数える(100本ノック17)

col1.take(2)

[u'\u9ad8\u77e5\u770c', u'\u57fc\u7389\u770c']

ここではもうファイルを読んでいない

Page 54: Apache Spark チュートリアル

もうちょっと変換 53

(続き)# 2カラム目でソートsorted_by_col2 = tuples.sortBy(lambda x: x[1])

# 3カラム目(数値)で降順ソート(100本ノック18)sorted_by_col3 = tuples

.sortBy(lambda x: float(x[2]), False)

# 1コラム目の頻度の高い順にソート(100本ノック19)from operator import add

frequency = col1.map(lambda x: (x, 1))

.reduceByKey(add)

.sortBy(lambda x: x[1], False)

# ここに書いたのは全部RDD変換定義のみで出力はない

昇順

降順

Page 55: Apache Spark チュートリアル

何をしているか(100本ノック19)54

高知県江川崎 41 2013-08-12埼玉県熊谷 40.9 2007-08-16岐阜県多治見 40.9 2007-08-16山形県山形 40.8 1933-07-25

:String

[高知県,江川崎,41,2013-08-12][埼玉県,熊谷,40.9,2007-08-16][岐阜県,多治見,40.9,2007-08-16][山形県,山形,40.8,1933-07-25]

高知県埼玉県岐阜県山形県

:String

(高知県,1)(埼玉県,1)(岐阜県,1)(山形県,1)

(String, Int)

(高知県,[1].reduce(add))(埼玉県,[1,1,1].reduce(add))(岐阜県,[1,1].reduce(add))(山形県,[1,1,1].reduce(add))

(String, Int)

map(x.split("\t"))

filter(len(x)>3)

map(x[0])

map((x, 1))

reduceByKey(add)

Array[String]

col2

sortBy(x[1], False)

tuples

型名はScala準拠

(埼玉県,3)(山形県,3)(山梨県,3)(群馬県,3)

:frequency

Page 56: Apache Spark チュートリアル

もうちょっとアクション 55

(続き)

import json

def pp(obj):

print json.dumps(obj, ensure_ascii=False)

pp(sorted_by_col3.take(3))

[["高知県", "江川崎", "41", "2013-08-12"], ["埼玉県",

"熊谷", "40.9", "2007-08-16"], ["岐阜県", "多治見",

"40.9", "2007-08-16"]]

# 先頭から3個

pp(col1.takeOrdered(3))

["千葉県", "千葉県", "和歌山県"]

# 自然順序づけで小さい方から3個pp(col1.top(3))

["高知県", "静岡県", "静岡県"]

# 自然順序づけで大きい方から3個

Page 57: Apache Spark チュートリアル

もうちょっとアクション 56

(続き)

for data in frequency.take(10):

print u'({}, {})'.format(*data)

(埼玉県, 3)

(山形県, 3)

(山梨県, 3)

(群馬県, 3)

(静岡県, 2)

(岐阜県, 2)

(愛知県, 2)

(千葉県, 2)

(高知県, 1)

(大阪府, 1)

Page 58: Apache Spark チュートリアル

Spark実例

(PMI計算)

57

Page 59: Apache Spark チュートリアル

テキストを読んで頻度カウント 58

val src = sc.textFile("tuples.tsv")

val tuples = src.

map(_.split("\t")).

filter(_.size > 1)

val aFreq = tuples.

map( t => (t(0), 1L) ).reduceByKey(_+_)

val bFreq = tuples.

map( t => (t(1), 1L) ).reduceByKey(_+_)

val instancesFreq = tuples.

map( t => ((t(0), t(1)), 1L) ).reduceByKey(_+_)

ここからは Scala です

combination\toffersalabama\thomewedding\tgreekevil\tdead:

Page 60: Apache Spark チュートリアル

何をしているか 59

combination offersalabama homewedding greekevil dead

String

[combination, offers][alabama, home][wedding, greek][evil, dead]

(offers, 81)(home, 36)(greek, 24)(dead, 20)

(String, Int)

map(_.split("\t"))

filter(_.size>1)

Array[String]

(combination, 20)(alabama, 40)(wedding, 40)(evil, 16)

(String, Int)

((combination, offers), 1)((alabama, home), 5)((wedding, greek), 5)((evil, dead), 3)

((String, String), Int)

map

reduceByKey

map

reduceByKey

map

reduceByKey

tuples

aFreq

bFreq

instanceFreq

Page 61: Apache Spark チュートリアル

頻度をつなぎあわせていく 60

val pmiSrc = instancesFreq.

map{ case ((a, b), t_f)

=> (a, (b, t_f)) }.

join(aFreq).

map{ case (a, ((b, t_f), a_f))

=> (b, (a, t_f, a_f)) }.

join(bFreq).

map{ case (b, ((a, t_f, a_f), b_f))

=> (a, b, t_f, a_f, b_f) }

Scalaだとパターンマッチで書けるけどPythonだと

map(lambda x: (x[1][0][0], x[0], x[1][0][1], x[1][0][2], x[1][1]))

pmi(a, b) = logP(a, b)

P(a) P(b)

(aの文字列, bの文字列, [a, b]の頻度, aの頻度, bの頻度)という組み合わせ(タプル)が欲しい

Page 62: Apache Spark チュートリアル

何をしているか 61

map

((combination, offers), 1)((alabama, home), 5)((wedding, greek), 5)((evil, dead), 3)

(combination, (offers, 1))(alabama, (home, 5))(wedding, (greek, 5))(evil, (dead, 3))

(combination, 20)(alabama, 40)(wedding, 40)(evil, 16)

(String, Int)

((String, String), Int)

(combination, ((offers, 1), 20))(alabama, ((home, 5), 40))(wedding, ((greek, 5), 40))(evil, ((dead, 3), 16))

(String, ((String, Int), Int))

join

(String, (String, Int))

("a", 1) ("a", 2)("b", 3)("c", 4)

("a", "あ") ("a", "い") ("b", "か")("d", "た")

から

※joinはinner joinするメソッド

("a", (1, "あ"))("a", (1, "い"))("a", (2, "あ"))("a", (2, "い"))("b", (3, "か")) を作る

(続く)

instanceFreq

aFreq

Page 63: Apache Spark チュートリアル

何をしているか 62

map

(combination, ((offers, 1), 20))(alabama, ((home, 5), 40))(wedding, ((greek, 5), 40))(evil, ((dead, 3), 16))

(offers, (combination, 1, 20))(home, (alabama, 5, 40))(greek, (wedding, 5, 40))(dead, (evil, 3, 16))

(offers, 81)(home, 36)(greek, 24)(dead, 20)

(String, Int)

(String, ((String, Int), Int)) (String, (String, Int, Int))

(offers, ((combination, 1, 20), 81))(home, ((alabama, 5, 40), 36))(greek, ((wedding, 5, 40), 24))(dead, ((evil, 3, 16), 20))

(String,((String, Int, Int), Int))

join

(combination, offers, 1, 20, 81)(alabama, home, 5, 40, 36)(wedding, greek, 5, 40, 24)(evil, dead, 3, 16, 20)

(String, String, Int, Int, Int)

map

=(a, b, [a, b]の頻度, aの頻度, bの頻度)

前ページ最後

pmiSrc

bFreq

Page 64: Apache Spark チュートリアル

計算する 63

val instancesAll = tuples.count

def calcDicountPmi(instance:Long, a:Long, b:Long) = {

def smooth (x:Long, y:Double) = {

x / (x + y) }

def discount(iTmp:Long, aTmp:Long, bTmp:Long) = {

smooth(iTmp, 1.0) * smooth(math.min(aTmp, bTmp), 1.0) }

def calcPmi(iTmp:Long, aTmp:Long, bTmp:Long) = {

import math.log

log(iTmp) - log(aTmp) - log(bTmp) + log(instancesAll) }

calcPmi(instance, a, b) * discount(instance, a, b)

}

val pmi = pmiSrc.map{

case (a, b, t_f, a_f, b_f)

=> (calcDicountPmi(t_f, a_f, b_f), a, b, t_f, a_f, b_f)

}

pmi.top(5).foreach(println)

(5.771154762538349,fat,greek,8,36,24)

(5.724583909571343,hong,kong,6,28,17)

(5.660412678732772,freaks,legged,4,16,9)

(5.632288650398451,greek,fat,5,20,19)

(5.590241876891969,scams,scams,3,8,7)

普通の定数

普通の関数(クロージャ)

RDD変換

Page 65: Apache Spark チュートリアル

無駄な処理をしているのでは?

その通り。aの頻度とbの頻度がメモリにのりきるならMap(=辞書)として具体化した方が速い

ただしこれはスケールアウトしない

64

val aFreqMap = aFreq.collectAsMap

val bFreqMap = bFreq.collectAsMap

val pmiSrc = instancesFreq.map{

case ((a, b), t_f) =>

(a, b, t_f, aFreqMap(a), bFreqMap(b))

}

Page 66: Apache Spark チュートリアル

まとめ

Sparkでもちゃんとスケールアウトするように書くのはコツが要る

それでも並列処理を書くコストはずっと小さくなっている

Sparkに求める事を意識して書き分ける

並列処理を簡単に書きたい のか

大規模データでもスケールアウトさせたい のか

65

Page 67: Apache Spark チュートリアル
Page 68: Apache Spark チュートリアル

補遺

67

Page 69: Apache Spark チュートリアル

val s:String="hello"

s: String = hello

値の束縛val i=1

i: Int = 1

型推論var j=1

j: Int = 1

変数への代入i=i+1 //間違い(再代入不可)error: reassignment to val

j=j+1

j: Int = 2

s.contains("el")

res: Boolean = true

メソッド呼び出しs contains "em"

res: Boolean = false

演算子スタイル1+2 は 1.+(2) のこと

val t1=("a",3)

t1: (String, Int) = (a,3)

タプルval t2="b"->4

t2: (String, Int) = (b,4)

2値タプルのシンタックスシュガー

t1._1

res: String = a

t1._2

res: Int = 3

タプルの要素

val nums = Seq(1,2,3)

nums: Seq[Int] = List(1, 2, 3)

シーケンス(Seqはファクトリでもある)

nums.map((x:Int)=>x*2)

res: Seq[Int] = List(2, 4, 6)

無名関数,マップnums.map(x=>x*2)

同上(型推論)nums.map(_*2)

同上(プレースホルダー)

nums.reduce((x,y)=>x+y)

res: Int = 6

畳み込みnums.reduce(_+_)

同上(プレースホルダー)

def even(x:Int):Boolean={

x%2==0

}

even: (x: Int)Boolean

関数(最後に評価した値が返り値)nums.filter(even)

res: Seq[Int] = List(2)

フィルタリング

for (i<-nums) {

println(i)

}

1

2

3

繰り返し,標準出力nums.foreach(println)

同上

val tuples=Seq(t1, t2)

tuples: Seq[(String, Int)] =

List((a,3), (b,4))

tuples.map{t=>

val i=t._2

t._1+i.toString

}

res: Seq[String] = List(a3, b4)

{} は複数行の無名関数を作るtuples.map{t=>

t match {

case (s,i)=>s+i.toString

}

}

同上(パターンマッチング)tuples.map{case (s,i)=>s+i.toString}

同上(パターンマッチング)

import scala.math

math.max(1,2)

res: Int = 2

パッケージのインポートimport scala.math._

max(1,2)

パッケージから全てインポート

s.split("l")

res: Array[String] = Array(he, "", o)

s(0)

res: String = he

配列のインデックスアクセスnums.mkString(",")

res: String = 1,2,3

t1.mkString(",") //間違いerror: value mkString is not a member

of (String, Int)

s"${t1._1},${t1._2}"

res: String = a,1

文字列への変数の埋め込み

Scala Cheat Sheetstatement

result in the REPL

補足

Page 70: Apache Spark チュートリアル
Page 71: Apache Spark チュートリアル

HDFSのはなし

70

Page 72: Apache Spark チュートリアル

HDFSとは?

Hadoop Distributed File System

分散仮想ファイルシステム

普通のファイルシステムとしてマウントできない(実用レベルでは)

71

Page 73: Apache Spark チュートリアル

HDFSクラスタ

node7

HDFS72

node1 node2 node3 node4

node5 node6 node8

300MBの

ファイル登録

Page 74: Apache Spark チュートリアル

HDFSクラスタ

node7

HDFS73

node1 node2 node3 node4

node5 node6 node8block3

block1

block2

登録

128MBごとの固まりに分ける

Page 75: Apache Spark チュートリアル

HDFSクラスタ

node7

HDFS74

node1 node2 node3 node4

node5 node6 node8block3

block1

block2

登録

block1

block1

block1block2

block2 block2

block3

block3

block3

3重に複製されてクラスタに保管される

Page 76: Apache Spark チュートリアル

HDFS

なぜ128MBごとに分割するか?

MapReduceではデータが分散されている必要がある

MapReduceでは各ワーカーの処理がメモリーに乗りきる必要がある

なぜ複製を作るのか?

障害耐性が高くなる

分散処理の効率がよくなる(データの移動が少なくなる)

75

Sparkでも同じ!

Page 77: Apache Spark チュートリアル

Hadoop-HDFSで使うコマンド

76

Page 78: Apache Spark チュートリアル

よくつかうコマンド

最近流行りのコマンド サブコマンド オプション

の形をしている。

hdfs コマンド

ファイルシステムに何かする

77

Page 79: Apache Spark チュートリアル

よくつかうコマンド

hdfs dfs 〜

hdfsの操作

hdfs dfs –ls 〜

hdfs dfs –rm 〜

hdfs dfs –rm –r 〜

hdfs dfs –du –s –h 〜

hdfs dfs –mkdir 〜

hdfs dfs –cp 〜 〜

hdfs dfs –mv 〜 〜

78

Page 80: Apache Spark チュートリアル

よくつかうコマンド

hdfs dfs –put ローカルエントリ hdfsエントリ

HDFSにファイルを送る

hdfs dfs –get hdfsエントリ ローカルエントリ

hdfsからファイルを持ってくる

79

Page 81: Apache Spark チュートリアル
Page 82: Apache Spark チュートリアル

MapReduceの様子

81

Page 83: Apache Spark チュートリアル

MapReduce82

あらかじめデータは分散されて置かれている

がノード

データ

Page 84: Apache Spark チュートリアル

MapReduce : Map & Partition83

分割する(ハッシュの剰余などを使う)

Page 85: Apache Spark チュートリアル

MapReduce : Shuffle & Sort84

集める

Page 86: Apache Spark チュートリアル

MapReduce : Reduce85

ノードことに処理する

Page 87: Apache Spark チュートリアル

MapReduceの様子

もう少しうまくやる

86

Page 88: Apache Spark チュートリアル

MapReduce87

Page 89: Apache Spark チュートリアル

MapReduce : Combine88

処理と分割(ハッシュの剰余などを使う)

Page 90: Apache Spark チュートリアル

MapReduce : Shuffle & Sort89

剰余が同じデータを同じノードに集める

Page 91: Apache Spark チュートリアル

MapReduce : Reduce90

処理する

Page 92: Apache Spark チュートリアル
Page 93: Apache Spark チュートリアル

並行処理について

92

Page 94: Apache Spark チュートリアル

並行処理について 93

(Python)

a = sc.parallelize(xrange(6), 2)

b = sc.parallelize(xrange(6), 1)

from operator import sub

a.reduce(sub)

3

b.reduce(sub)

-15

Page 95: Apache Spark チュートリアル

b

partiion 0

a

partiion 1

partiion 0

RDD94

0 1 2 3 4 5

0 1 2

3 4 5

Page 96: Apache Spark チュートリアル

a

partition 1

partition 0

a.reduce(sub)95

0 1 2

3 4 5

((0 - 1) - 2) =reduce

((3 - 4) - 5) =reduce

-3

-6

reduce

3

Page 97: Apache Spark チュートリアル

b

partition 0

b.reduce(sub)

順番で結果が変わるようなreduceを並行・並列で実行してはいけない

96

0 1 2 3 4 5

(((((0 - 1) - 2) - 3) - 4) - 5) =

reduce

-15