Slide 1

Slide 1 text

Apache Spark チュートリアル 東北⼤学 乾・岡崎研究室 ⼭⼝健史 2015-04-28

Slide 2

Slide 2 text

MapReduceのはなし 1

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

素朴な実装 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)

Slide 5

Slide 5 text

巨⼤なファイルだと…… 4 : for line in opened_file: for word in some_splitter(line): frequency[word] += 1 : 巨⼤な ファイル メモリが ⾜りない!!

Slide 6

Slide 6 text

⼊⼒を分割する 5 頻度 ファイル ファイル 頻度 同じ単語が両⽅のファイルに⼊ってるので 統合するときになにか⼯夫しないとやっぱり メモリが⾜りない

Slide 7

Slide 7 text

出⼒を分割する 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 もう⼀度読む 残り半分

Slide 8

Slide 8 text

組み合わせる 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 並列可能

Slide 9

Slide 9 text

これだとスケールアウトする p 並列に実⾏できるようになっている p ファイル読み込みの部分が並列実⾏可能 p それでもメモリが⾜りなくなるなら 分割数を増やせばいい p 100〜10,000分割でも動くアルゴリズム 8

Slide 10

Slide 10 text

でもHadoopは簡単じゃなかった p 簡単なことにたくさんソースを書く p Hadoopの仕組みを知らないと作れない p たいていのフレームワークはそうだけど…… 9

Slide 11

Slide 11 text

でもHadoopは簡単じゃなかった p 定型部分の⽅が多い p 何度もMapReduce する処理を書こうと すると、 ほとんど同じで 微妙な違いしかない ソースがたくさん できる 10 /* * 仮型引数 * FileInputFormatだと第1引数はLongWriteable, 第2引数はtext。 * 第3引数がmap出⼒のkey, 第4引数がmap出⼒のvalue */ public static class Map extends Mapper { 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); } } /* * */ public static class Reduce extends Reducer { private LongWritable outValue = new LongWritable(); @Override protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException { long total = 0L; for (LongWritable value : values) { total += value.get(); } outValue.set(total); context.write(key, outValue); } } ←⾏数をカウントする MapReduce 個⼈の感想です

Slide 12

Slide 12 text

Apache Spark のはなし 11

Slide 13

Slide 13 text

Sparkとは p Hadoop MapReduceにインスパイア された分散処理基盤 p Hadoop-HDFSのような分散ファイルシステムを ベースとして利⽤する p Scala製。PythonやJavaでも使える 12

Slide 14

Slide 14 text

Sparkのうれしいところ p ScalaやPythonのコードが MapReduceのように動く p SparkやHDFSがなにをしてくれるかは知らなく ても書ける(理解しておいた⽅はよい) p 途中のデータをメモリに保持できる p Sparkが⾼速と⾔われる理由 p Hadoop-MapReduceは必ずディスク保存だった 13

Slide 15

Slide 15 text

Sparkは簡単 p 例)タブ区切りの5カラム⽬を切り出し ”拡散希望”が含まれるものを抽出 p これで分散処理される 14 (Python) sc.textFile(u'tweets.txt').¥ map(lambda x: x.split('¥t')[4]).¥ filter(lambda x: u'拡散希望' in x)

Slide 16

Slide 16 text

Sparkは簡単 p 例)タブ区切りの5カラム⽬を切り出し ”拡散希望”が含まれるものを抽出 p これで分散処理される 15 (Scala) sc.textFile("tweets.txt"). map(_.split("¥t")(4)). filter(_.contains("拡散希望"))

Slide 17

Slide 17 text

研究室の環境について p 研究室のマシンではSparkクラスタが 常時起動しているわけではない p Hadoop-YARNにリソースを要求して 必要な時にクラスタを起動する 16

Slide 18

Slide 18 text

クラスタの起動と解消 p 要求/確保したリソースでクラスタを 構成する executor(並列処理のワーカー)6個分の メモリを要求している p spark-shellの終了がクラスタの解消 p 終了しないとリソースを確保した状態のまま 17 spark-shell --master yarn-client --num-executors 6

Slide 19

Slide 19 text

