Your SlideShare is downloading. ×
Apache Spark チュートリアル
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Apache Spark チュートリアル

210

Published on

2015-4-28に東北大学 乾・岡崎研究室でおこなったチュートリアルの資料です。 …

2015-4-28に東北大学 乾・岡崎研究室でおこなったチュートリアルの資料です。
研究室にはHadoopクラスタ(CDH5.3)があります。実験や実験の前処理といったタスクでそのクラスタを利用するためのチュートリアル、という前提です。

Published in: Software
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
210
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • ちょっとどこかを変えたらどこかが変わる、みたいな習熟が難しい
  • 3億7千万行 pmi計算
    30分 24並列
    12分 77並列
  • 昔はhadoopコマンドで全部できた。今は分離している。
  • hadoop fs が deprecated にされた?
  • Transcript

    • 1. Apache Spark チュートリアル 東北大学 乾・岡崎研究室 山口健史 2015-04-28
    • 2. MapReduceのはなし 1
    • 3. 単語の頻度カウントを例に 2
    • 4. 素朴な実装 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)
    • 5. 巨大なファイルだと…… 4 : for line in opened_file: for word in some_splitter(line): frequency[word] += 1 : 巨大な ファイル メモリが 足りない!!
    • 6. 入力を分割する 5 頻度 ファイル ファイル 頻度 同じ単語が両方のファイルに入ってるので 統合するときになにか工夫しないとやっぱり メモリが足りない
    • 7. 出力を分割する 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 もう一度読む 残り半分
    • 8. 組み合わせる 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 並列可能
    • 9. これだとスケールアウトする  並列に実行できるようになっている  ファイル読み込みの部分が並列実行可能  それでもメモリが足りなくなるなら 分割数を増やせばいい  100〜10,000分割でも動くアルゴリズム 8
    • 10. でもHadoopは簡単じゃなかった  簡単なことにたくさんソースを書く  Hadoopの仕組みを知らないと作れない  たいていのフレームワークはそうだけど…… 9
    • 11. でも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 個人の感想です
    • 12. Apache Spark のはなし 11
    • 13. Sparkとは  Hadoop MapReduceにインスパイア された分散処理基盤  Hadoop-HDFSのような分散ファイルシステムを ベースとして利用する  Scala製。PythonやJavaでも使える 12
    • 14. Sparkのうれしいところ  ScalaやPythonのコードが MapReduceのように動く  SparkやHDFSがなにをしてくれるかは知らなく ても書ける(理解しておいた方はよい)  途中のデータをメモリに保持できる  Sparkが高速と言われる理由  Hadoop-MapReduceは必ずディスク保存だった 13
    • 15. Sparkは簡単  例)タブ区切りの5カラム目を切り出し ”拡散希望”が含まれるものを抽出  これで分散処理される 14 (Python) sc.textFile(u'tweets.txt'). map(lambda x: x.split('t')[4]). filter(lambda x: u'拡散希望' in x)
    • 16. Sparkは簡単  例)タブ区切りの5カラム目を切り出し ”拡散希望”が含まれるものを抽出  これで分散処理される 15 (Scala) sc.textFile("tweets.txt"). map(_.split("t")(4)). filter(_.contains("拡散希望"))
    • 17. 研究室の環境について  研究室のマシンではSparkクラスタが 常時起動しているわけではない  Hadoop-YARNにリソースを要求して 必要な時にクラスタを起動する 16
    • 18. クラスタの起動と解消  要求/確保したリソースでクラスタを 構成する executor(並列処理のワーカー)6個分の メモリを要求している  spark-shellの終了がクラスタの解消  終了しないとリソースを確保した状態のまま 17 spark-shell --master yarn-client --num-executors 6
    • 19. Sparkのコンポーネント 18 node00 node01 node02 node03 node04 node05 node20 driver executor executor master executor executor executor executor executor executor executor executor executor (shell実行モードの場合)
    • 20. 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に分散され共有される)
    • 21. executor 20 node00 node01 node02 node03 node04 node05 node20 driver executor executor master executor executor executor executor executor executor executor executor executor 実際にジョブを実行している部分
    • 22. 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とやりとりしている(?)
    • 23. Sparkを体験する 22
    • 24. かんたんSpark体験  ローカルで2つのexecutorで実行する 23 #Mac Homebrewで brew install apache-spark #Scalaシェル spark-shell --master "local[2]" #Pythonシェル pyspark --master "local[2]"
    • 25. WebUI 24 http://localhost:4040/jobs/WebUI
    • 26. かんたん並行処理  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
    • 27. かんたん並行処理  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
    • 28. SparkContext  scはspark-shell起動時に生成される SparkContext型のインスタンス  SparkContextはジョブの実行状態や 参加しているexecutorの状態を持つ 27 a = sc.parallelize(xrange(1000000000), 2)
    • 29. RDD "Resilient Distributed Dataset"  分散データを扱う抽象コレクション  データの実体がどこにあるのかを意識しない  並列動作する変換操作(map, filter,,)  キャッシュの選択肢  メモリにキャッシュ, ディスクにキャッシュ 28
    • 30. 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のメモリ上で インスタンス化している という意味
    • 31. 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 での比較
    • 32. 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から具体的なデータへ変換する (ローカルコレクション, 値, ファイル)
    • 33. 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つの値からコレクションを返す」関数を元に複数データを生成
    • 34. 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)]
    • 35. 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"]))] 相当
    • 36. 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にはない
    • 37. RDD : 資料  http://spark.apache.org/docs/latest/api/scala/index.html#or g.apache.spark.rdd.RDD  http://spark.apache.org/docs/latest/api/python/pyspark.ht ml#pyspark.RDD  http://www.ne.jp/asahi/hishidama/home/tech/scala/spark/R DD.html 36 (mezcalのSparkバージョンに合わせて1.2.0へのリンク)
    • 38. RDDと並列可能性 37
    • 39. 並列コレクション 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 パーティション
    • 40. 並列コレクション 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つの場合)
    • 41. 分割可能なファイル  ローカリティを考慮してなるべくデータを保持するノードで処理する 40 (executorが3つの場合) (Python) sc.textFile("/work/test/data.txt") # 128MB未満 sc.count() # 1並列で処理される(HDFS上のファイルの場合) node1 node2 node3 executor executor executor data.txt RDD
    • 42. 分割可能なファイル  HDFSはファイルを128MBごとに 分割して分散させている(研究室の設定では)  プレーンテキストとbzip2ファイルが 分割可能なファイル形式  分割可能なファイルはブロック単位に並列分散 処理される 41
    • 43. 分割可能なファイル 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
    • 44. 分割可能なファイル  128MBの境界で文が分かれてしまわな いの?  心配いらない 改行をまたぐ部分がネットワークを移動して 文単位で処理される 43
    • 45. 分割可能なファイル  境界をまたぐ分が移動してから処理が始まる 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
    • 46. 分割可能でないファイル  分割可能でないファイル  例えばgzip圧縮ファイルとか  128MBを超えるファイルは後ろのブロックが すべてネットワーク越しにコピーされてから 処理が始まる=分散されない!!  128MBを超えないぐらいの大きさに抑える  トータルのパフォーマンスはこの置き方が一番いい 45
    • 47. 分割可能でないファイル 46 node1 node2 node3 executor executor executor data.gz (block1) data.gz (block2) data.gz (block3) HDFS Spark 300MBのgzipファイルがあって こんな風に分散されているとすると
    • 48. 分割不可能なファイル 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
    • 49. 分割可能でないファイル  この形が一番効率がいい 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
    • 50. Spark実例 (頻度カウント再び) 49
    • 51. 変換の連鎖  ここまで実行してもまだ テキストを読みこんでない (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江川崎t41t2013-08-12 埼玉県t熊谷t40.9t2007-08-16 岐阜県t多治見t40.9t2007-08-16 山形県t山形t40.8t1933-07-25 山梨県t甲府t40.7t2013-08-10 :
    • 52. アクション  いつの間にか並行処理されている 51 (続き) col1.count() 24 # ここではじめてファイルを読みこんで # split->filter->map->count が実行される col1.saveAsTextFile("col1") # またファイルを最初から読みこんで # split->filter->map->saveAsTextFile が実行される # これでcol1というテキストが作成される……かと思いきや # col1というディレクトリができている # ローカル実行だと2ファイルある(part-00000 part-00001) かもしれないし、されないかもしれない
    • 53. キャッシュの制御 52 (続き) # 毎回ファイルを読みこむのは効率が悪い col1.cache() # これで可能ならばメモリに保持するようになった col1.count() 24 # またファイルから読んでsplit->filter->map->count col1.distinct().count() 12 # 異なり数を数える(100本ノック17) col1.take(2) [u'u9ad8u77e5u770c', u'u57fcu7389u770c'] ここではもう ファイルを 読んでいない
    • 54. もうちょっと変換 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変換定義のみで出力はない 昇順 降順
    • 55. 何をしているか(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
    • 56. もうちょっとアクション 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個
    • 57. もうちょっとアクション 56 (続き) for data in frequency.take(10): print u'({}, {})'.format(*data) (埼玉県, 3) (山形県, 3) (山梨県, 3) (群馬県, 3) (静岡県, 2) (岐阜県, 2) (愛知県, 2) (千葉県, 2) (高知県, 1) (大阪府, 1)
    • 58. Spark実例 (PMI計算) 57
    • 59. テキストを読んで頻度カウント 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 です combinationtoffers alabamathome weddingtgreek eviltdead :
    • 60. 何をしているか 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
    • 61. 頻度をつなぎあわせていく 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の頻度) という組み合わせ(タプル)が欲しい
    • 62. 何をしているか 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
    • 63. 何をしているか 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
    • 64. 計算する 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変換
    • 65. 無駄な処理をしているのでは?  その通り。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)) }
    • 66. まとめ  Sparkでもちゃんとスケールアウトする ように書くのはコツが要る  それでも並列処理を書くコストは ずっと小さくなっている  Sparkに求める事を意識して書き分ける  並列処理を簡単に書きたい のか  大規模データでもスケールアウトさせたい のか 65
    • 67. 補遺 67
    • 68. 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 補足
    • 69. HDFSのはなし 70
    • 70. HDFSとは?  Hadoop Distributed File System  分散仮想ファイルシステム  普通のファイルシステムとして マウントできない(実用レベルでは) 71
    • 71. HDFSクラスタ node7 HDFS 72 node1 node2 node3 node4 node5 node6 node8 300MB の ファイル 登録
    • 72. HDFSクラスタ node7 HDFS 73 node1 node2 node3 node4 node5 node6 node8 block3 block1 block2 登録 128MBごとの 固まりに 分ける
    • 73. HDFSクラスタ node7 HDFS 74 node1 node2 node3 node4 node5 node6 node8 block3 block1 block2 登録 block1 block1 block1block2 block2 block2 block3 block3 block3 3重に複製されてクラスタに保管される
    • 74. HDFS  なぜ128MBごとに分割するか?  MapReduceではデータが分散されている必要が ある  MapReduceでは各ワーカーの処理がメモリーに 乗りきる必要がある  なぜ複製を作るのか?  障害耐性が高くなる  分散処理の効率がよくなる (データの移動が少なくなる) 75 Sparkでも同じ!
    • 75. Hadoop-HDFSで 使うコマンド 76
    • 76. よくつかうコマンド  最近流行りの コマンド サブコマンド オプション の形をしている。  hdfs コマンド  ファイルシステムに何かする 77
    • 77. よくつかうコマンド  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
    • 78. よくつかうコマンド  hdfs dfs –put ローカルエントリ hdfsエントリ  HDFSにファイルを送る  hdfs dfs –get hdfsエントリ ローカルエントリ  hdfsからファイルを持ってくる 79
    • 79. MapReduceの様子 81
    • 80. MapReduce 82 あらかじめ データは分散されて 置かれている がノード データ
    • 81. MapReduce : Map & Partition 83 分割する (ハッシュの剰余 などを使う)
    • 82. MapReduce : Shuffle & Sort 84 集める
    • 83. MapReduce : Reduce 85 ノードことに 処理する
    • 84. MapReduceの様子 もう少しうまくやる 86
    • 85. MapReduce 87
    • 86. MapReduce : Combine 88 処理と分割 (ハッシュの剰余 などを使う)
    • 87. MapReduce : Shuffle & Sort 89 剰余が同じデータを 同じノードに集める
    • 88. MapReduce : Reduce 90 処理する
    • 89. 並行処理について 92
    • 90. 並行処理について 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
    • 91. b partiion 0 a partiion 1 partiion 0 RDD 94 0 1 2 3 4 5 0 1 2 3 4 5
    • 92. 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
    • 93. b partition 0 b.reduce(sub)  順番で結果が変わるようなreduceを 並行・並列で実行してはいけない 96 0 1 2 3 4 5 (((((0 - 1) - 2) - 3) - 4) - 5) = reduce -15

    ×