Sparkのコンポーネント 18 node00 node01 node02 node03 node04 node05 node20 driver executor executor master executor executor executor executor executor executor executor executor executor (shell実⾏モードの場合)

Slide 20

Slide 20 text

driver 19 node00 node01 node02 node03 node04 node05 node20 driver executor executor master executor executor executor executor executor executor executor executor executor 利⽤者が⾒ているコンソール(spark-shell) WebUIも提供する 定数・関数などの定義(定義したものは各 executorに分散され共有される)

Slide 21

Slide 21 text

executor 20 node00 node01 node02 node03 node04 node05 node20 driver executor executor master executor executor executor executor executor executor executor executor executor 実際にジョブを実⾏している部分

Slide 22

Slide 22 text

master 21 node00 node01 node02 node03 node04 node05 node20 driver executor executor master executor executor executor executor executor executor executor executor executor SparkではなくHadoop YARNのコンポーネント なにをしているかはわかってない YARN Resource Managerとやりとりしている(︖)

Slide 23

Slide 23 text

Sparkを体験する 22

Slide 24

Slide 24 text

かんたんSpark体験 p ローカルで2つのexecutorで実⾏する 23 #Mac Homebrewで brew install apache-spark #Scalaシェル spark-shell --master "local[2]" #Pythonシェル pyspark --master "local[2]"

Slide 25

Slide 25 text

WebUI 24 http://localhost:4040/jobs/WebUI

Slide 26

Slide 26 text

かんたん並⾏処理 p a.reduceは2プロセスで b.reduceは1プロセスで実⾏される p 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

Slide 27

Slide 27 text

かんたん並⾏処理 p 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

Slide 28

Slide 28 text

SparkContext p scはspark-shell起動時に⽣成される SparkContext型のインスタンス p SparkContextはジョブの実⾏状態や 参加しているexecutorの状態を持つ 27 a = sc.parallelize(xrange(1000000000), 2)

Slide 29

Slide 29 text

RDD "Resilient Distributed Dataset" p 分散データを扱う抽象コレクション p データの実体がどこにあるのかを意識しない p 並列動作する変換操作(map, filter,,) p キャッシュの選択肢 p メモリにキャッシュ, ディスクにキャッシュ 28

Slide 30

Slide 30 text

RDD : 作成 p 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のメモリ上で インスタンス化している という意味

Slide 31

Slide 31 text

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つの要素 p RDDから具体的なデータへ変換する (ローカルコレクション, 値, ファイル) Pythonだとcmp()関数 Java/ScalaだとComparable での⽐較

Slide 32

Slide 32 text

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") # テキストファイルとして出⼒ p RDDから具体的なデータへ変換する (ローカルコレクション, 値, ファイル)

Slide 33

Slide 33 text

RDD : 変換(基本) p RDDを元に別のRDDを定義する p 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つの値からコレクションを返す」関数を元に複数データを⽣成

Slide 34

Slide 34 text

RDD : 変換(2-value tuple) p 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)]

Slide 35

Slide 35 text

RDD : 変換(2つの2-value tuple) p 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"]))] 相当

Slide 36

Slide 36 text

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にはない

Slide 37

Slide 37 text

RDD : 資料 p http://spark.apache.org/docs/latest/api/scala/index.html#or g.apache.spark.rdd.RDD p http://spark.apache.org/docs/latest/api/python/pyspark.ht ml#pyspark.RDD p http://www.ne.jp/asahi/hishidama/home/tech/scala/spark/R DD.html 36

Slide 38

Slide 38 text

RDDと並列可能性 37

Slide 39

Slide 39 text

並列コレクション 38 node1 node2 node3 executor executor executor (executorが3つの場合) (Python) sc.parallelize(xrange(100), 3) #パーティション数3を指定 sc.count() # 3並列で処理される 0 1 2 3 : 32 33 34 35 36 : 65 66 67 68 69 : 99 RDD パーティション

Slide 40

Slide 40 text

並列コレクション 39 (Python) sc.parallelize(xrange(100), 1) #パーティション数1を指定 sc.count() # 1並列で処理される node1 node2 node3 executor executor executor 0 1 2 : 98 99 RDD (executorが3つの場合)

Slide 41

Slide 41 text

分割可能なファイル p ローカリティを考慮してなるべくデータを保持するノードで処理する 40 (executorが3つの場合) (Python) sc.textFile("/work/test/data.txt") # 128MB未満 sc.count() # 1並列で処理される(HDFS上のファイルの場合) node1 node2 node3 executor executor executor data.txt RDD

Slide 42

Slide 42 text

分割可能なファイル p HDFSはファイルを128MBごとに 分割して分散させている(研究室の設定では) p プレーンテキストとbzip2ファイルが 分割可能なファイル形式 p 分割可能なファイルはブロック単位に並列分散 処理される 41

Slide 43

Slide 43 text

分割可能なファイル 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

Slide 44

Slide 44 text

分割可能なファイル p 128MBの境界で⽂が分かれてしまわな いの? p ⼼配いらない 改⾏をまたぐ部分がネットワークを移動して ⽂単位で処理される 43

Slide 45

Slide 45 text

分割可能なファイル p 境界をまたぐ分が移動してから処理が始まる 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

Slide 46

Slide 46 text

分割可能でないファイル p 分割可能でないファイル p 例えばgzip圧縮ファイルとか p 128MBを超えるファイルは後ろのブロックが すべてネットワーク越しにコピーされてから 処理が始まる=分散されない!! p 128MBを超えないぐらいの⼤きさに抑える p トータルのパフォーマンスはこの置き⽅が⼀番いい 45

Slide 47

Slide 47 text

分割可能でないファイル 46 node1 node2 node3 executor executor executor data.gz (block1) data.gz (block2) data.gz (block3) HDFS Spark 300MBのgzipファイルがあって こんな⾵に分散されているとすると

Slide 48

Slide 48 text

分割不可能なファイル 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

Slide 49

Slide 49 text

分割可能でないファイル p この形が⼀番効率がいい 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

Slide 50

Slide 50 text

Spark実例 (頻度カウント再び) 49

Slide 51

Slide 51 text

変換の連鎖 p ここまで実⾏してもまだ テキストを読みこんでない (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 :

Slide 52

Slide 52 text

アクション p いつの間にか並⾏処理されている 51 (続き) col1.count() 24 # ここではじめてファイルを読みこんで # split->filter->map->count が実⾏される col1.saveAsTextFile("col1") # またファイルを最初から読みこんで # split->filter->map->saveAsTextFile が実⾏される # これでcol1というテキストが作成される……かと思いきや # col1というディレクトリができている # ローカル実⾏だと2ファイルある(part-00000 part-00001) かもしれないし、されないかもしれない

Slide 53

Slide 53 text

キャッシュの制御 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'] ここではもう ファイルを 読んでいない

Slide 54

Slide 54 text

もうちょっと変換 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変換定義のみで出⼒はない 昇順 降順

Slide 55

Slide 55 text

何をしているか(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

Slide 56

Slide 56 text

もうちょっとアクション 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個

Slide 57

Slide 57 text

もうちょっとアクション 56 (続き) for data in frequency.take(10): print u'({}, {})'.format(*data) (埼⽟県, 3) (⼭形県, 3) (⼭梨県, 3) (群⾺県, 3) (静岡県, 2) (岐⾩県, 2) (愛知県, 2) (千葉県, 2) (⾼知県, 1) (⼤阪府, 1)

Slide 58

Slide 58 text

Spark実例 (PMI計算) 57

Slide 59

Slide 59 text

テキストを読んで頻度カウント 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¥toffers alabama¥thome wedding¥tgreek evil¥tdead :

Slide 60

Slide 60 text

何をしているか 59 combination offers alabama home wedding greek evil 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

Slide 61

Slide 61 text

頻度をつなぎあわせていく 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) = log P(a, b) P(a) P(b) (aの⽂字列, bの⽂字列, [a, b]の頻度, aの頻度, bの頻度) という組み合わせ(タプル)が欲しい

Slide 62

Slide 62 text

何をしているか 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

Slide 63

Slide 63 text

何をしているか 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

Slide 64

Slide 64 text

計算する 63 val instancesAll = tuples.count def calcDiscountPmi(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) => (calcDiscountPmi(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変換

Slide 65

Slide 65 text

無駄な処理をしているのでは? p その通り。aの頻度とbの頻度が メモリにのりきるなら Map(=辞書)として具体化した⽅が速い p ただしこれはスケールアウトしない 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)) }

Slide 66

Slide 66 text

まとめ p Sparkでもちゃんとスケールアウトする ように書くのはコツが要る p それでも並列処理を書くコストは ずっと⼩さくなっている p Sparkに求める事を意識して書き分ける p 並列処理を簡単に書きたい のか p ⼤規模データでもスケールアウトさせたい のか 65

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

補遺 67

Slide 69

Slide 69 text

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 Sheet statement result in the REPL 補⾜

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

HDFSのはなし 70

Slide 72

Slide 72 text

HDFSとは? p Hadoop Distributed File System p 分散仮想ファイルシステム p 普通のファイルシステムとして マウントできない(実⽤レベルでは) 71

Slide 73

Slide 73 text

HDFSクラスタ node7 HDFS 72 node1 node2 node3 node4 node5 node6 node8 300MB の ファイル 登録

Slide 74

Slide 74 text

HDFSクラスタ node7 HDFS 73 node1 node2 node3 node4 node5 node6 node8 block3 block1 block2 登録 128MBごとの 固まりに 分ける

Slide 75

Slide 75 text

HDFSクラスタ node7 HDFS 74 node1 node2 node3 node4 node5 node6 node8 block3 block1 block2 登録 block1 block1 block1 block2 block2 block2 block3 block3 block3 3重に複製されてクラスタに保管される

Slide 76

Slide 76 text

HDFS p なぜ128MBごとに分割するか? p MapReduceではデータが分散されている必要が ある p MapReduceでは各ワーカーの処理がメモリーに 乗りきる必要がある p なぜ複製を作るのか? p 障害耐性が⾼くなる p 分散処理の効率がよくなる (データの移動が少なくなる) 75 Sparkでも同じ!

Slide 77

Slide 77 text

Hadoop-HDFSで 使うコマンド 76

Slide 78

Slide 78 text

よくつかうコマンド p 最近流⾏りの コマンド サブコマンド オプション の形をしている。 p hdfs コマンド p ファイルシステムに何かする 77

Slide 79

Slide 79 text

よくつかうコマンド p hdfs dfs 〜 p hdfsの操作 p hdfs dfs –ls 〜 p hdfs dfs –rm 〜 p hdfs dfs –rm –r 〜 p hdfs dfs –du –s –h 〜 p hdfs dfs –mkdir 〜 p hdfs dfs –cp 〜 〜 p hdfs dfs –mv 〜 〜 78

Slide 80

Slide 80 text

よくつかうコマンド p hdfs dfs –put ローカルエントリ hdfsエントリ p HDFSにファイルを送る p hdfs dfs –get hdfsエントリ ローカルエントリ p hdfsからファイルを持ってくる 79

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

MapReduceの様⼦ 81

Slide 83

Slide 83 text

MapReduce 82 あらかじめ データは分散されて 置かれている がノード データ

Slide 84

Slide 84 text

MapReduce : Map & Partition 83 分割する (ハッシュの剰余 などを使う)

Slide 85

Slide 85 text

MapReduce : Shuffle & Sort 84 集める

Slide 86

Slide 86 text

MapReduce : Reduce 85 ノードことに 処理する

Slide 87

Slide 87 text

MapReduceの様⼦ もう少しうまくやる 86

Slide 88

Slide 88 text

MapReduce 87

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

MapReduce : Shuffle & Sort 89 剰余が同じデータを 同じノードに集める

Slide 91

Slide 91 text

MapReduce : Reduce 90 処理する

Slide 92

Slide 92 text

No content

Slide 93

Slide 93 text

並⾏処理について 92

Slide 94

Slide 94 text

並⾏処理について 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

Slide 95

Slide 95 text

b partiion 0 a partiion 1 partiion 0 RDD 94 0 1 2 3 4 5 0 1 2 3 4 5

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

b partition 0 b.reduce(sub) p 順番で結果が変わるようなreduceを 並⾏・並列で実⾏してはいけない 96 0 1 2 3 4 5 (((((0 - 1) - 2) - 3) - 4) - 5) = reduce -15